Your problem has to deal with "wrong" choice of serialization parameters.
You'll want inversion of control for this. Pass the serializer as parameter to the object you to serialize, or to serialization function if those are standalone (they need friend access for classes then)
template < class Serializer >void save( Serializer &s, int x ) { s << x;}template < class Serializer >void save( Serializer &s, char x ) { s << x;}// and so on for elementary typestemplate < class Serializer >void save( Serializer &s, const Foo &foo ){ save( s, foo.arg1 ); save( s, foo.arg2 ); save( s, foo.arg3 );}
Now consider adding a more complex type which contains foo:
class Bar{ Foo f; int x;}template < class Serializer >void save( Serializer &s, const Bar &bar ){ save( s, bar.f ); save( s, foo.x );}
You call this whole thing like this:
Bar bar;FileSerializer s("some_file");save( s, bar );s.close();
Templating the serializer isn't necessary. It's here just to demonstrate complete decoupling from serialization mechanics, where implementation of serialization doesn't matter, the only thing you do need to provide are the << and >> operators for common types.
Here you can then serialize arbitrarily composed classes. Using operator overloading it also becomes possible to reduce the syntax further (see boost serialization for more examples.
Ultimately, it's possible to reduce entire serialization declaration to:
class Foo {...template < class Archive >void serialize( Archive &archive ){ archive & arg1 & arg2 & arg3;}...}
But it does take some effort to get all the tiny details right, so using looking into boost might be a good idea for robust and flexible serialization.