Simply use the methods in your class template:
template <typename T>
struct Serializer
{
void serialize(T const & t) const { write(t.to_data()); }
void deserialize(T & t) const { t.from_data(read()); }
};
If the types you instantiate the template with have the appropriate member functions, everything will be fine. If they don't, the compiler will trigger an error:
struct Foo
{
int val;
int to_data() const { return val; }
void from_data(int i) { val = i; }
};
struct Bar {};
Serializer<Foo> sf;
sf.serialize(); // OK
Serializer<Bar> sb;
sb.serialize(); // compiler error: Bar has no member function named "to_data"
Note that the compiler error is only triggered when we try to use some functions of the class template. This is because member functions of class templates are only instantiated (compiled, if you will) when you use them. So it is perfectly fine to instantiate Serializer
with Bar
as long as you don't use the serialize
and deserialize
member function.
Regarding the second issue, namely how to provide a different behavior for primitive types, you have several solutions. The first one is to specialize your class template for the types you want to handle differently. For instance, the following code specializes Serializer
so that it handles int
differently:
template <>
struct Serializer<int>
{
void serialize(int i) const { write(i); }
void deserialize(int & i) const { i = read();
};
However, this implies writing a specialization for each particular type, even if some of them are in fact handled in the same way.
A less cumbersome solution would be to use type traits and std::enable_if
to select the correct implementation depending on some characteristics of the arguments types (in this case, whether they are primitive or not):
#include <type_traits>
template <typename T, typename Enable = void>
struct Serializer
{
// same as before
};
// Partial specialization for fundamental types
template <typename T>
struct Serializer<T, typename
std::enable_if<std::is_fundamental<T>::value>::type>
{
void serialize(T t) const { write(t); }
void deserialize(T & t) const { t = read(); }
};
Serializer<Foo> sf; // first implementation
Serializer<int> si; // second (specialized) implementation