Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling C++ from D

Tags:

c++

d

I have gone through the docs explaining how to call C++ from D explained here: http://dlang.org/cpp_interface.html . There are a however a couple of things that are not quite clear to me.

Taking the example provided on the D website:

#include <iostream>

using namespace std;

class D {
   public:
   virtual int bar(int i, int j, int k)
   {
       cout << "i = " << i << endl;
       cout << "j = " << j << endl;
       cout << "k = " << k << endl;
       return 8;
   }
};

D *getD() {
   D *d = new D();
   return d;
}

The C++ class can then be called from D as shown below:

extern (C++) {
    interface D {
        int bar(int i, int j, int k);
    }

    D getD();
}

void main() {
   D d = getD();
   d.bar(9,10,11);
}

What is not quite clear to me is how the C++ object gets deleted. Does the D garbage collector call delete on the C++ object or do we need to provide a "deleter" function which deletes the object and call it manually from D? It seems to me that if I add a destructor to the C++ class, it is never called. Also I noticed that the C++ class must declare the member functions in the exact same order as they are declared in the D interface (for example if I add a destructor before the bar() method, the C++ object cannot be called from D, but if the destructor is declared after the bar() method, everything works fine).

Also if the D interface is defined as:

extern(C++){
   interface D{
       int bar();
       int foo();
   }
}

And the correspondind C++ class is given by:

class D{
public:
   virtual int bar(){};
   virtual int foo(){};

};

How can you guarantee that the C++ virtual methods vtbl will be created in the same order as the methods declared in the D interface. To me there is no guarantee for this. In other words, how can we be sure that D::bar() will be in the first position in the vtbl? Is this not implementation/compiler dependant?

like image 218
BigONotation Avatar asked Jan 02 '14 22:01

BigONotation


People also ask

How do you call C in C++?

Just declare the C++ function extern "C" (in your C++ code) and call it (from your C or C++ code). For example: // C++ code: extern "C" void f(int);

Why is C not A or B?

Because C comes after B The reason why the language was named “C” by its creator was that it came after B language. Back then, Bell Labs already had a programming language called “B” at their disposal.

Why is C called C?

C is a general purpose computer programming language developed in 1972 by Dennis Ritchie at the Bell Telephone Laboratories for use with the Unix operating system. It was named 'C' because many of its features were derived from an earlier language called 'B'.

What is the main () in C?

Every C program has a primary function that must be named main . The main function serves as the starting point for program execution. It usually controls program execution by directing the calls to other functions in the program.


2 Answers

I wouldn't expect D's garbage collector to know how to free a C++ object. That would imply (at least) that the D runtime:

  1. make assumptions about the C++ runtime, i.e., how to delete a C++ object
  2. that the object is no longer needed by other C++ code

I'm sure you'll have to provide another C++ function that calls the object passed to it. In fact, many C++ libraries (even when also being used from C++), have this same pattern in cases where the constructor is called from inside the library. Even in straight C, it's usually a bad idea to allocate memory in one dll/exe, and free it in another. This can break badly if the two binaries don't share the same runtime library.

like image 97
Aaron Avatar answered Sep 18 '22 14:09

Aaron


The specific way this is implemented is the D object simply has a C++ compatible vtable. So only virtual functions on it work, and since the table is laid out by index, they must appear in the same order.

D has no idea about C++ constructors, destructors, or any other specialish method, but if they are virtual, it can throw off the vtable.

I wrote a quick little program called dtoh pending review right now that can help auto-generate C++ headers from D source, to keep this simple. It isn't finished yet, but it might be helpful anyway: https://github.com/adamdruppe/tools/blob/7d077b26d991dd5705e834900f66bea737a233b2/dtoh.d

First compile it, dmd dtoh.d, then make the JSON from your D file: dmd -X yourfile.d, then run dtoh yourfile.json and it should spit out a usable yourfile.h. But like I said, it isn't finished yet and is still waiting on review for the overall design, so it might suck horribly. You can always do what you're doing now though and do it yourself.

Anyway, the object as seen in D is just like a Class* in C++. You always pass it around through the pointer, so there's no construction or copy construction ever done.

D and C++ also don't understand each other's memory allocation systems. The rule I follow is anything your library creates, your library should be able to destroy. So if your C++ program newed it, be sure it is deleted in C++ as well. Any object you create in D for passing to C++ should also be destroyed by D... and you might want to do it manually. If your C++ function keeps a reference to the D object, but there isn't one in D, it might get garbage collected! So you'll either want to make sure there's always a living reference in D for the lifetime of the object, or create an destroy it yourself with functions like malloc and free.

I don't like having the caller use even generic free(), since the version won't necessarily match. I say always provide a method from your library to free things. Even if its implementation is just free(ptr);, giving your own function will make it explicit that it should be used, and give you protection against such mismatches.

like image 22
Adam D. Ruppe Avatar answered Sep 18 '22 14:09

Adam D. Ruppe