#c #c 20
Вопрос:
У меня есть вложенная структура (упрощенная):
struct Person {
struct JobDetails {
std::string company_name;
std::string address;
};
JobDetails job_details;
int32_t age;
std::string name;
};
Можно ли создать хэш-карту ключей и геттеров (функций) для каждого поля структуры, даже для вложенных?
Напр.
std::unordered_map<std::string, ???> hash_map{
"company_name", [](Person constamp; person) { return person.job_details.company_name; },
"age", [](Person constamp; person) { return person.age }
};
auto value = hash_map["company_name"](person);
Комментарии:
1. Поскольку вам потребуется получить доступ к записям другими способами (по крайней мере, по имени), я бы рекомендовал использовать
boost::multiindex
— что позволяет избежать дублирования полей и позволяет создавать индексы для различных полей.2. @Slava A
boost::multiindex
был бы очень полезен в качестве контейнераPerson
объектов, но поможет ли он в качестве контейнера функций получения?3. @aschepler, вероятно, для этой цели это было бы излишним.
Ответ №1:
В некотором роде. Вы захотите std::function<something>
, чтобы в качестве типа значения карты можно было хранить различные лямбды. Более сложная проблема заключается в том, что каждое выражение должно иметь один тип, известный во время компиляции. Если у меня есть a std::string key;
, то выражение hash_map[key](person)
может быть либо a std::string
, либо an int32_t
, либо, возможно, других типов.
Возможно, вместо этого вы могли бы иметь карту функций строковых свойств, другую карту функций числовых свойств и многое другое, если это необходимо.
Или, если вам действительно нужна одна карта, хранящая несколько типов значений, std::variant
может помочь a.
using PersonProperty = std::variant<std::string, std::int32_t>; // more?
using PersonPropertyGetter = std::function<PersonProperty(Person constamp;)>;
std::unordered_map<std::string, PersonPropertyGetter> hash_map{
{ "company_name", [](Person constamp; person) { return person.job_details.company_name; } },
{ "age", [](Person constamp; person) { return person.age } }
};
Тогда вам нужно будет использовать std::visit
или std::get
или std::get_if
на самом деле использовать std::variant
.
std::visit([amp;prop_name](auto constamp; value)
{ std::cout << prop_name << ": " << value; },
hash_map[prop_name]);
std::visit(overloaded{
[](std::string constamp;) { std::cout << " (string)n"; },
[](int32_t) { std::cout << " (int32_t)n"; } });
auto prop_value = hash_map[prop_name];
if (std::string const* str_value = std::get_if<std::string>(prop_value))
do_a_string_thing(*str_value);
int32_t age = std::get<int32_t>(hash_map["age"](person)); // Throws if wrong type!
overloaded
Инструмент, используемый в одном примере, определен в примере на странице cppreference для std::visit
. Просто поместите удобный файл заголовка:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;