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
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 the std::function
out of that, and calls it with the other arguments. Simple example to illustrate what I mean:
void call_it(int value, void * user) {
std::function * f = static_cast*>(user);
(*f)(value);
}
// pass it as callback:
registerCallback(call_it, static_cast(&my_std_function));
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:
registerCallback(trampoline<1, Single::Extract, void, int, void *>,
Single::wrap(my_std_function));
And your case would be:
// obj and ex passed as parameters
std::function fn =
[ex, &obj] (int a, double * b) { return obj.fr(a, b, ex); };
std::function gr =
[ex, &obj] (int a, double * b, double * c) { obj.grr(a, b, c, ex); };
void * fns = Multi<2>::wrap(fn, gr);
vmmin(... ,
trampoline<2, Multi<2>::Extract<0, double, int, double *>, double, int, double *, void *>,
trampoline<3, Multi<2>::Extract<1, void, int, double *, double *>, void, int, double *, double *, void *>,
..., fns, ...); // fns passed as ex
Multi<2>::free_wrap_result(fns);
My "scratch area" on ideone for forking and testing. Now, Templates to the rescue:
template<
std::size_t N, ///> index of parameter with the user data
typename Extractor,
typename R,
typename... Args>
R trampoline (Args... args) {
auto all = std::make_tuple(std::ref(args)...);
auto arguments = tuple_remove(all);
return std::apply(Extractor{}.get_function(std::get(all)),
arguments);
}
std::apply
is a C++17 thing, though you should be able to easily find a C++11 compatible version on this site. The N
specifies the (zero based) index of the parameter which contains the "user data" (i.e. the pointer to the actual function). The Extractor
is a type that has a static get_function
member function, which given a void *
returns something "callable" for std::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:
struct Single {
template
struct Extract {
std::function & get_function(void * ptr) {
return *(static_cast*>(ptr));
}
};
template
static void * wrap(std::function & fn) {
return &fn;
}
};
And one for multiple functions:
template
struct Multi {
template
struct Extract {
std::function & get_function(void * ptr) {
auto arr = static_cast *>(ptr);
return *(static_cast*>((*arr)[I]));
}
};
template
static void * wrap(Fns &... fns) {
static_assert(sizeof...(fns) == Num, "Don't lie!");
std::array arr = { static_cast(&fns)... };
return static_cast(new std::array(std::move(arr)));
}
static void free_wrap_result(void * ptr) {
delete (static_cast*>(ptr));
}
};
Note that here wrap
does an allocation, thus must be met with a corresponding de-allocation in free_wrap_result
. This is still very unidiomatic ... should probably be converted to RAII.
tuple_remove
still needs to be written:
template<
std::size_t N,
typename... Args,
std::size_t... Is>
auto tuple_remove_impl(
std::tuple const & t,
std::index_sequence) {
return std::tuple_cat(if_t>::from(t)...);
}
template<
std::size_t N,
typename... Args>
auto tuple_remove (std::tuple const & t) {
return tuple_remove_impl(t, std::index_sequence_for{});
}
if_t
(see further down) is just my shorthand for std:: conditional
, Use
and Ignore
need to be implemented:
struct Ignore {
template
static std::tuple<> from(Tuple) {
return {};
}
};
template
struct Use {
template
static auto from(Tuple t) {
return std:: make_tuple(std::get(t));
}
};
tuple_remove
exploits that std::tuple_cat
accepts empty std::tuple<>
arguments, and because it cannot get
something out of them, basically ignores them.
Shorthand for std::conditional
:
template
using if_t = typename std::conditional<
Condition, Then, Else>::type;