I'm trying to serialize a templated class MState<T>
more or less generically. To do so, I have a parent abstract class MVariable
which implements several serialization functions with this form:
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SOME_SPECIFIC_TYPE &t) const;
I want to allow T
to be almost anything. Serialization is done in JSON through RapidJSON::Writer. Because of that, I need to use specific member functions (e.g. Writer::String
, Writer::Bool
, Writer::Uint
...) in order to get the proper formatting for each type T
.
Serialization of basic types and STL-containers will be provided by MVariable
. However, instead of providing every single type (e.g. replacing SOME_SPECIFIC_TYPE
by float
, double
, bool
, etc.) I tried to implement a SFINAE-based solution that seems to have some flaws.
I have a set of typedef definitions and serialization functions like this:
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);
}
};
The implementation of is_stl_sequential_container
is based on this and the implementation of has_serialize
is borrowed from here. Both have been checked and seem to work properly:
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();
However, the recursive serialize
call inside the second function, fails to be instantiated. Compiler complains starting with this:
In instantiation of ‘void MVariable::serialize(Serializer&, SerializedType) const
[with Serializer = rapidjson::PrettyWriter<... blah, blah, blah>;
SerializedType = float]’:
The message continues with the static assert error, suggesting that all the previous overloaded template functions failed in their substitutions or that the last one was the best option.
Why is substitution "failing" here for float
and not when I try to serialize tvar0
or tvar1
?