Я буду настаивать на принципах безопасности типов: я знаю, что Marshal.GetFunctionPointerForDelegate
уже существует, но для этого требуется приведение типа указателя функции в C++/CLI и скрывает, как работает сортировка unmanaged->managed (отладка намного сложнее, и мне не нравится непонимание того, что происходит из-за сцена). Только что заметил, что подход похож на этот, но не нужен управляемый собственный класс (меньше накладных расходов). Пожалуйста, скажите мне, знаете ли вы, как еще больше упростить его (поддержание безопасности типов и управление маршалингом) и уменьшить накладные расходы.
Ниже приведен заголовок 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);
};
}
Следует определению 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();
}
}
Можно использовать таким образом (С#):
// 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;
});
person
ceztko
schedule
15.03.2011