将 D 对象指针转换为 void* 并传递给回调

Converting a D object pointer to void* and passing to a callback

我想将 D class 指针转换为 void*,将此 void* 指针与指向我的回调 extern(C) 函数的指针一起传递给 C 库例程.

C 库例程将调用我的回调 extern(C) 函数,该函数会将 void* 转换回 class 指针并使用此 class 的对象。

问题:我听说GC对象可能会被移动到其他位置(可能不是在当前的D版本中,而是在未来)。这是否意味着我的 void* 指针可能会变得无效(不再指向我的对象)?

如果确实存在问题,如何解决?

你可以通过import core.memory; GC.addRoot(ptr);函数告诉GC将指针作为根持有,并且告诉它不要移动它。这个例子完整地展示了它:

http://dpldocs.info/experimental-docs/core.memory.GC.addRoot.html#examples

// Typical C-style callback mechanism; the passed function
// is invoked with the user-supplied context pointer at a
// later point.
extern(C) void addCallback(void function(void*), void*);

// Allocate an object on the GC heap (this would usually be
// some application-specific context data).
auto context = new Object;

// Make sure that it is not collected even if it is no
// longer referenced from D code (stack, GC heap, …).
GC.addRoot(cast(void*)context);

// Also ensure that a moving collector does not relocate
// the object.
GC.setAttr(cast(void*)context, GC.BlkAttr.NO_MOVE);

// Now context can be safely passed to the C library.
addCallback(&myHandler, cast(void*)context);

extern(C) void myHandler(void* ctx)
{
   // Assuming that the callback is invoked only once, the
   // added root can be removed again now to allow the GC
   // to collect it later.
   GC.removeRoot(ctx);
   GC.clrAttr(ctx, GC.BlkAttr.NO_MOVE);

   auto context = cast(Object)ctx;
   // Use context here…
}

基于 Adam D. Ruppe 的回答,但进行了重组。

更好的 OO 代码:

import core.memory : GC;

class UnmovableObject {
    this() {
        //GC.addRoot(cast(void*)this); // prevents finalization
        GC.setAttr(cast(void*)this, GC.BlkAttr.NO_MOVE);
    }
    ~this() {
       //GC.removeRoot(cast(void*)this);
       GC.clrAttr(cast(void*)this, GC.BlkAttr.NO_MOVE);
   }
}