JJ C++ Builder - Streaming
JJ C++ Builder - Streaming
JJ C++ Builder - Streaming
Streaming
This month we’re taking a look at streaming, something try
{
which plays a key part in the VCL, and of course C++. In the Buffy = new char[TempSize + 1];
VCL TStream provides the base for all streaming. sStream->Read(Buffy, TempSize);
Buffy[TempSize] = ‘\0’; //add null
This allows us to write truly re-usable functions in our //terminator
applications and especially our own classes. This is best shown FMyString = Buffy;
}
by an example, so here we have a simple data class with a __finally
couple of properties which we can then stream in and out. {
if(Buffy)
class JonsClass : public TObject delete [] Buffy;
{ }
private: }
AnsiString FMyString; else
TDateTime FMyDateTime; FMyString = “”;
public: }
__fastcall JonsClass();
__fastcall ~JonsClass(); The LoadFromStream is a little more complex because of
//streaming the way we have to handle the AnsiString (*see hot tip in the
void __fastcall LoadFromStream(TStream
sidebar). The read of the TDateTime is simple enough, as is
*sStream);
void __fastcall SaveToStream(TStream the size of the data to read next, but we have to have a
*sStream); temporary buffer to read data into the AnsiString itself.
//properties
__property String MyString =
Again we write using pointers directly into memory locations
{read=FMyString, write=FMyString}; so ensure that memory allocations have been done correctly.
__property TDateTime MyDateTime = Final note: on using the sizeof function, pass in the actual
{read=FMyDateTime,write=FMyDateTime}; variable name and not a data type - this way, if you change
}; the variable’s data type, you don’t have to filter it down.
Right, we’ve got a simple class with the normal things, the Now we have a couple of very powerful functions. As you’ll
only addition being the LoadFrom... and SaveTo...Stream see in the demo code accompanying this article, we can save
functions. These two simple functions are defined as our data to a file simply with the following,
follows, void __fastcall JonsClass::SaveToFile(String
FFileName)
void __fastcall JonsClass::SaveToStream(TStream {
*sStream) TFileStream *MyFile = NULL;
{//write data
int TempSize; try
//write DateTime {
sStream->Write(&FMyDateTime, MyFile = new TFileStream(FFileName,
sizeof(FMyDateTime)); fmCreate|fmShareDenyWrite);
//write String SaveToStream(MyFile);
}
TempSize = FMyString.Length();
__finally
sStream->Write(&TempSize, sizeof(TempSize));
{
sStream->Write(FMyString.c_str(), TempSize); if(MyFile)
} delete MyFile;
}
This function defines how our data will appear on the stream. }
With the TDateTime object, or other simple class objects
and data types, we simply write out the data to the stream. Similarly with loading from file,
void __fastcall JonsClass::LoadFromFile(String
However, the string and any other variable length data needs FFileName)
writing with a size parameter, which we write first, followed {
by the data itself. Note the use of pointers to pass data onto TFileStream *MyFile = NULL;
the stream. Basically, we copy the memory from the pointer try
for as many bytes as we need. {
MyFile = new TFileStream(FFileName,
void __fastcall fmOpenRead|fmShareDenyNone);
JonsClass::LoadFromStream(TStream *sStream)
{ LoadFromStream(MyFile);
char *Buffy = NULL; }
int TempSize; __finally
{
//read datetime off stream. if(MyFile)
sStream->Read(&FMyDateTime, sizeof(TDateTime)); delete MyFile;
//read string off stream }
sStream->Read(&TempSize, sizeof(TempSize)); }
if(TempSize > 0)
{
The same principle is then used with loading and saving to
any of the supported stream types: try
{
MyFile = new TFileStream(FFileName,
• TStringStream (for manipulating in-memory strings)
fmCreate|fmShareDenyWrite);
SaveToStream(MyFile);
• TMemoryStream (for working with a memory buffer) }
__finally
• TBlobStream (for working with BLOB fields) {
if(MyFile)
• TWinSocketStream (for reading and writing over a delete MyFile;
socket connection) }
}
• TOleStream (for using a COM interface to read and
write) void __fastcall jClassList::LoadFromFile(String
FFileName)
You simply create the stream and load and save by passing {
TFileStream *MyFile = NULL;
the stream through our functions.
try
More Power {
MyFile = new TFileStream(FFileName,
The real power of this type of streaming comes once you
start building up arrays of your class, in something like a fmOpenRead|fmShareDenyWrite);
list. You create simple Load and Save streaming functions LoadFromStream(MyFile);
which call the classes own functions for each instance, and }
__finally
then in the same way you use one of the available streaming {
classes as above. if(MyFile)
delete MyFile;
In the demo code we have a basic TObjectList to hold }
multiple instances of our dataclass. Here are the lists load }
and save from stream functions,
Now, if you haven’t spotted it, here is the real use of
void __fastcall
jClassList::LoadFromStream(TStream *sStream) streaming, we can go polymorphic. Once we have a
{ LoadFrom... and SaveTo... stream methods for a given class,
JonsClass *sparejClass; we simply create generic LoadFrom and SaveTo for anything
that can stream.
sStream->Seek(0, soFromBeginning);
So, for instance, we can now create polymorphic functions
while(sStream->Position < sStream->Size)
{
called JonsLoadFromFile and JonsSaveToFile, which simply
sparejClass = new JonsClass; take a method pointer to the streaming functions, declared
sparejClass->LoadFromStream(sStream); as,
Add(sparejClass);
} typedef void __fastcall (__closure *MyFunc
} )(TStream *);