Is there an std::variant that holds all variants - c++

I am looking for a class that is like std::variant that holds all types instead of one of the types.
So I would like to be able to write:
std::all_variant<int, double> av{1, 3.14159};
int i = std::get<int>(av); // i should be 1.
double d = std::get<double>(av); // d should be 3.14159
This is basically a tuple but guarantees that each type appears only once so you can do std::get<Type> instead of std::get<Index>.
For context I want to be able to write the following code (so if there is a better way of doing that I would appreciate to know):
std::all_variant<int, double> av{1, 3.14159};
template <typename T> // T must be either int or double.
void MyFunc(std::all_variant<int, double> av) {
std::cout << std::get<T>(av);
}
int main() {
MyFunc<int>(av); // Should print 1.
MyFunc<double>(av); // Should print 3.14159.
MyFunc<std::string>(av); // Error! av doesn't have std::string variant.
}
I'm not sure what this would be called so I'm not sure if this is in the standard library. I guess it wouldn't be to hard to write one but I don't want to reinvent the wheel.

I am looking for a class that is like std::variant that holds all types instead of one of the types.
What you are looking for is called std::tuple
std::tuple<int,double> mytup( 1, 2.0 );
std::cout << std::get<int>( mytup ) << "\n"; // prints 1
std::cout << std::get<double>( mytup ) << "\n"; // prints 2
std::cout << std::get<std::string>( mytup ) << "\n"; // compiler error
Live example
As for single type appearance requirement you would get compilation error when you try to use std::get<type> instead of index with std::tuple with duplicated types. (thanks #max66)

In addition to Slava's answer, you can enforce type-uniqueness in a tuple using for example something like is_unique from this post:
#include <tuple>
#include <type_traits>
// From https://stackoverflow.com/a/47511516
template <typename...>
inline constexpr auto is_unique = std::true_type{};
template <typename T, typename... Rest>
inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
>{};
// Tuple that only accept unique type parameters
template<typename... Ts>
using uniq_tuple = typename std::enable_if<is_unique<Ts...>, std::tuple<Ts...>>::type;
int main()
{
// Compiles
uniq_tuple<int, float> t1;
// Fails
uniq_tuple<int, float, int> t2;
return 0;
}

Related

C++ map key to object

Hi is there a container where a key is a typename and value is an object/instance in boost or std???
What I want to achieve is I have a object pool for each data type and when I want to construct that object I want to just fetch by Key. I already have working code but I would be happier if i used something more standard.
Currently I have a code like this:
template<size_t index, size_t counter, typename T0, typename ...T>
struct PoolBuilderInternal
{
typedef typename boost::mpl::insert<
typename PoolBuilderInternal<index - 1, counter + 1, T...>::m
, boost::mpl::pair<T0, std::integral_constant<size_t, counter> > >::type m;
};
template<size_t counter, typename T0, typename ...T>
struct PoolBuilderInternal<1, counter, T0, T...>
{
typedef typename boost::mpl::map<boost::mpl::pair<T0, std::integral_constant<size_t, counter> > > m;
};
template<typename ...T>
struct Pools
{
std::tuple<
boost::object_pool<T>...> m_pools;
typedef typename PoolBuilderInternal<sizeof...(T), 0, T...>::m poolType;
template<typename T>
boost::object_pool<T>& GetPool()
{
return std::get< boost::mpl::at<poolType, T>::type::value>(m_pools);
}
};
Pools<int, std::string, int64_t> m_pools;
m_pools.Get<int>();
EDIT: What I want is a COMPILE-TIME map. You can imagine a std::tuple<> but some that wouldnt not be accessed by index std::get<0,1,2>... but rather by a key (?std::tuple_map)
If types in the pool are unique use c++ 14 std::get< T >(std::tuple(s))
#include <iostream>
#include <string>
#include <tuple>
struct A
{
int value = 17;
};
int main()
{
auto t = std::make_tuple(1, std::string{"Foo"}, 3.14, A{});
std::cout << "(" << std::get<int>(t) << ", "
<< std::get<std::string>(t)
<< ", " << std::get<double>(t)
<< ", " << std::get<A>(t).value << ")\n";
}
If I understand this question right (and I'm not sure I do), what you really want is some kind of class factory, and that, in various forms, is a well-known design pattern because it allows users of the factory to construct objects whose constructors (and indeed types, quite often, when a class hierarchy is involved) are unknown or off-limits to them.
On that basis, I humbly offer you the following proof-of-concept code. Please note that I threw this together in rather a hurry and it's probably not optimal. I'm sure more is possible, including passing parameters to the relevant constructors to make_string() and make_foo() from the call site (e.g. factory [t_string] ("My string"). I'll look into that when I have time, if you show any interest in this post.
OK, so here's your class factory. You should be able to apply it to any types, including boost types. As coded, any parameters that need to be passed to the object in question are currently defined when the factory function (e.g. make_string) is added to the map (which is probably not ideal). These factory functions also could / should be lambdas. Again, I'll add that in later.
#include <functional>
#include <unordered_map>
#include <variant>
#include <iostream>
struct Foo
{
Foo (int x) : x (x) { }
int x;
};
enum ObjectType { t_string, t_foo }; // ...
using all_possible_types = std::variant <std::string, Foo>; // ...
static all_possible_types make_string (const std::string& s)
{
std::cout << "make_string " << s << "\n";
return all_possible_types (std::string (s));
}
static all_possible_types make_foo (int x)
{
std::cout << "make_foo " << x << "\n";
return all_possible_types (Foo (x));
}
// ...
int main()
{
std::unordered_map <ObjectType, std::function <all_possible_types ()>> factory;
factory.insert ({t_string, std::bind (make_string, "Initial string value")});
factory.insert ({t_foo, std::bind (make_foo, 42)});
// ...
all_possible_types variant_string = factory [t_string] ();
std::cout << std::get <std::string> (variant_string) << "\n\n";
all_possible_types variant_foo = factory [t_foo] ();
std::cout << std::get <Foo> (variant_foo).x << "\n";
}
Output:
make_string Initial string value
Initial string value
make_foo 42
42
Live demo.
As I say, this doesn't look like much now but I will improve it later. In the meantime, I suggest you take a look at it to get your head around what I'm doing here.

Filter a tuple of types in c++17

std::tuple a{1,3,4,5} -> make it to numbers greater than 3
std::tuple b{4,5}
Or
std::tuple a{
std::integral_constant<int,1> {},
std::integral_constant<int,3> {},
std::integral_constant<int,4> {},
std::integral_constant<int,5> {}
}
to
std::tuple a{
std::integral_constant<int,4>{},
std::integral_constant<int,5>{}
};
How to convert this at compile time? I can do this using integer_sequence but that is a cumbersome. Is there a simpler way in C++17 using fold expressions or std::apply
Also after filter, also need to get a tuple of unique entries. But my assumption is if filtering can be done, then finding unique would be trivial.
Edit so that is more clear:
std::tuple<int_c<1>, int_c<3>,int_c<4>,int_c<5>> to std::tuple<int_c<4>,int_c<5> <-- If such is possible in a concise c++17 way without extra declare functions, it would do!.
Edit:
I was fiddling around, maybe something like this would work:
with template... C as the list of integrals constants:
constexpr auto result = std::tuple_cat(std::conditional_t<(C::value > 3), std::tuple<C>, std::tuple<>>{}...);
To turn out your tuple_cat with c++17:
constexpr auto result = std::apply([](auto...ts) {
return std::tuple_cat(std::conditional_t<(decltype(ts)::value > 3),
std::tuple<decltype(ts)>,
std::tuple<>>{}...);
}, tup);
A possible solution is to produce a trait that will output std::tuple<T> for desirable elements T and std::tuple<> for undesirable elements and to use std::tuple_cat to recombine those tuples into a single type. For example :
#include <tuple>
#include <type_traits>
#include <utility>
template <typename Pred, typename Tuple> struct filter;
template <typename t_Predicate, typename ...Ts>
struct filter<t_Predicate, std::tuple<Ts...>>
{
// If this element has to be kept, returns `std::tuple<Ts>`
// Otherwise returns `std::tuple<>`
template<class E>
using t_filter_impl = std::conditional_t<
t_Predicate<E>::value,
std::tuple<E>, std::tuple<>>;
// Determines the type that would be returned by `std::tuple_cat`
// if it were called with instances of the types reported by
// t_filter_impl for each element
using type = decltype(std::tuple_cat(std::declval<t_filter_impl<Ts>>()...));
};
Where t_Predicate<T> is any predicate type with a bool value; member which determines whether or not T is a desirable type. For example to apply this solution to the original question, first write a predicate type specialized for std::integral_constant :
// Non integral_constant are not kept
template<class T>
struct four_or_more : std::integral_constant<bool, false> {};
// integral_const types are kept if their value is >=4
template<class T, T V>
struct four_or_more<std::integral_constant<T, V>> :
std::integral_constant<bool, V >= 4> {};
And here is a demonstration :
#include <iostream>
int main()
{
auto a = std::make_tuple(
std::integral_constant<int,1> {},
std::integral_constant<int,3> {},
std::integral_constant<int,4> {},
std::integral_constant<int,5> {}
);
using b_type = filter<four_or_more, decltype(a)>::type;
std::cout << "size : " << std::tuple_size<b_type>() << std::endl;
std::cout << std::tuple_element_t<0, b_type>::value << std::endl;
std::cout << std::tuple_element_t<1, b_type>::value << std::endl;
}
You can do that with new STL utilities from C++17. That would be something like that:
template<typename T>
auto filter(T tup) {
return std::apply([&](auto first, auto... rest) {
auto filtered_rest = [&]{
if constexpr (sizeof...(rest)) {
return filter(std::tuple{rest...});
} else {
return std::tuple{};
}
}();
if constexpr (first > 3) {
return std::tuple_cat(std::tuple{first}, filtered_rest);
} else {
return filtered_rest;
}
}, tup);
}
Of course, there is many other ways to do it. In this case I used std::apply and recursion. I start by an empty tuple and I add one element at a time.
Live example: https://godbolt.org/z/qo63r4

Generating elements based on underlying tuple types during run-time

How can I deduce the type or pair elements during compile-time? I need it to make a correct instantion accordingly to argument type (e.g. for Integer_random_generator those may be long, int, unsigned int etc., for real_random_generator double, float and other floating-points). I need to make generators with certain bounds as you can see in the commented out lines 63-77.
#ifndef OPTIMALIZATION_HPP
#define OPTIMALIZATION_HPP
#include<utility>
#include<random>
#include<experimental/random>
#include<functional>
#include<experimental/functional>
#include<experimental/tuple>
#include<algorithm>
#include<type_traits>
#include<iostream>
#include"tuple_for_each.hpp"
//ZNAJDZ/ZROB INDEKSOWANY DOSTEP DO TUPLI W CZASIE RUN-TIME'U
namespace numerics{
template<
std::size_t population_size, std::size_t generations,
typename Func,
typename Compare, //GREATER, LESS, TYPE RETURNED BY FUNCTION
typename Generator=std::default_random_engine,
template<typename>
typename RealDistribution=std::uniform_real_distribution,
template<typename>
typename IntegerDistribution=std::uniform_int_distribution,
typename ...Ts
>
auto optimize(
const Func& function, const Compare& comp,
const std::pair<Ts,Ts>&... range
){
std::size_t range_argument_count=sizeof...(range);
static_assert(range_argument_count>2,
"Function needs at least two range arguments"
);
//RANDOM NUMBER GENERATORS
auto real_random_generator=[&](const std::pair<auto,auto> range){
std::cout << "DOUBLE" << std::endl;
return std::bind(
RealDistribution<long double>(range.first,range.second),
//RealDistribution<decltype(range.first)>(range.first,range.second);
//HOW TO DEDUCE TYPE OF DISTRIBUTION FROM PAIR DURING COMPILE-TIME???
Generator()
);
};
auto integer_random_generator=[&](const std::pair<auto,auto>& range){
std::cout << "INTEGER" << std::endl;
return std::bind(
IntegerDistribution<long long>(range.first,range.second),
//IntegerDistribution<decltype(range.first)>(range.first,range.second);
//HOW TO DEDUCE TYPE OF DISTRIBUTION FROM PAIR DURING COMPILE-TIME???
Generator()
);
};
std::cout << integer_random_generator(std::get<0>(std::make_tuple(range...)))() << std::endl;
std::cout << real_random_generator(std::get<1>(std::make_tuple(range...)))()<<std::endl;
std::cout << integer_random_generator(std::get<2>(std::make_tuple(range...)))()<<std::endl;
//GENERATORS DEPENDING ON TYPE, USED THROUGH WHOLE PROGRAM
/*std::tuple<> generators;
numerics::for_each(std::make_tuple(range...), [&](std::pair<auto,auto>& x){ //FOR_EACH SPRAWDZENIA
try{
generators=std::tuple_cat(
generators,
std::make_tuple(integer_random_generator(x.first)));
}
catch(...){
generators=std::tuple_cat(
generators,
std::make_tuple(real_random_generator(x.first)));
}
}
);*/
return "pls work";
}
}
#endif
Test cases:
#include<utility>
#include<iostream>
#include"optimize.hpp"
class Function{
public:
Function()=default;
double operator()(int x, double y, long z)const{
return (std::exp(x+1.25)*std::pow(y,z))/std::exp((x*y)/z);
}
};
int main(){
Function f{};
auto comp=std::less<double>();
auto x=numerics::optimize<100, 200>(
f, comp,
std::make_pair(-21, 37),
std::make_pair(14.88, 88.41),
std::make_pair(-13, 37)
);
std::cout << x << std::endl;
}
Write a helper function to determine whether to make a real or an integer generator. In this case it will be based off the type of the first parameter in the range. I will leave it to you to determine how to also check the second parameter. (I haven't tested this code, but I am confident that the idea works).
template <class Range>
auto get_distribution(Range range, std::true_type)
{
return real_random_generator(range);
}
template <class Range>
auto get_distribution(Range range, std::false_type)
{
return integer_random_generator(range);
}
Simply use the pack expansion syntax to call your helper function on every range
auto generators = std::make_tuple(get_distribution(ranges,
std::is_floating_point<decltype(ranges.first)>{})...);
The std::is_floating_point is what determines which overload gets called for each range.
If you like tricky, but more concise, code you could try this out:
auto fs = std::make_tuple(integer_random_generator, real_random_generator);
auto generator = std::make_tuple(std::get<std::is_floating_point<decltype(ranges.first)>::value>(fs)(ranges)...);
Construct a tuple of your generator-generating lambdas (this wouldn't work if they weren't objects) and use the condition as an index into the tuple and immediately call it. I don't advocate using this, but I thought I would share it nonetheless.

Copy two tuples with different sizes

I am experimenting with some tuples, and I find myself in the weird position of asking this: how can I copy two tuples that differ in their sizes? Of course, this is intended limited to the minimum length of the two tuples.
So, for instance, let's create three tuples:
std::tuple<int, char, float> a(-1, 'A', 3.14);
std::tuple<int, char, double> b = a;
std::tuple<long, int, double, char> c;
Now, a and b differ in types, and the assignment work (obviously). As for a and c the things get a little more confusing.
My first implementation failed, since I don't know how to recurse on variadic templates with a specific type, so something like this won't work:
template <class T, class U>
void cp(std::tuple<T> from, std::tuple<U> to)
{
}
template <class T, class... ArgsFrom, class U, class... ArgsTo>
void cp(std::tuple<T, ArgsFrom...> from, std::tuple<U, ArgsTo...> to)
{
std::get<0>(to) = std::get<0>(from);
// And how to generate the rest of the tuples?
}
That function won't do anything. So I've devised a second failing attempt, using not the types, but the sizes:
template<class From, class To, std::size_t i>
void copy_tuple_implementation(From &from, To &to)
{
std::get<i>(to) = std::get<i>(from);
copy_tuple_implementation<From, To, i - 1>(from, to);
}
template<>
void copy_tuple_implementation<class From, class To, 0>(From &from, To &to)
{
}
template<class From, class To>
void copy_tuple(From &from, To &to)
{
constexpr std::size_t from_len = std::tuple_size<From>::value;
constexpr std::size_t to_len = std::tuple_size<To>::value;
copy_tuple_implementation<From, To, from_len < to_len ? from_len - 1 : to_len - 1>(from, to);
}
But that won't compile. I have too many errors to display here, but the most significant ones are:
Static_assert failed "tuple_element index out of range"
No type named 'type' in 'std::__1::tuple_element<18446744073709551612, std::__1::__tuple_types<> >'
Read-only variable is not assignable
No viable conversion from 'const base' (aka 'const __tuple_impl<typename __make_tuple_indices<sizeof...(_Tp)>::type, int, int, double>') to 'const __tuple_leaf<18446744073709551615UL, type>'
The interesting part is the index out of range, and the fact that I cannot copy an element with std::get<>.
Can anyone help me in this?
Thanks!
Here's one possibility, using C++14's ready-made integer sequence template (but this is easily reproduced manually if your library doesn't include it):
#include <tuple>
#include <utility>
template <std::size_t ...I, typename T1, typename T2>
void copy_tuple_impl(T1 const & from, T2 & to, std::index_sequence<I...>)
{
int dummy[] = { (std::get<I>(to) = std::get<I>(from), 0)... };
static_cast<void>(dummy);
}
template <typename T1, typename T2>
void copy_tuple(T1 const & from, T2 & to)
{
copy_tuple_impl(
from, to,
std::make_index_sequence<std::tuple_size<T1>::value>());
}
Example:
#include <iostream>
int main()
{
std::tuple<int, char> from { 1, 'x' };
std::tuple<int, char, bool> to;
copy_tuple(from, to);
std::cout << "to<0> = " << std::get<0>(to) << "\n";
}
Another option is to use operator overloading to simulate partial-specialization of your function:
template <std::size_t N>
struct size_t_t {};
template<class From, class To, std::size_t i>
void copy_tuple_implementation(From &from, To &to, size_t_t<i>)
{
std::get<i>(to) = std::get<i>(from);
copy_tuple_implementation(from, to, size_t_t<i-1>{});
}
template<class From, class To>
void copy_tuple_implementation(From &from, To &to, size_t_t<0>)
{
std::get<0>(to) = std::get<0>(from);
}
Or you could just use a helper class:
template<class From, class To, std::size_t i>
struct CopyTuple
{
static void run(From &from, To &to)
{
std::get<i>(to) = std::get<i>(from);
CopyTuple<From,To,i-1>::run(from, to);
}
};
template<class From, class To>
struct CopyTuple<From,To,0>
{
static void run(From &from, To &to)
{
std::get<0>(to) = std::get<0>(from);
}
};
The goal here is to get a clean syntax at point of use.
I define auto_slice which takes a tuple, and auto slices it for the expression.
The intended use is
auto_slice(lhs)=auto_slice(rhs);
and it just works.
// a helper that is a slightly more conservative `std::decay_t`:
template<class T>
using cleanup_t = std::remove_cv_t< std::remove_reference_t< T > >;
// the workhorse. It holds a tuple and in an rvalue context
// allows partial assignment from and to:
template<class T,size_t s0=std::tuple_size<cleanup_t<T>>{}>
struct tuple_slicer{
T&&t;
// Instead of working directly within operators, the operators
// call .get() and .assign() to do their work:
template<class Dest,size_t s1=std::tuple_size<Dest>{}>
Dest get() && {
// get a pack of indexes, and use it:
using indexes=std::make_index_sequence<(s0<s1)?s0:s1>;
return std::move(*this).template get<Dest>(indexes{});
}
template<class Dest,size_t s1=std::tuple_size<Dest>{},size_t...is>
Dest get(std::index_sequence<is...>) && {
// We cannot construct a larger tuple from a smaller one
// as we do not know what to populate the remainder with.
// We could default construct them, I guess?
static_assert(s0>=s1,"use auto_slice on target");
using std::get;
return Dest{ get<is>(std::forward<T>(t))... };
}
// allows implicit conversion from the slicer:
template<class Dest>
operator Dest()&&{
return std::move(*this).template get<Dest>();
}
// now we are doing the assignment work. This function
// does the pack expansion hack, excuse the strangeness of the
// code in it:
template<class Src, size_t...is>
void assign(std::index_sequence<is...>,tuple_slicer<Src>&&rhs)&&{
using std::get;
int _[]={0,(void(
get<is>(std::forward<T>(t))=get<is>(std::forward<Src>(rhs.t))
),0)...};
(void)_; // remove warnings
}
// assign from another slicer:
template<class Src,size_t s1>
void operator=(tuple_slicer<Src,s1>&&rhs)&&{
using indexes=std::make_index_sequence<(s0<s1)?s0:s1>;
std::move(*this).assign(indexes{},std::move(rhs));
}
// assign from a tuple. Here we pack it up in a slicer, and use the above:
template<class Src>
void operator=(Src&& src)&&{
std::move(*this) = tuple_slicer<Src>{ std::forward<Src>(src) };
}
};
// this deduces the type of tuple_slicer<?> we need for us:
template<class Tuple>
tuple_slicer<Tuple> auto_slice(Tuple&&t){
return {std::forward<Tuple>(t)};
}
The slice is only required on whichever side is smaller, but can be done on both sides (for generic code) if required.
It also works at construction. On the right hand side, it should work with std::arrays and pairs and tuples. On the left hand side, it may not work with arrays, due to requirement to construct with {{}}.
live example
Here is the recursive solution your were originally trying to figure out:
#include <tuple>
// Limit case
template<std::size_t I = 0, typename ...From, typename ...To>
typename std::enable_if<(I >= sizeof...(From) || I >= sizeof...(To))>::type
copy_tuple(std::tuple<From...> const & from, std::tuple<To...> & to) {}
// Recursive case
template<std::size_t I = 0, typename ...From, typename ...To>
typename std::enable_if<(I < sizeof...(From) && I < sizeof...(To))>::type
copy_tuple(std::tuple<From...> const & from, std::tuple<To...> & to)
{
std::get<I>(to) = std::get<I>(from);
copy_tuple<I + 1>(from,to);
}
You do not need std::index_sequence or similar apparatus, and this
solution has two strengths that your accepted one does not:
It will compile, and do the right thing, when from is longer than to: the
excess trailing elements of from are ignored.
It will compile, and do the right thing, when either from or to is an
empty tuple: the operation is a no-op.
Prepend it to this example:
#include <iostream>
int main()
{
std::tuple<int, char> a { 1, 'x' };
std::tuple<int, char, bool> b;
// Copy shorter to longer
copy_tuple(a, b);
std::cout << "b<0> = " << std::get<0>(b) << "\n";
std::cout << "b<1> = " << std::get<1>(b) << "\n";
std::cout << "b<2> = " << std::get<2>(b) << "\n\n";
// Copy longer to shorter
std::get<0>(b) = 2;
std::get<1>(b) = 'y';
copy_tuple(b,a);
std::cout << "a<0> = " << std::get<0>(a) << "\n";
std::cout << "a<1> = " << std::get<1>(a) << "\n\n";
// Copy empty to non-empty
std::tuple<> empty;
copy_tuple(empty,a);
std::cout << "a<0> = " << std::get<0>(a) << "\n";
std::cout << "a<1> = " << std::get<1>(a) << "\n\n";
// Copy non-empty to empty
copy_tuple(a,empty);
return 0;
}
(g++ 4.9/clang 3.5, -std=c++11)

typedef for function template (partial instantiation)

Sorry I didn't really know how to call my question, hope it fits...
I have a function template which gets an argument as template parameter. For that argument I need to have another template parameter which declares the type of the argument but while calling that function later on, I would like to omit the type of the argument. So, I would like to have some kind of typedef (or other mechanism) to get rid of it.
I've seen a similar mechnism with other templates, e.g.
// given: rule is a template with three arguments
template<typename Attr> using Rule = rule<It, Attr(), Skipper>;
When using std::get one can get along without mentioning the enum class directly:
std::get<static_cast<std::size_t>(Enum::type1)>(tuple);
Here is the function, it is used to access a tuple with an enum (compare: https://stackoverflow.com/a/14835597/2524462)
template<typename Enum, Enum enum, class ... Types>
typename std::tuple_element<static_cast<std::size_t>(enum), std::tuple<Types...> >::type&
get(std::tuple<Types...>& t) {
return std::get<static_cast<std::size_t>(enum)>(t);
}
Since it will be used with several enums, I don't want to hardwire the enum in the template like he did.
It is called like: (1)
std::cout << get<myEnum, myEnum::type1>(tuple);
Questions:
Can I use a typedef or similar to call it just like:
std::cout << new_get < myEnum::type1 > (tuple);
Since it works with std::get, is there a way to have a smarter template, in the first place?
The get template here has the tuple types as last parameters. Why is it not necessary to spell them out here (1)? How does the compiler figure them out from the given parameter?
I'm using gcc 4.8.1 with C++11 enabled.
I think the best you are going to be able to do is to create a get<>() function for each enumeration. Here is an example:
#include <tuple>
#include <string>
#include <iostream>
typedef std::tuple<std::string,std::string> Tuple1;
typedef std::tuple<std::string,int> Tuple2;
enum class Enum1 {
name,
address
};
enum class Enum2 {
state,
zip_code
};
template <typename Enum>
constexpr std::size_t indexOf(Enum value)
{
return static_cast<std::size_t>(value);
}
template <typename Enum,Enum enum_value,typename Tuple>
constexpr auto get(const Tuple &value)
-> decltype(std::get<indexOf(enum_value)>(value))
{
return std::get<indexOf(enum_value)>(value);
}
template <Enum1 enum_value>
constexpr auto get(const Tuple1 &value)
-> decltype(get<Enum1,enum_value>(value))
{
return get<Enum1,enum_value>(value);
}
template <Enum2 enum_value>
constexpr auto get(const Tuple2 &value)
-> decltype(get<Enum2,enum_value>(value))
{
return get<Enum2,enum_value>(value);
}
int main(int,char**)
{
Tuple1 a("John","123 Foo St");
Tuple2 b("California",90210);
std::cerr << get<Enum1::name>(a) << "\n";
std::cerr << get<Enum1::address>(a) << "\n";
std::cerr << get<Enum2::state>(b) << "\n";
std::cerr << get<Enum2::zip_code>(b) << "\n";
}
It is tedious, however this does have the benefit of compile-time checking that the enumerations are compatible with the tuples.