I looked into google protocol buffers as well, and the code-pre-processor/generation feels like an awful hack to me.
It may not suit your particular needs, but for most users, protocol buffer's code-generation is really its #1 strength.
And thus we have tools like Protobuf to let us write our specifications just once, and never again deal with this sort of compatibility problem.
I suppose code generation with external tools is OK for some folks; in my case I've never wanted to use a code-generation technique.
Protobuf afaik also does not support object references/cycles; without building your own system atop it; which is a large part of the compexity to begin with. wire-formats like XML, JSON suffer the same issues; but, references and cycles are extremely common occurences in game state formats.
Metaclass management is not exactly simple or good usage of memory resources. The reason version conditionals are used often is because the solution takes almost no extra code to do right and supports the vast majority of cases (variable addition and removal) without any extra effort.
it was huge, and complicated; and also did not have important features such as; adding/removing serialized members; without having to write version conditionals.
In my own rather simple, template-based library, I just have to add a few arguments to a function call, like so:arch( size, arch.version >= 4, 0 ); // variable to serialize, condition, default valueDoes not seem that hard. Besides, I use the same function for serialization and unserialization and it works across two different serializers (text and binary).Not sure where you got that idea. Code generation has been around to solve various metaprogramming issues for a long time. Whoever makes you think it's a hack, better not take such "advice". Any method has its good sides and its bad sides and evaluating it by "feels" would be a big mistake. Try it, improve it, change it for something different. Exhaust all options to have a better idea of what works and what doesn't. It really is the only way.
and the code-pre-processor/generation feels like an awful hack to me.
As for your solution, what you're proposing is writing huge read/write functions that require lots of code, lots of declarations and lots of void pointer workarounds. Furthermore, it appears that only iostream is supported even though it would take just a two-function interface to read from and write to just about anything. Quite frankly, I don't see the point of using dynamic memory for what could easily reside in static memory as well.
First I'll address version blocks; in any /real/ complex game, especially where change is the norm; with version blocks you will accure 'entropy'; having to specify version blocks for new members and member drop-outs is a gross misuse of technical resources; and unescapably creates technical debt; that you will live with, until you decide to make a clean format break (breaking past user saves preceeding that point).
Concerning code generation; a large degree boils down to preference; I'll admit. If you're ok with preparing special markup and running external tools, when you essentially don't have to, that is obviously an option.
Concerning iostream, it is an obvious choice, as it supports both file level and in-memory (stringstream) for deep copying etc.
The issue with the 'two function' solution for arbitrary readers/writers is that for naive iostream usage, you end up having to tie it in.
You could say implementing your own format against iostream is a solution as well.