views:

210

answers:

4

I followed an excellent article by Rudy Velthuis about using C++ classes in DLL's. Everything was golden, except that I need access to some classes that do not have corresponding factories in the C++ DLL. How can I construct an instance of a class in the DLL? The classes in question are defined as

class __declspec(dllexport) exampleClass
{
public:
  void foo();
};

Now without a factory, I have no clear way of instantiating the class, but I know it can be done, as I have seen SWIG scripts (.i files) that make these classes available to Python. If Python&SWIG can do it, then I presume/hope there is some way to make it happen in Delphi too.

Now I don't know much about SWIG, but it seems like it generates some sort of map for C++ mangled names? Is that anywhere near right? Looking at the exports from the DLL, I suppose I could access functions & constructor/destructor by index or the mangled name directly, but that would be nasty; and would it even work? Even if I can call the constructor, how can I do the equivalent of "new CClass();" in Delphi?

+1  A: 

If python can, it doesn't necessarily mean that any language can.

Python (or any other scripting language) might be able to access C++ code in exactly the same compiler and version it was compiled with.

The litmus test is if the python code can access the class regardless of which it is compiled with (e.g. one python implementation with the same DLL generated with msvc and gcc)

In general, everything with mangled names is compiler and -version specific. So the only way is to compile a testprogram with the compiler you are targeting, disassemble it, find the apporiate runtime calls (for the class type, the constructor and the runtime helpers) and way of calling them, and mimic that with either cdecl functions or asm in Delphi.

Handy people like Rudy might clean that up use pascal functionatlity as much as possible and present it as something that looks easy, but cross-compiler compatibility of HLLs is a difficult (if not impossible) subject).

Marco van de Voort
+1  A: 

It is possible to do it using SWIG. At least is SHOULD be possible. I've written a SWIG module targetting ObjectPascal and I'm using it successfully in my own project. I've traslated GEOS, and GDAL/OGR library. I also have a branch in the SWIG repository, BUT I still have to complete the final steps to build all the test suites and to fixup the typemaps in order to have the module be accepted.

Someone would like to help?

Stefano Moratto [email protected] www.csiat.it

Stefano Moratto
I would definitely like to see that on the SWIG features list. Would be very useful to me and probably many other Delphi developers.
Alan G.
+4  A: 

The correct way to do it is to write a wrapper DLL that exposes a factory for the class you need.

I am not sure hiw SWIG works, but anything that relies on reverse-engineering the name mangling seems a dubious approach.

Besides a C++ object should be created only in C++ code. You ought to leave the object creation semantics to the C++ runtime.

There is a reason why COM exists. Precisely to make this cross language object metaphor work neatly.

I've written scores of COM objects that are called from Delphi, python and C#

rep_movsd
Ah a wrapper DLL in C++. Thanks, that will definitely help. I was expecting a usable solution to be far more complex.
Alan G.
+3  A: 

The way swig works is: 1) It creates a flat API for every class method and to build class factory/destroyer function eg class C { public : C() {...} int M1(int P1) {...} } it produces: C* New_C(); Destroy_C(C*self); int C_M1(C*self,int P1) {}

The flat api has to be compiled in a new DLL

2) it produces for the flat api a unit containing the flat api declaration in pascal 3) the flat pascal api can be optionally used to automatically build a Pascal class eg:

 type TC = class


 private

      FObj : pointer;
public

      constructor Create();
      destructor Destroy(); override;
      function m1(p1:integer: integer;
    ...

  constructor TC.Create();
  begin
     inherited;
     FObj := New_C();
  end;

  destructor TC.Destroy();
  begin
      Destroy_C(FObj);
      inherited;
  end;

  function TC.M1(P1:integer) : integer;
  begin
      Result := C_M1(FObj, P1);
 end;
Stefano Moratto
Very useful summary, thanks. That is exactly what I am seeing in python files from SWIG. I noticed it also seems to produce a .pyd file for python, which I presume is the flat interface DLL you talk about, although its an odd DLL; massive size yet only exports 1 init_interfacename function; the good stuff is probably only usable by Python, because the produced .py files are calling tons of semi-mangled interfacename.xxx methods that sure aren't in the C++ DLL.
Alan G.