我想写这样的东西:
template <typename E, E fallback>
E safe_cast_to_enum(std::underlying_type_t<E> e);
对于枚举类(或只是枚举?)E,将
e
转换为 E
的对应值(如果存在这样的值),否则转换为 fallback
值。
我可以一般地这样做吗?
备注:
fallback
是枚举的“有效”值,即它具有关联的命名标识符。另请参阅:
让我们创建一个查找枚举名称的函数:
template<auto value>
requires std::is_enum_v<std::decay_t<decltype(value)>>
consteval std::string_view enum_name() {
constexpr static auto location=std::source_location::current();
constexpr static std::string_view full_name= location.function_name();
constexpr static closeb = full_name.rfind('>');
constexpr static auto openb = full_name.find("<", 0, closeb);
return full_name.substr(openb, closeb - openb);
};
接下来我们必须确保上述函数的结果不是转换表达式。这部分很大程度上取决于平台:
constexpr bool is_enum_name(std::string_view name){
if (auto brace = name.find_last_of("})"); brace==name.npos)
return true;
else
return name.find(':',brace)!=name.npos;
};
如果没有大括号,转换表达式是不可能的。如果在最后一个右大括号之后有一个范围运算符(
::
),那么它必须是 enum class
名称限定符的一部分;否则就发生了转换。
接下来我们需要将名称和验证器放入 constexpr 数组中。我们现在可以做不同的事情;我选择存储原始值:
template<auto value>
requires std::is_enum_v<std::decay_t<decltype(value)>>
using enum_limits =
std::numeric_limits<
std::underlying_type_t<
std::decay_t< decltype(value)
>>>;
template<auto value>
requires std::is_enum_v<std::decay_t<decltype(value)>>
constexpr auto enum_strings = []<std::size_t ...i>{
using value_type = std::decay_t<decltype(value)>;
return std::array{
enum_name<
value_type{
static_cast<
std::underlying_type_t<
value_type>(i)
}>()...};
}( std::make_index_sequence
< std::min(1<<14
, std::size_t
{ std::enum_limits<value>::max()
- std::enum_limits<value>::min()
})>);
现在您可以根据自己的喜好使用上面的 LUT。也可以创建一个
bool
的验证器数组,但我暂时保留该数组。也可以使用 std::optional
或存储空 string_view
来表示无效值。
template<auto value>
requires std::is_enum_v<std::decay_t<decltype(value)>>
constexpr decltype(value)
to_enum( std::underlying_type_t
< std::decay_t
< decltype(value)>> num){
if (std::bit_cast<std::size_t>(num)
auto idx = static_cast<std::size_t>(num)
- enum_limits<value>::min();
return is_enum_name
( enum_string[idx] )
? decltype(value){num}
: value;
};
我还没有对此代码进行测试;所以可能存在很多错误和错误。当仅包含头文件的库作为答案时会发生这种情况。但它可以提供关于如何反映有关枚举的一些基本元数据的想法。这也相当丑陋,需要大量重构以提高可读性。