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 chars 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