say I have a C++ library function for computing PI:
// pi.h:
#ifdef BUILDING_DLL
#define DLL_MACRO __declspec(dllexport)
#else
#define DLL_MACRO __declspec(dllimport)
#endif
namespace Cpp {
class PI {
public:
static double DLL_MACRO compute();
};
};
// pi.cpp:
#include "pi.h"
#include <cmath>
double
Cpp::PI::compute() {
// Leibnitz summation formulae:
double sum = 0.0;
for(long n = 0; n < 100*1000*1000; n++)
sum += 4.0*pow(-1.0, n)/(2*n + 1.0);
return sum;
}
I need to call this function from C#, and I want to use C++/CLI as a "bridge". However this C++ function is somewhat slow. Therefore the C# code calling this function need to get callbacks telling it how far the function has come in %. The C# code might need some state, e.g. a progress bar, to deal with this info. So the callbacks from C++ must enter into a member function on the C# side.
So I introduce:
// piCLI.h: The C++/CLI "bridge" between C# and C++
#include "pi.h"
#pragma once
namespace CLI {
public ref class PI abstract {
public:
double compute() {
return Cpp::PI::compute();
}
virtual void progress(int percentCompleted) = 0;
};
};
and
namespace CSharp
{
public class PI : CLI.PI
{
public override void progress(int percentCompleted)
{
System.Console.WriteLine(percentCompleted + "% completed.");
}
}
}
Now invoking CSharp.PI.compute() works fine :-). It forwards the call to Cpp::PI::compute() as intended.
But how do I get the C++ library to forward progress updates to CSharp.PI.progress() whilst Cpp::PI::compute() is running?
Thanks in advance for any answers!
I would take a function pointer/delegate approach as well:
// pi.h:
#pragma once
#ifndef DLL_MACRO
#ifdef BUILDING_DLL
#define DLL_MACRO __declspec(dllexport)
#else
#define DLL_MACRO __declspec(dllimport)
#endif
#endif
namespace Cpp {
typedef void (__stdcall *ComputeProgressCallback)(int);
class PI {
public:
static double DLL_MACRO compute(ComputeProgressCallback callback);
};
}
// pi.cpp:
#include "pi.h"
#include <cmath>
double Cpp::PI::compute(Cpp::ComputeProgressCallback callback) {
double sum = 0.;
for (long n = 0L; n != 100000000L; ++n) {
sum += 4. * std::pow(-1., n) / (2L * n + 1.);
callback(/*impl*/);
}
return sum;
}
// piCLI.h: The C++/CLI "bridge" between C# and C++
#pragma once
#include "pi.h"
namespace CLI {
public delegate void ComputeProgressDelegate(int percentCompleted);
public ref class PI abstract sealed {
public:
static double compute(ComputeProgressDelegate^ callback) {
using System::IntPtr;
using System::Runtime::InteropServices::Marshal;
IntPtr cbPtr = Marshal::GetFunctionPointerForDelegate(callback);
return Cpp::PI::compute(
static_cast<Cpp::ComputeProgressCallback>(cbPtr.ToPointer())
);
}
};
}
namespace CSharp {
public static class PI {
public static double compute() {
CLI.PI.compute(
percentCompleted => System.Console.WriteLine(
percentCompleted.ToString() + "% completed."
)
);
}
}
}
Or, to override an abstract progress
method rather than creating a delegate on the C# side:
// piCLI.h: The C++/CLI "bridge" between C# and C++
#pragma once
#include "pi.h"
namespace CLI {
public ref class PI abstract {
delegate void ComputeProgressDelegate(int percentCompleted);
public:
double compute() {
using System::IntPtr;
using System::Runtime::InteropServices::Marshal;
ComputeProgressDelegate^ callback = gcnew ComputeProgressDelegate(
this,
&PI::progress
);
IntPtr cbPtr = Marshal::GetFunctionPointerForDelegate(callback);
return Cpp::PI::compute(
static_cast<Cpp::ComputeProgressCallback>(cbPtr.ToPointer())
);
}
protected:
virtual void progress(int percentCompleted) abstract;
};
}
namespace CSharp {
public sealed class PI : CLI.PI {
protected override void progress(int percentCompleted) {
System.Console.WriteLine(
percentCompleted.ToString() + "% completed."
);
}
}
}
Pass a C++/cli defined Native function to your native C++ as a callback and upon callback use a gcroot to call your managed C# function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With