Last update March 7, 2012

Porting From Cxx



Difference (last change) (no other diffs, normal page display)

Deleted: 391,501d390
jam tangan
jam tangan murah
jam tangan kw
hostgator coupon
kata mutiara
Jasa SEO
EZido
RDAnet
pioneer deh-1300mp
asus a53e-xa2
asus tf101-b1
asus tf101-a1
asus n53sv-eh72
asus republic of gamers g74sx
asus acer a5250
acer chromebook ac700
asus asus 53u
lg infinia 55lw5600
Sonicview 360 premier
asus 7 cu ft freezer
asus 30 single wall oven
brother cs6000i sewing machine
brother 1034d serger
brother sewing machines
Yokohama Geolandar H/T-S
crib tent tots in mind
kidco peapod plus
foscam fi8910w
samsung pl120 review
gopro helmet cam
Canon SX130IS
powershot s100
ContourHD 1080p
canon vixia hf r21
digital picture frame
canon ef 50mm f1.4
canon ef 70-300mm review
wide angle lenses
moving comfort sports bra
moving comfort bra
womens argyle sweater
bebe dresses
ViewSonic VX2250WM
Le Pan TC 970
Apple MacBook Air MC965LL
Sennheiser CX 880
plantronics cs540
ultrasonic jewelry cleaner
Sennheiser RS120
bose quietcomfort 15 acoustic noise cancelling headphones
logitech harmony one remote
logitech harmony 900
sony mhc-ec69i
sony mhcec909ip
bose wave music system
sony htss380
logitech squeezebox touch
sony dvp-fx970
onkyo tx-nr509
onkyo tx - nr609
onkyo ht-s3400
energy 5.1 take classic home theater system
polk audio psw505
onkyo ht-s5400
onkyo tx-nr709
belkin pf60
onkyo ht-rc360
denon avr-1912
Yamaha YHT-S400BL
fujitsu scansnap s1500
brother hl-2270dw
epson workforce 545
hp laserjet p2055dn
bushnell 8mp trophy cam
toshiba 32c110u
panasonic viera tc-p60s30
VIZIO E220VA
hauppauge wintv dcr-2650
Acer AM3970-U5022
Acer AspireRevo AR3700-U3002
Dell Inspiron i570
Dell GX620
Gateway FX6860-UR20P
Western Digital My Passport Essential SE 1 TB USB 3.0
Fujitsu ScanSnap S1300
Epson Perfection V300
Fujitsu SCANSNAP S1100
NeatDesk Desktop Scanner and Digital Filing System
Epson WorkForce Pro GT-S50
Kodak P811BK
Epson Perfection V330
Viewsonic VX2453MH
Asus VE228H
ViewSonic VA2431WM
Samsung B2230
HP 2711x
ASUS ML228H
Epson PowerLite Home Cinema 8350
Optoma PK301
Epson EX7210
Epson EX5210
ViewSonic PJD5133
Acer X1161P
FAVI RioHD-LED-2
Epson EX3210
ViewSonic PJD6531w
Trinity 360 Breville 800JEXL
Skil 3320-02
Delta 46-460
Grizzly G0555
Delta 18-900L

D is a better C++. Or at least becoming a better C++ was one of Walter's primary motivations. So it makes sense that there would be significant interest in porting C++ code to D. This page discusses the various porting options available, issues that arise, and how to work around them.

Table of contents of this page
To Port or To Wrap?   
Overview of the Issues   
General Porting Suggestions   
Work from the bottom up   
Write unittests   
Detailed Porting Suggestions   
Easy Mechanical Stuff   
Constructors   
Class vs Struct   
C++ virtual method / D override   
Struct inheritance   
Multiple inheritance   
Macros   
Reference return values   
Reference in general   
Const   
friend   
Iterators   
Smart pointers   
Template Specialization   
Namespaces   
IOStreams   
STL   
Operator overloads   
Implicit casts   
Non-member operator overloads   
String and Char   
Windows-specifc Issues   
Posix-specifc Issues   
Tools   
Related Topics   

To Port or To Wrap?    

D cannot directly call C++ code, unlike the situation with C. Thus it is necessary to either wrap C++ code in a C API that D can call, or do a full source port translating every line of C++ code into native D.
  • Porting
    • pro - the result is 100% D, making building and using from D easy.
    • pro - future developments are independent from C++
    • con - much more work
    • con - more difficult to keep up to date with changes to the orignal C++ library
  • Wrapping
    • pro - less work
    • pro - easier to keep up-to-date with upstream changes to the C++
    • con - the result is a hybrid that requires first building the C++ code before
    • con - some functionality may be lost (templates are difficult to wrap in a C API)
This page is actually about porting C++. For wrapping see the Wrapping C++ page.

Overview of the Issues    

This section gives a brief introduction to the types of things you'll encounter in C++ code that may cause you trouble when porting to D. This section doesn't provide any solutions, it just introduces the issues.
  • Constructors - They look and behave slightly differently in C++ than in D.
  • Class vs Struct - In C++ they're really the same, so it's difficult to determine which one you should use when translating to D.
  • Struct inheritance - In C++ structs can inherit from other structs. So C++ code that uses classes or structs as value types can be difficult to port.
  • Multiple inheritance - Some C++ code uses multiple inheritance. D has interfaces instead.
  • Macros - C++ has C's macros which can be used to do some pretty evil trickery.
  • Reference return values - C++ functions can return references.
  • Const - C++ has const.
  • friend - C++'s 'friend' keyword can give private-level access to any class desired.
  • Iterators - Iterators are very commonly used in C++ code, but the standard idioms rely on operator overloading tricks that D doesn't provide. Furthermore there's the issue of how these integrate with the canonical D approach of foreach/opApply.
  • Smart pointers - With operator overloading, deterministic destruction, and copy constructors, C++ is able to implement smart pointers that do things like reference counted memory management. D can't do that.
  • Template Specialization - C++ allows specializations of a template to appear in different source files. C++ also allows specializations to be selected via IFTI, which D has trouble with.
  • Namespaces - In C++ you create namespaces as you like and where you like. They aren't intrinsically linked to header files in any way, though good C++ code will usually follow a single namespace per header file.
  • IOStreams - Both phobos and Tango have stream based io that can stand in for C++'s iostreams in most cases. But one common idiom used for streaming objects is to put an overload of a particular function like 'write_object(ostream&,MyObject&)' into each class's header file. In porting to D you can run into issues with D's lack of argument-dependent lookup (ADL).
  • STL - C++ code often makes use of STL containers. Some of these containers have equivalents in D, some do not.
  • Operator overloads - All operator overloads have to be translated from their C++ form, operator blah to the more D-ish form opBlah. But not all C++ operators have a D equivalent. C++ allows overloading of individual comparison operators, the dereference operator (operator*), the member access operator (operator->), and casting operators (operator float(), eg)
  • Implicit casts - C++ allows classes to define implicit casts so that an instance of class 'C' can automatically be treated as, say, a 'float'.
  • Non-member operator overloads - C++ allows you to define operator overloads outside of a class.
  • String vs char - D has many string types, and C++ has several too.

General Porting Suggestions    

Work from the bottom up    

Find the parts of the code that have the fewest dependencies and start with porting those first.

Write unittests    

For every module you port, write some basic unittests. Make sure you can at least instantiate the main objects of the module, etc. Test as much as you can. At the very least make sure every file compiles.

Detailed Porting Suggestions    

Easy Mechanical Stuff    

A lot of the work of porting can be done in a mostly mechanical way. A good first pass at translating involves
  • Merge the source file foo.cpp into its corresponding header, foo.hpp.
  • Translate all #includes to imports.
  • Global replace '->' with '.'
  • Global replace '::' with '.'
  • Global replace 'typedef' with 'alias'
  • Global replace 'const' with '/*const*/' (or just remove it, but leaving it there serves as a documentation of intent and may be helpful with porting to const-ful D2.0 someday)
  • Delete all inline keywords (f.e. 'inline', '__inline', '__forceinline')
  • Change all class constructors from ClassName() to this(), destructors to ~this()
  • Replace all function parameters of the form "Foo& x" with "ref Foo x"
  • Replace all occurrences of "template<typename T> symbol" and "template<class T> symbol" with "symbol(T)", where symbol represents a function name, "class classname", or "struct structname"
  • Replace all template usages like templatename<T> with templatename!(T)
  • Replace dynamic_cast<T> with cast(T)
  • Replace reinterpret_cast<T> with 'cast(T)cast(void*)'
  • Replace "var == NULL" and "var != NULL" with "var is null" and "var !is null" -- In D, equality (== and !=) and identity (is and !is) are two separate operations. Equality can be overloaded with the opEquals operator overload. If you try to do "var == null" and var really is null, you'll just get an access violation as it tries to look up the opEquals method in var. Identity cannot be overloaded. Identity can be used with class references, array references (in which case it checks that the two references have the same pointer and length), and pointers. It can actually be used with any type; for non-reference types, identity is the same as equality.
  • Delete forward referenced function declarations.
A note on casting class pointers: in C++, if you have a class pointer 'a', and want to cast it to a pointer to another kind of class, all you have to do is write "(Other*)a". Translated directly to D, this would become "cast(Other)a". However, the default behavior for casting class references in D is to do a dynamic cast (like C++'s dynamic_cast<Other*> ) to assure that the reference you're casting really can be casted to the type that you're casting to. Chances are, if this is a cross-hierarchy cast (rather than a downcast), this cast will fail in D, giving 'null'. In order to get the C++-like behavior, use "cast(Other)cast(void*)a". That extra cast to a void pointer in there will circumvent the runtime type checking. For casts of non-class types, however, most of the time cast(Type) means the same as the equivalent C++ cast. (A notable exception is when you cast between array types, such as casting an "int[] x" to a float[]. In this case, the size of the array will be checked at runtime to assure that the length of the source array is an even multiple of the size of the elements of the new type, and the resulting array will have its length set to (potentially) a new size, to match the number of new elements.)

Constructors    

Probably it's easiest to just give an example. It's generally pretty clear what to do if you know C++ and D reasonably well.

C++

class MyClass : public BaseClass
{
public:
   int inum_;
   float fnum_;
   char[] str_;
   MyClass(int inum, int fnum, int str) : BaseClass(str),
       inum_(inum),fnum_(fnum),str_(str) 
   { 
      /* extra statements */
   }
}

As a D class:

class MyClass : BaseClass
{
   int inum_;
   float fnum_;
   char[] str_;
   this(int inum, int fnum, int str)
   { 
      super(str);
      inum_ = inum; 
      fnum_ = fnum;
      str_ = str;
      /* extra statements */
   }
}

As a D struct (assuming the C++ had no base class):

struct MyClass
{
   int inum_;
   float fnum_;
   char[] str_;
   static MyClass opCall(int inum, int fnum, int str)
   {
      MyClass ret; with (ret) {
      inum_ = inum; 
      fnum_ = fnum;
      str_ = str;
      /* extra statements */
      } return ret;
   }
}

todo: more detail about where to put the call to super()

Class vs Struct    

For every class or struct you run across in the C++ code you're going to have to decide whether it should be a class or struct in D. The only difference between classes and structs in C++ is that structs' members are public by default. So it is possible that something called 'struct' in C++ is a better fit for D's 'class', and vice versa, something called 'class' in C++ may be better off as a D 'struct'.

That said, choosing class is pretty much always a 'safe' choice. Almost any C++ class or struct can be made to work as a D class. But there are a few situations where you're better off going for a struct, mostly for either performance or convenience.

Here are some signs to look for when deciding whether or not to use a D struct.

  • Does it inherit? Then it probably has to be a D class.
  • Does it have methods declared virtual? Then it probably has to be a D class.
  • Is it used mostly as a value type? In other words, is it rare to see instances created with 'new'? Then may be good as a struct.
  • Does it have little or no data? Then it might make a good struct.
  • Are instances frequently copied? Then it might make a good struct.
Inheritance is usually a very strong indicator that it should be a D class. However, the presence of inheritance does not always mean struct is the wrong choice. If the thing seems overall more like a value-type, it may be using inheritance not for polymorphism, but to share common code. In that case you may be able to replace the inheritance with a template mixin.

Virtual methods, on the other hand, are pretty much a 100% tell-tale sign that you should use a D class.

C++ virtual method / D override    

todo

Struct inheritance    

todo

Multiple inheritance    

Multiple inheritance (MI) will usually be replaced with interfaces in D. Many uses of MI can be emulated in D using interfaces in combination with template mixins. The interface provides the is-a relationship and the mixin provides the implementation of that interface.

Macros    

todo

Reference return values    

todo

Reference in general    

// C++
int value = 100;
int & rIntRef = value;
if (rIntRef == 100)
  // be ashured my val is 100
rIntref = 101
if (&rIntref == &value)
  // Yes we have the same address

// D

int value = 100;
alias value rIntRef;

if (rIntRef == 100)
  // be ashured my val is 100
rIntRef = 101;

assert(value == 101);
assert(&value == &rIntRef); // compare address

Please note : Alias parameters also work in functions wrapped inside templates. Contributed by Jari-Matti? Mäkelä

Const    

todo

friend    

In D, friend access is implicit in being a member of the same module. So a simple solution is to place related classes into the same module.

Iterators    

todo

Smart pointers    

Smart pointers get used for many things in C++, but probably most often for reference-counted memory management, as with the boost::shared_ptr template. Another common one is
  • Reference counted pointers - (e.g. 'boost::shared_ptr') Most of the time these are simply a work-around for lack of real garbage collection in the language. If this is the case then you can just eliminate the shared_ptr layer completely.
  • RAII pointers - (e.g. 'std::auto_ptr') RAII pointers are intended to be initialized once, and after that their only job is to make sure the object created gets destroyed on scope exit, no matter how it happens. In D this can be accomplished using either a scope variable, or the statement 'scope(exit) delete foo;' See Stack allocation of classes, Scope guard statement, and Scope attribute.
  • Reference-counted RAII resource - this is pretty much the same thing as 'Reference counted pointers' above, but it's a usage case where the pointer is wrapping a shared resource that _must_ be destroyed deterministically and immediately once the refcount is zero. In this case your only recourse in D is the error-prone method of explicit reference counting. That is add 'acquire' and 'release' methods to the object and require users to call them explicitly.

Template Specialization    

todo

Namespaces    

D puts every module into its own namespace. In C++ namespaces are more a voluntary thing and often will spread across many different modules.

Package namespaces For package namespaces, the proper approach is often to turn the C++ namespace into a package (directory) in D. For example if you were porting the C++ standard library to D you wouldn't try to put everything into one module called 'std'. Instead you'd make a package directory called 'std', and put all the modules underneath it. Any elements meant to be common to members of the package can go in their own module like std.common or std.base. To get the equivalent of having the entire namespace under one D namespace, you can write a "std.all" or "std.api" module that does nothing besides "public imports" of the external API portions of the package.

Grouping namespaces Not every namespace in C++ that spans modules maps cleanly to a package. Sometimes they're used to group a related subset of functionality. In D this can be accomplished by grouping those modules into a module that does only public imports, similar to the "all.d" or "api.d" idea, but for a subset of modules that you want to be able to group under one namespace.

Anonymous namespaces Anonymous namespaces are the C++ version of C's static declarations. It means "this stuff is only visible from within this file". The closest thing in D is probably to replace 'namespace {...}' with 'private{...}'.

A nice trick in D for creating local namespaces that combine items from several modules under one name is to use a mixin:

template StdLib()
{
   import std.string;
   import std.stdio;
}
mixin StdLib!() stdlib;
...
stdlib.writefln("hi");
string s = stdlib.format("%s, %s", x,y);

IOStreams    

For porting with Phobos, there's std.stream and std.cstream, with din, dout, and derr, that are roughly equivalent to the C++ cin,cout,cerr, e.g.

import std.stdio;
import std.cstream; //derr, dout
...
writefln("%s", "Hello World");
derr.writefln("%s", "Hello World");

or see HowTo/printf for printing non-utf8 (e.g. latin1) strings.

If you aren't using the dark dusty corners of iostreams, it's not hard to write a simple emulation library that will allow quite a bit of C++ iostream code to compile and run without any changes. This will save a fair bit of effort for the initial port.

For porting with Tango, ...

todo: more specifics

STL    

vector D built-in arrays contain a fair bit of the functions of STL vector. The biggest issue is that there is no distinction between logical and physical size (i.e., there is no "capacity"). If you are going to be doing a lot of adding to an array, it is better to create a struct wrapping the array. For example:

struct vector(T) {
  T[] arr;
  size_t lsize;
  void push_back(T t) {
    reserve(++lsize);
    arr[lsize-1] = t;
  }
  void reserve(size_t s) {
    if (s < arr.length) return;
    if (lsize == arr.length)
      arr.length = (s > arr.length * 2) ? s : arr.length * 2;
  }
}

Of course, the rest of the vector interface can be implemented without much trouble either to ease porting.

map and set

D has built-in associative arrays (read hash tables). These will often be suitable replacements plus using them is pretty easy. There are a couple of things to be aware of:

  • Declaration:
C++
map<string,int> mymap;
D
int[string] mymap;

  • There is currently no way to clear a map. Instead:
int[string] mymap;
... code using mymap;
mymap = [];  // frees the old map storage and gives you an empty one to use

  • To emulate a set, use bool[key] and insert elements using myset[key1] = true. STL sets effectively do the same thing.
  • If you need to walk a map's elements in sorted key order, try:
int[string] mymap;
... fill map ...
foreach (string s; mymap.keys.sort) {
  writefln("key=%s val=%d", s, mymap[s]);
}

  • If you need the guaranteed O(log(n)) behavior of map access, you'll have to roll your own
todo: more specifics

Operator overloads    

Most C++ operators have straightforward translations to D.
C++ operatorD operator
operator+(T)opAdd(T)
operator-(T)opSub(T)
operator*(T)opMul(T)
operator/(T)opDiv(T)
operator+=(T)opAddAssign(T)
operator-=(T)opSubAssign(T)
operator*=(T)opMulAssign(T)
operator/=(T)opDivAssign(T)
todo: finish table

A few C++ operators have no D equivalent.

  • Conversion operators - conversion operators like 'operator float()' have no D equivalent right now. Eventually an opImplicitCast is expected to be added, but for now you have to translate this to an explicit conversion function like '.to_float'.
  • Dereference operator 'operator*()' - There's no equivalent to the unary 'operator*()' in D. Use of this is probably seen most often in C++ iterators where '*iter' returns a reference to the item the iterator points to. In D you have to use an explicit property like '.ptr' to return a pointer to the item, or '.val' to return a copy of the value.
  • Arrow operator 'operator->()' - There's also no member access operator in D. Instead use a property like .ptr that returns a pointer. Fortunately in D members can be accessed via pointers in the same way as via a reference or value type, using '.', so you end up with D usage like 'x.ptr.member' vs. C++'s 'x->member'.

Implicit casts    

todo

Non-member operator overloads    

todo

String and Char    

You can generally replace 'char*' and 'std::string' with D's 'char[]'.

Watch out for 'char*' being used as a generic container for binary data. In that case the proper translation is 'ubyte[]'.

In D 2.0, 'string' is separate from char[]. It is an immutable char[], so similar to Java Strings. You can generally use 'string' in place of 'std::string', and use char[] if you need to modify the string in place.

One nice advantage of the D string system is that substrings are just array slices, so they don't allocate new storage. In C++, this depends on the STL implementation.

If you work with std::wstring, you will have to be more careful. The size of wchar_t is not specified in the C++ standard. You'll have to use dchar or wchar accordingly.

For iterating over Unicode chars, it's easy to go between UTF-8 strings and UTF-16 or UTF-32:

string s = "some unicode data";
foreach (wchar w: s) { do something; }

Windows-specifc Issues    

CALLBACK

Let's say we have the following piece of C++ code :

static LRESULT CALLBACK WndProc(HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam)

Due to the fact that CALLBACK is defined as : #define CALLBACK __stdcall Our D translation becomes :

extern (Windows) static LRESULT WndProc(HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam)

TCHAR and _T()

todo

BOOL vs. bool

BOOL is defined as : typedef int BOOL

It means you May NOT simply replace BOOL with bool.

NULL vs. null

NULL is defined as : #define NULL 0

In D we are used to evaluate null as :

 if ( x !is null) // respective if (x is null)

Due to the fact that NULL == 0 we have to leave the following code sequel as it is

if (x != NULL) // respective if (x == NULL) 
NOT VERFIED

Posix-specifc Issues    

todo

Tools    

The same tools exist for porting C++ as for porting C.
  • htod - Walter Bright's tool which can handle some C code -- not actually a C++ aware tool.
  • BCD - Gregor Richard's translation tool based on gcc-xml, which can handle a subset of C++.
  • SWIG - SWIG is a software development tool that connects programs written in C and C++ with a variety of programming languages. SWIG supports D1 and D2. ATM you have to build SWIG from SVN. SWIG 2.02 will have official D support.

Related Topics    


FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: March 7, 2012 20:34 (diff))