简体   繁体   English

如何在C ++ / CLI中包装C库回调

[英]How to wrap C library callbacks in C++/CLI

Given the following C library with a callback event that ask to set a buffer, how to write a proper C++/CLI wrapper in a type safe manner? 鉴于以下带有回调事件的C库要求设置缓冲区,如何以类型安全的方式编写适当的C ++ / CLI包装器?

// The callback signature
typedef void (__cdecl *BUFFERALLOCATOR)(void *opaque, void **buffer);

// A struct that contains the context of the library
struct lib_context_base_s
{
    // The stored callback function pointer 
    BUFFERALLOCATOR buffer_allocator;
    // Opaque pointer that contain the local context. Needed in C because
    // C doesn't have closures (functions that knows the context where
    // they are defined)
    void* opaque;
};

typedef struct lib_context_base_s lib_context_base;

// Init the base context
lib_context_base* new_lib_context_base()
{
    return malloc(sizeof(lib_context_base));
}

// Free the base context
void free_lib_context_base(lib_context_base *lib_context_base)
{
    free(lib_context_base);
}

// Set the buffer allocation callback
void set_allocate_buffer_callback(lib_context_base *lib_context_base,
                                  BUFFERALLOCATOR allocate_buffer, void* opaque)
{
    lib_context_base->buffer_allocator = allocate_buffer;
    lib_context_base->opaque = opaque;
}

The library should be usable by managed code using the delegate void BufferAllocator(ref IntPtr buffer) . 使用delegate void BufferAllocator(ref IntPtr buffer)可以由托管代码使用该库。

I will insist on type-safe principles: I know there's already Marshal.GetFunctionPointerForDelegate but that requires function pointer type cast in C++/CLI and hides how marshalling unmanaged->managed works (debugging is much harder and I don't like not understanding what's happening behing the scene). 我将坚持类型安全原则:我知道已经有Marshal.GetFunctionPointerForDelegate但是这需要在C ++ / CLI中Marshal.GetFunctionPointerForDelegate函数指针类型,并隐藏了如何编组非托管->托管工作的方式(调试起来要困难得多,我不喜欢不理解什么内容)发生在现场)。 Just noticed the approach is similar to this but doesn't need a managed native class (less overhead). 刚刚注意到,该方法与类似,但是不需要托管的本机类(较少的开销)。 Please, tell me if you know how to further simplify it (mantaining type safety and marshaling control) and reduce overhead. 请告诉我您是否知道如何进一步简化它(保持类型安全性和封送控制)并减少开销。

The following is the C++/CLI Wrapper.h header: 以下是C ++ / CLI Wrapper.h标头:

#include <gcroot.h>

using namespace System;
using namespace System::Runtime::InteropServices;

namespace LibraryWrapper
{
    // Declare the cdecl function that will be used 
    void cdecl_allocate_buffer(void *opaque, void **buffer);

    public ref class Library
    {
    public:
        // The BufferAllocator delegate declaration, available to any clr language
    // [In, Out] attributes needed (?) to pass the pointer as reference
        delegate void BufferAllocator([In, Out] IntPtr% buffer);

    internal:
        // The stored delegate ref to be used later
        BufferAllocator ^_allocate_buffer;

    private:
        // Native handle of the ref Library class, castable to void *
        gcroot<Library^> *_native_handle;
        // C library context
        lib_context_base *_lib_context_base;

    public:
        Library();
        ~Library();
        // The clr callback setter equivalent to the C counterpart, don't need
        // the context because in CLR we have closures
        void SetBufferAllocateCallback(BufferAllocator ^allocateBuffer);
    };
}

Follows C++/CLi Wrapper.cpp defines: 遵循C ++ / CLi Wrapper.cpp定义:

#include "wrapper.h"

namespace LibraryWrapper
{
    Library::Library()
    {
        // Construct the native handle
        _native_handle = new gcroot<Library^>();
        // Initialize the library base context
        _lib_context_base = new_lib_context_base();
        // Null the _allocate_buffer delegate instance
        _allocate_buffer = nullptr;
    }

    Library::~Library()
    {
        free_lib_context_base(_lib_context_base);
        delete _native_handle;
    }

    void Library::SetBufferAllocateCallback(BufferAllocator ^allocateBuffer)
    {
        _allocate_buffer = allocateBuffer;
        // Call the C lib callback setter. Use _native_handle pointer as the opaque data 
        set_allocate_buffer_callback(_lib_context_base, cdecl_allocate_buffer,
            _native_handle);
    }

    void cdecl_allocate_buffer(void *opaque, void **buffer)
    {
        // Cast the opaque pointer to the hnative_handle ref (for readability)
        gcroot<Library^> & native_handle = *((gcroot<Library^>*)opaque);
        // Prepare a IntPtr wrapper to the buffer pointer
        IntPtr buffer_cli(*buffer);
        // Call the _allocate_buffer delegate in the library wrapper ref
        native_handle->_allocate_buffer(buffer_cli);
        // Set the buffer pointer to the value obtained calling the delegate
        *buffer = buffer_cli.ToPointer();
    }
}

Can be used in this way (C#): 可以这种方式使用(C#):

// Allocate a ~10mb buffer in unmanaged memory. Will be deallocated
// automatically when buffer go out of scope
IntPtr _buffer = Marshal.AllocHGlobal(10000000);

// Init the library wrapper
Library library = new Library();

// Set the callback wrapper with an anonymous method
library.SetBufferAllocateCallback(delegate(ref IntPtr buffer)
{
    // Because we have closure, I can use the _buffer variable in the outer scope
    buffer = _buffer;
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM