2

我需要解析和存储一个有点(但不是太)复杂的流,并且需要以某种方式存储解析的结果。该流本质上包含名称-值对,其中values 可能对于不同name的 s 具有不同的类型。基本上,我最终得到了一个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;

然而,在这种情况下,每次需要实际值时我都必须解析二进制数据,这在性能方面会非常昂贵。

考虑到我不太担心内存占用(数据量不大),但我担心性能,那么存储这些数据的正确方法是什么?

理想情况下,我想避免使用 boost,因为这会使最终应用程序的大小增加 3 倍,如果不是更多的话,我需要将其最小化。

4

3 回答 3

5

您正在寻找一个受歧视(或标记)的工会。

Boost.Variant 是一个例子,Boost.Any 是另一个例子。您确定 Boost 会将您的最终应用程序大小增加 3 倍吗?我会认为variant 只是标题,在这种情况下你不需要链接任何库。

如果你真的不能使用 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可以提升 anInt而不仅仅是处理Double......但前提是你手动滚动每个运算符)
  • 我没有operator==精确地实现,因为它需要一个开关。如果类型匹配,您不能只 memcmp 这两个联合,因为如果双精度所需的额外空间包含不同的位模式,相同的 32 位整数仍然可以比较不同

如果您关心这些问题,其中一些问题可以得到解决,但还有更多工作要做。因此,如果可以避免的话,我宁愿不重新发明这个特定的轮子。

于 2013-01-03T16:35:33.087 回答
2

既然你的数据类型是固定的,那么像这样的东西呢......

每种类型的值都有类似 std::vector 的东西。并且您的地图将具有数据索引作为该对的第二个值。

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 回答