在C ++中具有用户可扩展类型参数的类型化类

时间:2012-08-10 16:32:27

标签: c++ enums shared-libraries

假设您正在编写一个库,为类提供某种“类型”参数。为此,可以使用枚举:

namespace MyLib {
  class Event {
  public:
    enum EventType { TYPE1, TYPE2, ... };
    Event(EventType _type) : type(_type) { }
  private:
    EventType type;
  }
}

然后实例化:

new MyLib::Event(Event::TYPE1);

到目前为止很好。但是,如果您希望用户能够扩展事件类型列表,该怎么办?如果type属性是枚举,则无法执行此操作。

低质量的可能性包括要求他们#define自定义事件名称,或者只是使用字符串,尽管这些似乎都不是最理想的。

这是否有一般解决方案?

一个建议是使用struct EventType返回枚举中指定范围内的值。但是,虽然这解决了编译器安全问题,但它没有解决添加命名的类型的问题 - 它需要用户将这些添加到全局范围。

解决后者而不是前一个问题的一种可能性是将typedef EventType设置为整数类型,并将其留给用户以将自定义类型添加到库的命名空间或它们自己的名称空间。提供唯一值的工厂方法可以作为Event:

的一部分提供
#include <iostream>
#include <vector>

namespace MyLib {

  namespace EventType {
    typedef int T;
    enum { TYPE1, TYPE2, Count};
  }

  class Event {
  public:
    Event(EventType::T _type) : type(_type) { }
    EventType::T type;
    static EventType::T registerType() { return _typeid++; }
  private:
    static int _typeid;
  };

}

MyLib::EventType::T MyLib::Event::_typeid = EventType::Count;

// The user can then add types, including to the library's namespace
// (which may or may not be a good idea)
namespace MyLib { namespace EventType {
  MyLib::EventType::T MYTYPE = MyLib::Event::registerType();
} }

int main() {
  MyLib::Event ev1(MyLib::EventType::TYPE2);
  MyLib::Event ev2(MyLib::EventType::MYTYPE);
  std::cout << ev1.type << std::endl;
  std::cout << ev2.type << std::endl;
  return 0;
}

输出:

1
2

虽然这在技术上并不将参数限制为已注册的类型集,但是typedef&amp; namespacing在IDE中的构造函数定义和自动建议中提供了一个有用的语法提示,它可以防止用户类型污染全局范围,这可能是更大的问题。

有没有更好的方法来指定有限的,编译器检查但用户可扩展的值集作为类的类型参数,或者通常指定为函数/方法?

3 个答案:

答案 0 :(得分:2)

我认为最好的办法是简单地将事件类型作为普通的整数类型,使用枚举来定义内置类型,并提供用户定义的最小值和最大值,用户在这些值之间创建自己的整数值有一个单独的调查员。然后你只需使用断言来确保基本事件不会尝试处理未知的事件类型。

编辑:请注意,基本事件类包含一些理解的类型和一些不理解的类型可能是脆弱的,除非它只是保留子类的数据以便稍后再次选择。即使在这种情况下,考虑替代设计可能更好。您能详细说明事件类型在设置后使用的方式吗?

EDIT2:我想我现在理解得更好,事件存储事件类型,事件消费者可以从事件中检索它。如果消费者理解扩展事件类型,它可以进行适当的处​​理,否则委托,断言或简单地禁止操作。鉴于此,我认为使用积分事件类型似乎很好。

答案 1 :(得分:0)

我会扩展枚举类型的空间,如下所示:

/* UNTESTED */
struct MyType {
 enum e { type1, type2, type3, max_type = type3, _big = max_type+256 };

 /** Acceptable values are 0-255 */
 static e UserType(int i) { return e(max_type + 1 + i); }
};

void f (MyType::e t) {}

int main () {
  f(MyType::type1);
  f(MyType::UserType(47));
  enum UserEnum { utype1, utype2, utype3 };
  f(MyType::UserType(utype3));
}

答案 2 :(得分:0)

您无法使用枚举,但您可以定义新类型。

c ++枚举与类相同:

struct Enum
{
  int value;
  explicit Enum(int v) : value(v) {}
  operator int () {return value;}
};

static const Enum value_1 = Enum(1);
static const Enum value_2 = Enum(2);
...

嗯,它不完全相同,有一些差异,比如你不能从枚举继承,枚举是整数类型,所以你可以将int转换为枚举,但这些差别不大。

如果您这样做,那么您可以通过简单地添加新常量将新值添加到“枚举”。

static const Enum custom_value_1 = Enum(100);
static const Enum custom_value_2 = Enum(200);

你需要使它们static,因为C ++是愚蠢的,并且即使它们可以用作编译时常量,也会分配资源。 static的替代方法是将它们放入匿名命名空间:

namespace
{
  const Enum custom_value_1 = Enum(100);
  const Enum custom_value_2 = Enum(200);
}

如果您不执行其中一项操作,则会出现链接器错误。此方法将为您提供相同类型的类型检查枚举,并且您可以在单独的头文件中扩展值。