如何在C ++ / CLI中包装C库回调 [英] How to wrap C library callbacks in C++/CLI

查看:264
本文介绍了如何在C ++ / CLI中包装C库回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:提供的答案很快就会到达(我应该问一个问题,我知道答案吗?)。



给定下面的C库与回调事件,要求设置缓冲区,如何以类型安全的方式编写适当的C ++ / CLI包装?

  //回调签名
typedef void(__cdecl * BUFFERALLOCATOR)(void * opaque,void ** buffer);

//包含库上下文的struct
struct lib_context_base_s
{
//存储的回调函数指针
BUFFERALLOCATOR buffer_allocator;
//包含本地上下文的不透明指针。需要在C,因为
// C没有闭包(函数,知道上下文
//它们被定义)
void * opaque;
};

typedef struct lib_context_base_s lib_context_base;

//基本上下文
lib_context_base * new_lib_context_base()
{
return malloc(sizeof(lib_context_base));
}

//释放基本上下文
void free_lib_context_base(lib_context_base * lib_context_base)
{
free(lib_context_base);
}

//设置缓冲区分配回调
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;
}

库应该可以由托管代码使用 delegate void BufferAllocator(ref IntPtr buffer)

解决方案我会坚持类型安全的原则:我知道已经有 Marshal.GetFunctionPointerForDelegate ,但是需要函数指针类型转换在C ++ / CLI和隐藏如何编组unmanaged->托管工作(调试更困难,我不像不理解发生的事情behing的场景)。只是注意到该方法类似于,但不是需要一个托管的本地类(更少的开销)。请告诉我是否知道如何进一步简化(保护类型安全和封送控制)并减少开销。



以下是C ++ / CLI Wrapper.h header:

  #include< gcroot.h> 

使用命名空间System;
using namespace System :: Runtime :: InteropServices;

命名空间LibraryWrapper
{
//声明要使用的cdecl函数
void cdecl_allocate_buffer(void * opaque,void ** buffer);

public ref类库
{
public:
// BufferAllocator委托声明,可用于任何clr语言
// [In,Out]需要的属性(?)将引用传递给指针
delegate void BufferAllocator([In,Out] IntPtr%buffer);

internal:
//存储的委托引用以后使用
BufferAllocator ^ _allocate_buffer;

private:
// ref类库的本地句柄,可强制转换为void *
gcroot< Library ^> * _native_handle;
// C库上下文
lib_context_base * _lib_context_base;

public:
Library();
〜Library();
// clr回调设置器相当于C对等体,不需要
//上下文,因为在CLR中我们有闭包
void SetBufferAllocateCallback(BufferAllocator ^ allocateBuffer);
};
}

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

  #includewrapper.h

namespace LibraryWrapper
{
Library :: Library()
{
//构造本地句柄
_native_handle = new gcroot< Library ^>();
//初始化库基础上下文
_lib_context_base = new_lib_context_base();
//清除_allocate_buffer委托实例
_allocate_buffer = nullptr;
}

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

void Library :: SetBufferAllocateCallback(BufferAllocator ^ allocateBuffer)
{
_allocate_buffer = allocateBuffer;
//调用C lib回调setter。使用_native_handle指针作为不透明数据
set_allocate_buffer_callback(_lib_context_base,cdecl_allocate_buffer,
_native_handle);
}

void cdecl_allocate_buffer(void * opaque,void ** buffer)
{
//将opaque指针转换为hnative_handle ref $ b gcroot< Library ^> & native_handle = *((gcroot< Library ^> *)opaque);
//为缓冲区指针准备一个IntPtr包装器
IntPtr buffer_cli(* buffer);
//在库包装中调用_allocate_buffer委托ref
native_handle-> _allocate_buffer(buffer_cli);
//将缓冲区指针设置为调用委托的值
* buffer = buffer_cli.ToPointer();
}
}

可以这样使用/ p>

  //在非托管内存中分配〜10mb缓冲区。将被释放
//当缓冲区超出范围时自动
IntPtr _buffer = Marshal.AllocHGlobal(10000000);

//初始化库包装
库library = new Library();

//使用匿名方法设置回调包装器
library.SetBufferAllocateCallback(delegate(ref IntPtr buffer)
{
//因为我们有闭包,我可以使用外部范围中的_buffer变量
buffer = _buffer;
});


NB: The answer is provided and should arrive shortly (Should I ask a question I know the answer to?).

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?

// 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) .

解决方案

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). 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:

#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:

#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#):

// 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;
});

这篇关于如何在C ++ / CLI中包装C库回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆