Это сверхинженерия, но я готов принять вызов сегодня ...
Итак, вы хотите вернуть один из N типов хеш-карт, выбранных на основе пользовательского ввода? std::variant
- правильный тип для работы. variant<A,B,C>
- это тип суммы, который может содержать один из типов A
, B
или C
. Он был добавлен в C ++ 17, но вы можете найти реализации с одним заголовком, которые работают в C ++ 11, например this .
Как вы выяснили, шаблоны - лучший способсоздать шаблон. Похоже, вы хотите выбрать тип значения карты хеш-функции на основе символа, поэтому давайте дадим себе способ определить отображение в шаблоне:
template <char C, typename TValue>
struct Opt { };
Тогда наша цель - сгенерировать ваш переключатель, перечисливкуча этих опций и передача пользовательского ввода:
auto table = CreateHashMap<Opt<'i', int>, Opt<'d', double>, /*etc*/>(user_input);
Где таблица
std::variant<std::monostate, HashMap<int, int>, HashMap<int, double>, /*etc*/>
(Первая опция, std::monostate
, это «пустая» опция, если функцияне удается)
И сгенерированный код:
std::variant<std::monostate, HashMap<int, int>, HashMap<int, double>/*, etc */> CreateHashMap(char user_input) {
switch (user_input) {
case 'i':
return HashMap<int, int>(60);
case 'd':
return HashMap<int, double>(60);
/* case 'e': return etc; */
default:
return std::monostate{};
}
}
Шаг 1 - вывести тип варианта из списка параметров
Это можно сделать, начиная с std::variant<std::monostate>
, а затем рекурсивно добавляете типы один за другим:
template <typename... TOpts>
struct OptToVariantImpl;
// Recursive case - pass TValue to the variant
template <typename... TValues, char Code, typename TValue, typename... TOpts>
struct OptToVariantImpl<std::variant<TValues...>, Opt<Code, TValue>, TOpts...>
: OptToVariantImpl<std::variant<TValues..., HashMap<int, TValue>>, TOpts...> { };
// Terminal case - just expose the variant
template <typename TVariant>
struct OptToVariantImpl<TVariant> {
using Type = TVariant;
};
// Initial variant just has an empty state.
template <typename... TOpts>
using OptToVariant = typename OptToVariantImpl<std::variant<std::monostate>, TOpts...>::Type;
Шаг 2 - Генерация переключателя
Наш «переключатель» для каждого параметра прост, поскольку все параметры дают одинаковый результат,вариант. Вы можете объединить их в одну большую рекурсивную троицу:
return user_input == 'Code' ? return /*HashMapForThisOpt*/ : /*Pass the buck*/;
Умный компилятор O3 должен иметь возможность встроить это и остаться с блоком переключателей.
Заполненный код:
template <typename... TValues>
struct CreateHashMapImpl;
// Recursive case - Test code and return hashcode or evaluate next option
template <typename TVariant, char Code, typename TValue, typename... TOpts>
struct CreateHashMapImpl<TVariant, Opt<Code, TValue>, TOpts...>
: CreateHashMapImpl<TVariant, TOpts...> {
constexpr TVariant operator()(char code) {
return code == Code ? TVariant { HashMap<int, TValue>(60) } : CreateHashMapImpl<TVariant, TOpts...>::operator()(code);
};
};
// Terminal case - return std::monostate for failure result.
template <typename TVariant>
struct CreateHashMapImpl<TVariant> {
constexpr TVariant operator()(char code) { return TVariant { std::monostate{} }; }
} ;
template <typename... TOpts>
OptToVariant<TOpts...> CreateHashMap(char code) {
return CreateHashMapImpl<OptToVariant<TOpts...>, TOpts...>{}(code);
}
Шаг 3 - Используйте
auto table = CreateHashMap<Opt<'i', int>, Opt<'d', double>, Opt<'c', char>>(user_input);
if (auto* result = std::get_if<HashMap<int, double>>(&table)) {
std::cout << "double: " << result->find(42)->second;
}
Соберите все вместе: https://godbolt.org/z/VBnMq2
Вы можете увидеть пример, оптимизирующий до простого переключателя.