-
Notifications
You must be signed in to change notification settings - Fork 177
Using variadic templates with function calls using tuple
Wongoo Lee edited this page Feb 10, 2016
·
3 revisions
After this, I started working on this code.
template <class R>
void function0_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
boost::function<R ()>* f =
static_cast<boost::function<R ()>*>(sqlite3_user_data(ctx));
context c(ctx, nargs, values);
c.result((*f)());
}
template <class R, class P1>
void function1_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
boost::function<R (P1)>* f =
static_cast<boost::function<R (P1)>*>(sqlite3_user_data(ctx));
context c(ctx, nargs, values);
c.result((*f)(c.context::get<P1>(0)));
}
...
template <class R, class P1, class P2, class P3, class P4, class P5>
void function5_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
boost::function<R (P1, P2, P3, P4, P5)>* f =
static_cast<boost::function<R (P1, P2, P3, P4, P5)>*>(sqlite3_user_data(ctx));
context c(ctx, nargs, values);
c.result((*f)(c.context::get<P1>(0), c.context::get<P2>(1), c.context::get<P3>(2), c.context::get<P4>(3), c.context::get<P5>(4)));
}
It was much harder since it had to be able to call a method per parameter using index.
(*f)(c.context::get<P1>(0),
c.context::get<P2>(1),
c.context::get<P3>(2),
...);
}
So, I made a plan to do this in two steps.
- make a tuple for the params.
- and call the function using the tuple.
template <class... Ts>
std::tuple<Ts...> to_tuple() {
return to_tuple_impl(0, *this, std::tuple<Ts...>());
}
template<class H, class... Ts>
static inline std::tuple<H, Ts...> to_tuple_impl(int index, const context& c, std::tuple<H, Ts...>&&)
{
auto h = std::make_tuple(c.context::get<H>(index));
return std::tuple_cat(h, to_tuple_impl(++index, c, std::tuple<Ts...>()));
}
static inline std::tuple<> to_tuple_impl(int index, const context& c, std::tuple<>&&)
{
return std::tuple<>();
}
This is not the most efficient code that can do this but the cleanest one that I could write. Frankly, in modern C++, I'm not sure if I'm helping compiler optimize the code or doing opposite.
I found the solution for this by googling.
template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F&& f, T&& t, A&&... a)
-> decltype(Apply<N-1>::apply(std::forward<F>(f),
std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)),
std::forward<A>(a)...))
{
return Apply<N-1>::apply(std::forward<F>(f),
std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)),
std::forward<A>(a)...);
}
};
template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F&& f, T&&, A&&... a)
-> decltype(std::forward<F>(f)(std::forward<A>(a)...))
{
return std::forward<F>(f)(std::forward<A>(a)...);
}
};
template<typename F, typename T>
inline auto apply(F&& f, T&& t)
-> decltype(Apply<std::tuple_size<typename std::decay<T>::type>::value>::apply(std::forward<F>(f), std::forward<T>(t)))
{
return Apply<std::tuple_size<typename std::decay<T>::type>::value>::apply(
std::forward<F>(f), std::forward<T>(t));
}
Nice code, isn't it? See details in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3915.pdf and http://stackoverflow.com/a/12650100. The second link has the version for C++14 too.
template <class R, class... Ps>
void functionx_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
context c(ctx, nargs, values);
auto f = static_cast<std::function<R (Ps...)>*>(sqlite3_user_data(ctx));
c.result(apply(*f, c.to_tuple<Ps...>()));
}