I am trying to write a class template and internally it use a C
function (implementation of BFGS optimization, provided by the R
environment) with the following interface:
void vmmin(int n, double *x, double *Fmin,
optimfn fn, optimgr gr, ... ,
void *ex, ... );
where fn
and gr
are function pointers of type
typedef double optimfn(int n, double *par, void *ex);
and
typedef void optimgr(int n, double *par, double *gr, void *ex);
respectively. My C++
class template looks like this:
template <typename T>
class optim {
public:
// ...
void minimize(T& func, arma::vec &dpar, void *ex) {
std::function<optimfn> fn =
std::bind(&T::fr, func, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
std::function<optimgr> gr =
std::bind(&T::grr, func, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4);
// ERROR: cannot convert std::function to function pointer
vmmin(... , fn, gr, ...);
// ...
}
};
so that it can be instantiated by any class with two specified member functions, e.g.:
class Rosen {
public:
// ...
double fr(int n, double *par, void *ex);
void grr(int n, double *par, double *gr, void *ex);
private:
// ...
};
// main.cc
Rosen func;
optim<Rosen> obj;
obj.minimize(func, dpar, ex);
Is this possible? Or maybe there is a better way of doing this -- pass the two member functions separately as function pointer? (If the objective function and the corresponding gradient are simple, it is absolutely okay to write two functions. However, most of the time, the problem I got is far more complicated and I have to implement the problem as a class).
Basically, you need a free function that has the correct signature, takes the
void *
parameter with the "user data" (without which it won't work), somehow extracts a pointer/reference to thestd::function
out of that, and calls it with the other arguments. Simple example to illustrate what I mean:Of course you need to make sure that the pointer remains valid!
With the code below you don't need to write such call_it functions for every possible signature. Above example would read:
And your case would be:
My "scratch area" on ideone for forking and testing. Now, Templates to the rescue:
std::apply
is a C++17 thing, though you should be able to easily find a C++11 compatible version on this site. TheN
specifies the (zero based) index of the parameter which contains the "user data" (i.e. the pointer to the actual function). TheExtractor
is a type that has a staticget_function
member function, which given avoid *
returns something "callable" forstd::apply
to work with. The use case is inspired by your actual issue at hand: If you have only one pointer with "user data" which will be passed to two (or more) different callbacks, then you need a way to "extract" these different functions in the different callbacks.An "extractor" for a single function:
And one for multiple functions:
Note that here
wrap
does an allocation, thus must be met with a corresponding de-allocation infree_wrap_result
. This is still very unidiomatic ... should probably be converted to RAII.tuple_remove
still needs to be written:if_t
(see further down) is just my shorthand forstd:: conditional
,Use
andIgnore
need to be implemented:tuple_remove
exploits thatstd::tuple_cat
accepts emptystd::tuple<>
arguments, and because it cannotget
something out of them, basically ignores them.Shorthand for
std::conditional
:An alternative solution could be to have the
optim
class do its magic with two (possibly pure) virtual functions, and then inherit to define a new classRosen
which implements them. This could look likeLet me say up front:
I do not endorse the usage of the following library
And use it as
Live example
func_traits
Is just a helper traits type that will fetch the type of any callable in an easily accessible form
constexpr counter
This is half the evilness of what's going on. For details visit is stateful metaprogramming ill formed yet?
fnptr
The actual meat of the code. It takes any callable with appropriate signatures and implicitly declares an anonymous C function at every point it is called and coerces the callable into the C function.
It has the funky syntax
fnptr<>::get
andfnptr<>::cast<Ret(Args...)>
. This is intentional.get
will declare the anonymous C function with the same signature as the callable object.cast
works on any compatible callable type, that is, if the return type and arguments are implicitly convertible, it can be casted.Caveats
fnptr
implicitly declares an anonymous C function at each point in the code it is called. It is not the same asstd::function
that is actually a variable.If you call the same
fnptr
in the code again, all hell breaks lose.You have been warned.