# Trait class for a serialization framework

In a current project I have implemented a small non-intrusive serialization framework based on type traits. The generic trait template looks like this:

template<typename T>
class Serializable
{
public:
static const bool isSerializableToString = false;

static QString toString(const T &t) {
(void) Error<T>::Trait_does_not_support_serialization_to_strings();
return QString();
}

static T fromString(const QString &s, const T &defaultValue, bool *ok = 0) {
(void) Error<T>::Trait_does_not_support_serialization_to_strings();
return defaultValue;
}

private:
template<typename I>
struct Error { };
};


To implement serialization for a certain type, this trait is specialized for this type, the isSerializableToString constant is defined true and the appropriate methods are implemented.

For types that do not support serialization I intended to raise a compile time error in the generic template.

I came along like 1000 solutions to accomplish this just on Stackoverflow. Among others variations, most solutions use static_asserts or SFINAE to various degrees. Yet, while following these solutions and experimenting, I came up with the above solution which seems to work like a charm.

In contrast to most SFINAE-based solutions, it is quite simple to understand (the (void) could even be removed), it does not rely on static_assert or the workarounds provided by, e.g. boost (unfortunately, static_assert is still not supported everywhere I need it) and it generates an error message as compact as

'Trait_does_not_support_serialization_to_strings' is not a member of 'Serializable<SomeType>::Error<SomeType>'
(void) Error<T>::Trait_does_not_support_serialization_to_strings();


Now given the (imho) simplicity of this approach I am wondering if I am missing some caveat.

# Specialization for types

QDate can easily be turned into a string representation by means of its builtin QDate::toString() and QDate::fromString() methods, so the specialization is merely a wrapper around those:

// Header
template<>
class Serializable<QDate>
{
public:
static const char *const typeName;
static QString toString(const QDate &value);
static QDate fromString(const QString &s, const QDate &defaultValue, bool *ok = 0);
};

// Implementation
const char *const Serializable<QDate>::typeName = "date";

QString Serializable<QDate>::toString(const QDate &value) {
return value.toString(Qt::ISODate);
}

QDate Serializable<QDate>::fromString(const QString &s, const QDate &defaultValue, bool *ok) {
const QDate value = QDate::fromString(s, Qt::ISODate);
if (ok)
*ok = value.isValid();

return value.isValid() ? value : defaultValue;
}


Other types may require more elaborate implementations. A specialization for bool could generate the strings true and false and it might be tolerant to accept others like yes/no and on/off. The specialization for QColor might convert hexadecimal color strings as used in HTML (#112233).

# Usage

Usage should be straightforward:

QDate d = QDate::currentDate();
qDebug() << Serializable<QDate>::toString(d);

• I feel my problem is the other way round: What should I put in the generic trait template? Should I just remove the default (in this case, compile-time-arising) toString() and fromString() methods? If I leave them, what should they return? – Kamajii Jul 22 '17 at 18:45
• PS: If I leave the toString() and fromString() methods in the generic (possibly returning some invalid default value), I fear someone using it on a type that has no specialization, without noticing it. If I remove the methods, I don't have the (kind of self documenting) prototypes anymore. – Kamajii Jul 22 '17 at 18:49