简体   繁体   中英

Pass member function to C interface requiring callback

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)
    { ... }
};

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);
}

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 func2 s 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 .

This is simply an alternative to @RSahu's solution using static members in A class. IMHO, is is functionnally the same as global variables, but at least you get a namespace containment in you class:

class A
{
    static A* current_obj;

public:    
    void func1()
    {
         current_obj = this;
         registerCallback(func3);
    }

private:
    void func2(char* param)
    { ... }
    static void func3(char *param) {
        if (NULL == current_obj) {
            // error ...
        }
        current_obj->func2(param);
    }
...
};

And as demonstrated above, the registered functions can be private to the class, because the only function that need to be called externally is here func1 .

But it suffers the same problem: you can register only one object at the same time.

Maybe I'm not understanding the problem but why not just build a simple wrapper to hold the "this" pointer. So, the application will use this callback (or lambda)

void RegisterCallback_withthis(char* arg, void* thisptr)

Only a single callback function can be registered with the DLL at one time. So, a single global thisptr is good.

static void* thisptr;
void RegisterCallback(char* arg)
{
     RegisterCallback_withthis(argc, thisptr);
}

The application will have to setup the thisptr while registering the callback.

thisptr = this;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM