|
namespace tagged_tuple { |
|
// should probably move all these helper templates into a detail namespace |
|
template <class T> |
|
struct has_name_tag_helper { |
|
template<class U> static std::true_type check(typename U::name_tag*); |
|
template<class U> static std::false_type check(...); |
|
static constexpr bool value = decltype(check<T>(nullptr))::value; |
|
}; |
|
// detects if a type has a name_tag type defined |
|
template <class T> |
|
struct has_name_tag : std::integral_constant<bool, has_name_tag_helper<T>::value> {}; |
|
|
|
// simple class to hold two types |
|
// works with incomplete types, important for our use case |
|
template <class First, class Second> |
|
struct type_pair { |
|
using first = First; |
|
using second = Second; |
|
|
|
}; |
|
template <class T> |
|
struct is_type_pair_helper { |
|
template <class T1, class T2> static std::true_type check(type_pair<T1, T2>* ptr); |
|
template <class T1> static std::false_type check(T1*); |
|
static constexpr bool value = decltype(check((T*)(0)))::value; |
|
}; |
|
|
|
// detects if T is a type_pair |
|
template <class T> |
|
using is_type_pair = std::integral_constant<bool, is_type_pair_helper<T>::value>; |
|
|
|
template <class T, bool IsTag = is_type_pair<typename T::name_tag>::value> |
|
struct name_tag_traits_helper; |
|
template <class T> |
|
struct name_tag_traits_helper<T, true> { |
|
using type = typename T::name_tag; |
|
}; |
|
template <class T> |
|
struct name_tag_traits_helper<T, false> { |
|
using type = type_pair<typename T::name_tag, T>; |
|
}; |
|
|
|
template <class T, bool IsTypePair = is_type_pair<T>::value, bool HasNameTag = has_name_tag<T>::value> |
|
struct base_name_tag_traits; |
|
|
|
template <class T> |
|
struct base_name_tag_traits<T, true, false> { |
|
using type = T; |
|
}; |
|
template <class T> |
|
struct base_name_tag_traits<T, false, true> { |
|
using type = typename std::conditional<is_type_pair<T>::value, typename T::name_tag, type_pair<typename T::name_tag, T>>::type; |
|
}; |
|
|
|
// gets info about a classes name tag (key -> value type mapping) from the class |
|
// if it's a type info it just uses that |
|
// if the class has a name_tag type it will use it if it's a type_pair, otherwise it'll be the same as type_pair<typename T::name_tag, T> |
|
template <class T> |
|
struct name_tag_traits : base_name_tag_traits<T>::type { |
|
// aliases for our specific use case |
|
using tag_type = typename base_name_tag_traits<T>::type::first; |
|
using value_type = typename base_name_tag_traits<T>::type::second; |
|
using pair_type = typename base_name_tag_traits<T>::type; |
|
}; |
|
|
|
template <class T, class U, class... Types> |
|
constexpr std::size_t do_type_count(std::size_t count = 0) { |
|
return do_type_count<T, Types...>(count + std::is_same<T, U>::value ? 1 : 0); |
|
}; |
|
template <class T> |
|
constexpr std::size_t do_type_count(std::size_t count = 0) { return count; }; |
|
// counts the number of times T appears in parameter pack, eventually we should use this to make sure |
|
// that the name_tag_traits::tag_type only appears once in a tagged tuple list |
|
template <class T, class... Types> |
|
constexpr std::size_t type_count() { |
|
return do_type_count<T, Types...>(); |
|
}; |
|
|
|
|
|
// helper alias, turns a list of TypePairs supplied to tagged_tuple to a list of key/tag/name types |
|
template <class T> |
|
using name_tag_t = typename name_tag_traits<T>::tag_type; |
|
// same as above but returns a list of value types |
|
template <class T> |
|
using name_tag_value_t = typename name_tag_traits<T>::value_type; |
|
template <class Needle> |
|
constexpr size_t index_of_impl(size_t index, size_t end) { |
|
return end; |
|
}; |
|
template <class Needle, class T, class... Haystack> |
|
constexpr size_t index_of_impl(size_t index, size_t end) { |
|
return std::is_same<Needle, T>::value ? index : index_of_impl<Needle, Haystack...>(index + 1, end); |
|
}; |
|
// find the index of T in a type list, returns sizeof...(Haystack) + 1 on failure (think std::end()) |
|
template <class Needle, class... Haystack> |
|
static constexpr size_t index_of() { |
|
return index_of_impl<Needle, Haystack...>(0, sizeof...(Haystack) + 1); |
|
}; |
|
// and here's our little wrapper class that enables tagged tuples |
|
template <class... TypePairs> |
|
class tagged_tuple : public std::tuple<name_tag_value_t<TypePairs>...> { |
|
public: |
|
// not really needed for now but if we switch to private inheritance it'll come in handy |
|
using tag_type = std::tuple<name_tag_t<TypePairs>...>; |
|
using value_type = std::tuple<name_tag_value_t<TypePairs>...>; |
|
using value_type::value_type; |
|
using value_type::swap; |
|
using value_type::operator=; |
|
}; |
|
// our special get functions |
|
template <class Name, class... TypePairs> |
|
auto get(tagged_tuple<TypePairs...>& tuple) -> |
|
typename std::tuple_element<index_of<Name, name_tag_t<TypePairs>...>(), |
|
typename tagged_tuple<TypePairs...>::value_type>::type& |
|
{ |
|
return std::get<index_of<Name, name_tag_t<TypePairs>...>()>(tuple); |
|
}; |
|
template <class Name, class... TypePairs> |
|
auto get(const tagged_tuple<TypePairs...>& tuple) -> |
|
const typename std::tuple_element<index_of<Name, name_tag_t<TypePairs>...>(), |
|
typename tagged_tuple<TypePairs...>::value_type>::type& |
|
{ |
|
return std::get<index_of<Name, name_tag_t<TypePairs>...>()>(tuple); |
|
}; |
|
template <class Name, class... TypePairs> |
|
auto get(tagged_tuple<TypePairs...>&& tuple) -> |
|
typename std::tuple_element<index_of<Name, name_tag_t<TypePairs>...>(), |
|
typename tagged_tuple<TypePairs...>::value_type>::type&& |
|
{ |
|
return std::get<index_of<Name, name_tag_t<TypePairs>...>()>(tuple); |
|
}; |
|
|
|
} |
line 130 should be
return std::get<index_of<Name, name_tag_t<TypePairs>...>()>(std::move(tuple));