How to write data from type A to type B

advertisements

I'm implementing a thing that generates results and writes them to a file of certain format.

Fairly simple, but I want this to be dynamic.

I'll throw down a few classes.

Data - base class for all results
DataFile - base class for all file format types, has method addData(Data * data)

ErrorData - derived from Data, contains data about an error.
InfoData - derived from Data, contains generic information.

XmlFile - derived from DataFile, contains Data in XML format.
BinaryFile - derived from DataFile, contains data in binary format.

My question is this:

Where do i put the implementation on how to write ErrorData into an XmlFile?

What I don't like to see in the answer:

  1. A new member function to Data, DataFile, ErrorData or XmlFile (because this means that i need to add those each time I add a new Data or DataFile derived class)
  2. Type casting to derived types.
  3. General ugliness :)

I know my basic C++ virtualization and things, no need to be ultra specific.

I do appreciate a few code pieces though, they leave less ambiguity.

I had some thoughts on making a DataWriter base class and derive classes from that which would know how to write Data of certain type to a DataFile of certain type, but I'm a bit uncertain of the specifics of that.

EDIT:

I'll clarify a bit more in the form of an example.

Let's have 2 new file formats, FormatATxtFile and FormatBTxtFile.

Let's assume that we have an InfoData object and it has the parameters:

Line number of the message : 34
Message content : Hello World

The object, written into FormatATxtFile, looks like this in the file:

Line:34;Txt:Hello World;Type:Info

And in FormatBTxtFile it would look something like this:

@Info,34,Hello World

A sort of way to export data into a different format. I don't need Importing, at least now.

What the code using this would look like:

DataFile * file = DataFileFactory::createFile(type);

std::vector<Data*> data = generateData();

file->setData(data);
file->writeTo("./FileName"); // correct end is added by DataFile type, ie .txt

Edit:

It seems that I didn't make clear enough what problems arise with Xml and binary file formats. I'm sorry.

Let's use the same InfoData object as above and push it to the XmlFile format. It might produce something like this, under a certain element:

<InfoLog>
    <Info Line="34">Hello World</Info>
</InfoLog>

Let's assume that the ErrorData class would have these parameters:

Line number of the error : 56
Error text : LINK : fatal error LNK1168
10 Lines prior to error message: text1...
10 Lines after error message: text2...
Entire log af what happened: text3...

Now when this is pushed into an XML format, it would need to be something totally different.

<Problems>
    <Error>
        <TextBefore>text1...</TextBefore>
        <Text line = 56>
            LINK : fatal error LNK1168
        </Text>
        <TextAfter>text1...</TextAfter>
    </Error>
    ...
</Problems>

The entire file might look something like this:

<Operation>
    <InfoLog>
        <Info Line="34">Hello World</Info>
        <Info Line="96">Goodbye cruel World</Info>
    </InfoLog>
    <Problems>
        <Error>
            <TextBefore>text1...</TextBefore>
            <Text line = 56>
                LINK : fatal error LNK1168
            </Text>
            <TextAfter>text1...</TextAfter>
        </Error>
        <Error>
            <TextBefore>sometext</TextBefore>
            <Text line = 59>
                Out of cheese error
            </Text>
            <TextAfter>moretext</TextAfter>
        </Error>
    </Problems>
</Operation>


If you consider the code below, it presents a minimal illustration of how to arbitrarily combine generic field access with generic field streaming - factoring your various requirements. If the applicability or utility's not clear, do let me know....

#include <iostream>
#include <string>

struct X
{
    int i;
    double d;

    template <typename Visitor>
    void visit(Visitor& visitor)
    {
        visitor(i, "i");
        visitor(d, "d");
    }
};

struct XML
{
    XML(std::ostream& os) : os_(os) { }

    template <typename T>
    void operator()(const T& x, const char name[]) const
    {
        os_ << '<' << name << '>' << x << "</" << name << ">\n";
    }

    std::ostream& os_;
};

struct Delimiter
{
    Delimiter(std::ostream& os,
              const std::string& kvs = "=", const std::string& fs = "|")
      : os_(os), kvs_(kvs), fs_(fs)
    { }

    template <typename T>
    void operator()(const T& x, const char name[]) const
    {
        os_ << name << kvs_ << x << fs_;
    }

    std::ostream& os_;
    std::string kvs_, fs_;
};

int main()
{
    X x;
    x.i = 42;
    x.d = 3.14;

    XML xml(std::cout);
    Delimiter delimiter(std::cout);

    x.visit(xml);
    x.visit(delimiter);
}