Design patterns C++;你在用什么? 众所周知,C++中的内置枚举不是类型化的。 我想知道在那里使用了哪些实现类型安全枚举的类。。。 我自己使用以下“自行车”,但它有点冗长和有限:
typesafeenum.h:Design patterns C++;你在用什么? 众所周知,C++中的内置枚举不是类型化的。 我想知道在那里使用了哪些实现类型安全枚举的类。。。 我自己使用以下“自行车”,但它有点冗长和有限:,design-patterns,enums,enumeration,type-safety,c++03,Design Patterns,Enums,Enumeration,Type Safety,C++03,typesafeenum.h: struct TypesafeEnum { // Construction: public: TypesafeEnum(): id (next_id++), name("") {} TypesafeEnum(const std::string& n): id(next_id++), name(n) {} // Operations: public: bool operator == (const TypesafeEnum&
struct TypesafeEnum
{
// Construction:
public:
TypesafeEnum(): id (next_id++), name("") {}
TypesafeEnum(const std::string& n): id(next_id++), name(n) {}
// Operations:
public:
bool operator == (const TypesafeEnum& right) const;
bool operator != (const TypesafeEnum& right) const;
bool operator < (const TypesafeEnum& right) const;
std::string to_string() const { return name; }
// Implementation:
private:
static int next_id;
int id;
std::string name;
};
添加:
我想我应该更具体地说明要求。我试着总结一下:
优先级1:将枚举变量设置为无效值应该是不可能的(编译时错误),没有例外
优先级2:只需一个显式函数/方法调用,就可以将枚举值转换为int值或从int值转换为int值
优先事项3:尽可能紧凑、优雅、方便的声明和使用
优先级4:将枚举值转换为字符串或从字符串转换为枚举值
优先级5:(很高兴有)迭代枚举值的可能性。我没有。太多的开销几乎没有什么好处。此外,能够将枚举分类到不同的数据类型以进行序列化是一个非常方便的工具。我从未见过一个“类型安全”枚举值得在C++提供足够好的实现时的开销和复杂性。 < P>我认为java <代码>枚举> /Cord>是一个很好的模型。本质上,Java表单如下所示:
public enum Result {
OK("OK"), CANCEL("Cancel");
private final String name;
Result(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
BOOST_ENUM_VALUES(Level, const char*,
(Abort)("unrecoverable problem")
(Error)("recoverable problem")
(Alert)("unexpected behavior")
(Info) ("expected behavior")
(Trace)("normal flow of execution")
(Debug)("detailed object state listings")
)
Java方法的有趣之处在于OK
和CANCEL
是不可变的Result
的单例实例(使用您看到的方法)。您不能再创建Result
的任何实例。因为它们是单例,所以可以通过指针/引用进行比较——非常方便。:-)
ETA:在Java中,不是手动执行位掩码,而是使用
EnumSet
来指定位集(它实现set
接口,工作方式与集合类似,但使用位掩码实现)。比手写的位掩码操作更可读 一个很好的折衷方法是:
struct Flintstones {
enum E {
Fred,
Barney,
Wilma
};
};
Flintstones::E fred = Flintstones::Fred;
Flintstones::E barney = Flintstones::Barney;
与您的版本不同,它不是类型安全的,但是它的使用比标准枚举更好,并且您仍然可以在需要时利用整数转换。我在不同的主题上给出了答案。这是一种不同的方法,它允许大多数相同的功能,而不需要修改原始的枚举定义(从而允许在不定义枚举的情况下使用)。它还允许运行时范围检查
我的方法的缺点是,它没有以编程方式强制enum和helper类之间的耦合,因此它们必须并行更新。这对我来说是可行的,但YMMV。我的观点是,你在发明一个问题,然后在上面装配一个解决方案。我认为没有必要为枚举值创建一个复杂的框架。如果您致力于让您的值只成为某个集合的成员,那么您可以破解一个唯一集合数据类型的变体。我目前正在使用来自(filename
Enum\u rev4.6.zip
)的Boost.Enum建议。尽管它从未正式提交到Boost中,但它仍然可用。(缺少文档,但通过清晰的源代码和良好的测试来弥补。)
Boost.Enum允许您声明如下所示的枚举:
public enum Result {
OK("OK"), CANCEL("Cancel");
private final String name;
Result(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
BOOST_ENUM_VALUES(Level, const char*,
(Abort)("unrecoverable problem")
(Error)("recoverable problem")
(Alert)("unexpected behavior")
(Info) ("expected behavior")
(Trace)("normal flow of execution")
(Debug)("detailed object state listings")
)
并将其自动扩展为:
class Level : public boost::detail::enum_base<Level, string>
{
public:
enum domain
{
Abort,
Error,
Alert,
Info,
Trace,
Debug,
};
BOOST_STATIC_CONSTANT(index_type, size = 6);
Level() {}
Level(domain index) : boost::detail::enum_base<Level, string>(index) {}
typedef boost::optional<Level> optional;
static optional get_by_name(const char* str)
{
if(strcmp(str, "Abort") == 0) return optional(Abort);
if(strcmp(str, "Error") == 0) return optional(Error);
if(strcmp(str, "Alert") == 0) return optional(Alert);
if(strcmp(str, "Info") == 0) return optional(Info);
if(strcmp(str, "Trace") == 0) return optional(Trace);
if(strcmp(str, "Debug") == 0) return optional(Debug);
return optional();
}
private:
friend class boost::detail::enum_base<Level, string>;
static const char* names(domain index)
{
switch(index)
{
case Abort: return "Abort";
case Error: return "Error";
case Alert: return "Alert";
case Info: return "Info";
case Trace: return "Trace";
case Debug: return "Debug";
default: return NULL;
}
}
typedef boost::optional<value_type> optional_value;
static optional_value values(domain index)
{
switch(index)
{
case Abort: return optional_value("unrecoverable problem");
case Error: return optional_value("recoverable problem");
case Alert: return optional_value("unexpected behavior");
case Info: return optional_value("expected behavior");
case Trace: return optional_value("normal flow of execution");
case Debug: return optional_value("detailed object state listings");
default: return optional_value();
}
}
};
类级别:public boost::detail::enum\u base
{
公众:
枚举域
{
中止
错误,
警觉的,
信息,
跟踪,
调试,
};
升压静态常数(指数型,大小=6);
级别(){}
级别(域索引):boost::detail::enum_base(index){}
typedef boost::可选;
静态可选按名称获取(const char*str)
{
if(strcmp(str,“Abort”)==0)返回可选(Abort);
if(strcmp(str,“Error”)==0)返回可选(Error);
if(strcmp(str,“Alert”)==0)返回可选(Alert);
if(strcmp(str,“Info”)==0)返回可选(Info);
if(strcmp(str,“Trace”)==0)返回可选(Trace);
if(strcmp(str,“Debug”)==0)返回可选(Debug);
返回可选();
}
私人:
好友类boost::detail::enum_base;
静态常量字符*名称(域索引)
{
开关(索引)
{
案例中止:返回“中止”;
案例错误:返回“错误”;
案例预警:返回“预警”;
案例信息:返回“信息”;
案例跟踪:返回“跟踪”;
案例调试:返回“调试”;
默认值:返回NULL;
}
}
typedef boost::可选_值;
静态可选_值(域索引)
{
开关(索引)
{
案例中止:返回可选的_值(“不可恢复的问题”);
案例错误:返回可选_值(“可恢复问题”);
案例警报:返回可选_值(“意外行为”);
案例信息:返回可选_值(“预期行为”);
案例跟踪:返回可选的_值(“正常执行流”);
案例调试:返回可选的_值(“详细对象状态列表”);
默认值:返回可选的_值();
}
}
};
它满足您列出的所有五个优先级。我使用。我使用了一些帮助器模板/宏,它们提供了to/from字符串功能
enum class Result { Ok, Cancel};
不确定这篇文章是否太迟了,但GameDev.net上有一篇文章满足了除第5点(遍历枚举数的能力)之外的所有要求:
本文描述的方法允许对现有枚举进行字符串转换支持,而无需更改它们的代码。如果您只想支持新的枚举,我会选择Boost.Enum(如上所述)。我个人使用的是经过修改的版本。它并没有提供你在编辑中提到的所有五个“要求”,但我强烈反对其中的一些要求。例如,我看不出Prio#4(值到字符串的转换)如何具有
// This doesn't compile, and if it did it wouldn't work anyway
enum colors { salmon, .... };
enum fishes { salmon, .... };
// This, however, works seamlessly.
struct colors_def { enum type { salmon, .... }; };
struct fishes_def { enum type { salmon, .... }; };
typedef typesafe_enum<colors_def> colors;
typedef typesafe_enum<fishes_def> fishes;
if (colors::salmon == fishes::salmon) { .../* Ooops! */... }
// I'm using backports of C++11 utilities like static_assert and enable_if
template <typename Enum1, typename Enum2>
typename enable_if< (is_enum<Enum1>::value && is_enum<Enum2>::value) && (false == is_same<Enum1,Enum2>::value) , bool >
::type operator== (Enum1, Enum2) {
static_assert (false, "Comparing enumerations of different types!");
}
#ifndef _TYPESAFE_ENUMS_H
#define _TYPESAFE_ENUMS_H
#include <string>
#include <boost/variant.hpp>
#define ITEM(NAME, VAL) \
struct NAME##_t { \
std::string toStr() const { return std::string( #NAME ); } \
int toInt() const { return VAL; } \
}; \
static const NAME##_t NAME = NAME##_t(); \
template <typename T> \
bool is##NAME(const T & x) { if(boost::get<NAME##_t>(&x)) return true; return false; } \
class toStr_visitor: public boost::static_visitor<std::string> {
public:
template<typename T>
std::string operator()(const T & a) const {
return a.toStr();
}
};
template<BOOST_VARIANT_ENUM_PARAMS(typename T)>
inline static
std::string toStr(const boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> & a) {
return boost::apply_visitor(toStr_visitor(), a);
}
class toInt_visitor: public boost::static_visitor<int> {
public:
template<typename T>
int operator()(const T & a) const {
return a.toInt();
}
};
template<BOOST_VARIANT_ENUM_PARAMS(typename T)>
inline static
int toInt(const boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> & a) {
return boost::apply_visitor(toInt_visitor(), a);
}
#define ENUM(...) \
typedef boost::variant<__VA_ARGS__>
#endif
ITEM(A, 0);
ITEM(B, 1);
ITEM(C, 2);
ENUM(A_t, B_t) AB;
ENUM(B_t, C_t) BC;