Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Wrapping a C++ Callback in a .NET System::Action

5.00/5 (3 votes)
17 Nov 2011CPOL 21K  
Wrapping a C++ callback in a .NET Action so you can use the .NET Task Parallel Library
In this tip, you will see how to use a template class to wrap a C++ callback in an action.

Introduction

Here's a template class for wrapping a C++ callback in an action. This is so you can queue up tasks in C++ using the .NET Task Parallel Library.

C++
// This class bridges a C++ callback to a .NET Action, which
// can be queued up in a .NET Task.
template<typename T1, typename T2>
ref class CallbackToAction
{
public:
  typedef void (CallbackHandler)(T1, T2);

  // Converts function pointer and parameters to an action. Note that
  // the arguments need to be either pointers, values types, or managed pointers.
  // You cannot pass objects by value.
  static System::Action^ Convert(CallbackHandler* handler, T1 arg1, T2 arg2)
  {
    CallbackToAction<T1,T2>^ callbackToAction = gcnew CallbackToAction<T1,T2>();
    callbackToAction->_handler = handler;
    callbackToAction->_arg1 = arg1;
    callbackToAction->_arg2 = arg2;
    return callbackToAction->ToAction();
  }

private:

  void DoCallback()
  {
    _handler(_arg1, _arg2);
  }

  System::Action^ ToAction()
  {
    return gcnew Action(this, &CallbackToAction::DoCallback);
  }

  CallbackHandler* _handler;
  T1 _arg1;
  T2 _arg2;
};

Here's a whole console app that shows you how to use it:

C++
#include "CallbackToAction.h"
using namespace System;
using namespace System::Threading;
using namespace System::Threading::Tasks;

class MyClass
{
  int _sleepTime;
public:
  MyClass(int sleepTime)
  {
    _sleepTime = sleepTime;
  }

  void DoSomething()
  {
    Thread::Sleep(_sleepTime);
  }
};

typedef CallbackToAction<MyClass*, Barrier^> MyCallbackToAction;

void CallbackFunction(MyClass* myObject, Barrier^ barrier)
{
  myObject->DoSomething();
  Console::WriteLine(L"A task has finished");
  barrier->RemoveParticipant();
}

int main(array<System::String ^> ^args)
{
  Barrier^ barrier = gcnew Barrier(3);
  Console::WriteLine(L"Queing 2 tasks");
  MyClass myObject1(1000);
  MyClass myObject2(3000);
  Task::Factory->StartNew(MyCallbackToAction::Convert
                         (&CallbackFunction, &myObject1, barrier));
  Task::Factory->StartNew(MyCallbackToAction::Convert
                         (&CallbackFunction, &myObject2, barrier));

  Console::WriteLine(L"Waiting");
  barrier->SignalAndWait();
  Console::WriteLine(L"All Finished. Press Enter");
  Console::ReadLine();

  return 0;
}

History

  • 17th November, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)