Извините за то, что я оставил вас в покое, я был прерван после первоначального чтения, поэтому я мог посещать церковное богослужение в режиме онлайн (поскольку мы в значительной степени ограничены нашими домами и группами из 3 человек или меньше, если не дома).
Итак, на самом деле есть много способов сделать это, поэтому мы исследуем пару, а затем дадим советы о том, как делать другие ...
FWIW, я действительно рекомендую использовать библиотеку метапрограммирования для этого вещи. Boost.Hana, вероятно, самый простой в использовании и наиболее гибкий. Kvasir, пожалуй, самый быстрый, но немного менее гибкий и, хм ... не такой, как обычно.
Это здесь, в основном, чтобы показать некоторые базовые c инструменты в надежде, что вы сами сможете разработать что-то еще, что наилучшим образом соответствует вашим требованиям - если вы решите не использовать библиотеку метапрограммирования.
Я скажу, что как только вы импортируете библиотеку метапрограммирования, она почти всегда окупается, потому что она используется снова и снова. Если это было какое-то время, вы можете дать им еще один шанс - сейчас они быстрее компилируются - просто не используйте Boost.MPL, кроме как для изучения. Это затрудняется из-за необходимости обратной совместимости.
Во-первых, полностью ручной пример, который немного настраивается, но достаточно гибок.
// Primary template that provides the tuple index for a tuple/key pair
template <typename Tuple, typename Key>
struct TupleIndex : std::integral_constant<int, -1> { };
// Some key tag types
struct Foo { };
struct Blarg { };
struct Blip { };
struct Baz { };
// Explicit instantiations to map types and indices
// To actually use this, you will want a metafunction to create these
// and validate that the index is in the proper range.
template <> struct TupleIndex<std::tuple<int, double, char, float>, Foo>
: std::integral_constant<int, 0> { };
template <> struct TupleIndex<std::tuple<int, double, char, float>, Blarg>
: std::integral_constant<int, 1> { };
template <> struct TupleIndex<std::tuple<int, double, char, float>, Blip>
: std::integral_constant<int, 2> { };
template <> struct TupleIndex<std::tuple<int, double, char, float>, Baz>
: std::integral_constant<int, 3> { };
namespace detail {
template <typename T> struct a_tuple { };
template <typename ... Ts> struct a_tuple<std::tuple<Ts...>>
{
using type = std::tuple<Ts...>;
};
}
template <typename Key, typename T,
typename Tuple = typename detail::a_tuple<std::decay_t<T>>::type>
decltype(auto)
tuple_get(T && tuple)
{
if constexpr (TupleIndex<Tuple, Key>::value >= 0) {
return std::get<TupleIndex<Tuple, Key>::value>(std::forward<T>(tuple));
} else {
return std::get<Key>(std::forward<T>(tuple));
}
}
// Some simple tests...
using Tuple = std::tuple<int, double, char, float>;
static_assert(std::is_same_v<int&&,
decltype(tuple_get<int>(std::declval<Tuple>>()))>);
static_assert(std::is_same_v<double&&,
decltype(tuple_get<double>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<char&&,
decltype(tuple_get<char>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<float&&,
decltype(tuple_get<float>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<int&&,
decltype(tuple_get<Foo>(std::declval< Tuple >()))>);
static_assert(std::is_same_v<double&&,
decltype(tuple_get<Blarg>(std::declval< Tuple >()))>);
static_assert(std::is_same_v<char&&,
decltype(tuple_get<Blip>(std::declval< Tuple >()))>);
static_assert(std::is_same_v<float&&,
decltype(tuple_get<Baz>(std::declval< Tuple >()))>);
Вы также можете просто передайте список типов тегов другому классу, который представляет сопоставленный кортеж ...
// Some key tag types
struct Foo { };
struct Blarg { };
struct Blip { };
struct Baz { };
// A simple pack of types
template <typename ... Ts> struct Pack { };
// Some basic stuff to find the index of a type in a pack
namespace detail {
template <typename T, std::size_t I>
struct TypeAndIndex
{
using type = T;
using index = std::integral_constant<std::size_t, I>;
};
template <typename T, std::size_t Ndx>
static typename TypeAndIndex<T, Ndx>::index
check_type(TypeAndIndex<T, Ndx> *);
template <typename T>
static std::integral_constant<std::size_t, std::size_t(-1)>
check_type(void const *);
template <typename... Ts>
struct IndexOfType
{
template <typename Seq>
struct Impl;
template <std::size_t... Is>
struct Impl<std::index_sequence<Is...>> : TypeAndIndex<Ts, Is>...
{};
using Seq = std::make_index_sequence<sizeof...(Ts)>;
template <typename T>
using apply = decltype(check_type<T>(static_cast<Impl<Seq> *>(nullptr)));
};
}
template <typename TargetT, typename... Ts>
struct IndexOf : detail::IndexOfType<Ts...>::template apply<TargetT>
{ };
template <typename TargetT, typename... Ts>
struct IndexOf<TargetT, Pack<Ts...>> : IndexOf<TargetT, Ts...>
{ };
// A mapped-tuple type, that takes a Pack of keys and a tuple types
template <typename PackT, typename ... Ts>
struct MappedTuple;
template <typename ... KeyTs, typename ... Ts>
struct MappedTuple<Pack<KeyTs...>, Ts...> : std::tuple<Ts...>
{
static_assert(sizeof...(KeyTs) == sizeof...(Ts));
using Keys = Pack<KeyTs...>;
using Tuple = std::tuple<Ts...>;
};
template <typename T>
struct IsMappedTuple : std::false_type { };
template <typename ... KeyTs, typename ... Ts>
struct IsMappedTuple<MappedTuple<Pack<KeyTs...>, Ts...>> : std::true_type { };
// Get the type, by key
template <typename Key, typename T,
std::enable_if_t<IsMappedTuple<std::decay_t<T>>::value, bool> = true>
decltype(auto)
get(T && tuple)
{
using Keys = typename std::decay_t<T>::Keys;
if constexpr (IndexOf<Key, Keys>::value != std::size_t(-1)) {
return std::get<IndexOf<Key, Keys>::value>(std::forward<T>(tuple));
} else {
return std::get<Key>(std::forward<T>(tuple));
}
}
// The same set of tests
using Tuple = MappedTuple<Pack<Foo,Blarg,Blip,Baz>,
int, double, char, float>;
static_assert(std::is_same_v<int&&,
decltype(get<int>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<double&&,
decltype(get<double>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<char&&,
decltype(get<char>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<float&&,
decltype(get<float>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<int&&,
decltype(get<Foo>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<double&&,
decltype(get<Blarg>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<char&&,
decltype(get<Blip>(std::declval<Tuple>()))>);
static_assert(std::is_same_v<float&&,
decltype(get<Baz>(std::declval<Tuple>()))>);
Вы можете расширить последний, чтобы отправить список пар, который будет сочетанием двух, но изолируйте карту от внутренней части класса. Это дает вам возможность отображать несколько типов в один и тот же индекс, если вы того пожелаете.
Вы также можете использовать строки, либо вычисляя га sh - хотя я бы по крайней мере использовал MD5 га sh чтобы уменьшить вероятность столкновения - не так уж сложно сделать это с помощью функций constexpr.
Или вы можете сделать что-то вроде этого ...
template <typename T>
constexpr auto fname()
{
return std::string_view(__PRETTY_FUNCTION__);
}
template <typename T>
inline constexpr std::string_view tname = fname<T>();
Это работает только для clang и g cc, хотя я уверен, что другие компиляторы имеют похожие макросы.
Поскольку tname
теперь является встроенной переменной constexpr, вы можете использовать ссылки на них в качестве параметров шаблона, что дает вам возможность для создания шаблонов, используя «имя» типа.
Или вы можете просто засунуть string_view в таблицу constexpr. Вы можете выполнить простой линейный поиск или отсортировать их и выполнить бинарный поиск, или превратить таблицу в таблицу ha sh. Как и в обычном программировании, у каждого есть свое место.
Затем вы можете сохранить строку и индекс в таблице constexpr и выполнить поиск, чтобы найти их.
gcc / clang также поддерживает необработанные пользовательские определения строковые литералы, которые могут принимать строку и создавать уникальный тип, используя отдельные символы в качестве аргументов для набора c не типовых параметров типа char.
EDIT
Еще одна опция, о которой я подумал во время перерыва ... чтобы продемонстрировать что-то немного другое, но, возможно, полезное ...
template <std::size_t Ndx>
struct TokenMapKey
: std::integral_constant<std::size_t, Ndx>
{
};
template <typename ... Ts>
struct MappedTuple : std::tuple<Ts...>
{
template <std::size_t N>
decltype(auto) operator[](TokenMapKey<N> key) const & noexcept
{ return std::get<N>(*this); }
template <std::size_t N>
decltype(auto) operator[](TokenMapKey<N> key) & noexcept
{ return std::get<N>(*this); }
template <std::size_t N>
decltype(auto) operator[](TokenMapKey<N> key) const && noexcept
{ return std::get<N>(std::move(*this)); }
template <std::size_t N>
decltype(auto) operator[](TokenMapKey<N> key) && noexcept
{ return std::get<N>(std::move(*this)); }
};
struct Foo : TokenMapKey<0> { };
struct Blarg : TokenMapKey<1> { };
struct Blip : TokenMapKey<2> { };
struct Baz : TokenMapKey<3> { };
using TT = MappedTuple<int, double, char, float>;
static_assert(std::is_same_v<int&&,
decltype(std::declval<TT>()[Foo{}])>);
static_assert(std::is_same_v<double&&,
decltype(std::declval<TT>()[Blarg{}])>);
static_assert(std::is_same_v<char&&,
decltype(std::declval<TT>()[Blip{}])>);
static_assert(std::is_same_v<float&&,
decltype(std::declval<TT>()[Baz{}])>);
Вы можете сделать эту бесплатную функцию работать с любым подобным кортежу объектом, но это потребовало бы немного больше работы ...
template <std::size_t Ndx>
struct TokenMapKey
: std::integral_constant<std::size_t, Ndx>
{
};
struct Foo : TokenMapKey<0> { };
struct Blarg : TokenMapKey<1> { };
struct Blip : TokenMapKey<2> { };
struct Baz : TokenMapKey<3> { };
namespace detail {
using std::get;
template <std::size_t N, typename T>
constexpr auto
get(std::integral_constant<std::size_t, N>, T && t)
-> decltype(get<N>(std::forward<T>(t)))
{
return get<N>(std::forward<T>(t));
}
}
// Get using an integral constant parameter...
template <std::size_t N, typename T>
constexpr auto
get(std::integral_constant<std::size_t, N> k, T && t)
-> decltype(detail::get(k, std::forward<T>(t)))
{
return detail::get(k, std::forward<T>(t));
}
// Note the name here - getk, because the one in the std namespace
// gets selected by ADL.
template <typename Key, typename T>
constexpr auto
getk(T && t)
-> decltype(get(std::declval<typename Key::type>(), std::forward<T>(t)))
{
return get(typename Key::type {}, std::forward<T>(t));
}
using Tpl = std::tuple<int, double, char, float>;
static_assert(std::is_same_v<int&&,
decltype(getk<Foo>(std::declval<Tpl>()))>);
static_assert(std::is_same_v<double&&,
decltype(getk<Blarg>(std::declval<Tpl>()))>);
static_assert(std::is_same_v<char&&,
decltype(getk<Blip>(std::declval<Tpl>()))>);
static_assert(std::is_same_v<float&&,
decltype(getk<Baz>(std::declval<Tpl>()))>);