How to pass a member function of a class as a callback?

I use an API that requires me to pass a function pointer as a callback. I am trying to use this API from my class, but am getting compilation errors.

Here is what I did from my constructor:

m_cRedundencyManager->Init(this->RedundencyManagerCallBack); 

This does not compile - the following error appears:

Error 8 of error C3867: 'CLoggersInfra :: RedundencyManagerCallBack': list of calls to missing function arguments; use '& CLoggersInfra :: RedundencyManagerCallBack' to create a pointer to an element

I tried using &CLoggersInfra::RedundencyManagerCallBack - it didn't work for me.

Any suggestions / explanations for this ??

I am using VS2008.

Thank!!

+61
c ++ callback c ++ 03 function-pointers
Dec 30 '09 at 13:35
source share
9 answers

This does not work because a pointer to a member function cannot be treated like a regular pointer to a function, because it expects an argument to the this object.

Instead, you can pass a static member function as follows, which are similar to regular non-member functions:

 m_cRedundencyManager->Init(&CLoggersInfra::Callback, this); 

The function can be defined as follows

 static void Callback(int other_arg, void * this_pointer) { CLoggersInfra * self = static_cast<CLoggersInfra*>(this_pointer); self->RedundencyManagerCallBack(other_arg); } 
+45
Dec 30 '09 at 13:55
source share

This is a simple question, but the answer is surprisingly complex. Short answer: you can do what you are trying to do with std :: bind1st or boost :: bind. Longer answer below.

The compiler is correct to suggest you use & CLoggersInfra :: RedundencyManagerCallBack. First, if RedundencyManagerCallBack is a member function, the function itself does not belong to any particular instance of the CLoggersInfra class. It belongs to the class itself. If you have ever called a static function of a class before, you may notice that you are using the same syntax SomeClass :: SomeMemberFunction. Since the function itself is “static” in the sense that it belongs to a class and not to a specific instance, you use the same syntax. '&' Necessary, because technically speaking, you are not passing functions directly - functions are not real objects in C ++. Instead, you technically pass the memory address for the function, that is, a pointer to the place where the function instructions begin in memory. The result is the same, but you actually “pass the function” as a parameter.

But this is only half the problem in this case. As I said, the RedundencyManagerCallBack function does not "belong" to any particular instance. But it sounds like you want to pass it as a callback based on a specific instance. To understand how to do this, you need to understand what are actually member functions: ordinary functions are "not defined in any class" with an additional hidden parameter.

For example:

 class A { public: A() : data(0) {} void foo(int addToData) { this->data += addToData; } int data; }; ... A an_a_object; an_a_object.foo(5); A::foo(&an_a_object, 5); // This is the same as the line above! std::cout << an_a_object.data; // Prints 10! 

How many parameters does A :: foo accept? Usually we say 1. But under the hood, foo really takes 2. Looking at the definition of A :: foo, it needs a specific instance of A in order for the “this” pointer to make sense (the compiler should know that “this). Usually you indicate what you want it to be with the syntax MyObject.MyMemberFunction (). But it's just syntactic sugar for passing the MyObject address as the first parameter of MyMemberFunction. Similarly, when we declare member functions in class definitions, we don’t put ' this' to the parameter list, but it's just a gift from the language designers, allowing preserving typing. Instead, you must specify that the member function is static in order to discard it by automatically getting the additional parameter “this.” If the C ++ compiler converted the above example into C code (the original C ++ compiler really worked that way) he would probably write something like this:

 struct A { int data; }; void a_init(A* to_init) { to_init->data = 0; } void a_foo(A* this, int addToData) { this->data += addToData; } ... A an_a_object; a_init(0); // Before constructor call was implicit a_foo(&an_a_object, 5); // Used to be an_a_object.foo(5); 

Returning to your example, now there is an obvious problem. "Init" wants a pointer to a function that takes one parameter. But & CLoggersInfra :: RedundencyManagerCallBack is a pointer to a function that takes two parameters: a regular parameter and a secret this parameter. Thus, why are you still getting a compiler error (as a note: if you have ever used Python, this confusion is the reason that the "self" parameter is necessary for all member functions).

A detailed way to handle this is to create a special object that contains a pointer to the instance you need and has a member function called "run" or "execute" (or overloads the operator (), which takes parameters for the member function, and simply calls the member function with these parameters in the saved instance, but this will require you to change the 'Init' so that it uses your special object and not a raw pointer to the function, and it looks like Init is some other code. special class for every case when This problem will lead to bloat code.

So, finally, a good solution, boost :: bind and boost :: function, the documentation for each can be found here:

boost :: bind docs , boost :: function docs

boost :: bind will allow you to take a function and parameter for this function and create a new function where this parameter is "locked" in place. Therefore, if I have a function that adds two integers, I can use boost :: bind to create a new function in which one of the parameters is locked to say 5. This new function will only take one integer parameter and will always be specifically add 5 to this. Using this technique, you can "lock" the hidden parameter "this" so that it is a specific instance of the class, and generate a new function that takes only one parameter, as you want (note that the hidden parameter is always the first parameter, and normal parameters come in order after it). Look at the boost :: bind docs for examples; they even specifically discuss the use of it for member functions. Technically there is a standard function std :: bind1st that you can also use, but boost :: bind is more general.

Of course, there is only one more catch. boost :: bind will make a good boost :: function for you, but technically this is still not a raw function pointer, as Init probably wants. Fortunately, boost provides a way to convert boost :: function to raw pointers, as described here in StackOverflow. How this is implemented is beyond the scope of this answer, although it is also interesting.

Don't worry if this seems ridiculously complicated - your question crosses some of the darker corners of C ++, and boost :: bind is incredibly useful as soon as you learn it.

C ++ 11 update: instead of boost :: bind you can now use a lambda function that captures "this". It is basically that the compiler generates the same thing for you.

+104
Dec 31 '09 at 5:52
source share

This answer is a response to the comment above and does not work with VisualStudio 2008, but should be used with later compilers.




Meanwhile, you no longer need to use the void pointer, and there is no need for a raise, since std::bind and std::function . One of the advantages (compared to null pointers) is type safety, since the return type and arguments are explicitly specified using std::function :

 // std::function<return_type(list of argument_type(s))> void Init(std::function<void(void)> f); 

Then you can create a function pointer using std::bind and pass it to Init:

 auto cLoggersInfraInstance = CLoggersInfra(); auto callback = std::bind(&CLoggersInfra::RedundencyManagerCallBack, cLoggersInfraInstance); Init(callback); 

A complete example of using std::bind with members, static members, and non-member functions:

 #include <functional> #include <iostream> #include <string> class RedundencyManager // incl. Typo ;-) { public: // std::function<return_type(list of argument_type(s))> std::string Init(std::function<std::string(void)> f) { return f(); } }; class CLoggersInfra { private: std::string member = "Hello from non static member callback!"; public: static std::string RedundencyManagerCallBack() { return "Hello from static member callback!"; } std::string NonStaticRedundencyManagerCallBack() { return member; } }; std::string NonMemberCallBack() { return "Hello from non member function!"; } int main() { auto instance = RedundencyManager(); auto callback1 = std::bind(&NonMemberCallBack); std::cout << instance.Init(callback1) << "\n"; // Similar to non member function. auto callback2 = std::bind(&CLoggersInfra::RedundencyManagerCallBack); std::cout << instance.Init(callback2) << "\n"; // Class instance is passed to std::bind as second argument. // (heed that I call the constructor of CLoggersInfra) auto callback3 = std::bind(&CLoggersInfra::NonStaticRedundencyManagerCallBack, CLoggersInfra()); std::cout << instance.Init(callback3) << "\n"; } 

Possible conclusion:

 Hello from non member function! Hello from static member callback! Hello from non static member callback! 

In addition, using std::placeholders you can dynamically pass arguments return f("MyString"); the call (for example, this allows you to use return f("MyString"); in Init if f has a string parameter).

+10
Aug 05 '17 at 18:16
source share

What argument does Init matter? What is the new error message?

C ++ method pointers are a bit complicated to use. In addition to the method pointer itself, you also need to specify a pointer to an instance (in this case, this ). Maybe Init expects it as a separate argument?

+3
Dec 30 '09 at 13:38
source share

Can m_cRedundencyManager use member functions? Most callbacks are configured to use regular functions or static member functions. Check out this page in the C ++ FAQ Lite for more information.

Update: The function declaration you submitted shows that m_cRedundencyManager expects a form function: void yourCallbackFunction(int, void *) . Therefore, member functions are not acceptable as callbacks in this case. A static member function may work, but if that is not acceptable in your case, the following code will also work. Note that it uses the evil from void * .

 // in your CLoggersInfra constructor: m_cRedundencyManager->Init(myRedundencyManagerCallBackHandler, this); 
 // in your CLoggersInfra header: void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr); 
 // in your CLoggersInfra source file: void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr) { ((CLoggersInfra *)CLoggersInfraPtr)->RedundencyManagerCallBack(i); } 
+3
Dec 30 '09 at 13:47
source share

A pointer to a member function of a class does not match a pointer to a function. A class member accepts an implicit optional argument (this pointer) and uses a different calling convention.

If your API expects an unanswered callback function, then what you need to pass.

+3
Dec 30 '09 at 13:47
source share

I see that init has the following redefinition:

 Init(CALLBACK_FUNC_EX callback_func, void * callback_parm) 

where is CALLBACK_FUNC_EX

 typedef void (*CALLBACK_FUNC_EX)(int, void *); 
+1
Dec 30 '09 at 13:47
source share

This question and answer from the C ++ FAQ Lite I believe your question and considerations related to the answer are pretty good. A short snippet from the webpage I'm linked to:

Dont.

Since a member function is meaningless without calling the object it is on, you cannot do it directly (if the X Window system was rewritten in C ++, it will probably pass references to objects around, not just pointers to functions; naturally, the objects will be required function and probably a lot more).

0
Dec 30 '09 at 13:51
source share

Necromancing.
I think the answers to date are a bit unclear.

Let's make an example:

Suppose you have an array of pixels (an array of ARGB values ​​int8_t)

 // A RGB image int8_t* pixels = new int8_t[1024*768*4]; 

Now you want to create a PNG. To do this, you call the toJpeg function

 bool ok = toJpeg(writeByte, pixels, width, height); 

where writeByte is a callback function

 void writeByte(unsigned char oneByte) { fputc(oneByte, output); } 

The problem here is: FILE * output must be a global variable.
Very bad if you are in a multi-threaded environment (for example, an http server).

Therefore, you need some way to draw the output of a non-global variable, while maintaining the signature of the callback.

The immediate solution that comes to mind is a closure that we can emulate using a class with a member function.

 class BadIdea { private: FILE* m_stream; public: BadIdea(FILE* stream) { this->m_stream = stream; } void writeByte(unsigned char oneByte){ fputc(oneByte, this->m_stream); } }; 

And then do

 FILE *fp = fopen(filename, "wb"); BadIdea* foobar = new BadIdea(fp); bool ok = TooJpeg::writeJpeg(foobar->writeByte, image, width, height); delete foobar; fflush(fp); fclose(fp); 

However, contrary to expectations, this does not work.

The reason is that C ++ member functions are implemented as C # extension functions.

So do you have

 class/struct BadIdea { FILE* m_stream; } 

as well as

 static class BadIdeaExtensions { public static writeByte(this BadIdea instance, unsigned char oneByte) { fputc(oneByte, instance->m_stream); } } 

Therefore, when you want to call writeByte, you need to pass not only the writeByte address, but also the address of the BadIdea instance.

So when you have a typedef for the writeByte procedure, and it looks like this

 typedef void (*WRITE_ONE_BYTE)(unsigned char); 

And you have a writeJpeg signature that looks like this

 bool writeJpeg(WRITE_ONE_BYTE output, uint8_t* pixels, uint32_t width, uint32_t height)) { ... } 

it is fundamentally impossible to pass a two-address member function to a pointer to a unicast function (without changing writeJpeg), and there is no way around this.

The next best thing you can do in C ++ is to use a lambda function:

 FILE *fp = fopen(filename, "wb"); auto lambda = [fp](unsigned char oneByte) { fputc(oneByte, fp); }; bool ok = TooJpeg::writeJpeg(lambda, image, width, height); 

However, since lambda does nothing more than pass an instance to a hidden class (for example, "BadIdea" -class), you need to change the signature of writeJpeg.

The advantage of lambda over manual class is that you just need to change one typedef

 typedef void (*WRITE_ONE_BYTE)(unsigned char); 

at

 using WRITE_ONE_BYTE = std::function<void(unsigned char)>; 

And then you can leave everything else untouched.

You can also use std :: bind

 auto f = std::bind(&BadIdea::writeByte, &foobar); 

But this, behind the scenes, just creates a lambda function that also requires a change in typedef.

So no, there is no way to pass a member function to a method that requires a static function pointer.

But lambdas are an easy way to go if you have control over the source.
Otherwise, you're out of luck.
There is nothing you can do with C ++.

Remarks:
std :: function requires #include <functional>

However, since C ++ also allows you to use C, you can do this with libffcall in simple C, if you don't mind binding the dependency.

Download libffcall from GNU (at least in Ubuntu, do not use the distribution package - it does not work), unzip it.

 ./configure make make install gcc main.c -l:libffcall.a -o ma 

main.c:

 #include <callback.h> // this is the closure function to be allocated void function (void* data, va_alist alist) { int abc = va_arg_int(alist); printf("data: %08p\n", data); // hex 0x14 = 20 printf("abc: %d\n", abc); // va_start_type(alist[, return_type]); // arg = va_arg_type(alist[, arg_type]); // va_return_type(alist[[, return_type], return_value]); // va_start_int(alist); // int r = 666; // va_return_int(alist, r); } int main(int argc, char* argv[]) { int in1 = 10; void * data = (void*) 20; void(*incrementer1)(int abc) = (void(*)()) alloc_callback(&function, data); // void(*incrementer1)() can have unlimited arguments, eg incrementer1(123,456); // void(*incrementer1)(int abc) starts to throw errors... incrementer1(123); // free_callback(callback); return EXIT_SUCCESS; } 

And if you use CMake, add the linker library after add_executable

 add_library(libffcall STATIC IMPORTED) set_target_properties(libffcall PROPERTIES IMPORTED_LOCATION /usr/local/lib/libffcall.a) target_link_libraries(BitmapLion libffcall) 

or you can just dynamically link libffcall

 target_link_libraries(BitmapLion ffcall) 

Remarks:
You might want to include the libffcall headers and libraries or create a cmake project with the contents of libffcall.

0
Mar 27 '19 at 17:49
source share



All Articles