我有一个非托管库,它有这样的功能:
type* foo();
Run Code Online (Sandbox Code Playgroud)
foo基本上通过分配托管type堆上的非托管实例Marshal.AllocHGlobal.
我有一个托管版本type.它不是blittable但我MarshalAs在成员上设置属性,所以我可以使用它Marshal.PtrToStructure来获得它的托管版本.但不得不foo用额外的簿记打电话来打电话Marshal.PtrToStructure有点烦人.
我希望能够在C#方面做这样的事情:
[DllImport("mylib", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPStruct)]
type* foo();
Run Code Online (Sandbox Code Playgroud)
让C#的marshaller处理幕后转换,就像它对函数参数一样.我以为我应该能够这样做因为type在托管堆上分配了.但也许我不能?有没有办法让C#的内置编组器为我处理返回类型的非托管到托管转换而无需手动调用Marshal.PtrToStructure?
我遵循 Stefanus Du Toit 的沙漏模式,即在 C++ 中实现 C API,然后再次将其包装在 C++ 中。这与pimpl idiom非常相似,它对用户也是透明的,但可以防止更多与 ABI 相关的问题并允许更广泛的外语绑定。
与实现指针的方法一样,底层对象的大小和布局在编译时不被外界所知,因此它所在的内存必须动态分配(主要是)。然而,与pimpl情况不同,在pimpl情况下,对象已在分配点完全定义,这里它的属性完全隐藏在不透明指针后面。
获得的内存std::malloc是“适合任何标量类型”,这使得它不适合任务。我不确定new-expression。引自链接页面的分配部分:
此外,如果 new 表达式用于分配一个 char 数组或一个 unsigned char 数组,它可能会在必要时从分配函数中请求额外的内存,以保证不大于请求的数组大小的所有类型的对象的正确对齐,如果稍后将一个放入分配的数组中。
这是否意味着以下代码是合规的?
size_t object_size ( void ); // return sizeof(internal_object);
size_t object_alignment ( void ); // return alignof(internal_object);
void object_construct ( void * p ); // new (p) internal_object();
void object_destruct ( void * p ); // …Run Code Online (Sandbox Code Playgroud)