user1173240
user1173240

Reputation: 1475

Using std::bind and std::function to use a class member function as callback for another class member routine

This may have been explored before, but I am not certain how it works out, and how t should in my particular case..

Essentially I have a class, with a callback defined as :

class Foo
{
    public:
    using someCallbackName = std::function<void(int)>;
    void someFunc();
    Foo(int, someCallBackName);
    private : 
    someCallbackName m_callBack;
}

void Foo::someFunc()
{
    m_callBack(1);
}

I used to call this in main() or by just referencing function of similar signature..

void someOtherFunction(int x)
{
    cout << x;
}

int main()
{
    Foo::someCallbackName callBack = someOtherFunction;
    Foo foo(5, callBack);
}

I decided though, that I may need someOtherFunction as a class member, and put it as part of a class. However, using class member function someOtherFunction as a callback required making it static, which worked fine, but which would mean it wouldn't have access to non-static class members, which sort of defeated the purpose to put it in a class.

I tried using : C++ callback using class member

and the struct access as given in : https://en.cppreference.com/w/cpp/utility/functional/bind

..but it does not seem to work, the std::bind to

Foo::someCallbackName callBack = std::bind(not_sure_what_to_use_here);

keeps giving errors saying no suitable conversion, which makes me think that somewhere the signature of callback or mechanism of using std::bind in the code is erroneous.

Keeping class Foo as is, how can m_callBack call someOtherFunction ?

Upvotes: 2

Views: 4535

Answers (2)

Clonk
Clonk

Reputation: 2070

As @Daniel Langr said, you can use a lambda function.

Otherwise if you want to use a callback that is a member function you need to bind it to an object.

#include <functional>
#include <iostream>

class Foo
{
public:
    using someCallbackName = std::function<void(int)>;
    void someFunc();
    Foo(int, someCallbackName);

private:
    someCallbackName m_callBack;
};

class Bar
{
public:
    void someOtherFunction(int x);
};

Foo::Foo(int i, someCallbackName cb)
{
    m_callBack = cb;
    someFunc();
}

void Foo::someFunc()
{
    m_callBack(1);
}

void Bar::someOtherFunction(int x)
{
    std::cout << x;
}

int main()
{
    Bar bar;
    Foo::someCallbackName callBack = std::bind(&Bar::someOtherFunction, &bar, std::placeholders::_1);
    Foo foo(5, callBack);
}

Be careful about the lifetime of barwhen doing this.

Oone way to adress the lifetime issue is to make Foo responsible for the lifetime of Bar (this design pattern is called a composition).

#include <functional>
#include <iostream>

// If you declare both class in different files you may need to look into "forward declaration"
class Bar
{
public:
    void someOtherFunction(int x);
};

class Foo
{
public:
    using someCallbackName = std::function<void(int)>;
    void someFunc();
    Foo(int, someCallbackName);

private:
    someCallbackName m_callBack;
    Bar bar;
};

Foo::Foo(int i)
{
    m_callBack = std::bind(&Bar::someOtherFunction, &bar, std::placeholders::_1);;
    someFunc();
}

// someOtherFunction and someFunc are left unchanged

int main()
{
    Foo foo(5);
}

Upvotes: 1

Daniel Langr
Daniel Langr

Reputation: 23497

You can "bind" the callback to a non-static member function of a particular object by using labmda:

class X {
  public:
    void someOtherFunction(int x) const { std::cout << x; }
};

int main() 
  X x;
  Foo::someCallbackName callBack = [&x](int i){ x.someOtherFunction(i); };
  Foo foo(5, callBack);
  foo.someFunc();
}

Live demo: https://wandbox.org/permlink/fUmrnD6xn1xr7zn0.


To avoid dangling references after x is destroyed, you can employ shared pointers, as follows (note it is captured by value):

Foo::someCallbackName callBack;
{
  auto ptr_x = std::make_shared<X>();
  callBack = [ptr_x](int i){ ptr_x->someOtherFunction(i); };
}
Foo foo(5, callBack);
foo.someFunc();

Live demo: https://wandbox.org/permlink/23euPcuDUsDENdRe.

Upvotes: 2

Related Questions