我已经完成了解释如何从D解释如何调用C ++的文档:http://dlang.org/cpp_interface.html。然而,有一些事情对我来说并不十分清楚。
以D网站上提供的例子为例:
#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;
}
然后可以从D调用C ++类,如下所示:
extern (C++) {
interface D {
int bar(int i, int j, int k);
}
D getD();
}
void main() {
D d = getD();
d.bar(9,10,11);
}
我不太清楚C ++对象是如何被删除的。 D垃圾收集器是否在C ++对象上调用delete或者我们是否需要提供一个“删除器”函数来删除对象并从D中手动调用它?在我看来,如果我向C ++类添加析构函数,它永远不会被调用。另外我注意到C ++类必须以与D接口中声明的顺序完全相同的顺序声明成员函数(例如,如果我在bar()方法之前添加析构函数,则无法从D调用C ++对象,但是如果在bar()方法之后声明析构函数,一切正常。)
如果D接口定义为:
extern(C++){
interface D{
int bar();
int foo();
}
}
对应的C ++类由下式给出:
class D{
public:
virtual int bar(){};
virtual int foo(){};
};
如何保证将以与D接口中声明的方法相同的顺序创建C ++虚拟方法vtbl。对我来说,这并不能保证。换句话说,我们如何确定D :: bar()将位于vtbl的第一个位置?这不是实现/编译器依赖吗?
答案 0 :(得分:7)
我不希望D的垃圾收集器知道如何释放C ++对象。这意味着(至少)D运行时:
我确定你必须提供另一个调用传递给它的对象的C ++函数。实际上,许多C ++库(即使也是从C ++中使用的)在从库内部调用构造函数的情况下也具有相同的模式。即使在直接的C中,在一个dll / exe中分配内存并在另一个中释放它通常是个坏主意。如果两个二进制文件不共享相同的运行时库,这可能会严重破坏。
答案 1 :(得分:4)
实现它的具体方法是D对象只具有C ++兼容的vtable。因此,只有虚函数可以工作,并且由于表是按索引排列的,因此它们必须以相同的顺序出现。
D不知道C ++构造函数,析构函数或任何其他特殊方法,但如果它们是虚拟的,它可以抛弃vtable。
我现在编写了一个名为dtoh pending review的快速小程序,它可以帮助从D源自动生成C ++标头,以保持这一点。它尚未完成,但无论如何它可能会有所帮助: https://github.com/adamdruppe/tools/blob/7d077b26d991dd5705e834900f66bea737a233b2/dtoh.d
首先编译它,dmd dtoh.d
,然后从D文件中生成JSON:dmd -X yourfile.d
,然后运行dtoh yourfile.json
,它应该吐出一个可用的yourfile.h。但就像我说的那样,它尚未完成,仍在等待整体设计的审查,所以它可能会非常糟糕。你可以随时做你现在正在做的事情并自己做。
无论如何,D中看到的对象就像C ++中的Class *。你总是通过指针传递它,所以没有任何构造或复制结构。
D和C ++也不了解彼此的内存分配系统。我遵循的规则是您的图书馆创建的任何内容,您的图书馆应该能够销毁。因此,如果您的C ++程序新增了它,请确保它也在C ++中被删除。您在D中创建的用于传递给C ++的任何对象也应该被D ...销毁,您可能希望手动执行此操作。如果您的C ++函数保留对D对象的引用,但D中没有一个,则可能会收集垃圾!因此,您要么确保在对象的生命周期中始终存在D中的生命引用,要么使用malloc和free等函数自行创建它。
我不喜欢让调用者使用甚至通用的free(),因为版本不一定匹配。我说总是从你的图书馆提供一种方法来解放东西。即使它的实现只是免费的(ptr);,给出你自己的函数会明确它应该被使用,并为你提供防止这种不匹配的保护。
答案 2 :(得分:2)
您需要在D类中添加一个调用c ++ delete运算符的方法。或者,您可以使用全局销毁方法。
另外,不要忘记任何与另一种语言的接口都必须声明为extern“C”,以避免编译器函数名称损坏。
#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;
}
// option 1
virtual void destroy()
{
delete this;
}
};
extern "C"
D *getD() {
D *d = new D();
return d;
}
// option 2
extern "C"
void killD(void* d) {
delete d;
return;
}
然后在你的代码中你需要创建一个调用destroy方法的scope子句。
答案 3 :(得分:1)
由于您的问题标题为“从D调用C ++”,我将假设您正在尝试interface to C++。
D垃圾收集器是否在C ++对象上调用delete,还是需要提供“删除”功能,删除对象并从D中手动调用?
通过“ C ++对象”我假设您指的是使用new
运算符分配的对象。 D不知道用C ++ new运算符创建的C ++对象。因此,每当您需要删除C ++分配的对象时,您必须提供自己的代码来释放内存。
D中的C ++支持非常有限,这是有充分理由的。 - 完全C ++支持意味着应该在D编译器中包含一个完整的C ++编译器(带有C ++预处理器)。这会使D编译器的实现变得更加困难。
另外我注意到C ++类必须以与D接口中声明的顺序完全相同的顺序声明成员函数(例如,如果我在bar()方法之前添加析构函数,则无法从中调用C ++对象D,但是如果在bar()方法之后声明了析构函数,那么一切正常。)
在这种特殊情况下,我相信你首先编写C ++类,记住它将在D项目中使用,然后你编写D接口。 D接口应该与C ++类中的方法紧密匹配,因为D编译器将生成与C ++兼容的virtual table。
C ++支持将得到改善,但D极不可能拥有完整的C ++支持。已经完成了支持C ++命名空间的工作(D社区要求的改进)。
由于D完全支持C,最好的想法是将复杂的C ++代码“扁平化”为C,类似于文章“Mixed C and C++”中的方式。很久以前我用类似的方法从Delphi调用C ++方法。