C++ How to dynamically select a file handle according to the type of data that should be written?
I've got a class outputInterface;
that should handle the output (to files) of some data. The data is contained in objects of some custom classes, say dataClassA
and dataClassB
, that all derive from a common base class dataClassBase
.
Now I want the data to be written to different files according to its type. So data of type dataClassA
should go to fileA
, data of type dataClassB
should go to fileB
and so on. As this output happens very often I would like the file handles (fileA
and fileB
) to stay open, i.e. I don't want to open and close the files for the output of each piece of data. One outputInterface
object can be expected to exist all the time.
So what I would like to achieve is something like this:
- Dynamically associate data of type
dataClassA
with the file handlefileA
etc. - When receiving data of type
dataClassA
check whetherfileA
is already connected to a file, if not, open the file.
How can I get this behavior (or least something similar / better)?
I've been thinking of making the file handles static members of dataClassA
and dataClassB
(or the base class dataClassBase
?). But then, how do I take care of closing the files? I would have to somehow keep track of the dat开发者_如何学JAVAa types that have actually been used (the files that have actually been opened).
Try something like this:
#ifndef OUTPUTINTERFACE?H
#define OUTPUTINTERFACE?H
#include <string>
#include <fstream>
#include <map>
class DataClass
{
public:
virtual bool WriteData(std::ofstream& FStream) = 0;
};
class DataClass1 :
public DataClass
{
virtual bool WriteData(std::ofstream& FStream)
{
FStream << "teletubbies";
}
};
class DataClass2 :
public DataClass
{
virtual bool WriteData(std::ofstream& FStream)
{
FStream << "garbage";
}
};
class OutputInterface
{
public:
OutputInterface()
{
}
~OutputInterface()
{
//Release stream pointers
}
template<typename T>
bool WriteData(T& Data)
{
std::string dClassUID = std::string(typeid(T).name);
tFStreamMap::iterator it this->streamMap.find(dClassUID);
std::ofstream* stream = NULL;
if(it != streamMap.end())
{
stream = it->second;
}
else
{
stream = new std::ofstream();
stream->open(dClassUID + ".txt");
streamMap.insert(std::make_pair(dClassUID, stream));
}
Data.WriteData(stream);
}
private:
typedef std::map<std::string, std::ofstream*> tFStreamMap;
tFStreamMap streamMap;
};
#endif
This is just a prove of concept and can be optimized in many ways. I would rather stick with overloaded functions than with runtime type checks.
This is fairly easy to implement in C++11, using an
std::map<std::type_index, std::ostring*> outputMap
. (In C++03, you'll have to
implement the equivalent of std::type_index
yourself.) You get the
output stream using outputMap[typeid(*data)]
. The only problem is
getting the streams into the map to begin with: you can do something
like:
std::ostream*& destPtr = outputMap[typeid(*data)];
if ( destPtr == NULL ) {
destPtr = new std::ofstream("...");
}
std::ostream& dest = *destPtr;
But from where do you get the filename?
There's also the question of when you close the streams: you can't normally close an output stream in a destructor, since closing an output stream is an operation which can fail, and you need to detect and react to that failure. Probably with an exception, which is why you don't want to do it in a destructor.
Since the "data" part comes from dataClassBase
, you can make a virtual/pure-virtual function 'WriteData` in this class, and let derive class implement it.
The class outputInterface
may take objects of type dataClassBase
and would directly call WriteData
. Other than WriteData
you may also add other virtual functions in dataClassBase
You did not mention relationship between outputInterface
and dataClassBase
精彩评论