0

I know questions like this have been asked, but none of the answers have helped me. Although I have coded some C++ in the past as needed I am not fluent by any stretch. Today, I am stuck trying to pass a callback function to a function in another class. Below is a short example:

#include "stdafx.h"
#include <functional>

class A
{
private:
    int _someMemberVar = 7;

public:
    void SomeFunction(std::function<void(int)> func)
    {
        func(_someMemberVar);
    }
};

class B
{
public:
    void DoSomeWork()
    {
        A a;

        // error C2275: 'std::function<void (int)>' : illegal use of this type as an expression
        a.SomeFunction(std::function<void(int)> &B::MyCallback);
    }

    void MyCallback(int i)
    {
        printf("parameter is %d\r\n", i);
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    B b;
    b.DoSomeWork();

    return 0;
}

I have tried the example code from this std::function documentation page, but it doesn't compile. I had similar issues with other examples I found, such as those in the dissertation here. I am using Visual Studio 2013.

Googling the various compiler errors hasn't helped me sort this out and I'm feeling frustrated and confused. About the only thing I know for sure is that we C# programmers sure are spoiled. Any help is greatly appreciated.


Edit: Thanks so much for all the help everyone. All the answers provided a working solution, and if I could green check them all I would. I went with the answer posted by super because it had the most explanation and seems closest to what I am porting. Thanks again all!

Slippery Pete
  • 3,051
  • 1
  • 13
  • 15

3 Answers3

2

Logically, you need an object to call the callback function on. Here's one way to do it:

a.SomeFunction([this] (int const i) { MyCallback(i); });
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • @SlipperyPete yes, this a lambda. The reason why your version does not work is that member functions must be called on some object. Lambda simply captures that object and calls the function on it when needed. – Aykhan Hagverdili Apr 29 '20 at 19:17
  • @SlipperyPete see more about lambdas here https://es.cppreference.com/w/cpp/language/lambda – Aykhan Hagverdili Apr 29 '20 at 19:17
2

Apart from the missing brackets around &B::MyCallback (typo?) the main issue here is that a member function and a normal function are not the same thing.

A member function acts on an object, while a function does not, so a pointer to a member function can't just be converted to a normal function pointer.

The most straight forward solution in your case is to pass a lambda that captures this.

a.SomeFunction([&](int i){ MyCallback(i); });

The lambda will capture the current object and forward i as a parameter to the member function.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
super
  • 12,335
  • 2
  • 19
  • 29
  • This works also. Why does this one have the ampersand in brackets whereas @Ayxan's answer has "this" in brackets? – Slippery Pete Apr 29 '20 at 19:17
  • @SlipperyPete They're different ways of "capturing" things from the outside scope into the lambda, and a great example of complexity not needed for this particular task. You could read more about them in your C++11 book though – Asteroids With Wings Apr 29 '20 at 20:00
  • @SlipperyPete `[&]` means capture by reference by default. `[this]` means capture the current object by reference. Here they achieve the same thing since the only thing captured is the current object. – super Apr 29 '20 at 20:11
2

Here's another way to do the same thing. Assuming that you don't want to change the method into a static method and you want to call the MyCallback method on this

using namespace std::placeholders;

    std::function<void(int)> func = std::bind(&B::MyCallback, this, _1);
    a.SomeFunction(func);
john
  • 85,011
  • 4
  • 57
  • 81
  • Oh, no. Don't recommend `bind`. Lambdas made the whole thing simpler and retired `std::bind` a decade ago. – Aykhan Hagverdili Apr 29 '20 at 18:45
  • 1
    `bind` is fine here, Ayxan. – Asteroids With Wings Apr 29 '20 at 18:46
  • @AsteroidsWithWings Yes, it gets the job done, but why would you recommend it over the simpler alternative? – Aykhan Hagverdili Apr 29 '20 at 18:47
  • 1
    Since C++20, `bind_front` will be also hated :D But it looks nice `a.SomeFunction( std::bind_front(&B::MyCallback, this) );`. – rafix07 Apr 29 '20 at 18:50
  • @Ayxan It being simpler is subjective. I would argue that introducing a lambda just to bind a `this` parameter has an unnecessarily high cognitive load, particularly as you can't even see it doing that job. It's also a lot of symbols for your brain to parse, when a simple function call will do and is completely expressive in showing the programmer's intent. Finally, it avoids nasty argument-forwarding antics that can make the lambda variety quite long and quite unwieldy, quite quickly. – Asteroids With Wings Apr 29 '20 at 18:51
  • If you're not familiar with lambda syntax (which includes the OP I guess) then I think the above is a little easier to understand. – john Apr 29 '20 at 18:51
  • 1
    @AsteroidsWithWings It doesn't really apply in this example, but lambdas deal with overloaded functions, references and are more likely to get inlined by the compiler. So all reasons to prefer it are not subjective. – super Apr 29 '20 at 18:55
  • @super "Simpler" is subjective. Some other reasons are objective yes, but then you have to pit those against the ones I gave. And we are indeed talking about this specific case. There is no blanket "best" approach to anything, as I'm sure you know! – Asteroids With Wings Apr 29 '20 at 18:56
  • 1
    @AsteroidsWithWings Absolutely. I think that the lambda syntax is far more readable, so for me it's a no brainer. – super Apr 29 '20 at 18:58
  • @AsteroidsWithWings I too meant that lambdas are far more readable as you see what arguments go where. So, simpler in my opinion. – Aykhan Hagverdili Apr 29 '20 at 19:02
  • @Ayxan That's fine, that's your choice, but your initial comment made matter-of-fact statements that aren't true, or _at best_ are a matter of opinion. – Asteroids With Wings Apr 29 '20 at 19:03