Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass member function to C interface requiring callback

Tags:

c++

c

std

I have an old .dll with plain C interface which takes callbacks to invoke when some work is done. The callback it takes is of type void (f*)(char* arg).

I'm looking for a trick to pass a C++ function object there so that the callback is invoked with "this" pointer stored somewhere, something like bind, but simple bind doesn't work

To illustrate this: C interface:

typedef void (f*)(char* param) Callback;
void registerCallback(Callback c);

Usage in C++:

class A
{
    void func1()
    {
         registerCallback(std::bind(&A::func2, _1, this)); // obviously doens't work
    }

    void func2(char* param)
    { ... }
};
like image 318
Dmitry Avatar asked Sep 26 '22 05:09

Dmitry


2 Answers

The only way that to make this work with an instance of a class and a member function of the class, that I am aware of, is:

  1. Store a pointer to an object in a global variable.
  2. Register a non-member function.
  3. Call the member function from the non-member function using the global variable.
class A
{
    void func1();

    void func2(char* param)
    { ... }
};

// Global pointer
A* aPtr = NULL;

// Non-member function.
// extern "C" is probably needed if the older DLL is expecting
// an unmangled C function pointer.
extern "C" void globalFunc2(char* param)
{
   if ( aPtr == NULL )
   {
      // Deal with error
   }
   else
   {
      aPtr->func2(param);
   }
}

void A::func1()
{
    aPtr = this;
    registerCallback(globalFunc2);
}
like image 81
R Sahu Avatar answered Sep 29 '22 05:09

R Sahu


The way I see it, the core of the problem is letting the caller of the callback function (the entity registerCallback is registering with) know which A object's func2 to call.

As far as I can understand from your problem, you basically have a bunch of A objects and you only want a number of these A objects to execute their respective func2s when the callback event occurs. However, the caller of the callback function does not know who to call when the callback event occurs. Since you mentioned that it's an old .dll, I assume we cannot just go in and change how registerCallback works, but we need to store the A objects that are registered for callback.

Therefore,

class Informer {
public:
  static void InformAllMembers(char* param) {
    for(auto& a : m_Members) { //Inform all As registered with me
      a->func2(param);
    }
  }
  static void Register(A* a) {
    m_Members.push_back(a);
  }
private:
  static std::vector<A*> m_Members;
};
std::vector<A*> Informer::m_Members;
...

registerCallback(Informer::InformAllMembers);

A a;
Informer::Register(&a);

NOTE: You will have to handle cases where some of the registered A objects are destroyed and unregister them from the Informer.

like image 39
Nard Avatar answered Sep 29 '22 07:09

Nard