Difference (last change)
(no other diffs,
normal page display)
Deleted: 399,509d398
contributions needed!
This page discusses how to wrap C++ libraries so that they can be called from D.
The basic idea is to
- Create a C API that mirrors the C++ API, and then
- Port the resulting C headers to D, making a low-level C-like D API, and finally
- 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