​​

Jan:在D中有个​​extern(C++)​​类:

extern(C++) class A
{
~this();
// ...
}

及一个带​​const​​引用A的函数:

void CppFunc(const A& arg);

我该如何在D中绑定它?

extern(C++) void CppFunc(A arg); // 按A*传递
extern(C++) void CppFunc(ref const(A) arg);
// 按'A const * const &'传递

按D中​​构​​​声明解决了​​其他类​​​类似问题,但仅针对​​无虚函数类​​​.我现在有个确实需要在D端​​用类​​​的​​C++类​​​,把​​对象​​​传递给​​C++​​时遇到了问题.


evilrat:

可用​​extern(C++,struct)​​告诉​​编译器​​按​​构/类​​混杂.

extern (C++, struct) //即使它是类,也用构混杂
class SomeDClass
{
...
}

试了,但不管用,因为似乎D是按D中而不是​​C++​​中​​类/构​​来决定​​如何传递对象​​.因此,即使按你的改了,仍按前面指针​​传递​​.

​Ola​​,这有用吗?

extern(C++) class _A {}
extern(C++) struct A {}

extern(C++) void CppFunc(ref const(A) arg);

void func(_A a){
CppFunc(*cast(A*)a);
}

evilrat:

仅当​​该构与类​​内存布局​​匹配​​时,唯一潜在问题是​​C++​​端的​​构造器​​.

此外,​​C++​​类可能有​​非0初化​​且由于​​对齐​​有​​字节间隙​​,这会​​破坏​​,包括​​(默认)等价运算符​​等.要非常小心.


ola:是的,我不想用它,也许手动混杂更好,但仍很痛苦.​​常 A&​​在​​C++API​​中很普遍,真应开箱即用的支持它.只需在D​​规范​​中加个如​​ref const(@deref(A))​​的​​解引用类型构造器​​.

​jan​​:我完全同意.这种模式在​​C++​​中非常普遍,我很惊讶D无法做到这一点.与​​C++​​链接的​​整个想法​​是​​轻松互操作,小摩擦,高性能​​.

对按​​参数或返回类型​​传递的类,更改​​混杂​​为​​尾常​​,但会破坏许多代码.​​提案​​,加​​解引用类型构造器​​提案也允许​​尾常​​类.


jan:解引用,​​D​​尝试对​​C++​​应用​​类指针/引用​​语义,即使它可按不同方式.​​'Deref'​​只能解决该(常见)问题.但是,在​​C++​​中,​​按值的类​​也很常见.也许可给​​D编译器​​提供提示.

如果我有如下​​C++​​代码:

class A { ... };
void AsValue(A value);
void AsPtr(A* value);
void AsConstPtr(const A* value);
void AsRef(A& value);
void AsConstRef(const A& value);

我认为,目前只能绑定到"​​AsPtr​​​“和”​​AsConstPtr​​​"形式函数.如果​​按D中构声明A​​,可绑定到其他,但有时不行.

在​​D​​​中,如何用"​​构​​​"关键字​​按值类型​​传递给函数?

extern(C++) class A { ... }
extern(C++) void AsValue(struct A value);
extern(C++) void AsPtr(A value); // 同上
extern(C++) void AsPtr(struct A* value); // 同上
extern(C++) void AsConstPtr(const(A) value);
extern(C++) void AsConstPtr(const(struct A*) value); // 同上
extern(C++) void AsRef(ref struct A value);
extern(C++) void AsConstRef(const(ref struct A) value); // 同上

这里"​​构​​“关键字告诉编译器与​​C++​​一样,​​按值类型​​对待A,因此与​​C++同样​​应用​​指针,常量和引用语义​​.此外,如果遇到纯”​​A 构​​",编译器需要类似构​​在栈上​​创建​​对象副本​​,并传递给​​C++​​(可能会修改​​临时对象​​).实现可能难,但始终可​​传递类​​给​​C++​​.额外好处不应​​改变​​现有行为,因此无破坏.

前面​​cast​​方法不行,


tim:最简单方法可能是手动设置混杂:

pragma(mangle, "_Z7CppFuncRK1A")
extern(C++) void CppFunc(const A arg);
//针对Itanium,林操,对窗口不一样.

tim:D与​​C++​​交互方式是​​合理且非常有用​​的.​​链接兼容性​​大有帮助.

与​​C++​​不同,D变量默认有​​线程本地存储​​,要链接​​C++​​全局变量,要在D中用​​__gshared(类似共享,但由程序员(而非编译器)保证同步)​​的​​存储修饰符​​,

module a;
struct Foo {}
extern(C++)
__gshared Foo globalFoo;

我已成功使用了来自​DLL​​​的全局变量.​​DMD​​​现在也测试了​​C++DLL​​​:​​地址​

在​​C++​​中应该这样声明变量:

__declspec(dllexport) int value;

然后D这样访问它:

extern (C++) export extern __gshared int value;

你有你问题的测试用例吗?

jan:

class Test
{
public:
__declspec(dllexport) static int var;
};
//也试了不带extern(C++),及不带export
int Test::var = 42;
//D端
extern(C++, class) struct Test
{
extern (C++) export extern __gshared int var;
}

​dmd​​报错:

error LNK2019: unresolved external symbol "public: static int Test::var" (?var@Test@@2HA)

我检查了​​DLL​​​定义符号的​​.exp​​​文件,名字完全相同.是的,我大致正确链接到该​​DLL​​​,​​DMD​​​链接该​​DLL​​​中​​其他符号​​也很好.

tim:我可重现你的问题.如果​​var​​​是​​静​​的,似乎可工作:

extern(C++, class) struct Test
{
extern (C++) export extern static __gshared int var;//加个静
}

无静就无法工作,这可能是个错误.文档说也可应用​​__gshared​​​至​​成员变量和局部变量​​​.此时,​​__gshared​​​除了​​全局共享而非线程本地共享​​​,与​​静​​等价.

extern export __gshared static int var;
//`外`时为啥要导出?

确实工作了.​​__gshared​​​在​​D​​​中为​​静​​?

H.S:

不.此时,​​静​​:生命期是​​模块​​生命期.默认,​​静态变量​​在​​TLS​​中,有实例化​​它的线程​​生命期(且每线程一实例).

要用​​__gshared​​来使编译器​​按C/C++意义​​对待​​全局变量​​,即所有线程中​​只有1个实例​​.​​C 静==D __g共享​​.

tim:我同意​​__gshared​​应暗示​​静态​​.可能是​​编译器​​错误.

不带​​导出​​用​​外​​,仅适用于(在​​Windows​​上)静态链接.与在​​C++​​中的​​__declspec(dllexport)​​一样,不带​​外​​的导出会导出​​其他变量​​.与​​__declspec(dllimport)​​一样,组合​​导出​​和​​外​​来导入变量.