35

I can easily bind member functions to a std::function by wrapping them with a lambda expression with capture clause.

class Class
{
    Class()
    {
        Register([=](int n){ Function(n); });
    }

    void Register(std::function<void(int)> Callback)
    {

    }

    void Function(int Number)
    {

    }
};

But I want to bind them directly, something like the following.

// ...
Register(&Class::Function);
// ...

I think according to the C++11 standard, this should be supported. However, in Visual Studio 11 I get these compiler errors.

error C2440: 'newline' : cannot convert from 'int' to 'Class *'

error C2647: '.*' : cannot dereference a 'void (__thiscall Class::* )(int)' on a 'int'

danijar
  • 32,406
  • 45
  • 166
  • 297

6 Answers6

59

I think according to the C++11 standard, this should be supported

Not really, because a non-static member function has an implicit first parameter of type (cv-qualified) YourType*, so in this case it does not match void(int). Hence the need for std::bind:

Register(std::bind(&Class::Function, PointerToSomeInstanceOfClass, _1));

For example

Class c;
using namespace std::placeholders; // for _1, _2 etc.
c.Register(std::bind(&Class::Function, &c, _1));

Edit You mention that this is to be called with the same Class instance. In that case, you can use a simple non-member function:

void foo(int n)
{
  theClassInstance.Function(n);
}

then

Class c;
c.Register(foo);
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • Works, but can the syntax be simplified under the constraint that all the class reference are the same? – danijar Jun 16 '13 at 09:24
  • @danijar what do you mean by all the class references being the same? You need to pass a pointer to a `Class` instance. Which one you pass is up to you. – juanchopanza Jun 16 '13 at 09:26
  • 1
    Right. And this instance will always be the same. Now I would like to shorten the `bind(&Class::Function, this, _1)` with this information in mind. So, contentually, the `bind` is obsolete. Is there a technical way to get rid of it or perform the binding inside the function after passing? – danijar Jun 16 '13 at 09:31
  • @danijar will it be some kind of global instance or singleton? If so, then you can also make a global `std::function` where you bind to that instance. Then you just re-use that. Actually, there is no need to keep a global `std::function`. A simple non-member function could do the trick. – juanchopanza Jun 16 '13 at 09:33
  • No, sadly it isn't a global instance. – danijar Jun 16 '13 at 10:15
  • @danijar so what is it? Maybe you can post some code to clarify this? – juanchopanza Jun 16 '13 at 10:16
  • It is just a self containing class with different use cases. But if there is no way, I am going to accept that. – danijar Jun 16 '13 at 10:22
  • 1
    @Sasha Probably `void foo(int n)`. Fixed. – juanchopanza Oct 28 '19 at 21:54
  • dunno why, but `c.Register(std::bind(&(Class::Function), &c, _1));` worked for VS compiler, but failed for gcc and clang with a "error: call to non-static member function without an object argument" (that's gcc's error, clang's was similar). So don't add those extra parenthesis around `Class::Function`. – yano May 26 '23 at 22:00
47

According to Stephan T. Lavavej - "Avoid using bind(), ..., use lambdas". https://www.youtube.com/watch?v=zt7ThwVfap0&t=32m20s

In this case:

Class()
{
    Register([this](int n){ Function(n); });
}
Andy
  • 1,036
  • 12
  • 14
  • 1
    thanks for pointing this out and linking to the relevant portion of the video! – Nicolas Holthaus Sep 19 '16 at 19:19
  • This is arguably cleaner syntactically, but you've effectively nested a function inside a function rather than allowing it to be invoked directly, so each call is ever so slightly less efficient and slower. You've also used an allocating method to set it up, which burns just a bit more memory and CPU time on that step. The std::bind is ugly but more direct and efficient I believe. – BuvinJ Jun 09 '23 at 13:10
  • @BuvinJ Believe is not necessarily best engineering judgement, e.g.: godbolt.org/z/doaKrWWEb vs godbolt.org/z/jf7aoWa4P – Andy Jun 13 '23 at 06:44
  • @Andy Are you basically saying that some compilers optimize the use of a lamba which is written as a wrapper like this? But not, std::bind? – BuvinJ Jun 13 '23 at 13:05
  • Note that another option would be to just use an actual function pointer to a class member, rather then an `std::function` at all... I realize this is not quite as flexible but might be the MOST efficient solution? – BuvinJ Jun 13 '23 at 13:08
  • @BuvinJ yes - "some compilers" like Clang :) I am also saying that you cannot judge solution performance only looking at the code without taking into account compiler, target HW, ... Even n^2 loops can be performant for specific DSP and compiler. And this lamba vs bind is even more doubtful. – Andy Jun 19 '23 at 12:27
  • @Andy Thank you! How do you generally recommend determining the speed of such things? Reviewing the assembly produced? Writing tests? Using bench marking tools? – BuvinJ Jun 19 '23 at 12:52
  • @BuvinJ Remember 80/20 rule, benchmark solution in target env., profile, flame graphs, ... When important area spotted then think on improvements, and yes, sometimes using assembly (esp. when in embedded env.). – Andy Jun 19 '23 at 13:14
  • @Andy Great advice! Thank you for your time! – BuvinJ Jun 19 '23 at 13:20
9

You can use std::bind:

using namespace std::placeholders;  // For _1 in the bind call

// ...

Register(std::bind(&Class::Function, this, _1));
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Say, I want to set this function pointer as default argument for the function. Is this possible? I can't use calls like `bind` then, do I? I haven't the `this` pointer to find in the parameter list. – danijar Jun 16 '13 at 09:28
  • 1
    @danijar That's not possible, but can be worked around by having an overloaded variant of `Register` with no arguments that binds to your default function. – Some programmer dude Jun 16 '13 at 09:32
  • I was missing the std::placeholders in my code and this answer pointed me to it! – Martin Meeser Feb 08 '16 at 08:45
7

In C++ 17, you can use:

Register([=](auto && ...args){ return Function(args...); });

which is sweet especially if the argument list is longer long. Of course the member function's argument list must then be compatible with the std::function's ones.

peterchen
  • 40,917
  • 20
  • 104
  • 186
  • And the example code no longer works in C++20, due to the [implicit capture of `this` via `[=]` being deprecated](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0806r2.html). – ComicSansMS Jun 13 '23 at 13:24
2

With std::function and std::bind, you can treat different class member function the same.

#include <iostream>
#include <functional>
#include <vector>
using namespace std;
using namespace std::placeholders;

class Foo
{
public:
    void foo(const string &msg)
    {
        cout << msg << '\n';
    }
};

class Bar
{
public:
    void bar(const string &msg, const string &suffix)
    {
        cout << msg << suffix << '\n';
    }
};

int main(int argc, char **argv)
{
    Foo foo;
    Bar bar;

    vector<function<void (const string &msg)>> collection;
    collection.push_back(bind(&Foo::foo, &foo, _1));
    collection.push_back(bind(&Bar::bar, &bar, _1, "bar"));

    for (auto f : collection) {
        f("foo");
    }

    return 0;
}
Richard
  • 41
  • 1
0

As others have already stated, you can use std::bind. The syntax is pretty ugly though. As such, I offer these macros:

#define bindToThis( className, funcName ) \
    std::bind( &className::funcName, this, std::placeholders::_1 )

#define bindToThat( className, funcName, objPtr ) \
    std::bind( &className::funcName, objPtr, std::placeholders::_1 )

Here's an example implementation of the macro, after first typedefing the std::function to define the required signature (which makes that easier to read/use as well):

typedef std::function<void(const int)> MyIntHandler;

class MyClass 
{
public:
    MyClass( const MyIntHandler handler );
};

...
SomeOtherClass::SomeOtherClass() 
    : myObjPtr_( new MyClass( 
        MyIntHandler( bindToThis( SomeOtherClass, handleSomeInt ) ) )
{}

void SomeOtherClass::handleSomeInt( const int i )
{
    // Do something...
}
BuvinJ
  • 10,221
  • 5
  • 83
  • 96