Я пытаюсь написать общую оболочку для библиотеки C++ pugi xml, которая может сохранять и хранить значения в/из xml.
Они внедрили свои функции доступа к атрибутам узла xml (хранящиеся в виде строк) в такие функции, как attribute.as_int()
, attribute.as_bool()
и т. д.
Я хочу добиться той же функциональности, что и в библиотеке nlohmann::json, где вы можете вызвать .get<T>()
для некоторого объекта json и получить некоторый тип.
Единственный способ, который я могу придумать (который может даже не работать), - это использовать специализацию шаблона:
template <>
int foo<int>(xml_attribute param)
{
return param.as_int();
}
template <>
bool foo<bool>(xml_attribute param)
{
return param.as_bool();
}
И так далее.
Что, по-видимому, приводит к почти такому же объему кода, как и написание неуниверсальной оболочки...
Что ж, ваш подход на самом деле не так уж и плох. Я, пожалуй, остановлюсь на этом.
Однако есть и другой способ.
#include <type_traits>
template <typename T>
auto foo(xml_attribute param)
{
if constexpr (std::is_same<T, int>::value) {
return param.as_int();
}
else if constexpr (/*..*/){//...} //and so on
}
Мы можем использовать дополнительный параметр для создания различных перегрузок в зависимости от типа, а затем использовать разрешение перегрузки, чтобы найти нужную функцию. TypeTag
— это пустой класс, который мы можем использовать для различения разных перегрузок:
template<class T>
struct TypeTag{ /* empty */ };
Получив это, мы можем написать read_as
функции на основе тега:
int read_as(xml_attribute param, TypeTag<int>) {
return param.as_int();
}
bool read_as(xml_attribute param, TypeTag<bool>) {
return param.as_bool();
}
float read_as(xml_attribute param, TypeTag<float>) {
return param.as_float();
}
double read_as(xml_attribute param, TypeTag<double>) {
return param.as_double();
}
const pugi::char_t* read_as(xml_attribute param, TypeTag<const pugi::char_t*) {
return param.as_string();
}
Сделать общий шаблон теперь довольно просто:
template<class T>
T read_as(xml_attribute param) {
return read_as(param, TypeTag<T>());
}
Это решение работает в C++11 и компилируется быстрее, чем серия проверок if constexpr
, потому что компилятор может искать правильную версию с помощью разрешения перегрузки, а не выполнять серию проверок.
Спасибо, это то, что я пытался сделать, но не понял, как именно.
В оболочке должно быть больше кода, но после этого вы можете использовать foo в общем виде.