7

more than a general case, I have a very specific example in mind : in GSL (GNU Scientific Library), the main function type used (in order to perform integration, root finding,...) is gsl_function , which have an attribute function whose type is double(*)(double, void *)

Say I want to create a gsl_function from double a_squared(double a) {return a*a};. a__squared 's type is double(*)(double) I would like to create a convert function taking in argument (double(*)(double) f) and returning an object of type double(*)(double, void *) which would satisfy convert(f)(double a, NULL) == f(a)

But after some research, it seems like I can't define another function in my convert function. How to proceed ?

Jason
  • 3,777
  • 14
  • 27
user3640096
  • 175
  • 7
  • Why is this tagged `c++`? GSL is a C library and requires a C-type function pointer. So any solution based on C++ functionality (such as `std::bind`) fails. – Walter Oct 26 '15 at 23:18
  • C++ functionality don't fail if you use a small wrapper @Walter - and that includes lambda and `std::bind`. See my answer – Vivian Miranda Nov 02 '15 at 15:31

4 Answers4

4

The need to pass a raw function pointer to the GSL API limits your options considerably - you can't use anything based on std::function because there's no way to obtain a function pointer from a std::function (and this rules out lambdas using captures, which would have offered a neat solution).

Given these constraints, here's a possible solution making use of a static wrapper class. You could just as well have put the contents of this class in a namespace, but using the class at least gives some semblance of encapsulation.

typedef double gsl_function_type(double, void*);  // typedef to make things a bit more readable...

// static class to wrap single-parameter function in GSL-compatible interface
// this really just serves as a namespace - there are no non-static members,
// but using a class lets us keep the details private
class Convert
{
    Convert() = delete;  // don't allow construction of this class    

    // pointer to the function to be invoked
    static double (*m_target)(double);

    // this is the function we'll actually pass to GSL - it has the required signature
    static double target(double x, void*) {
        return m_target(x);  // invoke the currently wrapped function
    }

public:
    // here's your "convert" function
    static gsl_function_type* convert(double (*fn)(double)) {
        m_target = fn;
        return ⌖
    }
};

There's a live example here: http://coliru.stacked-crooked.com/a/8accb5db47a0c51d

atkins
  • 1,963
  • 14
  • 27
  • 1
    hiding `m_target` as `static` class member does not avoid the fact that this requires a global variable. Moreover, this only allows one function to be converted simultaneously (i.e. it fails, for example, if your want to compute the numerical integral of a function whose value requires a root finder, *and* you want to use this mechanism for both). – Walter Oct 26 '15 at 23:15
  • @Walter, the wrapper I linked doesn't have this limitation (one function at a time). – Vivian Miranda Nov 03 '15 at 16:47
1

You're trapped by gsl's (poor) design choice of using C (instead of C++) to provide a C-style function pointer. Thus, you cannot use (C++ style) function-objects (functor), but must provide the pointer to a real function and one cannot generate a function in the same way one can genarate functors.

(Not recommended) You can use a global variable to store the actual function (a_squared) and then define a particular gsl_function that actually calls that global variable:

// from some gsl header:
extern "C" {
  typedef double gsl_function(double, void*);
  // calls func(arg,data_passed_to_func)
  double gsl_api_function(gsl_function*func, void*data_passed_to_func);
}

// in your source code
double(*target_func)(double);           // global variable can be hidden in some namespace
extern "C" {
  double funtion_calling_target(double, void*)
}
double funtion_calling_target(double arg, void*)
{
  return target_func(arg);
}
bool test(double x, double(*func)(double))
{
  target_func = func;
  return x < gsl_api_function(function_calling_target,0);
}

(hiding target_func as static member of some class as in atkins's answer still requires a global variable). This works, but is poor, since 1) this mechanism requires a global variable and 2) only allows one target function to be used a any time (which may be hard to ensure).

(Recommended) However, you can define a special function that takes another function pointer as argument and passes it as data element. This was in fact the idea behind the design of gsl_function: the void* can point to any auxiliary data that may be required by the function. Such data can be another function.

// your header
extern "C" {
  double function_of_double(double, void*);
}
inline double function_of_double(double arg, void*func)
{
  typedef double(*func_of_double)(double);
  return reinterpret_cast<func_of_double>(func)(arg);
}

// your application
bool test(double x, double(*func)(double))
{
  return x < gsl_api_function(function_of_double, (void*)(func));
}

This does not require a global variable and works with as many different simultaneous functions as you want. Of course, here you are messing around with void*, the very thing that every sensible C++ programmer abhors, but then you're using a horrible C library which is based on void* manipulations.

Walter
  • 44,150
  • 20
  • 113
  • 196
1

Thought I would add my lambda-based attempts at this.

It works fine in principle:

// function we want to pass to GSL
double a_squared(double a) { return a*a; }

typedef double gsl_function_type(double, void*);  // convenient typedef

// lambda wrapping a_squared in the required interface: we can pass f directly to GSL
gsl_function_type* f = [](double x, void*) { return a_squared(x); };

But we'd really like to write a method to apply this to any given function. Something like this:

gsl_function_type* convert(double (*fn)(double))
{
    // The lambda has to capture the function pointer, fn.
    return [fn](double x, void*) { return fn(x); };
}

However, the lambda now has to capture the pointer fn, because fn has automatic storage duration (in contrast to the static function a_squared in the first example). This doesn't compile because a lambda which uses a capture cannot be converted to a simple function pointer, as required by the return value of our function. In order to be able to return this lambda we'd have to use a std::function, but there's no way to get a raw function pointer from that, so it's no use here.

So the only way I've managed to get this to work is by using a preprocessor macro:

#define convert(f) [](double x, void*) { return f(x); }

This then lets me write something like this:

#include <iostream>
using namespace std;

typedef double gsl_function_type(double, void*);  // convenient typedef

// example GSL function call
double some_gsl_function(gsl_function_type* function)
{
    return function(5.0, nullptr);
}

// function we want to pass to GSL
double a_squared(double a) { return a*a; }

// macro to define an inline lambda wrapping f(double) in GSL signature
#define convert(f) [](double x, void*) { return f(x); }

int main()
{
    cout << some_gsl_function(convert(a_squared)) << endl;
}

Personally, as much as I dislike using macros, I would prefer this over my other suggestion. In particular, it solves the problems @Walter pointed out with that idea.

atkins
  • 1,963
  • 14
  • 27
  • 1
    This assumes that you can pass a C++ lambda via function pointer to a function with C linkage. I don't know for sure whether that is supported by the standards, but would not recommend it. – Walter Nov 02 '15 at 16:38
1

Previous answers - including the accepted one - seem correct, but they are not general enough in case you need to convert other types of function to gsl_function (including member functions for example). So, let me add a more powerful alternative.

If you use the wrapper described here, then you can convert any C++ lambdas to gsl_functions in two simple lines

// Example
gsl_function_pp Fp([&](double x){return a_squared(x);}); 
gsl_function *F = static_cast<gsl_function*>(&Fp);

This solves any related conversion problems. You can also use std::bind and any std::functions.

Community
  • 1
  • 1
Vivian Miranda
  • 2,467
  • 1
  • 17
  • 27