2

結果を生成し、特定の形式のファイルに書き込むものを実装しています。

かなり単純ですが、これを動的にしたいと思います。

いくつかのクラスを破棄します。

Data - すべての結果の
基本クラスDataFile - すべてのファイル形式タイプの基本クラス、メソッドを持っていますaddData(Data * data)

ErrorData - Data から派生し、エラーに関するデータを含みます。
InfoData - Data から派生し、一般的な情報を含みます。

XmlFile - DataFile から派生し、XML 形式のデータを含みます。
BinaryFile - DataFile から派生し、バイナリ形式のデータを含みます。

私の質問はこれです:

ErrorDataXmlFileに書き込む方法の実装はどこに置くのですか?

答えで見たくないもの:

  1. DataDataFileErrorDataまたはXmlFileへの新しいメンバー関数(これは、新しいDataまたはDataFile派生クラスを追加するたびにそれらを追加する必要があることを意味するため)
  2. 派生型への型キャスト。
  3. 一般的な醜さ:)

基本的な C++ の仮想化などは知っているので、特に詳しく説明する必要はありません。

ただし、あいまいさが少なくなるいくつかのコードはありがたいです。

DataWriter基本クラスを作成し、特定のタイプのDataを特定のタイプのDataFileに書き込む方法を知っているクラスを派生させることについていくつか考えましたが、その詳細については少しわかりません。

編集:

例の形でもう少し明確にします。

FormatATxtFileFormatBTxtFileという 2 つの新しいファイル形式を用意しましょう。

InfoDataオブジェクトがあり、次のパラメーターがあるとします 。

メッセージの行番号:34
メッセージ内容:Hello World

FormatATxtFileに書き込まれたオブジェクトは、ファイル内で次のようになります。

行:34;テキスト:Hello World;タイプ:情報

FormatBTxtFileでは、次のようになります。

@Info,34,ハローワールド

データを別の形式にエクスポートする一種の方法。少なくとも今は、インポートは必要ありません。

これを使用したコードは次のようになります。

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

編集:

Xml およびバイナリ ファイル形式でどのような問題が発生するかを十分に明確にしていないようです。ごめんなさい。

上記と同じInfoDataオブジェクトを使用して、XmlFile 形式にプッシュしてみましょう。特定の要素の下で、次のようなものが生成される場合があります。

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

ErrorDataクラスにのパラメータがあるとします。

エラーの行番号: 56
エラー テキスト: リンク: 致命的なエラー LNK1168
エラー メッセージの前の 10 行: text1...
エラー メッセージの後の 10 行: text2...
何が起こったかのログ全体: text3...

これを XML 形式にプッシュすると、まったく別のものにする必要があります。

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

ファイル全体は次のようになります。

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

4 に答える 4

2

これをクラス内に配置する場所を探す代わりに、新しい関数はどうでしょうか?

void copyData(const ErrorData *data, DataFile *output)
{
    // ...
}

次に、変換するデータ型に対してこの関数をオーバーロードできます。

または、おそらくテンプレートを使用できます。

template<typename A, typename B> copyData(const A *data, const B *output);

次に、サポートする必要がある特定の型に合わせてテンプレートを特殊化できます。

于 2011-05-06T06:43:24.383 に答える
2

Do like the standard library does - go with virtual functions / operators. We all can use istream& and extract what we want from it with operator>>, while we totally don't care for the underlying stream, be it cin, fstream or a stringstream. And rather take your data by reference then (Data& data).

于 2011-05-06T06:45:44.687 に答える
1

私はあなたの質問で少し遊んでいます、そしてそれは私が思いついたものです:

#include <iostream>
#include <list>
#include <map>
#include <string>

using namespace std;

class DataFile;

class Data {
public:
    virtual void serializeTo(DataFile*) = 0;
};

class DataFile {
public:
    void addData(Data* d) {
        _data.push_back(d);
    }
    virtual void accept(string paramName, string paramValue) {
        _map[paramName] = paramValue;
    }

    virtual void writeTo(string const& filename) = 0;

protected:
    list<Data*> _data;
    map<string, string> _map;
};

class FormatATxtFile: public DataFile {
public:
    void writeTo(string const& filename) {
        cout << "writing to " << filename << ".txt:" << endl;
        for(list<Data*>::iterator it = _data.begin(); it != _data.end(); ++it) {
            (*it)->serializeTo(this);       

            cout << "Line:" << _map["Line"] << "; "
                << "Txt:" << _map["Txt"] << "; "
                << "Type: " << _map["Type"]
                << endl;
        }
        cout << endl;
    }
};

class FormatBTxtFile: public DataFile {
public:
    void writeTo(string const& filename) {
        cout << "writing to " << filename << ".b-txt" << endl;
        for(list<Data*>::iterator it = _data.begin(); it != _data.end(); ++it) {
            (*it)->serializeTo(this);

            cout << "@" << _map["Type"] << "," << _map["Line"] << "," << _map["Txt"]
                << endl;
        }
        cout << endl;
    }
};

class InfoData: public Data {
    public:
        void serializeTo(DataFile* storage) {
            storage->accept("Line", line);
            storage->accept("Txt", txt);
            storage->accept("Type", "Info");
        }
        string line;
        string txt;
    };

int main()
{
    FormatATxtFile fileA;
    FormatBTxtFile fileB;
    InfoData info34;
    info34.line = "34";
    info34.txt = "Hello World";
    InfoData info39;
    info39.line = "39";
    info39.txt = "Goodbye cruel World";
    fileA.addData(&info34);
    fileA.addData(&info39);
    fileB.addData(&info34);
    fileB.addData(&info39);
    fileA.writeTo("./Filename");
    fileB.writeTo("./Filename");    
}

実際には、ファイルに書き込むことはありませんが、ニーズに合わせて簡単に変更できます。

このサンプルコードの出力は次のとおりです。

./Filename.txtへの書き込み:行:34;
Txt:Hello World; タイプ:情報行:39;
Txt:さようなら残酷な世界; タイプ:情報

./Filename.b-txtへの書き込み
@Info、34、Hello World
@ Info、39、Goodbye cruel World

ご覧のとおり、名前と値で識別されるパラメータDataを提供する必要があります。各スペシャライゼーションは、それを好きなように処理します。DataFileDataFile

HTH

于 2011-05-06T08:58:45.633 に答える
1

以下のコードを検討すると、汎用フィールド アクセスと汎用フィールド ストリーミングを任意に組み合わせる方法 (さまざまな要件を考慮したもの) の最小限の図が示されます。適用性または有用性が明確でない場合は、お知らせください....

#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);
}
于 2011-05-06T08:05:08.927 に答える