How can I pass a member function where a free function is expected?

296,371

Solution 1

There isn't anything wrong with using function pointers. However, pointers to non-static member functions are not like normal function pointers: member functions need to be called on an object which is passed as an implicit argument to the function. The signature of your member function above is, thus

void (aClass::*)(int, int)

rather than the type you try to use

void (*)(int, int)

One approach could consist in making the member function static in which case it doesn't require any object to be called on and you can use it with the type void (*)(int, int).

If you need to access any non-static member of your class and you need to stick with function pointers, e.g., because the function is part of a C interface, your best option is to always pass a void* to your function taking function pointers and call your member through a forwarding function which obtains an object from the void* and then calls the member function.

In a proper C++ interface you might want to have a look at having your function take templated argument for function objects to use arbitrary class types. If using a templated interface is undesirable you should use something like std::function<void(int, int)>: you can create a suitably callable function object for these, e.g., using std::bind().

The type-safe approaches using a template argument for the class type or a suitable std::function<...> are preferable than using a void* interface as they remove the potential for errors due to a cast to the wrong type.

To clarify how to use a function pointer to call a member function, here is an example:

// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
    fptr(context, 17, 42);
}

void non_member(void*, int i0, int i1) {
    std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}

struct foo {
    void member(int i0, int i1) {
        std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
    }
};

void forwarder(void* context, int i0, int i1) {
    static_cast<foo*>(context)->member(i0, i1);
}

int main() {
    somefunction(&non_member, nullptr);
    foo object;
    somefunction(&forwarder, &object);
}

Solution 2

@Pete Becker's answer is fine but you can also do it without passing the class instance as an explicit parameter to function1 in C++ 11:

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

void function1(std::function<void(int, int)> fun)
{
    fun(1, 1);
}

int main (int argc, const char * argv[])
{
   ...

   aClass a;
   auto fp = std::bind(&aClass::test, a, _1, _2);
   function1(fp);

   return 0;
}

Solution 3

A pointer to member function is different from a pointer to function. In order to use a member function through a pointer you need a pointer to it (obviously ) and an object to apply it to. So the appropriate version of function1 would be

void function1(void (aClass::*function)(int, int), aClass& a) {
    (a.*function)(1, 1);
}

and to call it:

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);

Solution 4

Since 2011, if you can change function1, do so, like this:

#include <functional>
#include <cstdio>

using namespace std;

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

template <typename Callable>
void function1(Callable f)
{
    f(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main()
{
    aClass obj;

    // Free function
    function1(&test);

    // Bound member function
    using namespace std::placeholders;
    function1(std::bind(&aClass::aTest, obj, _1, _2));

    // Lambda
    function1([&](int a, int b) {
        obj.aTest(a, b);
    });
}

(live demo)

Notice also that I fixed your broken object definition (aClass a(); declares a function).

Solution 5

I asked a similar question (C++ openframeworks passing void from other classes) but the answer I found was clearer so here the explanation for future records:

it’s easier to use std::function as in:

 void draw(int grid, std::function<void()> element)

and then call as:

 grid.draw(12, std::bind(&BarrettaClass::draw, a, std::placeholders::_1));

or even easier:

  grid.draw(12, [&]{a.draw()});

where you create a lambda that calls the object capturing it by reference

Share:
296,371

Related videos on Youtube

Jorge Leitao
Author by

Jorge Leitao

All content I created on this website until Sep 5 2019, at 13:53 is licensed under 3.0 CC BY-SA, and not 4.0 CC BY-SA as SO states.

Updated on July 08, 2022

Comments

  • Jorge Leitao
    Jorge Leitao almost 2 years

    The question is the following: consider this piece of code:

    #include <iostream>
    
    
    class aClass
    {
    public:
        void aTest(int a, int b)
        {
            printf("%d + %d = %d", a, b, a + b);
        }
    };
    
    void function1(void (*function)(int, int))
    {
        function(1, 1);
    }
    
    void test(int a,int b)
    {
        printf("%d - %d = %d", a , b , a - b);
    }
    
    int main()
    {
        aClass a;
    
        function1(&test);
        function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?
    }
    

    How can I use the a's aClass::test as an argument to function1? I would like to access a member of the class.

    • amdn
      amdn over 11 years
    • CarLuva
      CarLuva almost 10 years
      This is absolutely not a duplicate (at least not of the particular question that is linked). That question is about how to declare a member that is a pointer to a function; this is about how to pass a pointer to a non-static member function as a parameter.
  • Jorge Leitao
    Jorge Leitao over 11 years
    Ok, I like this answer! Can you please specify what you mean with "call your member through a forwarding function which obtains an object from the void* and then calls the member function", or share a useful link to it? Thanks
  • Jorge Leitao
    Jorge Leitao over 11 years
    I think I got it. (I've edited your post) Thanks for the explanation and example, really helpful. Just to confirm: for every member function that I want to point, I have to make a forwarder. Right?
  • Dietmar Kühl
    Dietmar Kühl over 11 years
    Well, yes, kind of. Depending on how effective you are using templates, you can get away with creating forwarding templates which can work with different classes and member functions. How to do this would be a separate function, I'd think ;-)
  • Deqing
    Deqing about 9 years
    Is void function1(std::function<void(int, int)>) correct?
  • Dorky Engineer
    Dorky Engineer about 9 years
    You need to give the function argument a variable name and then the variable name is what you actually pass. So: void function1(std::function<void(int, int)> functionToCall) and then functionToCall(1,1);. I tried to edit the answer but someone rejected it as not making any sense for some reason. We'll see if it gets upvoted at some point.
  • Matt Phillips
    Matt Phillips about 9 years
    @DorkyEngineer That's pretty weird, I think you must be right but I don't know how that error could have gone unnoticed for so long. Anyway, I've edited the answer now.
  • kevin
    kevin about 9 years
    I found this post saying that there is a severe performance penalty from std::function.
  • Superlokkus
    Superlokkus almost 9 years
    I dislike this answer because it uses void*, which means you can get very nasty bugs, because it is not typed checked anymore.
  • Dietmar Kühl
    Dietmar Kühl almost 9 years
    @Superlokkus: can you please enlighten us with an alternative?
  • Superlokkus
    Superlokkus almost 9 years
    DietmarKühl see the answers below by PeterBecker and especially @MattPhillips
  • Dietmar Kühl
    Dietmar Kühl almost 9 years
    @Superlokkus: Note that my answer mentions actually both of these approaches! It just explains in some detail how to actually do the dance calling a member function as doing so isn't directly obvious.
  • Superlokkus
    Superlokkus almost 9 years
    @DietmarKühl But your answer A) mentions the safe approaches only after the unsafe one in a very non-prominent way B) don't mentions the potential hazardous unsafe implications of your void*solution C) suggests with "your best option is to always pass a void* to your function" and static_cast<foo*>(context)->member(i0, i1); this is the only solution and is safe, which is not We all know the problems caused by working around the type system, and your answer IMHO encourages this misuse.
  • kritzel_sw
    kritzel_sw over 8 years
    Thank you very much. I just found out that brackets count here:function1(&(aClass::test), a) works with MSVC2013 but not with gcc. gcc needs the & directly in front of the class name (which I find confusing, because the & operator takes the address of the function, not of the class)
  • germannp
    germannp about 8 years
    How would a template look like, that takes functions and non-static member functions? I am struggling ... Cheers!
  • Dietmar Kühl
    Dietmar Kühl about 8 years
    @qiv: it seems that's a separate question... The simple answer is that you'd just pass a template <typename Fun> void f(Fun fun /*...*/) and in the implementation of f() use the fun object with a suitable helper (e.g. std::bind() and call the function via the helper with suitable arguments.
  • Olumide
    Olumide over 6 years
    I thought function in void (aClass::*function)(int, int) was a type, because of it is a type in typedef void (aClass::*function)(int, int).
  • Pete Becker
    Pete Becker over 6 years
    @Olumide — typedef int X; defines a type; int X; creates an object.
  • super
    super almost 6 years
    So instead of using std::bind or a lambda to wrap the instance you rely on a global variable. I can not see any advantage to this approach compared to the other answers.
  • Necktwi
    Necktwi almost 6 years
    @super, Other answers can not call existing functions taking in plain C functions as arguments.
  • super
    super almost 6 years
    The question is how to call a member function. Passing in a free function is already working for OP in the question. You are also not passing in anything. There's only hard coded functions here and a global Foo_ pointer. How would this scale if you want to call a different member function? You would have to rewrite the underlying functions, or use different ones for each target.
  • mathengineer
    mathengineer over 5 years
    Not only solve to the main question "How can I use the a's aClass::test as an argument to function1?" but also it is recommended to avoid modify class private variables using code external to the class
  • Sohaib
    Sohaib almost 5 years
    Given that this is tagged CPP I think this is the cleanest solution. We could use a constant reference to std::function in draw though instead of copying it on every call.
  • kroiz
    kroiz almost 5 years
    The placeholder should not be used in this example as the function does not takes any params.
  • Jorge Leitao
    Jorge Leitao over 4 years
    @kevin, you may want to revise your comment, as that post's answers showed a flaw in the benchmark.
  • James S.
    James S. about 4 years
    Great answer - this is exactly what I was looking for. One small detail though - I believe it should be: auto fp = std::bind(&aClass::test, &a, _1, _2); If you don't pass the class instance as a reference, it looks like the compiler (at least MSVC2019) tries to move it which it probably not what you want. See also the example here (auto f3 = ...).
  • Bim
    Bim over 2 years
    Note that as of C++14 lambdas should be prefered to std::bind for a couple of reasons (see e.g. "Effective Modern C++", Item 34: Prefer lambdas to std::bind)