为属性表创建调试器友好的枚举

时间:2013-01-15 16:29:33

标签: c++ enums

我的情况是我有一个类的层次结构:WidgetDoobry是从Base继承的对象类型(实际上有超过2种类型)。对象的每个实例都有一个属性列表。有些属性对所有对象都是通用的,并且某些属性特定于每个项类型。一个简单的实现可能如下所示:

enum PropertyType {
  COMMON_SIZE=0,              // first section: properties common to all
  COMMON_POSITION,
  ...
  WIDGET_PROPERTY_START=100,   // ensure enough space for expansion
  WIDGET_DONGLE_SIZE,
  WIDGET_TEXT,
  ...
  DOOBRY_PROPERTY_START=200
  DOOBRY_COLOUR
  ....
};

class Base {
public:
    Base();

    std::vector<std::pair<PropertyType, string>>  properties;
};

这实现了一个目标,即在调试器中我可以看到映射到有意义名称的属性列表。然而,它有一些缺点:

  • 所有项目的属性都在一个标题中定义(不适合封装)
  • 如果我们为其中一个类添加更多属性,我们必须为每个类的起始位置选择一些任意数字,以便为将来的扩展留出足够的空间。

我的问题是,是否有另一种实现这一目标的方法。一种想法是我可以使用字符串常量来存储更大,查找速度更慢,但其优点是更容易使名称唯一,每种项目类型都可以定义自己的属性。

编辑:   要求属性将被序列化,因此必须保持稳定(即枚举不会更改)。最多可能有1M个对象,但绝大多数都有空属性表(因为它们使用默认值)。查找性能比插入更重要,并且执行字符串哈希的性能可能可以忽略不计(我们无法测量它是否还没有写入它!)。

1 个答案:

答案 0 :(得分:1)

struct UniqueTag {
  friend TagManager;
  char const* tag;
  UniqueTag( UniqueTag const& other):tag(other.tag) {}
  UniqueTag():tag(nullptr) {}; // being able to create the null tag is useful
  bool operator<( UniqueTag const& other )const{ return tag < other.tag; }
  bool operator==( UniqueTag const& other )const{ return tag == other.tag; }
  // do other operators
private:
  UniqueTag( char const* t ):tag(t) {}
};

#include <map> // or unordered_map for performance
class TagManager {
  std::map<std::string, UniqueTag> cache;
  std::vector< std::unique_ptr< char[] > > data;
public:
  TagManager() {};
  UniqueTag GetTag( std::string s ) {
    auto range = cache.equal_range(s);
    if (range.first != range.second) {
      return range.first->second;
    }
    std::unique_ptr< char[] > str( new char[ s.size()+1 ] );
    std::copy( s.begin(), s.end(), &str[0] );
    str[s.length()] = '\0';
    UniqueTag retval( str.get() );
    data.push_back( std::move(str) );
    if(s.length()==0) {
      retval = UniqueTag(); // empty string is null tag, so we don't have both!
    }
    cache.insert( range.first, make_pair( std::move(s), retval ) );
    return retval;
  }
};

单个TagManager维护着一串对字符串的唯一指针。我们可以进行快速比较,因为我们比较指针值。从字符串转换为其中一个唯一标记的速度很慢,并且它具有隐含的单个标记管理器的反模式,但是......

替代版本包括让你的UniqueTag在自身旁边粘贴一个哈希,然后在哈希上查找(使用某种断言在调试中没有两个字符串哈希到相同的值 - 生日悖论使得发生的可能性远远超出人们的天真预期。这摆脱了单个管理器类(至少在发布时 - 在调试中,你有办法检测冲突。如果你的哈希是确定性的,调试中没有冲突可能意味着在发布时不会发生冲突)。 p>

具有适当可视化工具和一些运算符重载的boost::variant<enum1, enum2, enum3>可以让您拥有多个独立的enum。或者enum以上的家庭酿造联盟,主要enum表示哪个是有效的,并且在其上方有一个可视化工具,可以让您在整个地方拆分管理。在这两种情况下,您导出enum的“类型”索引,然后导出enum值 - 因此enum的顺序必须是稳定的,并且其中的值每个enum必须是稳定的,但不需要魔术整数。要检查是否相等,需要进行两个整数的链式比较,而不是一个(如果速度更快,您可以进行单个64位比较)。