How to directly bind a member function to an std::function in Visual Studio 11?
Asked Answered
T

6

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'

Transpacific answered 16/6, 2013 at 9:17 Comment(0)
L
62

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);
Libbie answered 16/6, 2013 at 9:20 Comment(9)
Works, but can the syntax be simplified under the constraint that all the class reference are the same?Transpacific
@Transpacific 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.Libbie
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?Transpacific
@Transpacific 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.Libbie
No, sadly it isn't a global instance.Transpacific
@Transpacific so what is it? Maybe you can post some code to clarify this?Libbie
It is just a self containing class with different use cases. But if there is no way, I am going to accept that.Transpacific
@Sasha Probably void foo(int n). Fixed.Libbie
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.Garlinda
C
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); });
}
Chaoan answered 13/4, 2016 at 10:58 Comment(8)
thanks for pointing this out and linking to the relevant portion of the video!Beside
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.Scandalmonger
@Scandalmonger Believe is not necessarily best engineering judgement, e.g.: godbolt.org/z/doaKrWWEb vs godbolt.org/z/jf7aoWa4PChaoan
@Chaoan 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?Scandalmonger
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?Scandalmonger
@Scandalmonger 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.Chaoan
@Chaoan Thank you! How do you generally recommend determining the speed of such things? Reviewing the assembly produced? Writing tests? Using bench marking tools?Scandalmonger
@Scandalmonger 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.).Chaoan
P
9

You can use std::bind:

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

// ...

Register(std::bind(&Class::Function, this, _1));
Preeminence answered 16/6, 2013 at 9:20 Comment(3)
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.Transpacific
@Transpacific 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.Preeminence
I was missing the std::placeholders in my code and this answer pointed me to it!Americano
G
8

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.

Gorlicki answered 19/7, 2019 at 13:54 Comment(2)
And the example code no longer works in C++20, due to the implicit capture of this via [=] being deprecated.Germaine
isn't std::forward needed there to make perfect forwarding?Duester
P
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;
}
Parkman answered 13/8, 2013 at 11:29 Comment(0)
S
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...
}
Scandalmonger answered 9/6, 2023 at 13:35 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.