I would like to define something like a new cout, which I can use to log my data:
some_type cout2( Message_type message ){
cout << message;
logfile.save(message);
}
so I will use it with
cout2 << "some message" << endl;
Up to now I was not able to find out how the code above has to look like exactly.
Thanks for your help.
You can create your own logger like:
class Logger {
public:
Logger(std::string const& filename)
: stream_(filename, std::ofstream::out | std::ofstream::app)
{
if (!stream_) {
// Error...
}
}
Logger& operator<<(std::string const& str) {
std::cout << str;
stream_ << str;
return *this;
}
private:
std::ofstream stream_;
};
Generally speaking, classes from C++ standard library are not designed to be derived, and that is true for stream classes.
So IMHO, it is better to specify what method you will need on your cout2 object, and then:
design a class containing a ostream& object, initialized in ctor
delegate actual output to that internal object
do whatever log you need in your methods
You should use templated a operator << to be able to easily process any class that std::ostream can process.
class LogStream {
std::ostream& out;
Logfile logfile;
LogStream(std::ostream& out, /* param for logfile initialization */ ...)
: out(out), logfile(...) {}
... // other useful methods for state
};
template<typename T>
LogStream& operator << (LogStream& out, T val) {
out.out << message;
// should first test whether T is manipulator, and choose whether and how it should be logged
logfile.save(message);
}
You don't want to modify std::cout.
Instead, you want to create a specialised std::streambuf that writes to two buffers rather than one. For example;
#include <streambuf>
template <typename char_type,
typename traits = std::char_traits<char_type> >
class basic_teebuf:
public std::basic_streambuf<char_type, traits>
{
public:
typedef typename traits::int_type int_type;
basic_teebuf(std::basic_streambuf<char_type, traits> * sb1,
std::basic_streambuf<char_type, traits> * sb2)
: sb1(sb1)
, sb2(sb2)
{
}
protected: // override virtuals inherited from std::basic_streambuf
virtual int sync()
{
int const r1 = sb1->pubsync();
int const r2 = sb2->pubsync();
return r1 == 0 && r2 == 0 ? 0 : -1;
}
virtual int_type overflow(int_type c)
{
int_type const eof = traits::eof();
if (traits::eq_int_type(c, eof))
{
return traits::not_eof(c);
}
else
{
char_type const ch = traits::to_char_type(c);
int_type const r1 = sb1->sputc(ch);
int_type const r2 = sb2->sputc(ch);
return
traits::eq_int_type(r1, eof) ||
traits::eq_int_type(r2, eof) ? eof : c;
}
}
private:
std::basic_streambuf<char_type, traits> * sb1;
std::basic_streambuf<char_type, traits> * sb2;
};
typedef basic_teebuf<char> teebuf;
Then you need to create a specialised ostream which uses such a buffer
#include <ostream>
class teestream : public std::ostream
{
public:
// Construct an ostream which tees output to the supplied
// ostreams.
teestream(std::ostream & o1, std::ostream & o2);
private:
teebuf tbuf;
};
teestream::teestream(std::ostream & o1, std::ostream & o2)
: std::ostream(&tbuf)
, tbuf(o1.rdbuf(), o2.rdbuf())
{
}
All the above does is create a specialised std::ostream that uses our specialised buffer, which in turn makes use of two buffers.
Now, our teestream needs to be initialised using two streams. For example
#include <fstream>
#include <iostream>
// include the preceding definition of teestream here
int main()
{
std::ofstream logfile("hello-world.log");
teestream tee(std::cout, logfile);
// tee is now a stream that writes the same output to std::cout and logfile
tee << "Hello, world!\n";
return 0;
}
The advantage of this is that all stream insertions (operator <<) will work with our teestream - even for classes with overloaded versions.
When main() returns, the streams will also be closed cleanly.
I've written the specalised streambuf as a template (std::streambuf is a specialisation of a templated class named std::basic_streambuf). For generality, it would probably be better to do the same with the stream (using the fact that std::ostream is also a specialisation of a templated std::basic_ostream). I leave that sort of generalisation as an exercise.
The logging systems I've seen, built on this idea look something like this:
#define MY_PRINT(x, ...) \
{ \
fprintf(logFile, x, ##__VA_ARGS__); \
fflush(acuLogFile); \
}
And you would use it like:
MY_PRINT("this is just a test\n");
Even though this is the C way of doing things, it is very versatile and work in C++ as well.
You just have to use your newly define print function everywhere in the code.
Maybe you should have an instance based logger with a .log() method, that, depending on implementation can log to file, write to stdout etc, rather than trying to overload free functions from std namespace?
Your intent will be clearer and it will be more object orientated.
In fact here is an example of a pretty cookie-cutter event logger I wrote recently, if that helps you to understand the sort of thing I'm talking about. My design allows for dependnacy injection, as well as keeping boring decisions about how something should be formatted as output and where it should go (stdout or file etc) in the logger.
Of course you can define your own cout.
First you need a class which can handle the << operator and contains an fstream and an ostream (like cout).
class logstream
{
private:
fstream *filestream;
ostream *cout;
public:
logstream(fstream* filestream, ostream* cout)
{
this->cout = cout;
this->filestream = filestream;
}
string operator<< (char* s)
{
*cout << s;
*filestream << s;
return s;
}
};
To use this, simply initialize it with cout and a fstream.
fstream lout = fstream("Filename", ios::app);
logstream cout = logstream(&lout, &std::cout);
And now you have what you wanted.
cout << "message";
EDIT: Don't forget to include
#include <iostream>
#include <fstream>
and
using namespace std;
Related
I have currently a function
int myfun(const int a) {
...
return rval;
}
that performs several actions.
I mean to adapt it to write debug information on its behaviour or not according to some parameter that I can pass.
In the cases I want to write that info, I also want to pass the ofstream to use.
And I want applications that were using myfun to still work with no modifications.
So I would ideally change to
int myfun(const int a, ofstream & ofs) {
...
if (!(ofs == NULL)) {
ofs << ...
}
...
if (!(ofs == NULL)) {
ofs << ...
}
return rval;
}
with a default value similar to &ofs=NULL. I know NULL is not appropriate.
What is an appropriate way of handling this?
Note 1:
I could pass an optional parameter with the output file name, but that is less flexible.
Note 2:
I could also change to
int myfun(const int a, const bool debug_info, ofstream & ofs) {
...
if (debug_info) {
ofs << ...
}
with a default value debug_info=false.
I guess this still requires a default value for ofs as above.
Note 3:
The accepted answer in Default ofstream class argument in function proposes an overload without the ofs parameter.
In my case that may not work, as I mean to not write anything when "ofs=NULL".
Note 4:
This other answer apparently works, but it seems somewhat contrived to me, and I am not sure it provides all the same functionality as with passing an ofs.
Related:
Is there a null std::ostream implementation in C++ or libraries?
I want applications that were using myfun to still work with no modifications.
If so, use an ofs with default nullptr
int myfun(const int a, ofstream *ofs = nullptr)
{
if (ofs != nullptr)
{
// (*ofs) << ... ;
}
// ...
}
You can't use a reference parameter ofstream& ofs for such function because a reference cannot be null.
Make an abstract Logger class. It has a method for logging a message. In derived classes you can add logging to file (ofstream) or simply do nothing. You can use any logger, the implementation of myfun() stays the same.
#include <fstream>
class Logger {
public:
virtual void log(const char*) = 0;
};
class NullLogger: public Logger {
public:
void log(const char*) override {};
};
class FileLogger: public Logger {
public:
FileLogger(std::ofstream& s): ofs(s){}
void log(const char* msg) override {
ofs << msg;
}
private:
std::ofstream& ofs;
};
static NullLogger defaultLogger;
int myfun(const int a, Logger& logger=defaultLogger)
{
logger.log("hello");
// ...
logger.log("asdf");
}
int main(){
std::ofstream ofs;
FileLogger fileLogger(ofs);
NullLogger nullLogger;
myfun(10,fileLogger); // logs to file
myfun(10,nullLogger); // logs nothing
myfun(10); // also logs nothing
return 0;
}
In C++17 there is a solution involving std::optional but since it requires default constructible types, std::reference_wrapper has to be used too.
#include <fstream>
#include <optional>
#include <functional>
int myfun(const int a, std::optional<std::reference_wrapper<std::ofstream>> ofs)
{
if (ofs) {
ofs->get() << "...";
return 1;
}
else{
return 0;
}
}
#include <iostream>
int main(){
std::ofstream file;
//Calling is quite nice.
std::cout<<myfun(10,{file})<<'\n'; //Prints 1
std::cout<<myfun(10,{})<<'\n'; //Prints 0
}
The downside of this solution, although idiomatic, is being verbose and heavy on the syntax in some cases.
I have a simple GUI program that uses a custom stringstream to redirect output from the console to a text field in the GUI (under some circumstances). currently. the window redraws any time I hit enter, but it's possible that output could be generated at other times. Is there a way to register a function with the stringstream that gets executed every time the << operator is used on the stream?
NOTE
I should have pointed out that I cannot use C++11 in my solution. the machines on which this will be compiled and run will not have c++11 available.
Personally, I wouldn't use an std::ostringstream (or even an std::stringstream) for this at all! Instead, I would create my own stream buffer taking care of sending the data to the GUI. That is, I'd overwrite std::streambuf::overflow() and std::streambuf::sync() to send the current data to the GUI. To also make sure that any output is sent immediately, I'd set up an std::ostream to have std::ios_base::unitbuf set. Actually, sending the changes to a function is quite simple, i.e., I'll implement this:
#include <streambuf>
#include <ostream>
#include <functional>
#include <string>
#include <memory>
#include <iostream> // only for testing...
#if HAS_FUNCTION
typedef std::function<void(std::string)> function_type;
#else
class function_type
{
private:
struct base {
virtual ~base() {}
virtual base* clone() const = 0;
virtual void call(std::string const&) = 0;
};
template <typename Function>
struct concrete
: base {
Function d_function;
concrete(Function function)
: d_function(function) {
}
base* clone() const { return new concrete<Function>(this->d_function); }
void call(std::string const& value) { this->d_function(value); }
};
std::auto_ptr<base> d_function;
public:
template <typename Function>
function_type(Function function)
: d_function(new concrete<Function>(function)) {
}
function_type(function_type const& other)
: d_function(other.d_function->clone()) {
}
function_type& operator= (function_type other) {
this->swap(other);
return *this;
}
~function_type() {}
void swap(function_type& other) {
std::swap(this->d_function, other.d_function);
}
void operator()(std::string const& value) {
this->d_function->call(value);
}
};
#endif
class functionbuf
: public std::streambuf {
private:
typedef std::streambuf::traits_type traits_type;
function_type d_function;
char d_buffer[1024];
int overflow(int c) {
if (!traits_type::eq_int_type(c, traits_type::eof())) {
*this->pptr() = traits_type::to_char_type(c);
this->pbump(1);
}
return this->sync()? traits_type::not_eof(c): traits_type::eof();
}
int sync() {
if (this->pbase() != this->pptr()) {
this->d_function(std::string(this->pbase(), this->pptr()));
this->setp(this->pbase(), this->epptr());
}
return 0;
}
public:
functionbuf(function_type const& function)
: d_function(function) {
this->setp(this->d_buffer, this->d_buffer + sizeof(this->d_buffer) - 1);
}
};
class ofunctionstream
: private virtual functionbuf
, public std::ostream {
public:
ofunctionstream(function_type const& function)
: functionbuf(function)
, std::ostream(static_cast<std::streambuf*>(this)) {
this->flags(std::ios_base::unitbuf);
}
};
void some_function(std::string const& value) {
std::cout << "some_function(" << value << ")\n";
}
int main() {
ofunctionstream out(&some_function);
out << "hello" << ',' << " world: " << 42 << "\n";
out << std::nounitbuf << "not" << " as " << "many" << " calls\n" << std::flush;
}
A fair chunk of the above code is actually unrelated to the task at hand: it implements a primitive version of std::function<void(std::string)> in case C++2011 can't be used.
If you don't want quite as many calls, you can turn off std::ios_base::unitbuf and only sent the data upon flushing the stream, e.g. using std::flush (yes, I know about std::endl but it unfortunately is typically misused to I strongly recommend to get rid of it and use std::flush where a flush is really meant).
In order to do this you should create your own streambuf class. streambuf classes represent IO devices and each one takes care of the various issues specific to that kind of device. The standard defines a streambuf for files and another for strings. Network access would use another, and output to a GUI should also be represented as another kind of device if you're going to use streams at all.
Writing an appropriate streambuf class isn't trivial and seems to be kind obscure, but there are resources out there. The C++ Standard Library - A Tutorial and Reference has a small section on this. Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference provides in-depth information. A search for subclassing basic_streambuf will also turn up some free resources online.
If you haven't already, can you derive a subclass from stringstream and overload its stream insertion operator to generate events?
Pseudocode:
class AlertingStream : public stringstream
{
ostream& operator << (type)
{
for (each listener in listeners)
{
listener.notify();
}
perform insertion;
return *this;
}
}
I'm making a logger and I wish to have some kind of stream-like happenings going on, ideally doing CLogger << "Testing, " << 1 << ",2,3\n"; instead of CLogger->log("Testing, %i,2,3", 1);
My question is how would I do this? I don't want to directly create a stream to stdout as I want to use my own method which includes writing files and such. I've considered overloading with a certain struct that'd flush the current stream buffer to a method, but I'd have to do CLogger << flush << "Test!\n"; which is kind of odd.
Does anybody know how to do this?
If all that you need is directing certain log messages to files, have you considered std::ofstream?
Otherwise, I like to derive my logging class from std::ostream, so I get all of the stream goodness. The trick is to put all of your application-specific code in the associated streambuf class. Consider:
#include <iostream>
#include <sstream>
class CLogger : public std::ostream {
private:
class CLogBuf : public std::stringbuf {
private:
// or whatever you need for your application
std::string m_marker;
public:
CLogBuf(const std::string& marker) : m_marker(marker) { }
~CLogBuf() { pubsync(); }
int sync() {
std::cout << m_marker << ": " << str();
str("");
return std::cout?0:-1;
}
};
public:
// Other constructors could specify filename, etc
// just remember to pass whatever you need to CLogBuf
CLogger(const std::string& marker) : std::ostream(new CLogBuf(marker)) {}
~CLogger() { delete rdbuf(); }
};
int main()
{
CLogger hi("hello");
CLogger bye("goodbye");
hi << "hello, world" << std::endl;
hi << "Oops, forgot to flush.\n";
bye << "goodbye, cruel world\n" << std::flush;
bye << "Cough, cough.\n";
}
Notes:
The CLogger constructor can take whatever parameters you need to use -- a filename, an output language, a pointer to the underlying log data, whatever. Just pass the data onto the CLogBuf class.
The CLogBuf's sync() is automatically called during in response to std::flush.
Check out operator <<, which is what STL's streams overload.
class CLogger
{
public:
CLogger& operator << (const std::string& _rhs)
{
// work with it here
return *this;
}; // eo operator <<
}; // eo class CLogger
EDIT:
See this page that outlines how std::ostream overloads operator << for different types:
http://www.cplusplus.com/reference/iostream/ostream/operator%3C%3C/
Implement a proxy object that gives you operator<< and pass an ownership marker to the returned proxy object. When an object with the ownership marker dies, you flush the stream.
An easy way to do this would be to wrap ostringstream in an auto_ptr in your proxy and flushing to your logger when the auto_ptr is not null in the proxy's d-tor.
That'll give you the formatting possible with ostream, but still result in only one call to your logger, which I thought was the real problem.
Think of something like this:
class CLoggingProxy
{
public:
template <class T>
CLoggingProxy operator<<( const T& rhs )
{
if ( stream )
*stream << rhs;
return *this;
}
~CLoggingProxy()
{
if ( stream )
logger->log(stream->str());
}
private:
std::auto_ptr<std::ostringstream> stream;
CLogger* logger;
friend class CLogger;
CLoggingProxy( CLogger* logger ) // call this e.g. from the logger to "start" input
: stream(new std::ostringstream), logger(logger) {}
};
All of the operator<<() functions are defined on the class ostream, which you can inherit from and implement its methods.
I'm just going to copy-paste my current implementation of this below, it does all you need (and handles things like std::endl and the like). AMBROSIA_DEBUGis macro defined in debug builds, so in theory, every call to this output class should be omitted in release builds (haven't checked though, but seems logical overhead is kept to a minimum. The functionality is based on QDebug functionality, plus a little addition of mine debugLevel, which would allow you to filter debug messages by hand in your code depending on a runtime parameter. Right now it also adds the same amount of spaces before each message.
// C++ includes
#include <iostream>
#include <string>
typedef std::ostream& (*STRFUNC)(std::ostream&);
#ifdef AMBROSIA_DEBUG
static int debugLevel;
const static int maxDebugLevel = 9;
#endif
class Debug
{
public:
#ifdef AMBROSIA_DEBUG
Debug( const int level = 0 )
: m_output( level <= debugLevel ),
m_outputSpaces( true ),
m_spaces( std::string(level, ' ') )
#else
Debug( const int )
#endif // AMBROSIA_DEBUG
{}
template<typename T>
#ifdef AMBROSIA_DEBUG
Debug& operator<<( const T &output )
{
if( m_output )
{
if( m_outputSpaces )
{
m_outputSpaces = false;
std::cerr << m_spaces;
}
std::cerr << output;
}
#else
Debug& operator<<( const T & )
{
#endif // AMBROSIA_DEBUG
return *this;
}
// for std::endl and other manipulators
typedef std::ostream& (*STRFUNC)(std::ostream&);
#ifdef AMBROSIA_DEBUG
Debug& operator<<( STRFUNC func )
{
if( m_output )
func(std::cerr);
#else
Debug& operator<<( STRFUNC )
{
#endif // AMBROSIA_DEBUG
return *this;
}
private:
#ifdef AMBROSIA_DEBUG
bool m_output;
bool m_outputSpaces;
std::string m_spaces;
#endif // AMBROSIA_DEBUG
};
Example usage:
int main()
{
debugLevel = 9; // highest allowed in my app...
Debug(4) << "This message should have an indentation of 4 spaces." << endl;
Debug(8) << "This is a level 8 debug message.\n";
return 0;
}
MathWorks currently doesn't allow you to use cout from a mex file when the MATLAB desktop is open because they have redirected stdout. Their current workaround is providing a function, mexPrintf, that they request you use instead. After googling around a bit, I think that it's possible to extend the std::stringbuf class to do what I need. Here's what I have so far. Is this robust enough, or are there other methods I need to overload or a better way to do this? (Looking for portability in a general UNIX environment and the ability to use std::cout as normal if this code is not linked against a mex executable)
class mstream : public stringbuf {
public:
virtual streamsize xsputn(const char *s, std::streamsize n)
{
mexPrintf("*s",s,n);
return basic_streambuf<char, std::char_traits<char>>::xsputn(s,n);
}
};
mstream mout;
outbuf = cout.rdbuf(mout.rdbuf());
You don't really want to overload std::stringbuf, you want to overload std::streambuf or std::basic_streambuf (if you want to support multiple character types), also you need to override the overflow method as well.
But I also think you need to rethink your solution to your problem.
cout is just an ostream, so if all classes / functions takes an ostream then you can pass in anything you like. e.g. cout, ofstream, etc
If that's too hard then I would create my own version of cout, maybe called mycout that can be defined at either compiler time or runtime time (depending on what you want to do).
A simple solution may be:
#include <streambuf>
#include <ostream>
class mystream : public std::streambuf
{
public:
mystream() {}
protected:
virtual int_type overflow(int_type c)
{
if(c != EOF)
{
char z = c;
mexPrintf("%c",c);
return EOF;
}
return c;
}
virtual std::streamsize xsputn(const char* s, std::streamsize num)
{
mexPrintf("*s",s,n);
return num;
}
};
class myostream : public std::ostream
{
protected:
mystream buf;
public:
myostream() : std::ostream(&buf) {}
};
myostream mycout;
And the cout version could just be:
typedef std::cout mycout;
A runtime version is a bit more work but easily doable.
Shane, thanks very much for your help. Here's my final working implementation.
class mstream : public std::streambuf {
public:
protected:
virtual std::streamsize xsputn(const char *s, std::streamsize n);
virtual int overflow(int c = EOF);
};
...
std::streamsize
mstream::xsputn(const char *s, std::streamsize n)
{
mexPrintf("%.*s",n,s);
return n;
}
int
mstream::overflow(int c)
{
if (c != EOF) {
mexPrintf("%.1s",&c);
}
return 1;
}
...
// Replace the std stream with the 'matlab' stream
// Put this in the beginning of the mex function
mstream mout;
std::streambuf *outbuf = std::cout.rdbuf(&mout);
...
// Restore the std stream buffer
std::cout.rdbuf(outbuf);
I have changed the OP's final implementation a little bit, adding a constructor and destructor. Creating an object of this class automatically replaces the stream buffer in std::cout, and when the object goes out of scope, the original stream buffer is restored. RAII!
class mxstreambuf : public std::streambuf {
public:
mxstreambuf() {
stdoutbuf = std::cout.rdbuf( this );
}
~mxstreambuf() {
std::cout.rdbuf( stdoutbuf );
}
protected:
virtual std::streamsize xsputn( const char* s, std::streamsize n ) override {
mexPrintf( "%.*s", n, s );
return n;
}
virtual int overflow( int c = EOF ) override {
if( c != EOF ) {
mexPrintf( "%.1s", & c );
}
return 1;
}
private:
std::streambuf *stdoutbuf;
};
To use the stream buffer in a MEX-file, simply:
mxstreambuf mout;
std::cout << "Hello World!\n";
... and don't worry about forgetting anything.
cout is a particular character output stream. If you want a cout that writes to a file, use an fstream, particularly an ofstream. They have the same interface that cout provides. Additionally, if you want to grab their buffer (with rdbuf) you can.
Continuing from the question that I asked here: C++ multi-dimensional data handling
In my example: I have many Chips, each Chip has many Registers, each Register has many Cells, and each Cell has many Transistors. I asked whether to use one complex STL container for them, or to implement full classes for them. And, as advised, I chose to implement full classes for them. I have:
class Chip
{
map<RegisterLocation, Register> RegistersPerLocation;
};
class Register
{
map<CellLocation, Cell> CellsPerLocation;
};
// etc..
Now, I need to fill the data to the classes, and I can't decide: Should reading the data be responsibility of these classes, or should they just wrap the containers and the reading will be done outside.
I mean I have to choose one of the following: Either:
class Chip
{
map<RegisterLocation, Register> RegistersPerLocation;
public:
void AddRegisterPerLocation(RegisterLocation, Register);
};
void ReadChipData(Chip & chip)
{
for (RegisterLocation loc = 0; loc < 10; loc++)
{
Register reg;
ReadReg(reg);
chip.AddRegisterPerLocation(loc, reg);
}
}
void ReadReg(Register & reg)
{
for (CellLocation loc = 0; loc < 10; loc++)
{
Cell cell;
ReadCell(cell);
reg.AddRegisterPerLocation(loc, cell);
}
}
//etc...
Or:
class Chip
{
map<RegisterLocation, Register> RegistersPerLocation;
public:
void ReadData();
};
void Chip::ReadData()
{
for (RegisterLocation loc = 0; loc < 10; loc++)
{
Register reg;
reg.ReadData();
RegistersPerLocation[loc] = reg;
}
}
//etc...
void ReadChipData(Chip & chip)
{
chip.ReadData();
}
Thank you.
If you are thinking of tying the reader/writer to the domain objects in order to follow the principle of encapsulation, you are correct to a certain extent. But remember: You bind not just any action, but a valid behavior. Valid as in makes sense for the object in the domain.
Another thing to keep in mind is separation of concerns. Serializability is not a Chip's intrinsic behavior -- modeling that into the domain object would be unfair IMO. YMMV.
Separate the reading(and writing) from the classes. As the library does. Expose iterators if you have to. And you can overload the '<<' and '>>' operators for syntactic sugar ;)
A minor nit on the classes -- a template based approach looks so promising.
Here's some code I cooked up: you can try the following as well.
(I've successfully compiled and run this on a MS VS2005 but check it out on your system. Also, can someone fix the tabbing -- feeling too lazy to do this :P)
/*+-----------8<----------------------------8<-----------+*/
#include <vector>
#include <iostream>
#include <algorithm>
#include <map>
#include <iterator>
/* mother template */
template<class _Item>
struct Hardware
{
Hardware() : _myCont(2 + ::rand() % 5) {}
private:
typename vector<_Item> _myCont;
// i/o friends
template<class _Item>
friend ostream& operator<<(ostream& output,
const Hardware<_Item>& me);
template<class _Item>
friend istream& operator>>(istream& in,
const Hardware<_Item>& me);
};
/* actual domain objects */
/* base object */
struct Transistor {
};
/* built objects */
typedef Hardware<Transistor> Cell;
typedef Hardware<Cell> Register;
typedef Hardware<Register> Chip;
/* poorman's introspection utility */
template<class T>
const char *who() { return ""; }
template<>
const char *who<Transistor>() { return "Transistor"; }
template<>
const char *who<Cell>() { return "Cell"; }
template<>
const char *who<Register>() { return "Register"; }
template<>
const char *who<Chip>() { return "Chip"; }
/* writer/serialize out */
template<class T>
ostream& operator<<(ostream& out, const Hardware<T>& hw) {
// whatever you need to do to write
// os << chip works fine, because you will provide a specialization
out << "[ " << ::who<Hardware<T>>() << " ]\n\t";
std::copy(hw._myCont.begin(), hw._myCont.end(),
std::ostream_iterator< T >(std::cout, "\n\t"));
return out;
}
/* specialize for base object */
ostream& operator<< (ostream& out, const Transistor& hw) {
out << "[ " << ::who<Transistor>() << " ]\n";
return out;
}
/* reader/serialize in */
template<class T>
istream& operator>>(istream& in, const Hardware<T>& hw) {
// whatever you need to do to read
// similarly in >> chip works fine,
return in;
}
// driver showing relationships
// Chip -> Register -> Cell -> Transistor
int main() {
Transistor t;
std::cout << t << std::endl;
Cell ce;
std::cout << ce << std::endl;
Register r;
std::cout << r << std::endl;
Chip C;
std::cout << C << std::endl;
}
/*+-----------8<----------------------------8<-----------+*/
Caveat: Haven't tested, so there may be quite a few compiler errors/warnings. But this should give you an idea of what I am trying to say.
Making classes responsible for their serialization is better - once you change the class fields you have to change the same class serializeation method, not some reader/writer code over there.