删除枚举元素的重复
Remove duplication of enumerated elements
我有以下枚举器,它可能会在程序开发过程中得到扩展:
enum myEnum {
Element1,
Element2,
Element3
...
ElementX
Last
};
我有一个按以下方式使用枚举器的函数:
bool CheckEnumValidity(myEnum a)
{
bool valid = false;
switch (a) {
case Element1:
case Element2:
case Element3:
case ...
case ElementX:
valid true;
break;
case Last:
valid false;
break;
};
return valid;
}
问题:
1) 我在程序的两个地方复制了 Element1
、Element2
等。如何以最安全的方式摆脱重复?
2) 我是否应该在上述 switch
语句中有 default
抛出异常(或 return false
)的行为,因为 CheckEnumValidity()
有myEnum
类型的参数?
备注:
C++ 11 不适用于我的应用程序。
如果您的枚举确实不包含任何显式赋值,那么您可以这样写:
if (a <= Last) {
return (a < Last);
} else {
throw AnyExceptionYouWant();
}
通过编码指南、同行压力、政策执行(解雇任何不遵守编码指南的程序员)或其他方式来确保仅使用您的 enum
的调用代码可能会更容易曾经提供命名值。
换句话说,不允许将整数值转换为枚举类型。毕竟,做这样的事情首先否定了使用枚举类型的大部分原因。
如果尽管有这个建议,你还是想测试,我会编写一个小程序来解析你的头文件,找到所有 enum
类型,并自动生成你的 SomeFunction()
。使用 makefile,很容易确保程序在相关头文件更改时 运行,这意味着函数将被更新、重新编译并链接到您的程序中,以保持检查与类型定义一致。
至于你的检查函数是否应该抛出异常,这归结为一个值未通过测试的后果。如果你的程序真的不应该继续,那么抛出一个异常。如果错误是良性的并且您的程序可以以某种方式继续,只需记录一条错误消息(例如 std::cerr
)并继续。
要回答您的第一个问题,在 C++ 中没有非常直接的方法来执行此操作,但我会根据您的问题发表评论,指出一些方法。
对于你的第二个问题,我建议你使用default
案例。这就是为什么。第一个原因比较弱,后两个比较强
- 有人可能会在不检查整数是否有效的情况下将整数显式转换为枚举值。这应该是被禁止的,但它有时仍然会发生,如果在代码审查中遗漏了这个编程错误,你应该在 运行 时发现它。
- 您可能会从不受信任的外部来源读取
struct
或其他数据,其中 struct
包含一个枚举字段,而忘记对其进行正确验证。不受信任的外部源甚至可以是使用旧版本程序保存的文件,其中枚举具有一组不同的有效值。
- 您可能在某处有未初始化的枚举。
甚至像这样简单的事情:
enum A {X = 1, Y, Z};
int main()
{
A foo;
switch (foo) {
case X: return 0;
case Y: return 1;
case Z: return 2;
}
}
至于在默认情况下你应该做什么,这取决于你的项目和具体的枚举。例如,如果枚举应该在进入你的大部分程序之前总是被验证,从而防止无效值,并且如果违反了它失败是可以的,那么你应该抛出一个异常或者甚至调用 exit
,在打印之后一条合适的错误消息——这是在 运行 时间捕获的编程失败。
如果这样的失败不是一个选项,您应该至少仍然尝试记录它,至少在调试版本中,这样您就可以检测到问题。
如果无效值对特定枚举有意义,则根据其有意义的原因,按照您认为适合该枚举的方式处理它。
我有以下枚举器,它可能会在程序开发过程中得到扩展:
enum myEnum {
Element1,
Element2,
Element3
...
ElementX
Last
};
我有一个按以下方式使用枚举器的函数:
bool CheckEnumValidity(myEnum a)
{
bool valid = false;
switch (a) {
case Element1:
case Element2:
case Element3:
case ...
case ElementX:
valid true;
break;
case Last:
valid false;
break;
};
return valid;
}
问题:
1) 我在程序的两个地方复制了 Element1
、Element2
等。如何以最安全的方式摆脱重复?
2) 我是否应该在上述 switch
语句中有 default
抛出异常(或 return false
)的行为,因为 CheckEnumValidity()
有myEnum
类型的参数?
备注:
C++ 11 不适用于我的应用程序。
如果您的枚举确实不包含任何显式赋值,那么您可以这样写:
if (a <= Last) {
return (a < Last);
} else {
throw AnyExceptionYouWant();
}
通过编码指南、同行压力、政策执行(解雇任何不遵守编码指南的程序员)或其他方式来确保仅使用您的 enum
的调用代码可能会更容易曾经提供命名值。
换句话说,不允许将整数值转换为枚举类型。毕竟,做这样的事情首先否定了使用枚举类型的大部分原因。
如果尽管有这个建议,你还是想测试,我会编写一个小程序来解析你的头文件,找到所有 enum
类型,并自动生成你的 SomeFunction()
。使用 makefile,很容易确保程序在相关头文件更改时 运行,这意味着函数将被更新、重新编译并链接到您的程序中,以保持检查与类型定义一致。
至于你的检查函数是否应该抛出异常,这归结为一个值未通过测试的后果。如果你的程序真的不应该继续,那么抛出一个异常。如果错误是良性的并且您的程序可以以某种方式继续,只需记录一条错误消息(例如 std::cerr
)并继续。
要回答您的第一个问题,在 C++ 中没有非常直接的方法来执行此操作,但我会根据您的问题发表评论,指出一些方法。
对于你的第二个问题,我建议你使用default
案例。这就是为什么。第一个原因比较弱,后两个比较强
- 有人可能会在不检查整数是否有效的情况下将整数显式转换为枚举值。这应该是被禁止的,但它有时仍然会发生,如果在代码审查中遗漏了这个编程错误,你应该在 运行 时发现它。
- 您可能会从不受信任的外部来源读取
struct
或其他数据,其中struct
包含一个枚举字段,而忘记对其进行正确验证。不受信任的外部源甚至可以是使用旧版本程序保存的文件,其中枚举具有一组不同的有效值。 - 您可能在某处有未初始化的枚举。
甚至像这样简单的事情:
enum A {X = 1, Y, Z};
int main()
{
A foo;
switch (foo) {
case X: return 0;
case Y: return 1;
case Z: return 2;
}
}
至于在默认情况下你应该做什么,这取决于你的项目和具体的枚举。例如,如果枚举应该在进入你的大部分程序之前总是被验证,从而防止无效值,并且如果违反了它失败是可以的,那么你应该抛出一个异常或者甚至调用 exit
,在打印之后一条合适的错误消息——这是在 运行 时间捕获的编程失败。
如果这样的失败不是一个选项,您应该至少仍然尝试记录它,至少在调试版本中,这样您就可以检测到问题。
如果无效值对特定枚举有意义,则根据其有意义的原因,按照您认为适合该枚举的方式处理它。