gpt4 book ai didi

class - struct vs class 用于围绕外语编写 D 包装器

转载 作者:行者123 更新时间:2023-12-04 18:19:09 26 4
gpt4 key购买 nike

(注意:这与 Usage preference between a struct and a class in D language 有关,但用于更具体的用例)

在为 C++ 代码编写 D 接口(interface)时,SWIG 和其他代码会执行以下操作:

class A{
private _A*ptr;//defined as extern(C) elsewhere
this(){ptr=_A_new();}//ditto
this(string s){ptr=_A_new(s);} //ditto
~this(){_A_delete(ptr);} //ditto
void fun(){_A_fun(ptr);}
}

假设不需要继承。

我的问题是:为此使用结构而不是类不是更好吗?

优点是:

1)效率(堆栈分配)

2)易用性(无需到处写新的,例如: auto a=A(B(1),C(2)) vs auto a=new A(new B(1),new C(2)))?

缺点是:
需要额外的字段 is_own 来通过 postblit 处理别名。

最好的方法是什么?
还有什么需要担心的吗?
这是一个尝试:
struct A{
private _A*ptr;
bool is_own;//required for postblit
static A opCall(){//cannot write this() for struct
A a;
a.ptr=_A_new();
a.is_own=true;
return a;
}
this(string s){ptr=_A_new(s); is_own=true;}
~this(){if(is_own) _A_delete(ptr);}
void fun(){_A_fun(ptr);}
this(this){//postblit;
//shallow copy: I don't want to call the C++ copy constructor (expensive or unknown semantics)
is_own=false; //to avoid _A_delete(ptr)
}
}

请注意,在调用以下函数的情况下,postblit 是必需的:
myfun(A a){}

最佳答案

我建议你阅读this page .您可以在 D 中调用的 C++ 类上的唯一函数是虚函数。这意味着

D can­not call C++ spe­cial mem­ber func­tions, and vice versa. These in­clude con­struc­tors, de­struc­tors, con­ver­sion op­er­a­tors, op­er­a­tor over­load­ing, and al­lo­ca­tors.



当你在 D 中声明一个 C++ 类时,你使用 extern(C++) interface .所以,你的类/结构看起来像这样
extern(C++) interface A
{
void fun();
}

但是,您需要另一个 extern(C++)分配 A 类型的任何对象的函数,因为它是 C++ 代码必须这样做,因为 D 代码无权访问任何构造函数。您还需要一种方法将其传递回 C++ 代码,以便在完成后将其删除。

现在,如果你想将该接口(interface)包装在一个将调用 extern(C++) 的类型中。构造它的函数和 extern(C++)删除它的函数(这样您就不必担心手动执行此操作),那么您是使用类还是结构完全取决于您要使用它做什么。

一个类将是一个引用类型,它反射(reflect)了 C++ 类的实际情况。因此,无需您做任何特别的事情,就可以传递它。但是,如果您想要保证包装的 C++ 对象已被释放,则必须手动执行此操作,因为无法保证 D 类的终结器将永远运行(并且大概是您放置代码的地方)调用 C++ 函数来删除 C++ 对象)。您必须使用 clear (实际上将在编译器的下一个版本 - dmd 2.060 中将其重命名为 destroy)以销毁 D 对象(即调用其终结器并处理其任何值类型的成员变量的销毁),或者您d 必须调用 D 对象上的一个函数,该函数调用 C++ 函数来删除 C++ 对象。例如
extern(C++) interface A
{
void fun();
}

extern(C++) A createA();
extern(C++) void deleteA(A a);

class Wrapper
{
public:
this()
{
_a = createA();
}

~this()
{
deleteA(_a);
}

auto opDispatch(string name, Args...)(Args args)
{
return mixin("_a." ~ name ~ "(args)");
}

private:

A _a;
}

void main()
{
auto wrapped = new Wrapper();

//do stuff...

//current
clear(wrapped);

//starting with dmd 2.060
//destroy(wrapped);
}

但这确实有缺点,如果您不调用 clear/ destroy ,并且垃圾收集器永远不会收集您的包装对象, deleteA永远不会在 C++ 对象上调用。这可能很重要,也可能无关紧要。这取决于 C++ 对象是否真的需要在程序终止之前调用它的析构函数,或者如果 GC 永远不需要收集包装器,当程序终止时它是否可以让它的内存返回给操作系统(而不调用它的析构函数)目的。

如果你想要确定性破坏,那么你需要一个结构。这意味着您需要担心将结构变成引用类型。否则,如果它被复制,当其中一个被销毁时,C++ 对象将被删除,而另一个结构将指向垃圾(然后它将在被销毁时尝试删除)。要解决这个问题,您可以使用 std.typecons.RefCounted .然后你会得到类似的东西
extern(C++) interface A
{
void fun();
}

extern(C++) A createA();
extern(C++) void deleteA(A a);

struct Wrapper
{
public:
static Wrapper opCall()
{
Wrapper retval;
retval._a = createA();
return retval;
}

~this()
{
if(_a !is null)
{
deleteA(_a);
_a = null;
}
}

auto opDispatch(string name, Args...)(Args args)
{
return mixin("_a." ~ name ~ "(args)");
}

private:

A _a;
}


void main()
{
auto wrapped = RefCounted!Wrapper();
//do stuff...
}

您还可以定义包装器,使其具有引用计数逻辑并避免 RefCounted ,但这肯定会更复杂。

无论如何,我绝对会反对您使用 bool 的建议。标记包装器是否拥有 C++ 对象,因为如果原始包装器对象在所有副本之前被销毁,那么您的副本将指向垃圾。

如果您确实希望使用 C++ 对象的复制构造函数(因此将 C++ 对象视为值类型),另一个选择是添加 extern(C++)函数获取 C++ 对象并返回它的副本,然后在 postblit 中使用它。
extern(C++) A copyA(A a);
this(this)
{
if(_a !is null)
_a = copyA(a);
}

希望这能让事情变得足够清楚。

关于class - struct vs class 用于围绕外语编写 D 包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11058906/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com