开发者

How to create a function pointer from a function with certain parameters fixed in C++

Suppose I want to call a function f that asks for a function pointer of type

int (*foo)(int)

The function is something like

double f( int (*foo)(int))

I have a function that does this, but it takes another object.

int bar(int, MyClass*)

This object is created at runtime, say

int main()
    // ... read some file / input
    MyClass myclass = MyClass(input);

    // Now pass the function pointer
    double retval = f( bar( int, &myclass))

Is there any way to implem开发者_JAVA技巧ent the last line? I am looking for an answer without global objects.


No.

Do you have any influence over the function f? In C++, this would normally be either a template which would accept any callable object, or it would use a simple interface from which you could derive. In either case, you would define a class which could contain the additional data. Taking a pointer to a function, as such, is very poor design.


Edit to account for the fact that f cannot be modified:

Since you can't modify f, the only solution I can think of is using some sort of global variable together with a forwarding function:

int bar(int i, MyClass * mc);

namespace bar_parameters
{
    MyClass * mc = 0;
}

int binded_bar(int i)
{ 
    return bar(i, bar_parameters::mc);
}

int main()
{
    MyClass myclass(input);

    bar_parameters::mc = &myclass;
    double retval = f(binded_bar);
}

This is quite ugly, however...


It is not possible to bind parameters of a function. However, it is possible with functor (function object).

The first thing you need to do is to modify f's parameter to make it a function object such as std::function or boost::function, instead of a function pointer:

double f(std::function<int (int)> foo);

You can use this function either with function pointers or functors with the correct signature.

After that, you can use binding methods such as std/boost::bind (or bind1st/bind2nd in C++03):

double retval = f(std::bind(bar, _1, &myclass));

If you don't have access to Boost or C++0x, you can use the usual technique which consists in making the parameter template in order to accept any callable entities (function pointers and functors):

template <typename Func>
double f(Func foo);

The drawback of this approach is that the signature of the function is only enforced by the way you use it in f.

To bind the second parameter, you can then use ptr_fun to convert a function pointer to a function object, and bind2nd to actually bind the parameter:

double retval = f(std::bind2nd(std::ptr_fun(bar), &myclass));


You can achieve something similar by using Boost.Bind. This is now part of the C++ standard and may even be already available with your compiler. Note that Boost.Bind would still require that you modify f's signature. If that is not possible you could do something like

MyClass* _only_to_be_used_by_ugly;

int ugly(int i) {
  return bar(i, _only_to_be_used_by_ugly)
}

int main() {
  _only_to_be_used_by_ugly = MyClass(input);
  double retval = f(ugly);
}

But I'd be the first to advise against a similar approach.


In your case you will have to do something evil, like:

MyClass* g_myclass = 0;
int my_f(int i) {
    return bar(i, g_myclass);
}

int main()
    // ... read some file / input
    MyClass myclass = MyClass(input);

    // Now pass the function pointer
    g_myclass = &myclass;
    double retval = f(&my_f);
    g_myclass = 0; // no longer needed (you hope).
    ...
}

YMMV. If the function pointer you pass is retained somewhere and used again later then you will be in for trouble.. In which case you would need to have a list of static functions and a global array of pointers to MyClass and allocate/free from this list as neccessary. You can generate the functions using a recursive template metaprogram. Not nice but possible.

OR, figure out how to generate the required assembler to do the binding and encode the MyClass pointer in the assembly instructions. The function is then allocated on the heap. Not very portable but should also be do-able. I've never tried this though so cannot guarantee it would work or be easy to do. The code would also look hideous.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜