Class 用于围绕外语编写D包装的结构与类

Class 用于围绕外语编写D包装的结构与类,class,struct,wrapper,d,Class,Struct,Wrapper,D,(注意:这与更具体的用例有关) 当编写一个D接口,比如说C++代码时,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);}

(注意:这与更具体的用例有关)

当编写一个D接口,比如说C++代码时,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) 易用性(无需到处编写新代码,例如:
autoa=a(B(1),C(2))
vs
autoa=newa(new B(1),new C(2))

缺点是: require additional字段是通过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){}
我建议你读一读。C++中唯一可以调用的函数是虚函数。也就是说

d不能调用C++的SPE-EMEM函数,反之亦然。这些包括结构、断路器、转换操作器、过载操作器和过载操作器

并且在d中声明C++类时,使用<代码>外部(C++)接口< /代码>。因此,您的类/结构将如下所示

extern(C++) interface A
{
   void fun();
}

但是,您需要另一个<代码>外部(C++)< />函数来分配任何类型的代码< > < /COD>,因为它是C++代码,因为D代码不能访问任何构造函数。你还需要一种方法把它传递给C++代码,当你完成它时,它将被删除。 现在,如果您想将该接口包装为一个类型,该类型将调用

extern(C++)
函数来构造它,并调用
extern(C++)
函数来删除它(这样您就不必担心手动执行),那么您是否使用类或结构完全取决于您试图对其执行的操作

类是一个引用类型,它反映C++类实际上是什么。因此,在不需要做任何特别的事情的情况下,将它传递出去是可行的。但是如果你想保证被包装的C++对象被释放,你必须手动完成,因为不能保证D类的终结器会运行(大概,这就是你把C++函数调用的代码删除C++对象的地方)。您必须使用

clear
(在下一版本的编译器-DMD2.060中将实际重命名为
destroy
)来销毁d对象(即调用其终结器并处理其任何值类型的成员变量的销毁),或者,你必须调用一个函数,它调用C++函数来删除C++对象。e、 g

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);
}
<>但这确实有缺点,如果你不调用<代码>清除<代码> >代码>销毁< /C> >,垃圾回收器永远不会收集你的包装对象,<代码> DELTEEA< /Cord>将永远不会被调用C++对象。这可能重要,也可能不重要。这取决于C++对象在程序终止之前是否需要调用它的析构函数,或者当程序不需要收集包装对象时,它是否可以让程序的内存返回到OS(没有调用它的析构函数)。 如果你想要确定性的破坏,那么你需要一个结构。这意味着您需要担心将结构变成引用类型。否则,如果复制,当其中一个被破坏时,C++对象将被删除,而另一个结构将指向垃圾(当它被破坏时它将尝试删除)。要解决这个问题,你可以使用。然后你会得到这样的结果

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...
}
您还可以定义包装器,使其包含ref计数逻辑,并避免
RefCounted
,但这肯定会更复杂

无论如何,我绝对反对使用<代码>布尔O/CODE标记包装器是否拥有C++对象的建议,因为如果在所有的拷贝之前原始包装对象被破坏,那么你的副本将指向垃圾。

如果您确实希望使用C++对象的复制构造函数(也因此将C++对象作为值类型)添加一个“<代码>外部(C++)”/Cuth>函数,它接收C++对象并返回它的副本,然后在PbLITIT中使用它。

extern(C++)A copyA(A)

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

我建议你读一读。C++中唯一可以调用的函数是虚函数。也就是说

d不能调用C++的SPE-EMEM函数,反之亦然。这些包括结构、断路器、转换操作器、过载操作器和过载操作器

并且在d中声明C++类时,使用<代码>外部(C++)接口< /代码>。因此,您的类/结构将如下所示

extern(C++) interface A
{
   void fun();
}

但是,您需要另一个<代码>外部(C++)< />函数来分配任何类型的代码< > < /COD>,因为它是C++代码,因为D代码不能访问任何构造函数。你还需要一种方法把它传递给C++代码,当你完成它时,它将被删除。 现在,如果您想将该接口包装为一个类型,该类型将调用

extern(C++)
函数来构造它,并调用
extern(C++)
函数来删除它(这样您就不必担心手动执行),那么您是否使用类或结构完全取决于您试图对其执行的操作

类是一个引用类型,它反映C++类实际上是什么。因此,在不需要做任何特别的事情的情况下,将它传递出去是可行的。但是如果你想保证被包装的C++对象被释放,那么你必须手动完成,因为