Хэш — карта ключей структуры и функций геттера

#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...>;