Joh*_*ice 13 c++ enums templates design-patterns cross-platform
我正在研究消息解析器/生成器子系统.我正在创建一个自动生成器,它使用包含有关此协议的所有信息的数据库,包括枚举列表,以生成代码.我遇到的一件事是需要分层枚举.
更新
(我试图通过不描述完整的问题来简化事情,但下面的评论显然我错误地简化了太多.)
正在使用的数据库将存储为简化字符串(客户决策),但协议仅说"字节三元组"(又名Hierarchical Enum).完整的问题可以这样描述:
给定一组唯一的字符串,其各自对应一个独特的三重的,1)找到任何给定的字符串三重,和 2)找到任何给定的三元组的字符串.确保考虑"Undefined"和"No Statement"枚举(没有与之关联的字符串).[正如一张海报所说,是的,这是疯了.]
(警告:我已经做了十多年的C++,但去年我一直在做Java - 我的C++可能已"损坏"了.)
因此,使用一个公认的人为例子,给出:
// There is only one category
// POP= "P", COUNTRY= "K", CLASSICAL= "C"
enum Category {POP, COUNTRY, CLASSICAL};
// There is one Type enum for each Category.
// ROCK= "R", BIG_BAND = "B", COUNTRY_POP= "C"
enum PopType {ROCK, BIG_BAND, COUNTRY_POP};
enum CountryType {CLASSICAL_COUNTRY, MODERN_COUNTRY, BLUEGRASS, COUNTRY_AND_WESTERN};
// ...
// There is one Subtype for each Type
// EIGHTIES= "E", HEAVY_METAL= "H", SOFT_ROCK= "S"
enum RockSubType { EIGHTIES, HEAVY_METAL, SOFT_ROCK};
// ...
Run Code Online (Sandbox Code Playgroud)
当我得到0,0,0(Pop,Rock,Eighties)时,我需要将其转换为"PRE".相反,如果我在数据库中看到"PC",则需要将其作为0,2(Pop,Country,NULL)发送出去.
我公然忽略了"Undefined"和No Statement".从字符串生成三元组看起来很简单(使用无序映射,字符串为三元组).从三元组生成一个字符串(可能包含一个NULL)最后一个条目... ...没那么多.我所知道的大多数"枚举技巧"都不起作用:例如,类型重复值 - 每个类型枚举从零开始 - 所以我无法索引基于数组在Enum值上抓取字符串.
我得到的是这种关系.乍一看,这似乎是一个相当直接的"is-a"关系,但这不起作用,因为这种情况是双向的.leaf - > root导航非常简单,适用于类层次结构; 不幸的是,走另一条路并不是那么直截了当.
我不能"手动"这个 - 我必须生成代码 - 所以这可能会消除任何基于XML的解决方案.它也必须"相当快"."Java解决方案"涉及使用受保护的静态变量,在构造时初始化和抽象基类; 但是,我不相信这会在C++(初始化顺序等)中起作用.另外,在美学上,我觉得这应该是...更"常".我见过的解决此问题的其他代码使用了联合,明确列出了联合中的所有枚举类型.
我能想出的另一件事是使用模板专业化和明确的专业化,但我不知所措.我对此进行了网络搜索,但我发现没有什么可以告诉我它是否会起作用.但是,如果它可以通过联合完成,那么不能用模板专业化来完成吗?
是否可以使用模板,专业化,显式专业化来做这样的事情?是否有另一个更明显的解决方案(即我忘记的设计模式),我错过了?
哦,在我忘记之前 - 解决方案必须是便携式的.更具体地说,它必须适用于Windows(Visual Studio 2010)和Redhat Enterprise 6/Centos 6(GCC 4.4.4 IIRC).
而且,为免我遗忘,这个协议是巨大的.理论上最大值约为133,000个条目; 一旦我包含"Undefined"和"No Statement",我可能会有很多条目.
谢谢.
实际上,你在这里有点紧张.
我的建议意味着首先使用3个枚举:
在各种类型或子类型之间没有区别(起初)(我们只是将它们全部放在同一个篮子中).
然后,我只想使用一个结构:
struct MusicType {
Category category;
Type type;
SubType subtype;
};
Run Code Online (Sandbox Code Playgroud)
并定义一个简单set的有效类型:
struct MusicTypeLess {
bool operator()(MusicType const& left, MusicType const& right) const {
if (left.category < right.category) { return true; }
if (left.category > right.category) { return false; }
if (left.type < right.type) { return true; }
if (left.type > right.type) { return false; }
return left.subtype < right.subtype;
}
};
MusicType MusicTypes[] = {
{ Category::Pop, Type::Rock, SubType::EightiesRock },
...
};
// Sort it on initialization or define in sorted during generation
Run Code Online (Sandbox Code Playgroud)
然后您可以定义简单查询:
typedef std::pair<MusicType const*, MusicType const*> MusicTypeRange;
MusicTypeRange listAll() {
return MusicTypeRange(MusicTypes, MusicTypes + size(MusicTypes));
}
namespace {
struct MusicTypeCategorySearch {
bool operator()(MusicType const& left, MusicType const& right) const {
return left.category < right.category;
}
};
}
MusicTypeRange searchByCategory(Category cat) {
MusicType const search = { cat, /* doesn't matter */ };
return std::equal_range(MusicTypes,
MusicTypes + size(MusicTypes),
search,
MusicTypeCategorySearch());
}
namespace {
struct MusicTypeTypeSearch {
bool operator()(MusicType const& left, MusicType const& right) const {
if (left.category < right.category) { return true; }
if (left.category > right.category) { return false; }
return left.type < right.type;
}
};
}
MusicTypeRange searchByType(Category cat, Type type) {
MusicType const search = { cat, type, /* doesn't matter */ };
return std::equal_range(MusicTypes,
MusicTypes + size(MusicTypes),
search,
MusicTypeTypeSearch ());
}
// little supplement :)
bool exists(MusicType const& mt) {
return std::binary_search(MusicTypes, MusicTypes + size(MusicTypes), mt);
}
Run Code Online (Sandbox Code Playgroud)
因为数组是排序的,所以操作很快(log N),所以它应该顺利进行.
首先,感谢大家的帮助。由于这个问题的性质,我实际上无法“按原样”使用任何答案:
我最终找到了Boost bimap,事实证明bimap层次结构可以很好地解决这个问题。对于那些还没有见过它们的人来说,Boost“bimap”是一个双向容器,它使用一对作为键,另一个作为值。
我可以制作一个bimap“整数,字符串”(在本例中为 uint8_t,因为这里的枚举都保证很小),并添加“子枚举”作为与使用相关的bimap信息with_info。
层次结构代码如下所示:
// Tags
struct category_enum_value {};
struct type_enum_value {};
struct subtype_enum_value {};
struct category_string {};
struct music_type_string {};
struct music_subtype_string {};
struct music_type_info {};
struct music_subtype_info {};
// Typedefs
typedef bimap<
unordered_set_of< tagged<uint8_t, subtype_enum_value> >,
unordered_set_of< tagged<std::string, music_subtype_string> >
> music_subtype;
typedef music_subtype::value_type music_subtype_value;
typedef bimap<
unordered_set_of< tagged<uint8_t, type_enum_value> >,
unordered_set_of< tagged<std::string, music_type_string> >,
with_info< tagged<music_subtype, music_subtype_info> >
> music_type_type;
typedef music_type_type::value_type music_type_value;
typedef bimap<
unordered_set_of< tagged<uint8_t, category_enum_value> >,
unordered_set_of< tagged<std::string, category_string> >,
with_info< tagged<music_type_type, music_type_info> >
> category_type;
typedef category_type::value_type category_value;
Run Code Online (Sandbox Code Playgroud)
我选择unordered_set是出于性能原因。由于这严格来说是一个“常量”层次结构,因此我不必担心插入和删除时间。而且因为我永远不会比较顺序,所以我不必担心排序。
要通过枚举值获取类别信息(给定枚举时获取字符串值),我使用标签category_enum_value:
category_type::map_by<category_enum_value>::iterator cat_it = categories.by<category_enum_value>().find(category);
if(cat_it != categories.by<category_enum_value>().end())
{
const std::string &categoryString = cat_it->get_right();
// ...
Run Code Online (Sandbox Code Playgroud)
通过使用type_enum_value标签(子类型几乎相同),我可以从中获取适当的类型信息:
music_type_type &music_type_reference = cat_it->get<music_type_info>();
music_type_type::map_by<type_enum_value>::iterator type_it = music_type_reference.by<type_enum_value>().find(type);
if(type_it != music_type_reference.by<type_enum_value>().end())
{
// ... second verse, same as the first ...
Run Code Online (Sandbox Code Playgroud)
要获取给定字符串的枚举值,请将标记更改为category_string并使用与以前类似的方法:
std::string charToFind = stringToFind.substr(0, 1);
category_type::map_by<category_string>::iterator cat_it = categories.by<category_string>().find(charToFind);
if(cat_it != categories.by<category_string>().end())
{
retval.first = cat_it->get_left();
// ... and the beat goes on ...
Run Code Online (Sandbox Code Playgroud)
任何给定级别所需的任何附加信息(例如菜单项字符串)都可以通过将信息类型从 a 更改bimap为struct包含 abimap以及我可能需要的任何信息来添加。
由于这些都是常量值,因此我可以“预先”完成所有艰苦的工作并设计简单的查找函数 - O(1) - 以获得我需要的东西。
| 归档时间: |
|
| 查看次数: |
1894 次 |
| 最近记录: |