Я пытаюсь сериализовать шаблонный класс MState<T>
более или менее в общем виде. Для этого у меня есть родительский абстрактный класс MVariable
, который реализует несколько функций сериализации в этой форме:
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SOME_SPECIFIC_TYPE &t) const;
Я хочу разрешить T
быть почти любым. Сериализация выполняется в формате JSON через RapidJSON:: Писатель. Из-за этого мне нужно использовать определенные функции-члены (например, Writer::String
, Writer::Bool
, Writer::Uint
...), чтобы получить правильное форматирование для каждого типа T
.
Сериализацию базовых типов и STL-контейнеров обеспечит MVariable
. Однако вместо предоставления каждого отдельного типа (например, замены SOME_SPECIFIC_TYPE
на float
, double
, bool
и т. д.) я попытался реализовать решение на основе SFINAE, которое, похоже, имеет некоторые недостатки.
У меня есть набор определений typedef и функций сериализации, например:
class MVariable
{
template <class SerT> using SerializedFloating =
typename std::enable_if<std::is_floating_point<SerT>::value, SerT>::type;
template <class SerT> using SerializedSeqCntr =
typename std::enable_if<is_stl_sequential_container<SerT>::value, SerT>::type;
/* ... and many others. */
/* Serialization of float, double, long double... */
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SerializedFloating<SerializedType> &t) const {
s.Double(t);
}
/* Serialization of vector<>, dequeue<>, list<> and forward_list<> */
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SerializedSeqCntr<SerializedType> &t) const {
/* Let's assume we want to serialize them as JSON arrays: */
s.StartArray();
for(auto const& i : t) {
serialize(s, i); // ----> this fails to instantiate correctly.
}
s.EndArray();
}
/* If the previous templates could not be instantiated, check
* whether the SerializedType is a class with a proper serialize
* function:
**/
template <class Serializer, class SerializedType>
void serialize(Serializer&, SerializedType) const
{
/* Check existance of:
* void SerializedType::serialize(Serializer&) const;
**/
static_assert(has_serialize<
SerializedType,
void(Serializer&)>::value, "error message");
/* ... if it exists then we use it. */
}
};
template <class T>
class MState : public MVariable
{
T m_state;
template <class Serializer>
void serialize(Serializer& s) const {
s.Key(m_variable_name);
MVariable::serialize<Serializer, T>(s, m_state);
}
};
Реализация is_stl_sequential_container
основана на этом, а реализация has_serialize
заимствована из здесь. Оба были проверены и, кажется, работают правильно:
MState<float> tvar0;
MState<double> tvar1;
MState<std::vector<float> > tvar2;
rapidjson::StringBuffer str_buf;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(str_buf);
writer.StartObject();
tvar0.serialize(writer); /* --> First function is used. Ok! */
tvar1.serialize(writer); /* --> First function is used. Ok! */
tvar2.serialize(writer); /* --> Second function is used, but there's
* substitution failure in the inner call.
**/
writer.EndObject();
Однако рекурсивный вызов serialize
внутри второй функции не может быть создан. Компилятор жалуется, начиная с этого:
In instantiation of ‘void MVariable::serialize(Serializer&, SerializedType) const
[with Serializer = rapidjson::PrettyWriter<... blah, blah, blah>;
SerializedType = float]’:
Сообщение продолжается статической ошибкой утверждения, предполагая, что все предыдущие перегруженные функции шаблона не справились с заменой или что последняя была лучшим вариантом.
Почему замена "не работает" здесь для float
, а не при попытке сериализовать tvar0
или tvar1
?