在运行时从元组中提要模板函数元素?
Feed template function element from tuple at runtime?
在 C++ 中,我有一个包含一些元素的元组:
std::tuple <int, char> my_tuple(3, 'q');
还有一些模板函数可以完美地处理整数和字符:
template <class T> void my_function(T);
现在,假设我想在运行时对元组的其中一个元素运行 my_function(但我不知道是哪个)。我注意到无法执行以下操作:
unsigned int n;
// Give a value to n
my_function(std::get <n> (my_tuple));
但是,原则上我需要的应该与以下内容相同:
unsigned int n;
// Give a value to n
switch(n)
{
case 0:
my_function(std::get <0> (my_tuple));
break;
case 1:
my_function(std::get <1> (my_tuple));
break;
default:
// Do nothing or throw an exception
}
所以我觉得这应该可行..是吗?
n
是一个运行时值,它不能用于在编译时实例化模板。您的 switch
之所以有效,是因为您手动实例化了每个 std::get<N>
,并将它们连接到相应的运行时值。
但是,是的,写那个脑残 switch
树有点麻烦。为什么不让编译器生成带有一点 TMP 的样板文件?
#include <tuple>
#include <cassert>
#include <iostream>
template <class T>
void my_function(T);
// Test specialisations to see what's going on
template <> void my_function(int i) { std::cout << "int " << i << '\n'; }
template <> void my_function(char c) { std::cout << "char " << c << '\n'; }
namespace detail {
// Available in std in C++14
template <bool P, class T>
using enable_if_t = typename std::enable_if<P, T>::type;
// Mockup function signature to pick up the call when enable_if shunts
template <std::size_t N, class T = void>
void callMyFunc(T&&, ...) {
assert(!"Index not in range !");
}
// "Simple" recursive solution, removes itself from the overload set
// to stop recursion
template <std::size_t N, class... Ts,
class = enable_if_t<N < sizeof...(Ts), void>>
void callMyFunc(std::tuple<Ts...> &tuple, std::size_t n) {
return n == N
? my_function(std::get<N>(tuple))
: callMyFunc<N + 1>(tuple, n);
}
}
// Tiny user-friendly wrapper
template <class... Ts>
void callMyFunc(std::tuple<Ts...> &tuple, std::size_t n) {
detail::callMyFunc<0u>(tuple, n);
}
int main(int, char**) {
std::tuple <int, char> my_tuple(3, 'q');
// Success.
callMyFunc(my_tuple, 0u);
callMyFunc(my_tuple, 1u);
return 0;
}
以下内容可能会有所帮助:
template <typename T> struct caller;
template <typename... Ts> struct caller<std::tuple<Ts...>>
{
template <typename F>
void operator() (F f, std::tuple<Ts...>& t, int n)
{
(*this)(f, t, n, std::index_sequence_for<Ts...>());
}
private:
template <typename F, std::size_t ... Is>
void operator() (F f, std::tuple<Ts...>& t, int n, std::index_sequence<Is...>)
{
std::function<void(F, std::tuple<Ts...>&)> fs[] = { &helper<F, Is>... };
fs[n](f, t);
}
template <typename F, std::size_t I>
static void helper(F f, std::tuple<Ts...>& t)
{
f(std::get<I>(t));
}
};
template <typename F, typename T>
void call(F f, T& t, int n)
{
caller<T>()(f, t, n);
}
在 C++ 中,我有一个包含一些元素的元组:
std::tuple <int, char> my_tuple(3, 'q');
还有一些模板函数可以完美地处理整数和字符:
template <class T> void my_function(T);
现在,假设我想在运行时对元组的其中一个元素运行 my_function(但我不知道是哪个)。我注意到无法执行以下操作:
unsigned int n;
// Give a value to n
my_function(std::get <n> (my_tuple));
但是,原则上我需要的应该与以下内容相同:
unsigned int n;
// Give a value to n
switch(n)
{
case 0:
my_function(std::get <0> (my_tuple));
break;
case 1:
my_function(std::get <1> (my_tuple));
break;
default:
// Do nothing or throw an exception
}
所以我觉得这应该可行..是吗?
n
是一个运行时值,它不能用于在编译时实例化模板。您的 switch
之所以有效,是因为您手动实例化了每个 std::get<N>
,并将它们连接到相应的运行时值。
但是,是的,写那个脑残 switch
树有点麻烦。为什么不让编译器生成带有一点 TMP 的样板文件?
#include <tuple>
#include <cassert>
#include <iostream>
template <class T>
void my_function(T);
// Test specialisations to see what's going on
template <> void my_function(int i) { std::cout << "int " << i << '\n'; }
template <> void my_function(char c) { std::cout << "char " << c << '\n'; }
namespace detail {
// Available in std in C++14
template <bool P, class T>
using enable_if_t = typename std::enable_if<P, T>::type;
// Mockup function signature to pick up the call when enable_if shunts
template <std::size_t N, class T = void>
void callMyFunc(T&&, ...) {
assert(!"Index not in range !");
}
// "Simple" recursive solution, removes itself from the overload set
// to stop recursion
template <std::size_t N, class... Ts,
class = enable_if_t<N < sizeof...(Ts), void>>
void callMyFunc(std::tuple<Ts...> &tuple, std::size_t n) {
return n == N
? my_function(std::get<N>(tuple))
: callMyFunc<N + 1>(tuple, n);
}
}
// Tiny user-friendly wrapper
template <class... Ts>
void callMyFunc(std::tuple<Ts...> &tuple, std::size_t n) {
detail::callMyFunc<0u>(tuple, n);
}
int main(int, char**) {
std::tuple <int, char> my_tuple(3, 'q');
// Success.
callMyFunc(my_tuple, 0u);
callMyFunc(my_tuple, 1u);
return 0;
}
以下内容可能会有所帮助:
template <typename T> struct caller;
template <typename... Ts> struct caller<std::tuple<Ts...>>
{
template <typename F>
void operator() (F f, std::tuple<Ts...>& t, int n)
{
(*this)(f, t, n, std::index_sequence_for<Ts...>());
}
private:
template <typename F, std::size_t ... Is>
void operator() (F f, std::tuple<Ts...>& t, int n, std::index_sequence<Is...>)
{
std::function<void(F, std::tuple<Ts...>&)> fs[] = { &helper<F, Is>... };
fs[n](f, t);
}
template <typename F, std::size_t I>
static void helper(F f, std::tuple<Ts...>& t)
{
f(std::get<I>(t));
}
};
template <typename F, typename T>
void call(F f, T& t, int n)
{
caller<T>()(f, t, n);
}