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.

Reply via email to