#c #templates #inheritance #template-meta-programming
Вопрос:
Мотивация: В наборе программного обеспечения мы используем несколько двоичных файлов с философией unix, которые выполняют различные задачи. Эти двоичные файлы будут загружать файлы конфигурации различных типов (yaml, json, xml, ini), и дело дошло до того, что нам нужно завернуть эти различные функции в аккуратную оболочку. Все библиотеки (simdjson, yamlcpp, rapidxml) поставляются с довольно современными интерфейсами C , но все они немного отличаются друг от друга.
Например, при yamlcpp
проверке наличия ключа просто путем приведения узла к bool, simdjson
вам нужно получить элемент и сравнить его .error()
значение с полем не найдено константа.
Все вышесказанное означает, что тонкие ошибки становятся все более и более распространенными из-за слегка отличающихся методов обработки, и просить разработчика устать от особенностей каждой библиотеки-это бремя. Кроме того, некоторые библиотеки загружают файлы json или yaml, и это означает, что существует два пути кода для загрузки одной и той же функциональной конфигурации.
Следует особо отметить, что приведение в каждой библиотеке отличается, и возникающие исключения (иногда их нет) неоднородны.
(Поскольку это одноразовая проблема с настройкой при запуске, скорость не является серьезной проблемой.)
Решение: решение в принципе довольно простое. Я хотел бы создать чрезвычайно тонкую шаблонную оболочку вокруг библиотек.
Однако проблема, с которой я сталкиваюсь, заключается в том, что файл конфигурации передается во время выполнения, и поэтому это не может быть просто шаблонным решением во время компиляции.
Вопрос: представив мотивацию и попытку решения, вот моя проблема
Рассмотрим следующий минимальный код:
#include lt;iostreamgt; class simdjson {}; class YAML {}; // for compilability of example struct Node { operator int() const { return 123; } }; templatelt;typename Tgt; struct NodeImpl : public Node { operator int() const; /* ... a bunch of common code ... */ }; /* ... tiny bits of specialization ... */ templatelt;gt; NodeImpllt;YAMLgt;::operator int() const {return 42; /* return node.asInt(); */ } templatelt;gt; NodeImpllt;simdjsongt;::operator int() const {return 52; /* return node.aslt;intgt;(); */} int main() { Node no_worky = NodeImpllt;YAMLgt;(); NodeImpllt;YAMLgt; a = NodeImpllt;YAMLgt;(); std::cout lt;lt; "expect 42, got: " lt;lt; (int)a lt;lt; std::endl lt;lt; "expect 42, got: " lt;lt; (int)no_worky; return 0; }
There are 2 problems:
- тот факт, что тип формата конфигурации может быть определен во время выполнения, требует, чтобы это было нечто иное, чем строго шаблонное решение (отсюда и наследование).
- создание его на основе наследования означает, что я (думаю) У меня нет другого решения, кроме виртуальных указателей и виртуального базового класса узлов. Это огорчает меня тем, что я не могу передавать узлы по значению (что я обычно мог бы сделать в чисто шаблонной реализации).
Есть ли элегантное решение, которого мне здесь не хватает, или просто ограничение времени выполнения означает, что я вынужден использовать виртуальные базовые классы.
Редактировать после довольно долгих размышлений я пришел к выводу, что это должно быть реализовано полностью статически и что код конфигурации должен представлять собой неспециализированный шаблонный вызов, например:
templatelt;typename Kgt; void configure(NodeImpllt;Kgt; root) { std::cout lt;lt; "expect 42, got: " lt;lt; (int)root lt;lt; std::endl; /* ... do configuration stuff ... */ } int main() { if( /* file is yaml */ ) configure(NodeImpllt;YAMLgt;()); else if (/* file is json */) configure(NodeImpllt;jsongt;()); return 0; }
Сначала это было похоже на дублирование кода, пока я не понял, что так или иначе будут выпущены 2 ветви кода — независимо от того, скрываю я это за виртуализацией или нет.
Приведенный выше подход полностью шаблонен и является совершенно более здравым.
Комментарии:
1.
Node
может быть вашим типом стирания типа, и именно он содержит виртуальный базовый класс.2. Может ли это/может ли это решить проблему ценности, которую приятно иметь?
3. Да, это возможно. В основном это просто обертывание
unique_ptr
и пересылка к нему.