It's probably the only way to do it, and not much more work than a 'conventional' custom loader / saver. This does pretty much the same thing, but then dumps the variables straight to non-volatile memory, without bothering with type information and what not.
Generalisation can be cumbersome depending on whether you want to be able to simply 'plug & go'. With unions you can code this quite elegantly; with pointers you can 'shadow' variables throughout the program with but a single assignment. Nevertheless, if you want to be able to dump N-dimensional arrays, strings, and complex structs, things can become tricky. You will need to specify some sort of loader format too: is it write-as-you-ask, or will you group all strings near the end of the file; will you provide error checking or not; etcetera.
However, you cannot make it completely general, as structs are not completely general. You will need some program code to deal with the specific nature of the structs you're dumping, although the building blocks they are made up of (ints, chars, pointers, floats, ...) are general.
In my experience though, such little blocks of code are extremely useful and reusable if you've coded them as general and robust as you can. You might spend a week getting it to work, but the effort is worth it. I created a small set of routines to set all kinds of parameters in the program in a standardised way (complete with input checking against preset bounds) and various function hooks to do some pre- or postprocessing, and it has worked marvellously in any program I've linked it to. Write down the question, provide some glue code to link the input values to program parameters, and forget about the rest.