4

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 implement the last line? I am looking for an answer without global objects.

3
  • Do you really want to pass a type (int) as a parameter of your function call or is that just a bad name choice for some variable ? Commented Aug 17, 2011 at 8:25
  • @ereOn: if I understand correctly, he wants to bind only the second parameter and leave the first one unbinded. Commented Aug 17, 2011 at 8:27
  • If you're looking for an answer without global objects, then maybe using thread-local storage will help. Depends on your actual code, and why you need to avoid globals. If it's for simple thread-safety then it helps, if it's for recursive re-entrancy not so much. Commented Aug 17, 2011 at 9:32

4 Answers 4

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Even if f is a C API, the decent thing for it to do would be to take a int(*)(void*, int) plus an additional void* parameter to f, which it does nothing with other than passing it into the user's callback. It is occasionally a real pain that certain C functions like qsort don't take a user data pointer, meaning for example that in order to sort strings in C, you need to set the collation rules globally (e.g. using the locale). This looks to be one of those occasions.
@Steve Totally agreed; it's a sign of a poor C interface that doesn't do this. I started to mention it, then considered that if it were a C interface, it probably wasn't in his power to change it (or if it was, he could change it to C++).
2

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

2 Comments

If 'f' can't be modified, how is this the accepted answer? 'f' cannot accept a function object.
@Pete: I edited the answer after reading that f cannot be modified (it was not stated in the OP).
1

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.

4 Comments

boost::bind won't create anything that can be passed to a function expecting a int (*)(int). The problem is in the signature of the function he's calling.
You're right. If that signature is fixed the only way I see to achieve what the OP wants is by using a global variable.
Can you explain why boost::bind would not work? At first sight it seems to me lkie it does the right thing.
Because boost::bind returns a function object and not a pointer to function of the given signature.
1

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.

1 Comment

This is how it is done right now (I inherited this code) but found it too evil to live with...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.