So I have a c++ function that takes a method as a std function which take a raw pointer and populates it and returns the accumulation of all values, and I wish to expose this to a C# API. Here's my approach:
// acc_process.h
#include <vector>
#include <numeric>
template<typename T>
static T process_accumulate(std::function<void(T*, size_t)> populate_func)
{
std::vector<T> data(1000);
populate_func(data.data(), data.capacity());
return std::accumulate(data.begin(), data.end(), 0);
}
Followed by a cli wrapper to expose this to C#:
// acc_process_cli.h
#include acc_process.h
#pragma managed(push, off)
typedef void(__stdcall *ReadCallbackDouble)(double* data, size_t data_size);
#pragma managed(pop)
delegate void PopFunctionDoubleDelegate(cli::array<double> ^ % data, size_t data_size );
public ref class ProcessDoubleCLI {
public:
ProcessDoubleCLI() {};
double accumulate_cli(PopFunctionDoubleDelegate^ delegate_func);
};
with implementation:
// acc_process_cli.cpp
#include "acc_process_cli.h"
double ProcessDoubleCLI::accumulate_cli(PopFunctionDoubleDelegate^ delegate_func)
{
IntPtr FunctionPointer =
Marshal::GetFunctionPointerForDelegate(delegate_func);
ReadCallbackDouble func_ptr = static_cast<ReadCallbackDouble>(FunctionPointer.ToPointer());
auto func_bind = std::bind(
(void (*)(double* , size_t))func_ptr, std::placeholders::_1, std::placeholders::_2);
return process_accumulate<double>(func_bind);
}
And a final C# sample application to provide a delegate function used to fill data, (e.g. populate_func parameter for process_accumulate):
// test.cs
class DoubleReader {
public static void PopulateFunctionDoubleIncr(ref double[] data, ulong size_data) {
ulong idx = 0;
ulong size = size_data;
while (idx < size) {
data[idx] = (double)idx;
++idx;
}
}
static void Main(string[] args)
{
DoubleReader dr = new DoubleReader();
PopFunctionDoubleDelegate func = PopulateFunctionDoubleIncr;
ProcessDoubleCLI process = new ProcessDoubleCLI();
double acc_value = process.accumulate_cli(func);
}
}
But the array is always null when passed to PopulateFunctionDoubleIncr on the C#. Am I doing this correctly or is this possible?
Note: if this is possible, is it also possible to directly convert from some sort of managed vector reference to an unmanaged std::vector reference instead of raw pointers, that would be even better.
__stdcallbut the template expects a__cdeclfunction. You can't do that.