2

やや複雑なストリームを解析して保存する必要があり、解析した結果を何らかの方法で保存する必要があります。ストリームには基本的に名前と値のペアが含まれ、valuesは異なるsに対して異なるタイプである可能性がありnameます。基本的に、私はkey(常に文字列)のペアへのマップになり<type, value>ます。

私はこのようなものから始めました:

typedef enum ValidType {STRING, INT, FLOAT, BINARY} ValidType;
map<string, pair<ValidType, void*>> Data;

しかし、私は本当に嫌いvoid*で、ポインタを保存しています。もちろん、私はいつでも値をバイナリデータとして保存できます(vector<char>たとえば)。その場合map

map<string, pair<ValidType, vector<char>>> Data;

ただし、この場合、実際の値が必要になるたびにバイナリデータを解析する必要があり、パフォーマンスの点で非常にコストがかかります。

メモリーフットプリント(データ量はそれほど多くない)についてはあまり心配していませんが、パフォーマンスについては心配していることを考えると、そのようなデータを保存する正しい方法は何でしょうか?

理想的には、ブーストの使用は避けたいと思います。ブーストを使用すると、最終的なアプリのサイズが3倍になり、それを最小限に抑える必要があるためです。

4

3 に答える 3

5

差別された(またはタグ付けされた)共用体を探しています。

Boost.Variantはその一例であり、Boost.Anyは別の例です。Boostによって最終的なアプリのサイズが3倍になると確信していますか?バリアントはヘッダーのみだと思っていたでしょう。その場合、ライブラリをリンクする必要はありません。

Boostを実際に使用できない場合は、単純な識別された共用体を実装することはそれほど難しくありません(一般的で完全に正しいものは別の問題です)。少なくとも、今何を検索すればよいかはわかっています。


完全を期すために、素朴な識別された共用体は次のようになります。

class DU
{
public:
    enum TypeTag { None, Int, Double };
    class DUTypeError {};
private:
    TypeTag type_;
    union {
        int i;
        double d;
    } data_;

    void typecheck(TypeTag tt) const { if(type_ != tt) throw DUTypeError(); }
public:
    DU() : type_(None) {}
    DU(DU const &other) : type_(other.type_), data_(other.data_) {}
    DU& operator= (DU const &other) {
        type_=other.type_; data_=other.data_; return *this;
    }

    TypeTag type() const { return type_; }
    bool istype(TypeTag tt) const { return type_ == tt; }

#define CONVERSIONS(TYPE, ENUM, MEMBER) \
    explicit DU(TYPE val) : type_(ENUM) { data_.MEMBER = val; } \
    operator TYPE & () { typecheck(ENUM); return data_.MEMBER; } \
    operator TYPE const & () const { typecheck(ENUM); return data_.MEMBER; } \
    DU& operator=(TYPE val) { type_ = ENUM; data_.MEMBER = val; return *this; }

    CONVERSIONS(int, Int, i)
    CONVERSIONS(double, Double, d)
};

現在、いくつかの欠点があります。

  • 非PODタイプをユニオンに保存することはできません
  • タイプを追加するということは、列挙型とユニオンを変更し、新しい行を追加すること忘れないことを意味しCONVERSIONSます(マクロがないとさらに悪いことになります)
  • これでビジターパターンを使用することはできません(または、独自のディスパッチャーを作成する必要があります)。これは、クライアントコードに多くのswitchステートメントがあることを意味します。
    • タイプを追加する場合は、これらのスイッチのすべてを更新する必要がある場合もあります
    • ビジターディスパッチを作成した場合、タイプを追加する場合は更新が必要です。すべてのビジターも更新する必要があります
  • これらを使って算術演算のようなことをしたい場合は、組み込みのC ++型変換ルールのようなものを手動で再現する必要があります(つまり、処理するだけでなく、すべての演算子を手動でロールする場合にのみoperator double、プロモートできます)IntDouble
  • operator==スイッチが必要なため、正確には実装していません。タイプが一致する場合、2つの和集合を単にmemcmpすることはできません。これは、doubleに必要な余分なスペースが異なるビットパターンを保持している場合、同一の32ビット整数が異なる比較を行う可能性があるためです。

これらの問題のいくつかは、気にかければ対処できますが、それ以上の作業です。したがって、回避できるのであれば、この特定のホイールを再発明しないことを好みます。

于 2013-01-03T16:35:33.087 に答える
2

あなたのデータ型は固定されているので、このようなものはどうですか...

値のタイプごとにstd::vectorのようなものを用意します。そして、マップはペアの2番目の値としてデータへのインデックスを持ちます。

std::vector<int> vInt;
std::vector<float> vFloat;
.
.
.

map<std::string, std::pair<ValidType, int>> Data;
于 2013-01-03T16:42:13.433 に答える
0

タイプキーによるアクセスを可能にするC++11のstd::tupleの優れた機能を活用することで、マルチタイプマップを実装できます。これをラップして、任意のキーによるアクセスを作成できます。これについての詳細な説明(そして非常に興味深い読み物)はここにあります:

https://jguegant.github.io/blogs/tech/thread-safe-multi-type-map.html

最新のC++機能は、古い問題を解決するための作成方法を提供します。

于 2016-02-06T13:35:54.270 に答える