Introduction
Very often you want to add special features to a stream class (istream
, ostream
or iostream
). A way to do this, is to create a specialized streambuf
class and to overload stream base class methods.
A Xstream
class constructor has a streambuf
parameter that is attached to the stream. For example, to write to a file, you can do:
filebuf fb;
fb.open ("test.txt",ios::out);
ostream os(&fb);
But if your objective isn't to write to a file, but to a generic memory buffer, you can construct your ostream
derived class like that:
class MyStreamBuf : public streambuf {
};
class MyOutputStream : public ostream {
public:
MyOutputStream() : ostream(&stBuf) {}
private:
MyStreamBuf stBuf;
};
The same for istream
and iostream
classes. Overloading of Xstream
methods permit you to create a specialized stream class.
If you want to serialize primitive types or other objects to MyOutputStream
, you must create a method that returns a MyOutputStream
reference object, so you can write something like that:
MyOutputStream mos;
mos << "hello";
We now see an example where this technique is applied to an Encoder
class where the encode operation can be performed in this way:
Encoder obj;
obj << "Hello";
and the decode operation can be performed so
string str;
obj >> str;
Using the code
Here, we will see an example that allows to manipulate a string through the operators <<
and >>
of a derived class from ostream
. Furthermore, we create a specialized streamuf
class that encodes and decodes a string buffer.
In this example code, like we have explained before, a string can be encoded and decoded in this way:
Encoder y;
y << "Hello"
y >> str;
The Encode
method simply consists of an ASCII conversion of char
s within the string and Decode
reverses this manipulation.
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
class BufferEncoder: public streambuf {
private:
string buffer;
int numCar;
public:
BufferEncoder() : streambuf(), numCar(0) {}
~BufferEncoder() {}
void Encode(const string& s){
char tmp[10];
for (unsigned int i=0; i< s.length(); ++i){
itoa(s[i],tmp,10);
buffer+= (string)tmp + (string)" ";
numCar +=strlen(tmp)+1;
}
}
void Decode(string& s){
string decodeStr;
for (int i=0; i< numCar;++i){
string tokenStr;
while ((buffer.at(i) != ' ')&& (buffer.at(i)!='\n') && (i< numCar)){
tokenStr += buffer.at(i);
i++;
}
int v = atoi(tokenStr.c_str());
decodeStr += (char) v;
}
s = decodeStr;
}
void EndOfLine() {
buffer += '\n';
numCar ++;
}
char* GetBuffer() {return ((char*) buffer.c_str());}
};
class Encoder : public ostream {
private:
BufferEncoder myStrBuf;
public:
Encoder(filebuf* fb) : ostream(fb),ios(0) {}
Encoder() : ostream(&myStrBuf),ios(0) {}
_Myt& operator<<(_Myt&(__cdecl *_Pfn )(_Myt&) ){
myStrBuf.EndOfLine();
return ((*_Pfn)(*this));
}
friend ostream& operator<<(ostream & s, Encoder& c) {
s << c.myStrBuf.GetBuffer();
return s;
}
Encoder& operator<<(const char * s) {
myStrBuf.Encode(s);
return *this;
}
Encoder& operator>>(string& s) {
myStrBuf.Decode(s);
return *this;
}
};
int main ()
{
Encoder y;
y <<"Hello World!";
cout <<"ENCODED: "<< y << endl;
string s ;
y >> s;
cout << "DECODED: " << s << endl;
return 0;
}
Points of Interest
This code can help you to learn how to serialize an object (primitive or used defined), to or from an object stream, like predefined objects cin
and cout
. In fact, like a istream
object, cin
, and ostream
object cout
, in C++, you can define derived and user- defined objects from these classes and create methods to or from these objects.
History
- v 1.1 fixed some code style tricks
- v 1.0