Reputation: 113
I have a std::tuple
filled with objects instantiated from class template with one type parameter. Now I want to get, at compile time, an element with specified type parameter from my tuple. Here is the code:
template<typename Params, typename Descriptor>
struct IsParamsEqual;
template<typename Params1, typename Params2, ApiCommand::Value value>
struct IsParamsEqual<Params1, Descriptor<value, Params2>>
{
static constexpr bool v = std::is_same<Params1, Params2>::value;
};
template<typename Params, size_t I, typename... Args>
constexpr size_t getIndexByParamsHelper(const IndexSequence<I>&, const std::tuple<Args...> &)
{
return I;
}
template<typename Params, size_t I, size_t... Indexes, typename... Args>
constexpr size_t getIndexByParamsHelper(const IndexSequence<I, Indexes...> &,
const std::tuple<Args...> &tuple)
{
return IsParamsEqual<Params, typename std::tuple_element<I, std::tuple<Args...>>::type>::v ?
I : getIndexByParamsHelper<Params>(IndexSequence<Indexes...>(), tuple);
}
template<typename Params, size_t... Indexes, typename... Args>
constexpr size_t getIndexByParams(const IndexSequence<Indexes...> &seq,
const std::tuple<Args...> &tuple)
{
return getIndexByParamsHelper<Params>(seq, tuple);
}
template<typename Params, typename... Args>
constexpr auto getByParamsImpl(const std::tuple<Args...> &tuple)
{
constexpr size_t I = getIndexByParams<Params>(
typename MakeIndexSequence<sizeof...(Args)>::type(), tuple);
static_assert(std::is_same<typename std::remove_reference<decltype(
std::get<I>(tuple))>::type::paramType, Params>::value,
"Param not found");
return std::get<I>(tuple);
}
This compiles fine on gcc 4.8.4 but not on vs2015u2. The error is in the getByParamsImpl()
and it says:
error C2131: expression did not evaluate to a constant
note: failure was caused by non-constant arguments or reference to a non-constant symbol
see usage of 'I'
Obviously, the compiler thinks that getIndexByParams()
return value is not constexpr.
Why, and - more importantly - how can this be fixed?
Upvotes: 1
Views: 151
Reputation: 113
Since I can't just throw away 'bad' compiler, I've implemented workaround. Maybe it will help someone.
template<typename Params, typename Descriptor>
struct IsParamsEqual;
template<typename Params1, typename Params2, ApiCommand::Value value>
struct IsParamsEqual<Params1, Descriptor<value, Params2>>
{
static constexpr bool v = std::is_same<Params1, Params2>::value;
};
template<typename Params, typename IS, typename... Args>
struct GetIndexByParam;
template<typename Param, typename... Args>
struct GetIndexByParam<Param, IndexSequence<>, Args...>
{
typedef typename std::integral_constant<size_t, 0> type;
};
template <typename Param, size_t I, size_t... IndexesTail, typename Head,
typename ...Tail>
struct GetIndexByParam<Param, IndexSequence<I, IndexesTail...>, Head, Tail...>
{
typedef typename std::conditional<IsParamsEqual<Param, Head>::v,
std::integral_constant<size_t, I>,
typename GetIndexByParam<Param, IndexSequence<IndexesTail...>, Tail...>::type >::type type;
};
template<typename Params, typename... Args>
constexpr auto getByParamsImpl(const std::tuple<Args...> &tuple)
{
typedef typename GetIndexByParam<Params,
typename MakeIndexSequence<sizeof...(Args)>::type, Args...>::type IndexValueType;
static_assert(std::is_same<
typename std::remove_reference<decltype(std::get<IndexValueType::value>(tuple))>::type::paramType, \
Params>::value, "Parameter not found");
return std::get<IndexValueType::value>(tuple);
}
Upvotes: 0
Reputation: 24956
If I get it right you're trying to do something like
constexpr std::tuple<int, float, int> t1{ 8, 2.0f, 1 };
constexpr std::tuple<int, float, double> t2{ 1, 2.0f, 3.0 };
constexpr std::tuple<unsigned, float, double> t3{ 1u, 2.0f, 3.0 };
constexpr auto f1 = find_by_type<int>(t1);
constexpr auto f2 = find_by_type<double>(t2);
constexpr auto f3 = find_by_type<unsigned>(t3);
where
f1
should be equivalent to constexpr int f1 = 8;
f2
should be equivalent to constexpr double f2 = 3.0;
f3
should be equivalent to constexpr unsigned f3 = 1u;
You can get something like this via recursion through the paramter pack:
// helper
template<class ... Ts> struct Finder;
// empty to have compiler error if type is not found
template<class T> struct Finder<T> { };
// if first type in pack is type to find, we stop
template<class ToFind, class ... Ts>
struct Finder<ToFind, ToFind, Ts...>
{
constexpr static std::size_t N = 0u;
};
// if first type in pack is not type to find
// we increase by one and go down the pack
template<class ToFind, class First, class ... Amongst>
struct Finder<ToFind, First, Amongst...>
{
constexpr static std::size_t N = 1u + Finder<ToFind, Amongst...>::N;
};
template<class P, class ... Args>
constexpr auto find_by_type(const std::tuple<Args...> &t)
{
return std::get<Finder<P, Args...>::N>(t);
}
Upvotes: 0