在地图中存储不同的数据类型 - 包含类型信息

时间:2013-01-03 16:20:49

标签: c++ types map

我需要解析并存储一些(但不是太)复杂的流,并且需要以某种方式存储解析的结果。该流本质上包含名称 - 值对,value可能是不同类型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倍,我需要将其最小化。

3 个答案:

答案 0 :(得分: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类型
  • 添加类型意味着修改枚举,而union,会记住添加新的CONVERSIONS行(如果没有宏,情况会更糟)
  • 你不能使用访问者模式(或者,你必须为它编写自己的调度程序),这意味着客户端代码中有很多switch语句
    • 如果添加类型
    • ,这些开关中的每一个都可能还需要更新
    • 如果您确实编写了访问者调度, 需要更新,如果您添加类型,那么每个访问者都可以
  • 你需要手动重现内置C ++类型转换规则之类的内容,如果你想对这些内容进行算术运算(即operator double可以提升Int而不是仅仅处理{ {1}} ......但只有当你手动滚动每个操作员时)
  • 我没有精确地实现Double,因为它需要切换。如果类型匹配,你不能只记忆两个联合,因为如果双精度所需的额外空间保持不同的位模式,相同的32位整数仍然可以比较不同

如果您关心它们,可以解决其中一些问题,但这些问题还有很多工作要做。因此,如果可以避免,我倾向于不重新发明这种特殊的轮子。

答案 1 :(得分:2)

由于你的数据类型是固定的,所以这样......

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

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

map<std::string, std::pair<ValidType, int>> Data;

答案 2 :(得分:0)

您可以通过利用C ++ 11中std :: tuple的漂亮功能来实现多类型映射,这允许通过类型键进行访问。您可以将其包装以通过任意键创建访问权限。这里有一个深入的解释(以及非常有趣的读物):

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

现代C ++功能提供了解决旧问题的方法。