Boost logo

Boost :

Subject: [boost] [tuple] any interest in a unique_tuple<...>?
From: Hui Li (hui.li_at_[hidden])
Date: 2014-03-08 12:24:19


Testing the water, see if anyone might find this useful.
Any comments/suggestions are absolutely welcome and appreciated!

unique_tuple<T...>, where types in <T...> may themselves be other unqiue_tupe<...>'s,
contains exactly one copy of each of the "finally nested" element types in <T...>,
regardless of how many times a type appears in the type parameter pack,
or how deeply it is nested inside one or more unique_tuple's in <T...>.

unique_tuple<T...> differs from std::tuple<T...> and boost::fusion::set<T...> etc in that nested unique_tuple's
are effectively "flattened", and elements contained are unique no matter how complex the nesting
gets, or how many times or how deeply nested the element type is in <T...>.

unique_tuple<T...> in a way emulates virtual inheritance, but at compile time.
The implementation does NOT use virtual inheritance.

implementation is available at:
https://www.dropbox.com/sh/f7ur7j6jetabovj/MuhhhNWU0K/unique_tuple.h

and some demonstration of uses at (tested with "clang-3.5 -std=c++1y" on mac osx/macport)
https://www.dropbox.com/sh/f7ur7j6jetabovj/cy_cvpPIYc/main.cpp

In the following example, t1,t2,t3 all contain exactly one A, and exactly one B:

   struct A{};
   struct B{};
   
   unique_tuple<A,B,A,B> t1(A{},B{});
   unique_tuple<A,unique_tuple<B>,unique_tuple<A,B>> t2{B{},A{}}; // order doesn't matter in constructor
   
   // B is default constructed, empty unique_tuple (possibly nested) doesn't matter
   unique_tuple<A,unique_tuple<unique_tuple<>>,unique_tuple<A,B,unique_tuple<A,B,unique_tuple<A,B>>>> t3{A{}};
 
   // unique_tuple<A,B> t4(A{},B{},A{}); // doesn't compile because more than one A is supplied
   // unique_tuple<A,B> t5(A{},B{},1.0); // doesn't compile because double doesn't belong

There are helper classes and functions, e.g.:
 
   A& a = get<A>(t0);
 
   auto x = make_unique_tuple(A{},B{},1,1.0); // unique_tuple<A,B,int,double>
 
   static_assert( is_unique_tuple<decltype(t0)>::value, "t0 is a unique_tuple type" );
   static_assert( is_element_of<A,decltype(t1)>::value, "A is an element in t1" );
   static_assert( is_subset_of<decltype(t1),decltype(x)>::value, "<A,B> is a subset of <A,B,int,double>" );
   static_assert( unique_tuple_size<decltype(t3)>::value == 2, "there are 2 elements in t3: A and B" );

Equality comparason exists for two unique_tuple<...>s that contain the same element types, e.g.,
   t0 == t2 && t1 != t3;

Mixed operations with std::tuple are available if std::get<typename>(std::tuple<...>) exists (c++14), which
effectively requires that the element types are all unique in the std::tuple<...>.

synopsis:

template < typename... T >
struct unique_tuple
{
   template < typename... U > explicit constexpr unique_tuple(U&&...);
   template < typename... U > explicit constexpr unique_tuple(const std::tuple<U...>&);
   template < typename... U > explicit constexpr unique_tuple(std::tuple<U...>&&);

   // allocator extended constructors
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, U&&...);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, const unique_tuple<U...>&);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, unique_tuple<U...>&&);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, const std::tuple<U...>&);
   template < typename Alloc, typename... U > unique_tuple(std::allocator_arg_t, const Alloc& a, std::tuple<U...>&&);
   
   // assign
   template < typename... U > unique_tuple& operator=(const unique_tuple<U...>&);
   template < typename... U > unique_tuple& operator=(unique_tuple<U...>&&);
   template < typename... U > unique_tuple& operator=(const std::tuple<U...>&);
   template < typename... U > unique_tuple& operator=(std::tuple<U...>&&);
   
   // swap
   template < typename... U > void swap(unique_tuple<U...>&);
   template < typename... U > void swap(std::tuple<U...>&);
};

// helpers
template < typename... T > unique_tuple<typename std::decay<T>::type...> make_unique_tuple(T&&... t);

template < typename... T > struct unique_tuple_size<unique_tuple<T...>>; // number of unique elements
template < typename T > struct is_unique_tuple;
template < typename T, typename... U > struct is_element_of<T,unique_tuple<U...>>;
template < typename... T, typename... U > struct is_subset_of<unique_tuple<T...>,unique_tuple<U...>>;

// element access
template < typename T, typename... U > T& get<T>(unique_tuple<U...>&);
template < typename T, typename... U > const T& get<T>(const unique_tuple<U...>&);
template < typename T, typename... U > T&& get<T>(unique_tuple<U...>&&);

// equality compare
template < typename... T, typename... U > bool operator==(const unique_tuple<T...>&, const unique_tuple<U...>&);
template < typename... T, typename... U > bool operator==(const unique_tuple<T...>&, const std::tuple<U...>&);
template < typename... T, typename... U > bool operator==(const std::tuple<T...>&, const unique_tuple<U...>&);

template < typename... T, typename... U > bool operator!=(const unique_tuple<T...>&, const unique_tuple<U...>&);
template < typename... T, typename... U > bool operator!=(const unique_tuple<T...>&, const std::tuple<U...>&);
template < typename... T, typename... U > bool operator!=(const std::tuple<T...>&, const unique_tuple<U...>&);

// swap
template < typename... T, typename... U > void swap(unique_tuple<T...>&, unique_tuple<U...>&);
template < typename... T, typename... U > void swap(unique_tuple<T...>&, std::tuple<U...>&);
template < typename... T, typename... U > void swap(std::tuple<T...>&, unique_tuple<U...>&);


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk