Best way to have a C ++ member function called by a C callback?

Given a typical class:

  struct whatever
 {
     void Doit ();
 };

 Whatever w;

What is the best way to get a member function to be called with a C void * callback like pthread_create () or a signal handler?

  pthread_t pid;

 pthread_create (& pid, 0, ... & w.Doit () ...);
+4
source share
9 answers

Most C callbacks allow you to specify an argument, for example.

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); 

So you may have

 void myclass_doit(void* x) { MyClass* c = reinterpret_cast<MyClass*>(x); c->doit(); } pthread_create(..., &myclass_doit, (void*)(&obj)); 
+6
source

The most concise solution is to define in the header file shared by all your code:

  template <typename T, void (T :: * M) ()>
 void * thunk (
     void * p)
 {
     T * pt = static_cast <T *> (p);

     (pt -> * M) ();

     return 0;
 }

You probably want to define 4 versions: each where thunk returns void and void * , and each where the member function returns void and void * . That way, the compiler can do the best, depending on the circumstances (and in fact it will complain if everything doesn't match.)

Then, all you need to enter every time you run into one of the following situations:

pthread_create (& pid, 0, & thunk <Whatever, & Whatever :: doit>, & w);

This will work when the method is private if the method is referenced from class code. (If not, I need to wonder why the code refers to a private method.)

+5
source

Use a shell C function, for example:

 struct Whatever { void Doit(); }; extern "C" static int DoItcallback (void * arg) { Whatever * w = (Whatever *) arg; w->DoIt(); return something; } 

Only works if you can pass a pointer to the class in any way. Most callback mechanisms allow this.

Afaik is the only way to do this. You cannot directly call a method from C without a big hack.

+3
source

Is a member function private? If not, use the standard idiom:

 void* pthread_foo_caller(void* arg) { Foo* foo = static_cast<Foo*>(arg); foo->bar(); return NULL; } 

If the member function is private, you can declare a static method in the class that takes a "this" pointer and calls the corresponding method. For instance:

 class Foo { public: static pthread_foo_caller(void* arg); ... }; void* Foo::pthread_foo_caller(void* arg) { Foo* foo = static_cast<Foo*>(arg); foo->private_bar(); return NULL; } 
+3
source

A member function MUST be static. Non-static have an implied argument of "this". Pass a pointer to your any instance as void * so that the static member can get the instance.

+1
source

Here is an easy way to do this, remember to properly manage the lifetime of your MemberFunction.

 #include class MyClass { public: void DoStuff() { printf("Doing Stuff!"); } }; struct MemberFunction { virtual ~MemberFunction(){} virtual void Invoke() = 0; }; void InvokeMember(void *ptr) { static_cast(ptr)->Invoke(); } template struct MemberFunctionOnT : MemberFunction { typedef void (T::*function_t)(); public: MemberFunctionOnT(T* obj, function_t fun) { m_obj = obj; m_fun = fun; } void Invoke() { (m_obj->*m_fun)(); } private: T *m_obj; function_t m_fun; }; template MemberFunction* NewMemberFunction(T *obj, void (T::*fun)()) { return new MemberFunctionOnT(obj, fun); } //simulate a C-style function offering callback functionality. void i_will_call_you_later(void (*fun)(void*), void *arg) { fun(arg); } int main() { //Sample usage. MyClass foo; MemberFunction *arg = NewMemberFunction(&foo, &MyClass::DoStuff); i_will_call_you_later(&InvokeMember, arg); return 0; } 
+1
source

One thing you should be aware of is that if you write code like this:

 try { CallIntoCFunctionThatCallsMeBack((void *)this, fCallTheDoItFunction); } catch (MyException &err) { stderr << "badness."; } void fCallTheDoItFunction(void *cookie) { MyClass* c = reinterpret_cast<MyClass*>(cookie); if (c->IsInvalid()) throw MyException; c->DoIt(); } 

You may encounter serious problems depending on your compiler. It turns out that in some compilers, when they optimize, they see one C call in the try / catch block and exclaim with joy: "I call the C function, which, since it is good, the old-fashioned C, cannot throw away! Calloo-cally I will delete all the try residues / catch, since it will never be reached.

Stupid compiler.

Do not call C, which calls you and expects you to be able to catch it.

+1
source

See link

In principle, this is not possible because: "Pointers to non-static members are different from regular C function pointers, because they need this pointer to the class object to be passed. Thus, ordinary function pointers and [pointers to] non-static member functions have different and incompatible signatures

0
source

Until I used it from C, I highly recommend looking at libsigC ++ for callbacks . This was what I needed several times when making C ++ callbacks.

0
source

Source: https://habr.com/ru/post/1277225/


All Articles