c++ interop in the library (just for fun)

Adam D. Ruppe destructionator at gmail.com
Wed May 29 18:05:28 PDT 2013


So now that there's a pragma(mangle), we can start hacking this 
up! I'm using Linux g++ 32 bit.

C++:

class Class {
public:
         Class() {}// printf("constructed\n"); }
         virtual void addNum(int a) { num += a; }
         virtual void addNum2(int a) { num2 += a; }

         void talk() { printf("%d %d\n", num, num2); }
         int num;
         int num2;
};


D:

         struct Class {
                 extern(C++) interface Vtable {
                         void addNum(int num);
                         void addNum2(int num2);
                 }
                 mixin CPlusPlusObject!(Vtable, typeof(this));

                 void cppCtor();

                 extern(C++) void talk();
                 int num;
                 int num2;
         }

magic D:


         string cppMangleCtor(alias Func)() {
                 import std.conv;
                 string m = "_ZN";
                 string cn = __traits(parent, Func).stringof;
                 m ~= to!string(cn.length) ~ cn;
                 m ~= "C2Ev"; // fixme: the actual argument list
                 return m;
         }

         string CppCtors(T)() {
                 string code;

                 foreach(member; __traits(allMembers, T)) {
                         static if(member == "cppCtor") {
                                 code ~= "pragma(mangle, 
`"~cppMangleCtor!(__traits(getMember, T, member))~"`) static 
extern(C++) void cppConstructor("~T.    stringof~"*);";
                                 code ~= "\n";
                                 code ~= "static " ~ T.stringof ~ 
" opCall() {
                                         auto c = 
"~T.stringof~".init;
                                         cppConstructor(&c);
                                         return c;
                                 }";
                         }
                 }
                 return code;
         }

         mixin template CPlusPlusObject(Virtuals, Main) {
                 void* virts;
                 Virtuals getVirtuals() { return cast(Virtuals) 
&this; }
                 alias getVirtuals this;

                 @disable this();

                 mixin(CppCtors!Main());
                 pragma(msg, CppCtors!Main());
         }



Test functions, D:

extern(C++)  void doit(Class*);
extern(C++)  void whoa(Class* c) {
                 import core.stdc.stdio;
                 printf("whoa\n");
                 c.num += (30);
                 c.talk();

                 c.addNum(10);
         }


void main() {
         import std.stdio;
         writeln("MOVING ON");
         auto c = Class.opCall();
         doit(&c);
         writeln("All Done!");
}


C++:


extern void whoa(Class* c);

void doit(Class* ptr) {
         if(ptr == NULL)
                 ptr = new Class2();

         ptr->num = 10;
         ptr->num2 = 20;

         ptr->talk();
         whoa(ptr);
         ptr->talk();
}


Output:

$ ./testcpp
MOVING ON
constructed
10 20
whoa
40 20
50 20
All Done!



No crash! Now, normally, I'd say we should probably construct the 
objects in their native language, but I just had to try this and 
the fact that it worked I thought was pretty cool.

Here we see access to both virtual and non-virtual member 
functions of the C++ object from D, as well as direct access to 
the member variables! Liable to break? Oh yeah. Hacky? 
Definitely. But I thought this was kinda cool.

Inheritance can work too, but there's still a ways to go to make 
that right. (It works easily enough if you just access virtuals 
though, D's plain extern(C++) interface can do that.)



Maybe if we were to see this through it could be a good enough 
hack for some real world use too.


More information about the Digitalmars-d mailing list