What about using variadic templates ?
#include <type_traits>
#include <iostream>
template <typename T, T...>
struct O;
template <typename T, T val>
struct O<T, val>
{
static constexpr bool has_value { true };
using type = T;
T value { val };
};
template <typename T>
struct O<T>
{
static constexpr bool has_value { false };
using type = T;
};
template <typename...>
struct S;
template <char ... Cs, int ... Is, long ... Ls, long long ... Lls,
bool ... Bs>
struct S<O<char, Cs...>, O<int, Is...>, O<long, Ls...>,
O<long long, Lls...>, O<bool, Bs...>>
{
O<char, Cs...> v1;
O<int, Is...> v2;
O<long, Ls...> v3;
O<long long, Lls...> v4;
O<bool, Bs...> v5;
};
int main ()
{
S<O<char>, O<int, 123>, O<long>, O<long long>, O<bool, true>> s;
std::cout << s.v1.has_value << std::endl;
std::cout << s.v2.has_value << ", " << s.v2.value << std::endl;
std::cout << s.v3.has_value << std::endl;
std::cout << s.v4.has_value << std::endl;
std::cout << s.v5.has_value << ", " << s.v5.value << std::endl;
}
If you can use C++17, you can simplify a little, using auto
for the type of the value but maintaining the requirements abut the type of the optional for S
(an optional char
, an optional int
, an optional long
, an optional long long
and an optional bool
).
#include <iostream>
template <auto ...>
struct O;
template <auto val>
struct O<val>
{
static constexpr bool has_value { true };
using type = decltype(val);
type value { val };
};
template <>
struct O<>
{ static constexpr bool has_value { false }; };
template <typename...>
struct S;
template <char ... Cs, int ... Is, long ... Ls, long long ... Lls,
bool ... Bs>
struct S<O<Cs...>, O<Is...>, O<Ls...>, O<Lls...>, O<Bs...>>
{
O<Cs...> v1;
O<Is...> v2;
O<Ls...> v3;
O<Lls...> v4;
O<Bs...> v5;
};
int main ()
{
S<O<>, O<123>, O<>, O<>, O<true>> s;
std::cout << s.v1.has_value << std::endl;
std::cout << s.v2.has_value << ", " << s.v2.value << std::endl;
std::cout << s.v3.has_value << std::endl;
std::cout << s.v4.has_value << std::endl;
std::cout << s.v5.has_value << ", " << s.v5.value << std::endl;
}