Reputation: 26333
I have a function that takes as an argument a pointer to function:
void MySolver::set_pt2func(double(*pt2function)(double*))
{
pt2Function = pt2function;
}
In another class, some method want to initialize an instance my_solver with this code:
double (MyClass::*pt2apply)(double* parameter) = NULL;
pt2apply = &MyClass::apply_func;
my_solver->set_pt2func(pt2apply);
the last line shows compile error:
error: double (MyClass::*pt2apply)(double*)
argument of type (MyClass::* )(double*) is not compatible
with argument of type double(*)(double*)
how to fix?
thanks!
EDIT
I use this tip
http://www.parashift.com/c++-faq/memfnptr-to-const-memfn.html
I set typedef:
typedef double (MyClass::*Pt2apply_func)(double*) const;
in my method, I try this:
Pt2apply_func pt2apply = &MyClass::apply_func;
my_solver->set_pt2func(pt2apply);
and compile error shows up with &MyClass
, with again compatible types. other way to handle my issue? thanks!
Upvotes: 1
Views: 2011
Reputation: 366133
You need a pointer to a function:
double(*)(double*)
What you have is a pointer-to-member-function:
double (MyClass::*)(double*)
These aren't the same thing. Or even all that similar. The first can be called directly; the second can only be called through an object, with the ->*
or .*
operator.
And this isn't just the compiler being picky about types; a pointer-to-member-function isn't even a pointer (or at least not just a pointer) under the covers; it needs more information in order to handle dynamic binding.
See http://www.parashift.com/c++-faq/pointers-to-members.html for more details.
The "classic" way to use pointers-to-member-functions with C-style APIs is to write a wrapper function that gets a MyClass*
from somewhere else (usually the "userinfo", "thunk", etc. passed along with the function pointer) and calls myobj->*pmf(args)
with it. Then, you can pass a pointer to that wrapper, since it's just a plain function. Smarter modern ways to do it include bind
and lambdas.
Let's deal with the modern way first, because it's much simpler, and it's what you should do if you can. First, if you have C++11, you can use std::bind
, or std::mem_fn
, or just write a lambda:
auto pt2apply = [&](double *parameter) { return my_solver->pt2apply(parameter); }
Now, the type of this thing is not double(*)(double*)
. In fact, it's something unspecified, and probably unspeakably ugly. Here we've just used auto
to avoid dealing with it, but we've got to pass it to set_pt2func
, and then that method has to store it in a member variable, so we need some kind of type that we can type. The answer is std::function<double(double *)>
. This is an object that can store a copy of anything that's callable with a double *
and returns a double
. So:
void MySolver::set_pt2func(std::function<double(double*)> pt2function)
{
pt2Function = pt2function;
}
And that's it.
Now, this requires C++11. If you're using C++98, you can use boost::lambda
instead of C++11 lambdas (a lot uglier, but the same basic ideas). Or you can use boost::bind
(almost identical to C++11 std::bind
, which was based on the Boost library), or one of many other libraries with similar functionality. And to store the functor, boost::function
(again, almost identical to C++11 std::function
, although sometimes less efficient). (If you've got C++03 with TR1, you may have some of the C++11 features in std
or std::tr1
, but the different compiler manufacturers didn't all get the details straightened out until C++11 was almost done, so the easiest way to use that is through boost::tr1
, and if you've got Boost, you might as well just use it.)
If you're stuck with C++98 and no Boost for some reason, you can use std::mem_fn
, and store the result in a std::binary_function
. This is a lot uglier, so I won't give the full details here.
Now let's look at the classic solution. This is really only worth doing when you've got an existing API that uses function pointers for, e.g., callbacks, and you can't change it. I'd suggest looking for sample code that uses C functions like qsort_r
, because the idea is pretty much the same: you're passing a function pointer together with a "thunk", and the two of them always travel together. (The modern solutions are all basically about binding the two things into a single object that you can just call without having to keep track of the thunk, to make your code easier to read and write, and easier for the compiler to catch errors in.)
The key here is that you need a wrapper function, a plain C-style function that you can take a function pointer to. Your thunk is going to be a pointer to your MyClass
object. In my example, I'm going to pass the thunk around as a void *
because, even though this is obviously inferior (more code to write, less chance for the compiler to catch errors), it's what you're going to see most often in general-purpose C APIs that you need to deal with.
So, putting it all together:
void MySolver::set_pt2func(double(*pt2function)(double*, void *), void *userinfo)
{
pt2Function = pt2function;
userInfo = userinfo;
}
double wrapMyClassPt2Apply(double *parameter, void *userinfo)
{
return ((MyClass*)userinfo)->apply_func(parameter);
}
my_solver->set_pt2func(wrapMyClassPt2Apply, (void *)my_solver);
Note that if you were using your pt2apply
pointer-to-member-function, you'd call it with ->*
in the wrapper. But we don't need pt2apply
anymore; the wrapper can just call the apply_func
member with ->
.
Then, when MySolver wants to call the function, it does this:
pt2Function(parameter, userInfo);
One last thing: wrapMyClassPt2Apply can't be a member function, or you'd be stuck with the same problem you started with; a pointer to it would be a pointer-to-member-function rather than a pointer. So, you have two choices:
Make it a free function. This is a perfectly reasonable thing to do. Free functions can be part of the interface of a class. Or, they can be hidden even better than private methods (by putting them in the .cpp file and not mentioning them at all in the .h file).
Make it a static method. Some people prefer this, but really, the only time it's beneficial is when someone outside of MyClass.cpp is going to be doing the set_pt2func
call, and is already a friend (or subclass) of MyClass
, because then you can make the method private (or protected).
Upvotes: 6
Reputation: 45484
Use C++11 instead of C. this means: use std::function
and std:mem_fn
to bind a member function.
Upvotes: -1
Reputation: 180295
There's no easy solution. Pointers to member functions are different, since they require an object to be called on. How is MySolver
going to provide a MyClass
? Basically, once you have that answer, the underlying problem is mostly gone. Two common solutions are that MySolver
knows about MyClass
, in which case you can fix the signature of set_pt2func, or you provide MySolver
with the result of std::bind(pt2apply, SomeObj, _1)
.
Upvotes: 1
Reputation: 843
Pointers to member functions are not compatible with regular ones (c-style function pointers). This is because members take an implicit this
argument that is not part of the signature. If you want to achieve similar behavior, take a look at boost::bind.
Upvotes: 0