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

C++ 在地图中存储不同的数据类型-包含类型信息,c++,types,map,C++,Types,Map,我需要解析和存储一个稍微复杂(但不是太复杂)的流,并且需要以某种方式存储解析的结果。流基本上包含名称-值对,其中值s对于不同的名称s可能具有不同的类型。基本上,我最终得到了一个键(总是字符串)到一对的映射 我是这样开始的: typedef enum ValidType {STRING, INT, FLOAT, BINARY} ValidType; map<string, pair<ValidType, void*>> Data; 然而,在这种情况下,每次需要实际值时,我

我需要解析和存储一个稍微复杂(但不是太复杂)的流,并且需要以某种方式存储解析的结果。流基本上包含名称-值对,其中
s对于不同的
名称
s可能具有不同的类型。基本上,我最终得到了一个
(总是字符串)到一对
的映射

我是这样开始的:

typedef enum ValidType {STRING, INT, FLOAT, BINARY} ValidType;
map<string, pair<ValidType, void*>> Data;
然而,在这种情况下,每次需要实际值时,我都必须解析二进制数据,这在性能方面是非常昂贵的

考虑到我不太担心内存占用(数据量不是很大),但我担心性能,存储此类数据的正确方式是什么


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

您正在寻找一个受歧视(或标记)的联盟

变体是一个例子,而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
    行(如果没有宏,情况会更糟)
  • 您不能将visitor模式用于此(或者,您必须为其编写自己的dispatcher),这意味着客户机代码中有很多switch语句
    • 如果添加类型,这些开关中的每一个都可能需要更新
    • 如果您确实编写了访客分派,那么如果您添加了类型,则需要更新该分派,每个访客也可能需要更新
  • <> LI>如果需要做任何类似算术的操作,则需要手动复制一些C++的类型转换规则(即,<代码>操作符双< /COD>可以促进<代码> int /COD>而不是仅处理<代码>双…但只有手动操作每个操作符)
  • 我没有实现操作符==,因为它需要一个开关。如果类型匹配,则不能仅memcmp这两个并集,因为如果double所需的额外空间包含不同的位模式,相同的32位整数仍然可以比较不同

如果你关心这些问题,其中一些问题是可以解决的,但这需要更多的工作。因此,如果可以避免的话,我倾向于不重新发明这个特殊的轮子。

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

对于每种类型的值都有类似于std::vector的内容。 您的映射将具有数据索引作为该对的第二个值

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

map<std::string, std::pair<ValidType, int>> Data;
std::vector-vInt;
std::向量vFloat;
.
.
.
地图数据;

您可以通过利用C++11中std::tuple的漂亮特性来实现多类型映射,该特性允许通过类型键进行访问。您可以将其包装以创建通过任意键的访问。关于这一点的深入解释(这是一篇非常有趣的文章)可以在这里找到:


<现代C++特性提供了解决旧问题的方法。

查看Booo:variant@leemes不使用boost?请注意我的最后一句话。您是否尝试过隔离
boost::variant
?@当我说“按系数3”时,我只是根据过去使用boost的经验,而不是boost::variant。如果boost::variant实际上只是一个标题,那么它可能就是我要寻找的答案。我将尝试并确认一下。只是出于兴趣(并冒着脱离主题的风险),哪个boost库做到了这一点?这次我决定使用boost::variant。顺便说一下,在64位linux上,我的可执行文件大小增加了大约50K。
std::vector<int> vInt;
std::vector<float> vFloat;
.
.
.

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