Although I already posted an answer to my own question, and I accepted an answer from another user, I thought I could post another possibility in tackling this problem using the following struct
template <typename... Ts> struct type_sequence { };
which I learnt about in this talk by Andrei Alexandrescu. Since I learnt quite a bit by using it and the result is a bit simpler than the original answer that used two nested structs I thought I would share it here. However, the solution I would actually implement is the one that was accepted.
This is the full code with a main
function included. Notice the change in the specification of function return_something
. Now this function indicates the return type (which I like very much, perhaps I'm old fashioned) in a more readable way than in my first answer. You can try it out here.
#include <type_traits>
#include <iostream>
#include <vector>
template <bool... values>
struct which {
template <std::size_t idx, bool v1, bool... vs>
struct _which_impl {
static constexpr std::size_t value =
(v1 ? idx : _which_impl<idx + 1, vs...>::value);
};
template <std::size_t idx, bool v>
struct _which_impl<idx, v> {
static constexpr std::size_t value = (v ? idx : idx + 1);
};
static constexpr std::size_t value = _which_impl<0, values...>::value;
};
template <std::size_t ith_idx, typename... Ts>
struct ith_type {
template <std::size_t cur_idx, typename t1, typename... ts>
struct _ith_type_impl {
using type =
std::conditional_t<
ith_idx == cur_idx,
t1,
typename _ith_type_impl<cur_idx + 1, ts...>::type
>;
};
template <std::size_t cur_idx, typename t1>
struct _ith_type_impl<cur_idx, t1> {
using type = std::conditional_t<ith_idx == cur_idx, t1, std::nullptr_t>;
};
using type = typename _ith_type_impl<0, Ts...>::type;
};
template <std::size_t ith_idx, typename... ts>
using ith_type_t = typename ith_type<ith_idx, ts...>::type;
template <bool... conds>
static constexpr std::size_t which_v = which<conds...>::value;
template <typename... Ts> struct type_sequence { };
template <bool... values> struct bool_sequence {
static constexpr std::size_t which = which_v<values...>;
};
template <std::size_t ith_idx, typename... Ts>
struct ith_type<ith_idx, type_sequence<Ts...>> : ith_type<ith_idx, Ts...>
{ };
template <typename bool_sequence, typename type_sequence>
struct conditional_list {
using type = ith_type_t<bool_sequence::which, type_sequence>;
};
template <typename bool_sequence, typename type_sequence>
using conditional_list_t =
typename conditional_list<bool_sequence, type_sequence>::type;
enum class list_whats {
what1,
what2,
what3,
what4,
what5,
};
template <list_whats what>
conditional_list_t<
bool_sequence<
what == list_whats::what1,
what == list_whats::what2,
what == list_whats::what3,
what == list_whats::what4,
what == list_whats::what5
>,
type_sequence<
int,
float,
double,
std::string,
std::vector<int>
>
>
return_something() noexcept {
if constexpr (what == list_whats::what1) { return 1; }
if constexpr (what == list_whats::what2) { return 2.0f; }
if constexpr (what == list_whats::what3) { return 3.0; }
if constexpr (what == list_whats::what4) { return "42"; }
if constexpr (what == list_whats::what5) { return {1,2,3,4,5}; }
}
int main() {
[[maybe_unused]] auto s1 = return_something<list_whats::what1>();
[[maybe_unused]] auto s2 = return_something<list_whats::what2>();
[[maybe_unused]] auto s3 = return_something<list_whats::what3>();
[[maybe_unused]] auto s4 = return_something<list_whats::what4>();
[[maybe_unused]] auto s5 = return_something<list_whats::what5>();
}