Выбор шаблона на основе содержимого строкового аргумента - PullRequest
1 голос
/ 05 июля 2019

Я столкнулся с проблемой при использовании boost::property_tree, которая, вероятно, имеет более общие приложения.Моя цель состояла в том, чтобы создать функцию загрузчика, которая способна выбрать правильный загрузчик в зависимости от расширения файла.Мое решение состоит в использовании последовательных if else-if утверждений:

auto extension = input_filename.extension().string();
if(iequals(extension, ".json"))      pt::read_json(input_filename.string(), tree);
else if(iequals(extension, ".info")) pt::read_info(input_filename.string(), tree);
else if(iequals(extension, ".xml"))  pt::read_xml(input_filename.string(), tree);
else if(iequals(extension, ".ini"))  pt::read_ini(input_filename.string(), tree);
else   ... // unknown file type

Я думаю, что должно быть лучшее решение, чем это.Если я вспоминаю Python, где вы можете сделать что-то вроде map<extension, function>[extension](input_filename.string(), tree), это вдохновило меня на следующее.Идея заключалась в том, чтобы использовать карту для создания связи между строкой расширения и функцией:

using loader_fct = void(*)(const std::string &, pt::ptree &, const std::locale &);
using loader_table = std::map<std::string, loader_fct>;

const loader_table load_funct{
        {".json", &pt::read_json},
        {".ini",  &pt::read_ini},
        {".info", &pt::read_info},
//      {".xml",  &pt::read_xml}
};

Первая проблема заключалась в том, что, очевидно, не все функции загрузчика (-templates) имели одинаковую сигнатуру, поэтомумы не можем использовать XML.Кроме того, мы должны определить все аргументы шаблона, затем мы можем использовать его следующим образом:

auto extension = boost::to_lower_copy(input_filename.extension().string());
auto loader = load_funct.find(extension);
loader->second(input_filename.string(), tree, std::locale());

Мне удалось решить проблему с различными сигнатурами функций с помощью некоторых шаблонов, которые позволяют намчтобы упростить немного больше:

template<typename Ptree>
void read_json(const std::string & s, Ptree & t) { pt::read_json(s, t); }

template<typename Ptree>
void read_ini(const std::string & s, Ptree & t) { pt::read_ini(s, t); }

template<typename Ptree>
void read_info(const std::string & s, Ptree & t) { pt::read_info(s, t); }

template<typename Ptree>
void read_xml(const std::string & s, Ptree & t) { pt::read_xml(s, t); }

using loader_table = std::map<std::string, void(*)(const std::string &, pt::ptree &)>;
loader_table load_funct{
        {".json", &read_json},
        {".ini",  &read_ini},
        {".info", &read_info},
        {".xml",  &read_xml}
};
auto extension = boost::to_lower_copy(input_filename.extension().string());
if(load_funct.count(extension))
{
        load_funct[extension](input_filename.string(), tree);
}

Мой основной вопрос:

Есть ли лучшие способы сделать это?Или другие идеи?

Последнее решение заставило меня задуматься, есть ли возможность сделать выбор правильного загрузчика уже в шаблоне?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...