How can I pass a member function where a free function is expected?
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
Related videos on Youtube
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, 2022Comments
-
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
'saClass::test
as an argument tofunction1
? I would like to access a member of the class.-
amdn over 11 yearsTake a look at this answer stackoverflow.com/questions/2402579/… and also this C++ FAQ parashift.com/c++-faq/pointers-to-members.html
-
CarLuva almost 10 yearsThis 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 over 11 yearsOk, 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 over 11 yearsI 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 over 11 yearsWell, 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 about 9 yearsIs
void function1(std::function<void(int, int)>)
correct? -
Dorky Engineer about 9 yearsYou 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 thenfunctionToCall(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 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 about 9 yearsI found this post saying that there is a severe performance penalty from std::function.
-
Superlokkus almost 9 yearsI 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 almost 9 years@Superlokkus: can you please enlighten us with an alternative?
-
Superlokkus almost 9 yearsDietmarKühl see the answers below by PeterBecker and especially @MattPhillips
-
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 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" andstatic_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 over 8 yearsThank 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 about 8 yearsHow would a template look like, that takes functions and non-static member functions? I am struggling ... Cheers!
-
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 off()
use thefun
object with a suitable helper (e.g.std::bind()
and call the function via the helper with suitable arguments. -
Olumide over 6 yearsI thought
function
invoid (aClass::*function)(int, int)
was a type, because of it is a type intypedef void (aClass::*function)(int, int)
. -
Pete Becker over 6 years@Olumide —
typedef int X;
defines a type;int X;
creates an object. -
super almost 6 yearsSo 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 almost 6 years@super, Other answers can not call existing functions taking in plain
C
functions as arguments. -
super almost 6 yearsThe 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 over 5 yearsNot 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 almost 5 yearsGiven that this is tagged CPP I think this is the cleanest solution. We could use a constant reference to
std::function
indraw
though instead of copying it on every call. -
kroiz almost 5 yearsThe placeholder should not be used in this example as the function does not takes any params.
-
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. about 4 yearsGreat 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 over 2 yearsNote 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)