1

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!

kiriloff
  • 25,609
  • 37
  • 148
  • 229
  • what are you trying to achieve? – Luchian Grigore Sep 28 '12 at 07:43
  • Before we go any further, two questions: (1) Can you use C++11? (If you don't know, what compiler and version are you using?) (2) Are you the person writing the interface to MySolver, or is that third-party code that you can't modify? – abarnert Sep 28 '12 at 08:05
  • @abarnert i cannot use C++11, and I am writting interface to MySolver . however, this interface derives from an abstract class that i did not write and cannot change. thus, signature must comply with the signature of abstract parent, and in particular i cannot user `userinfo` as you suggested. thanks!! – kiriloff Sep 28 '12 at 08:14
  • In your new version, where do you get the compiler error? If you haven't changed `set_pt2func` to take a `Pt2apply_func` (and also changed `MySolver::pt2Function` to be that type), you're still trying to do the exact same illegal cast as in the first place. – abarnert Sep 28 '12 at 08:16
  • Also, even after you get that part right, `MySolver` needs to store (or otherwise have access to) a `MyClass` instance as well as the `Pt2apply_func`; otherwise, it has no way to _call_ the function. (The only way to call it is `myClassInstance->*pt2Function(param)`.) You may find it simpler to just store a functor (custom, or from `bind` or `mem_fn`) that wraps the object and function up together (like a C# delegate, a Python bound method, etc.; just about every OO language but C++ has a simple way to do it…), so you can just call the functor like a function. – abarnert Sep 28 '12 at 08:20
  • Also, may I ask why you can't use C++11 (or, for that matter, say, Python)? You've chosen to dive into one of the trickiest parts of C++03, and that might have been a great idea for learning purposes a few years ago, but now all you're doing is banging your head against a wall to teach yourself obsolete information. – abarnert Sep 28 '12 at 08:24
  • One more question: What is the actual error your new version gets (the whole thing, with the types)? – abarnert Sep 28 '12 at 08:26

4 Answers4

6

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:

  1. 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).

  2. 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).

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • I dont really understand the classic way. would you some reference to precise example or doc, for both classic and smarter ways ? thanks!! – kiriloff Sep 28 '12 at 07:55
  • I updated the answer. But did you follow the link? Most of the information you need is available there, and it's worth reading it all so you really understand the issue. – abarnert Sep 28 '12 at 07:57
  • I don't see a `const` anywhere in the current version of the question. – abarnert Sep 28 '12 at 08:04
  • The `const` you've added means that it's a pointer-to-const-member-function. If that's the way you defined the method in MyClass (`double pt2apply(double *) const`), then it's correct; if not (`double pt2apply(double *)`), then it's not. – abarnert Sep 28 '12 at 08:22
  • in your example, the signature of `set_pt2func` seems not be compatible with what you give in arguments in `my_solver->set_pt2func(wrapMyClassPt2Apply, (void *)my_solver);` : in this call, it seems to me that first argument has signature `double(*pt2function)(double*, void*)`, and not ` double(*pt2function)(double*)`. is it my mistake or yours? – kiriloff Sep 28 '12 at 08:25
  • Right, sorry, you need to pass the userinfo into the function as well. I'll edit it. (The fact that it's so easy to get this stuff wrong, and so non-obvious how to fix it, should be another clue that you're trying to learn something difficult. And, again, it's obsolete knowledge, so you're not getting much benefit out of learning it) – abarnert Sep 28 '12 at 08:28
  • !thanks! so, is it correct that `double wrapMyClassPt2Apply(double *parameter, void *userinfo){//...}` is a member of the class where some method has the call `my_solver->set_pt2func(wrapMyClassPt2Apply, (void *)my_solver);` ? could you confirm ? – kiriloff Sep 28 '12 at 08:39
  • then, I should not have member declaration `double (*pt2Function)(double*,void*);` but smthg like `double (*pt2Function)(double*,userInfo);` !? what I mean is that second argument for function pointer member of MySolver class is always another member, with type void*: `void* userInfo`. is that right? how to make this second argument always member `userInfo`? – kiriloff Sep 28 '12 at 08:43
  • No, the wrapper function should be a free function, not a member of the class, or you end up with the same problem you were trying to solve. You _can_ make it a static method (which is basically the same thing as a free function under the covers), if you really want to; then you just need to add `MyClass::` in a couple places. Some people prefer static methods either because (a) they don't get that free functions are part of a class interface, or (b) the caller is a friend and they want to be able to make the function private. – abarnert Sep 28 '12 at 08:43
  • got it! when calling function pointer on some arguments, I always pass userInfo as a second argument!! – kiriloff Sep 28 '12 at 08:44
  • Yes, that's the idea. The function pointer and the object always travel together. (Maybe it would have helped to first show an example of passing a C function and struct to `qsort_r`, because this is basically the same thing…) The modern solutions are basically ways to bind the two into a single object, to make everything much simpler (and let the compiler catch a lot of errors for you). – abarnert Sep 28 '12 at 08:48
  • thanks, i made it static. besides, should `pt2apply` be added to arguments list of `double wrapMyClassPt2Apply(double *parameter, void *userinfo)` ? how to do if not? thanks ! – kiriloff Sep 28 '12 at 08:52
  • I think the newer version makes it clearer. The previous version was assuming you'd already created the `pt2apply` pointer-to-member-function at file scope, and I was showing how you'd call that. But you really don't need it (unless, for some reason, you want to hide the complete definition of `MyClass` from parts of its own implementation); it's simpler to just call `apply_func` directly. – abarnert Sep 28 '12 at 09:10
1

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).

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • `std::bind`: what library is it ? is it boost ? could you hint at some reference ? with `using namespace std`, and providing an instance of Myclass instead of your `SomeObj`, I still have a compile error. thanks! – kiriloff Sep 28 '12 at 07:52
  • `std::bind` is part of the standard library, hence the `std` namespace. But it's part of C++11, not C++98, so you'll need a reasonably up to date compiler, and may need to set a flag. – abarnert Sep 28 '12 at 07:59
  • The one big thing this answer is missing is an explanation of how to pass and store the result of `std::bind`, since it's no more a `double(*)(double *)` than the pointer-to-member-function was. – abarnert Sep 28 '12 at 09:16
  • @abarnert: Fair point; the answer was written before the question was updated with more details (which is why I query about the relation between `MyClass` and `MySolver). – MSalters Sep 28 '12 at 09:35
  • True. If he'd answered, you could have elaborated on whichever one was more appropriate; as it was, the only option was to elaborate on both, and if you went down that path I suspect you would have ended up with a 3-page-long answer and 300 comments back and forth… – abarnert Sep 28 '12 at 09:44
0

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.

mtsvetkov
  • 885
  • 10
  • 21
  • @mtsetkov thanks! I would like to avoid use of boost or any library except standard library. could we find another way to pass a pointer to member function here although the argument has type the c-style function pointer ? thanks for help – kiriloff Sep 28 '12 at 07:48
  • I'd recommend switching from function pointers to [functors](http://stackoverflow.com/questions/356950/c-functors-and-their-uses). It may not be an option though if you're using a 3rd party library that expects you to give it a function pointer. If that's the case, you'll have to use a normal c-style function that calls the one you want (and in your case it would need to find which object to call `pt2apply` on) – mtsvetkov Sep 28 '12 at 08:04
  • @fonjibe look at my answer: standard library only. you must, however, change your function (that expects C-style arguments) if you want to use C++ code. avoid C rubbish! – Walter Sep 28 '12 at 08:54
  • @Walter: He's already said he can't use C++11 (although he hasn't said why). – abarnert Sep 28 '12 at 09:12
  • -1 because this isn't really why the two types are incompatible. – abarnert Sep 28 '12 at 09:12
-1

Use C++11 instead of C. this means: use std::function and std:mem_fn to bind a member function.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • 1
    This doesn't explain at all _how_ to use `function` and `mem_fn`. And, given that MSalters's answer showing how to do it with `std::bind` was already here for quite some time before your answer, you should probably explain _why_ you'd use `mem_fn` instead. Most of all, you don't give any information as to why the OP is having a problem in the first place. – abarnert Sep 28 '12 at 09:15
  • @abarnert Unlike you, I don't have all the time in the world to explain that, nor to read all the answers fully (it only appears towards the end of MSalters answer). Any decent programmer knows where to find online C++ documentation (though the comments from the questioner to MSalters answer make be suspect that this characterization does not apply here) – Walter Sep 28 '12 at 09:23