Compile Error When Passing A Member Function as a Callback

Hi friends,

Basically, I want to pass a callback to a function and I'm getting an error. Could you help?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

#include <iostream>
#include <functional>

class Foo
{
public:
	Foo() = default;
	~Foo() = default;

	void write(std::string message, const std::function<void()>& callback) { /* Do something with callback */ }
private:

};


class Bar
{
public:
	Bar() = default;
	~Bar() = default;

	void callback(){ /* Do something */ }

	void start() { foo.write("hi", &Bar::callback); }
private:
	Foo foo;

};

int main(int argc, char* argv[])
{

	Bar bar;
	bar.start();

}



1
2
3
4
5
6
7
Undefined symbols for architecture x86_64:
  "std::__1::__any::__any(...)", referenced from:
      void std::__1::__invoke_void_return_wrapper<void>::__call<void (Bar::*)()>(void (Bar::*)()) in main.o
  "std::__1::__invoke(std::__1::__any, ...)", referenced from:
      void std::__1::__invoke_void_return_wrapper<void>::__call<void (Bar::*)()>(void (Bar::*)()) in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Last edited on
When I change it to this:

1
2
	void write(std::string message, void (*func)(void) callback) { /* Do something with callback */ }


I get

1
2
3
error: cannot initialize a parameter of type 'void (*)()' with an
      rvalue of type 'void (Bar::*)()'
        void start() { foo.write("hi", &Bar::callback); }
I don't really think you want to use std::function.

I think you'd be better off with std::bind

Think: In your current form, if it worked, where does f.write end up knowing the instance of bar upon which to perform the callback?

Putting an instance of Bar (which at that moment is "*this"), takes a few steps you might not need with another approach.

Consider std::bind, or maybe just pass "*this" and a pointer to the member function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Bar;

class Foo
{
public:
	Foo() = default;
	~Foo() = default;

	void write(std::string message, Bar &, void (Bar::*f)() );
private:

};


class Bar
{
public:
	Bar() = default;
	~Bar() = default;

	void callback(){ /* Do something */ }

	void start() 
       {
        foo.write("hi", *this, &Bar::callback ); 
       }
private:
	Foo foo;

};


void write( std::string message, Bar &b, void ( Bar::*f )() )
{
 (b.*f)();
}
Consider using a templated callback, and invoking it with std::invoke.
https://en.cppreference.com/w/cpp/utility/functional/invoke
That offers a great deal of flexibility.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <functional>
#include <string>
#include <cstdio>

struct foo
{
    template < typename FN, typename... ARGS >
    void call_it( FN fn, ARGS&&... args ) const
    {
        std::cout << "foo::call_it => std::invoke -> " ;
        std::invoke( fn, std::forward<ARGS>(args)... ) ;
    }
};

int main()
{
    struct bar
    {
        void call_back( int i ) const
        { std::cout << "bar::call_back(" << i << "), this == " << this << '\n' ; }

        void start() { f.call_it( &bar::call_back, this, 1234 ) ; }

        foo f ;
    };

    bar b ;
    b.start() ;

    b.f.call_it( [] { std::cout << "main::closure: hello world!\n" ; } ) ;

    b.f.call_it( &std::printf, "std::printf: %d, %f, %s\n", 1234, 56.78, "bye!" ) ;
}

http://coliru.stacked-crooked.com/a/e9e9f0e3d7e4c6b3
https://rextester.com/KWQU22216
Topic archived. No new replies allowed.