我正在https://bitbucket.org/chopsii/typesafe-enums
编写自己的类型安全枚举头文件库这个想法是替换非类型安全的c风格的枚举,如:
enum ItemCategory
{
BLOCK,
WEAPON
};
使用适当类型安全的东西。
到目前为止,我的解决方案使用了一个宏,对于一个与上面的枚举相当的示例,它看起来像这样:
TypesafeEnum(ItemCategory,
(BLOCK)
(WEAPON)
);
并扩展为看起来像这样的东西:
template<typename InnerType>
class Wrapped {
public:
InnerType getValue() const { return _val; }
bool operator<(const Wrapped<InnerType>& rhs) const { ; return _val < rhs._val; }
bool operator>(const Wrapped<InnerType>& rhs) const { ; return _val > rhs._val; }
bool operator==(const Wrapped<InnerType>& rhs) const { ; return _val == rhs._val; }
private:
InnerType _val;
protected:
explicit Wrapped<InnerType>(const InnerType& val) : _val(val) {}
void setValue(const InnerType& val) { _val = val; }
};
class WrappedTypeItemCategory : private Wrapped<int>
{
private:
typedef const std::string* strptr;
typedef const std::string* const cstrptr;
explicit WrappedTypeItemCategory(const std::string& label, int val): Wrapped<int>(val), str(&label)
{}
cstrptr str;
public:
static WrappedTypeItemCategory make(const std::string& label, int val)
{
return WrappedTypeItemCategory(label, val);
}
void operator=(const WrappedTypeItemCategory& rhs)
{
;
setValue(rhs.getValue());
const_cast<strptr>(str) = rhs.str;
}
int getValue() const
{
return Wrapped<int>::getValue();
}
const std::string& getString() const
{
return *str;
}
bool operator<(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() < rhs.getValue();
}
bool operator>(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() > rhs.getValue();
}
bool operator==(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() == rhs.getValue();
}
friend std::ostream & operator<<(std::ostream &os, const WrappedTypeItemCategory& rhs)
{
;
return os << *rhs.str << "(" << rhs.getValue() << ")";
}
};
;
namespace {
template<typename T> class ItemCategoryInner : public TypesafeEnumBase
{
public:
static const WrappedTypeItemCategory BLOCK;
static const WrappedTypeItemCategory WEAPON;
static const std::string BLOCKStr;
static const std::string WEAPONStr;
};
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::BLOCK = WrappedTypeItemCategory::make(ItemCategoryInner<T>::BLOCKStr, 0);
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::WEAPON = WrappedTypeItemCategory::make(ItemCategoryInner<T>::WEAPONStr, 1);
template<typename T> const std::string ItemCategoryInner<T>::BLOCKStr("ItemCategory::BLOCK");
template<typename T> const std::string ItemCategoryInner<T>::WEAPONStr("ItemCategory::WEAPON");
struct ItemCategoryTemplateConstantTrick
{};
};
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
{
private:
const WrappedTypeItemCategory* const val;
public:
class InvalidValueError : public std::runtime_error
{
public:
const int val;
InvalidValueError(int val): std::runtime_error(std::string("Invalid value given for ") + "ItemCategory::make"), val(val)
{}
};
ItemCategory(const WrappedTypeItemCategory& value): val(&value)
{}
void operator=(const ItemCategory& rhs)
{
const_cast<const WrappedTypeItemCategory*>(val) = rhs.val;
}
static ItemCategory make(const int& val)
{
if (val == ItemCategory::BLOCK.getValue()) return ItemCategory(ItemCategory::BLOCK);
if (val == ItemCategory::WEAPON.getValue()) return ItemCategory(ItemCategory::WEAPON);
;
throw InvalidValueError(val);
}
const WrappedTypeItemCategory* const getWrappedValue() const
{
return val;
}
int getValue() const
{
return val->getValue();
}
const std::string & getString() const
{
return val->getString();
}
bool operator<(const ItemCategory& rhs) const
{
return *val < *rhs.val;
}
bool operator>(const ItemCategory& rhs) const
{
return *val > *rhs.val;
}
bool operator==(const WrappedTypeItemCategory& rhs) const
{
return *val == rhs;
}
bool operator!=(const WrappedTypeItemCategory& rhs) const
{
return !(*val == rhs);
}
bool operator<=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val < rhs);
}
bool operator>=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val > rhs);
}
void print(std::ostream& os) const override
{
os << *val;
}
friend std::ostream & operator<<(std::ostream &os, const ItemCategory& rhs)
{
rhs.print(os);
return os;
}
};
;
如果我手动预扩展它,就像我在这里做的那样 - 通过预编译到文件 - 然后intellisense处理它直到所说的行:
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
此时它开始认为ItemCategoryInner
和ItemCategoryTemplateConstantTrick
不明确,以及文件的每几行都有许多其他内容。
包含此代码的标头包含在许多地方。我知道我违反了One Definition规则,这就是为什么我使用模板常量技巧,但我认为我需要违反ODR,因为我的目标是为C ++枚举提供一个易于使用的基于宏的类型安全替换。
我不确定是否违反ODR是导致我的问题或其他原因。我尝试了__declspec(selectany),但它似乎没有帮助 - 我更希望这个宏最终是跨平台的,因为如果它成功了,我还有其他项目我会用它。
无论哪种方式,使用枚举的.cpp文件等都可以,并且智能感知正确地建议选项。
然而,在一个可能相关的说明中,如果我不预先扩展宏,智能感知不能解析它,它根本不知道ItemCategory
是什么,即使它编译并正常工作。
我只是希望我的intellisense能够与我的类型安全枚举一起正常工作 - 它会减慢智能感知并将其混淆在同一项目中的其他代码中。