我需要解析并存储一些(但不是太)复杂的流,并且需要以某种方式存储解析的结果。该流本质上包含名称 - 值对,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倍,我需要将其最小化。
答案 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)
};
现在,有几个缺点:
CONVERSIONS
行(如果没有宏,情况会更糟)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 ++功能提供了解决旧问题的方法。