Suppose I have a class as follows:
class A
{
...private members
public:
void write_text(std::ostream& os); //writes members as text
void write_binary(std::ostream& os); //writes objects as binary
};
How do I create a manipulator that like text
and binary
depending on which I can call appropriate function write_text()
or write_binary()
to write to filestream like so:
std::ofstream file1("textfile.txt");
std::ofstream file2("binfile.bin");
A obj; // assume obj has data members set
file1<<text<<obj; // here obj.write_text() should be invoked
file2<<binary<<obj; // here obj.write_binary() should be invoked
Do I need to store something like a state or a variable in the stream like in this example to be able to do this or is there a simpler way?
There are two primary ways the standard uses to manipulate input & output operations.
1. Storing values in the stream state
You can store formatting state within streams by using
std::ios_base::xalloc()
.This gives you a
long
andvoid*
value in each stream that you can access withiword()
/pword()
.This is the same mechanism that standard io manipulators like
std::hex
,std::boolalpha
use.Note that if you change the stream state it'll stay that way until you change it again, e.g.:
You could e.g. implement it like this for your
A
class:operator<<
forA
checks which format type is currently stored in the stream (0 for text, 1 for binary) and calls the corresponding methodtext
&binary
are the io manipulators that change the stream state when applied to a stream.Example Usage:
godbolt example
2. Wrapper function
Another kind of io manipulators you'll also encounter in the standard library are wrappers that change the input / output of a single element.
Examples of this would be
std::quoted
,std::get_money
,std::put_money
, etc...Those functions only change the format for a single operation, in contrast to the above method that changes the format of all following input / output operations. Example:
You could e.g. implement it like this for your
A
class:binary_impl
) that implements a differentoperator<<
forA
objects.Example Usage:
godbolt example
The methods listed above are only the ones the standard library itself uses (and therefore probably the most recognized ones).
You can of course also create your own custom method for it, e.g. by using member methods that return objects that will serialize the object in a specific way:
Usage:
godbolt example