как я могу обернуть библиотеки шаблонным кодом, который требует отправки во время выполнения и/или наследования

#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 и пересылка к нему.