TITLE: defining a non-ASCII based stream (Source: comp.lang.c++.moderated, 13 Sep 2000) KANZE: > > Basically, you want to define a new format, rather than the ASCII > > format already defined in istream and ostream. So do it. You can't > > derive from istream/ostream for this, but nothing prevents you from > > deriving from ios (or whatever it is called now). Obviously, since it > > is a new format, you'll have to provide the necessary formatting > > operators for the basic types. And probably for your own types as > > well, since it is hard to imagine that their operator>> will work if > > they aren't provided the standard format. On the other hand, if you > > derive from ios, you get the same error handling conventions as with > > istream/ostream, and you certainly should use streambuf, in order to > > leverage off the existing sources and sinks. ASPELI: Martin Aspeli > Hmm.... so how would I do that? Could you give some simple example? > (the streams library is so confusing ;p) Ideally, I'd like an > example that stores strings in a stream, prefixed with a size and > then reads them back using the size to figure out how much to read. KANZE: James Kanze class obstream : public std::ios { public: explicit obstream( std::streambuf* sb ) : ios( sb ) { } obstream& operator<<( std::string const& src ) { size_t length = src.size() ; assert( length < 0x10000 ) ; writeByte( length >> 8 ) ; writeByte( length ) ; for ( std::string::const_iterator iter = src.begin() ; iter != src.end() ; ++ iter ) { writeByte( *iter ) ; } return *this ; } private: void writeByte( unsigned char val ) { if ( *this ) { if ( rdbuf()->sputc( val ) == traits_type::eof() ) { setstate( failbit ) ; } } } } ; This writes the string with a 16 bit length, high byte first, in front. You'll obviously want conversion operators for all of the other types as well. Doing the numeric types portably requires an assert (as I do for the length), since you cannot be certain that an int (for example) on the machine which reads the data will have more than 16 bits. ASPELI: > Is it possible to derive from istream/ostream? KANZE: Possible, yes. Take ifstream and ofstream, for example. However, a reasonable derivation will maintain the contract of the base classes, and this contract involves textual formatting. ASPELI: > Where I stop understanding is in the exact role of streambufs. I > know that they implement buffering strategies and access to > low-level source/destination of the stream (console, file etc.), but > I don't understand how to *use* it. Similarly, they seem very > character oriented - why convert a 5 digit integer into 5 chars (=5 > bytes) when a 4-byte integer would do? It must be very inefficient, > both in terms of storage space and in terms of speed. Portability? KANZE: Char's are also C/C++'s equivalent of bytes. A streambuf opened normally can be considered character oriented, a streambuf opened with ios::binary byte (raw data) oriented. Except for filebuf, I don't think it ever makes a difference. Streambuf's are sinks and sources for raw data, either characters (type char) or bytes (type char). You use them by calling their public functions. Conceptually, a streambuf represents a sequence of char's, with a current pointer for input and output in the sequence. You can read with one of four functions; you can assimilate them to manipulating the input pointer (p): sgetc *p sbumpc *p++ snextc *++p sgetn(d,n) memcpy( d , p , n ) , p += n Of course, incrementing may involve actually accessing an external source, and there are always bounds checks, which cause EOF to be returned, or less than the requested number of characters. Writing is similar, with (p) as the output pointer: sputc *p++ sputn(s,n) memcpy( p , s , n ) , p += n There's also support for pushing back already extracted characters (decrementing the read pointer), but since you can look at a character without advancing the pointer, it is rarely needed. Whether changing the input pointer affects the output pointer or not depends on the streambuf type -- in a filebuf, the two pointers are always equal, whereas in a stringbuf, they are separate. ASPELI: > > > Basically what I'm looking for is some way to make the above > > > hypothetical code work so that I could use different types of > > > streams and not have to write wrapper classes (like Description > > > in your example) or manage whitespace etc. in the individual > > > class' serialize () / restore () functions. KANZE: > > I'm afraid you will have to provide new formatting routines. That > > is the whole point, it seems -- the standard formatting routines > > don't do what you want. Binary or not, some sort of formatting is > > necessary if you hope to every read the data back in the future. ASPELI: > And those are provided by istream and ostream, right? Which in turn > use a streambuf to access the actual "stream". KANZE: Right. ASPELI: > So I'd write > replacements for/derivatives of istream and ostream to handle > formating (e.g. prefix strings with a lenth when writing and using > this when reading). KANZE: The only time I'd write a derivative of istream or ostream is when the derived class maintained the guarantees they offer. Guarantees which involve formatted text, and not binary data. In practice, istream and ostream are extended in two ways: - a derived class which creates an instance of a new type of streambuf (see ifstream and ofstream), but changes nothing else, and - user defined operator<< and operator>>, which define the formatting protocol for new types. ASPELI: > Come to think of it, the standard iostreams are suitable for > anything but strings (whitespace delimited doesn't work for me) and > possibly that it leaves newlines in the stream thereby confusing > reading of strings after reading of numbers. KANZE: Newlines count as whitespace, so they might not be a problem. If the only problem is strings, I'd just create a new type (ParsableString), which acts as a handle to an std::string, and define operator<< and operator>> for it. (In my debugging code, I have a DisplayString which not only outputs an '"' at each end, but also converts any non-printable characters to the corresponding escape sequence. I haven't defined an operator>> for it, but in principle, it shouldn't be too difficult.) _______________________________________________ cpptips mailing list http://cpptips.hyperformix.com