1

I use a function from an optimization toolbox that numerically calculates derivatives of functions. It accepts a pointer to a function with one argument, but what if I need to pass a pointer to a function with multiple arguments to it and expect the additional, less important parameters to just be passed through?

//Function from toolbox
double num_differentation(double (func(const double)), double x)
{
//makes various calls to func, but with only one parameter
func(x);
}

Now assume I have a function I'd like to pass to the above but which has additional arguments not directly related to the optimization problem, but that are nevertheless required to arrive at a meaningful result for the function.

double my_func_to_be_input(double a, double b, double c) {
return a+b*c;
}

Is there a way to rewrite num_differentiation so that at the time of calling func it passes through all three parameters? I tried the following but it did not work:

double num_differentation(double (func(const double arg1, const double arg2, const double arg3)), double x)
{
//makes various calls to func, but with only one parameter
func(x, arg2, arg3);
}

And if we wanted to be even more fancy, would it be possible for num_differentiation to accept a function with varying numbers of parameters with the irrelevant ones just being passed through?

In any case, I would not like to have to resort to global variables to route these additional parameters.

I'm using Visual Studio 2013 and would welcome any helpful suggestions, especially with concrete code examples.

UPDATE: This is how I solved it.

//Rewritten function from toolbox
double rewritten_num_differentation(std::function<double(double)> func, double x)
{
func(x);
}

In my main code:

using namespace std::placeholders;    
auto my_func_to_be_input_with_forwarded_arguments= std::bind(my_func_to_be_input, _1, second_argument_to_be_forwarded, third_argument_to_be_forwarded);

return rewritten_num_differentiation(my_func_to_be_input_with_forwarded_arguments, x_arg);

This is akin to CrazyEdie's suggested first solution. Perhaps there are still more elegant ways that do not involve rewriting the toolbox function, but it seems to be awfully complicated to convert the std::function type to a C-style function pointer (like they try here http://www.cplusplus.com/forum/general/63552/).

Steve06
  • 741
  • 2
  • 15
  • 32
  • To do this at compile-time you can use variadic templates . I'm not sure how good VS2013's support would be for that though. – M.M Nov 03 '14 at 05:37
  • in lieu of that you may have to use a solution such as making `func` accept a `vector` of arguments . – M.M Nov 03 '14 at 05:37

2 Answers2

3
double num_differentials(std::function<double(double)> fun, double x)
{
    return fun(x);
}

auto n = num_differentials(std::bind(&fun, _1, arg1, arg2), x);

alternatively:

auto n = num_differentials([arg1,arg2](double x) { return fun(x,arg1,arg2); }, x);

I believe both should work in VC2013.

Edit in response to update:

It's not all that hard to wrap up a std::function for calling through a C callback system.

Assuming:

void register_callback(double (*fun)(double,void*), void* client_data);

Write this:

double wrap_legacy(double d, void* client_data)
{
    auto fun = static_cast<std::function<double(double)>*>(client_data);
    return (*fun)(d);
}

Or if you want to add some security, this:

double wrap_legacy(double d, void* client_data)
{
    auto any = static_cast<boost::any*>(client_data);
    auto fun = boost::any_cast<std::function<double(double)>>(*any);
    return fun(d);
}

Here you just use boost::any as a standard type for any and all such constructs. Always cast to any and always wrap in any. Then if someone screws up and sends the wrong kind of data you'll get exceptions rather than undefined behavior.

Use like this:

auto fun = std::function<double(double)>{[](double d) { return d; }};
register_callback(&wrap_legacy, &fun);

Or the safe version: auto any = boost::any{std::function{[](double d){ return d; }}}; register_callback(&wrap_legacy, &fun);

Of course, generally speaking the client_data lifetime requirements point to heap allocation so you'll need to deal with that. There's also further wrapping you can do and such--like wrapping up register_callback--that can increase safety.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • Thanks and one +1 form me. My solution above is closely inspired from your first suggestion. I did not quite get the second one, or it seemed too cumbersome for me as I would have to put the whole body of the algorithm into these curly brackets that are themselves a function argument. – Steve06 Nov 03 '14 at 16:37
  • Your solution to wrapping std:function is very interesting, but where exactly does the call to my own function my_func_to_be_input with multiple arguments to be forwarded enter? Also could such a wrap_legacy wrapper be generalized to work for all kinds of (a) legacy functions (like your register_callback) and (b) client functions with multiple arguments to be passed (like my my_func_to_be_input)? – Steve06 Nov 11 '14 at 21:15
0

A well designed C style toolbox would have the prototype be:

//Function from toolbox
double num_differentation(double (func(const double, void*)), 
                          double x, 
                          void * user_data)
{
 //makes various calls to func, but with only one parameter
 func(x, user_data);
}

And then expect the user to write

typedef struct my_data { int p; } my_data
double func(double x, void * user_data) {
   my_data * data = (my_data*)user_data;
   return pow(x, data->p);
}

And you'd use the library like this:

my_data data;
data.p = 7;
num_differentiation(&func, 3.0, &data);

However its sounds like you're stuck with a nasty toolbox, and the "standard" was to use that is to use a global, instead of passing the user-data.

my_data g_data;
double func(double x) {
   return pow(x, g_data.p);
}

Then use it like

g_data.p=7;
num_differentiation(&func, 3.0);
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • Note the style for a well designed C++ toolbox is quite different, but many of the numerical toolboxes are C rather than C++, and your code is certainly using a C style library. – Michael Anderson Nov 03 '14 at 05:46
  • The toolbox in question is indeed the Numerical Recipes 2.1 which are very C-like although they use namespaces and other features of C++. I am not sure if the more recent version 3 is any better in this regard. – Steve06 Nov 03 '14 at 14:51
  • I gave you +1 for reminding me how to do it in the old-fashioned C way. – Steve06 Nov 03 '14 at 16:38