Last update March 7, 2012

Wrapping Cxx



contributions needed!

This page discusses how to wrap C++ libraries so that they can be called from D.

The basic idea is to

  1. Create a C API that mirrors the C++ API, and then
  2. Port the resulting C headers to D, making a low-level C-like D API, and finally
  3. Wrap the low-level D API with a more object-oriented D API (optional).
The best example of this 3-step approach is perhaps the wxD project. The fltk4D wrappers appear to take this approach as well.

Wrapping C++ classes

coolstuff.h:
class Class
{
public:
    void *attribute;
    void *method(void *arg0, void *arg1, void *argN);
};

wrapper.cpp:

#include "coolstuff.h"

extern "C"
{
    void *_Class_this()
    {
        return new Class();
    }
    
    void _Class_destroy(Class *classptr)
    {
        delete classptr;
    }
    
    void *_Class_get_attribute(Class *classptr)
    {
        return classptr->attribute;
    }
    
    void _Class_set_attribute(Class *classptr, void *value)
    {
        classptr->attribute = value;
    }
    
    void *_Class_method(Class *classptr, void *arg0, void *arg1, void *argN)
    {
        return classptr->method(arg0, arg1, argN);
    }
}

coolstuff.d:

class Class
{
    void* classptr;
    bool newed;
    
    this()
    {
        this.classptr = _Class_this();
        this.newed = true;
    }
    
    ~this()
    {
        if(newed)
            _Class_destroy(classptr);
    }
    
    this(void* classptr)
    {
        this.classptr = classptr;
    }
    
    void* attribute()
    {
        return _Class_get_attribute(classptr);
    }
    
    void attribute(void* value)
    {
        _Class_set_attribute(classptr, value);
    }
	
    void* method(void* arg0, void* arg1, void* argN)
    {
        return _Class_method(classptr, arg0, arg1, argN);
    }
}

extern (C):
void* _Class_this();
void _Class_destroy(void* classptr);
void *_Class_get_attribute(void *classptr);
void _Class_set_attribute(void *classptr, void *value);
void* _Class_method(void* classptr, void* arg0, void* arg1, void* argN);

Wrapping structs

D's struct is compatible with C's struct, and can thereby be passed through the D and extern C part of the wrapper without change, but C++'s struct isn't compatible with C's struct (unless declared extern C).

So the only way a struct can go is: D->C C->D

Example

vector3f.h:

struct Vector3f
{
    float x, y, z;

    Vector3f(float x, float y, float z)
    {
        this->x = x;
        this->y = y;
        this->z = z;
    }
}

void setPosition(Vector3f newPos);

wrapper.cpp:

#include "vector3f.h"

extern "C"
{
    struct CVector3f
    {
        float x, y, z;
    
        CVector3f(float x, float y, float z)
        {
            this->x = x;
            this->y = y;
            this->z = z;
        }
    }
    
    void _setPosition(CVector3f newPos)
    {
        setPosition(Vector3f(newPos.x, newPos.y, newPos.z));
    }
}

vector3f.d:

struct Vector3f
{
    float x, y, z;
}

void setPosition(Vector3f newPos)
{
    _setPosition(newPos);
}

extern(C):
_setPosition(Vector3f newPos);

Note: As a struct and class in C++ is almost the same thing, the struct in vector3f.h could be changed to a class, without any changes to the wrapper.

Wrapping templated classes and structs

You have to create a D class/struct for each instanced template.

Example

dimension2d.h:

template <class T>
class Dimension2D
{
public:
    T w, h;
};

wrapper.cpp:

#include "dimension2d.h"

extern "C"
{
    //int
    void *_Dimension2D_this()
    {
        return new Dimension2D<int>();
    }
	
    void _Dimension2D_destroy(Dimension2D<int> *classptr)
    {
        delete classptr;
    }
    
    int _Dimension2D_get_w(Dimension2D<int> *classptr)
    {
        return classptr->w;
    }
    
    void _Dimension2D_set_w(Dimension2D<int> *classptr, int value)
    {
        classptr->w = value;
    }
    
    int _Dimension2D_get_h(Dimension2D<int> *classptr)
    {
        return classptr->h;
    }
	
    void _Dimension2D_set_h(Dimension2D<int> *classptr, int value)
    {
        classptr->h = value;
    }
    
    //float
    void *_Dimension2Df_this()
    {
        return new Dimension2D<float>();
    }
	
    void _Dimension2Df_destroy(Dimension2D<float> *classptr)
    {
        delete classptr;
    }
	
    float _Dimension2Df_get_w(Dimension2D<float> *classptr)
    {
        return classptr->w;
    }
    
    void _Dimension2Df_set_w(Dimension2D<float> *classptr, float value)
    {
        classptr->w = value;
    }
    
    float _Dimension2Df_get_h(Dimension2D<float> *classptr)
    {
        return classptr->h;
    }
    
    void _Dimension2Df_set_h(Dimension2D<float> *classptr, float value)
    {
        classptr->h = value;
    }
}

dimension2d.d:

class Dimension2D
{
    void* classptr;
    bool newed;
    
    this()
    {
        this.classptr = _Dimension2D_this();
        this.newed = true;
    }
    
    ~this()
    {
        if(newed)
            _Dimension2D_destroy(classptr);
    }
    
    this(void* classptr)
    {
        this.classptr = classptr;
    }
    
    int w()
    {
        return _Dimension2D_get_w(classptr);
    }
    
    void w(int value)
    {
        _Dimension2D_set_w(classptr, value);
    }
    
    int h()
    {
        return _Dimension2D_get_h(classptr);
    }
    
    void h(int value)
    {
        _Dimension2D_set_h(classptr, value);
    }
}

class Dimension2Df
{
    void* classptr;
    bool newed;
    
    this()
    {
        this.classptr = _Dimension2Df_this();
        this.newed = true;
    }
    
    ~this()
    {
        if(newed)
            _Dimension2Df_destroy(classptr);
    }
	
    this(void* classptr)
    {
        this.classptr = classptr;
    }
    
    float w()
    {
        return _Dimension2Df_get_w(classptr);
    }
    
    void w(float value)
    {
        _Dimension2Df_set_w(classptr, value);
    }
    
    float h()
    {
        return _Dimension2Df_get_h(classptr);
    }
    
    void h(float value)
    {
        _Dimension2Df_set_h(classptr, value);
    }
}

extern(C):
//int
void* _Dimension2D_this();
void _Dimension2D_destroy(void* classptr);
int _Dimension2D_get_w(void* classptr);
void _Dimension2D_set_w(void* classptr, int value);
int _Dimension2D_get_h(void* classptr);
void _Dimension2D_set_h(void* classptr, int value);

//float
void* _Dimension2Df_this();
void _Dimension2Df_destroy(void* classptr);
float _Dimension2Df_get_w(void* classptr);
void _Dimension2Df_set_w(void* classptr, float value);
float _Dimension2Df_get_h(void* classptr);
void _Dimension2Df_set_h(void* classptr, float value);

Argument overloading

You can't use argument overloading in extern C code. An alternative is to use method1, method2, methodN instead.

Example

void draw(int x, int y, Image image);
void draw(int x, int y, Color color);

//becomes
void draw1(int x, int y, Image image);
void draw2(int x, int y, Color color);

Wrapping with tango

You will want to import tango.stdc.stringz for easy C-string handling with toStringz and fromUtf8z.

Compiling and linking

Using rebuild on x86_64

g++ wrapper.cpp -c
rebuild test.d wrapper.o -L-lstdc++ -L-L/usr/lib/gcc/x86_64-pc-linux-gnu/4.2.2/ -full -oq.build

Automation

The described wrapping process could probably be automated by a smallish program.

Collaborations

Many languages are able to make calls directly into C libraries. So there is potential for collaboration when creating the C-wrappers of a large C++ API. For instance the D and C# wrappers for wxWidgets share the same low-level C wrapper of the wxWidgets API.

Tools

bcd.gen http://www.dsource.org/projects/bcd/ - generates C or C++ bindings from .h files


Back to the Porting Overview


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

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