Compile Error When Passing A Member Function as a Callback

Jun 26, 2019 at 10:14pm
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 Jun 26, 2019 at 10:16pm
Jun 26, 2019 at 10:25pm
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); }
Jun 26, 2019 at 10:41pm
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)();
}
Jun 27, 2019 at 1:55am
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.