Search code examples
c#delegatesc++-cliclr

C++/CLI from typedef std::function to managed delegate


I have a big class from extern library and I need to use a unmanaged callback in managed code. Unmanaged code simplified:

typedef std::function<void(const std::string &, Float)> ProgressCallback;
class MeshGenerator{
public:   
ProgressCallback progress;
/// <summary>
/// set callback
/// </summary>
/// <param name="_callback"></param>
inline void SetCallback(ProgressCallback _callback){ this-> progress =_callback; }   
};

Now my manage Code simplified:

public delegate void CallbackDelegate(String^ cap, float data);
public ref class MeshWrapper
{

private:
MeshGenerator *gen;
public:
[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
CallbackDelegate^ _Delegate;
inline MeshWrapper(){ 
  this->gen = new MeshGenerator();
  _Delegate = gcnew CallbackDelegate(this, &MeshWrapper::Progress);
  auto ptr = Marshal::GetFunctionPointerForDelegate(_Delegate).ToPointer();
  ProgressCallback *p = static_cast<ProgressCallback*>(ptr);
  this->gen->SetCallback(*p);
}   
inline void Progress(String^ cap, float s){ System::Console::WriteLine(cap + s);}
};

this code compile, but in MeshWrapper constructor the static cast ("static_cast<ProgressCallback*>(ptr);") give me an empty delegate.

I'm probably making some conceptual mistakes. Thanks for your help.


Solution

  • You cannot simply cast a function pointer to a std::function pointer, you need to construct a std::function object:

    auto pc = ProgressCallback(reinterpret_cast<void(*)(const std::string &, Float)>(ptr));
    this->gen->SetCallback(pc);
    

    Alternatively, pass a lambda that performs the cast in-line and maybe fixes up some of the arguments:

    this->gen->SetCallback([=](const std::string & s, Float f) {
      auto fn = reinterpret_cast<void(*)(const std::string &, float)>(ptr);
      fn(msclr::interop::marshal_as<String^>(s), f);
    });