我正在编写一个反应软件,它反复接收输入,处理它并发出相关输出.主循环看起来像:
initialize();
while (true) {
Message msg,out;
recieve(msg);
process(msg,out);
//no global state is saved between loop iterations!
send(out);
}
Run Code Online (Sandbox Code Playgroud)
我想要在进程阶段发生的任何错误,无论是内存错误,逻辑错误,无效的断言等,程序都会清理它所做的任何事情,并继续运行.我假设它是无效输入,只是忽略它.
对于那种情况,C++的异常非常好,我可以process用try/catch子句包围,并且只要有问题就抛出异常.我唯一需要确保在抛出异常之前清理所有资源.这可以通过RAII验证,也可以通过编写全局资源分配器(例如,如果析构函数可能抛出异常)来验证,并专门用于所有资源.
Socket s = GlobalResourceHandler.manageSocket(new Socket());
...
try {
process(msg,out);
catch (...) {
GlobalResourceHandler.cleanUp();
}
Run Code Online (Sandbox Code Playgroud)
但是,在我们的编码标准中(也在Google的C++标准 BTW中)禁止使用异常,因此所有代码都是在异常关闭的情况下编译的,我相信没有人会改变一切只为我的设计问题工作的方式.
此外,这是嵌入式平台的代码,因此我们使用的C++额外功能越少,代码就越快,便携性也就越高.
我可以考虑一种替代设计吗?
更新: 我感谢大家对愚蠢代码标准的回答.我唯一可以说的是,在大型组织中,你必须有严格的,有时不合逻辑的规则,以确保没有白痴会来,让你的好代码不可维护.标准更多的是人而不是技术性.是的,坏人可以让每个代码都变得一团糟,但是如果你给他额外的工具来完成任务会更糟糕.
我还在寻找技术上的答案.
我整天都在编写这类服务,我理解你的问题.虽然我们的代码中确实存在异常,但我们不会将它们返回到调用它的外部库,而是我们有一个简单的"tribool".
enum ReturnCode
{
OK = 0, // OK (there is a reason for it to be 0)
KO, // An error occurred, wait for next message
FATAL // A critical error occurred, reboot
};
Run Code Online (Sandbox Code Playgroud)
我必须说FATAL......非常特别.除了初始化之外,应用程序中没有任何返回它的代码路径(如果未正确初始化,则无法做很多事情).
C++在这里带来了很多RAII,因为它嘲笑多条返回路径并保证它所拥有的对象的确定性释放.
对于实际的代码检查,您可以简单地使用一些宏:
// Here is the reason for OK being 0 and KO and Fatal being something else
#define CHECK_RETURN(Expr) if (ReturnCode code = (Expr)) return code;
#define CHECK_BREAK(Expr) if (ReturnCode code = (Expr)) \
if (KO == code) break; else return code;
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它们:
CHECK_RETURN( initialize() )
while(true)
{
Message msg,out;
CHECK_BREAK( receive(msg) )
CHECK_BREAK( process(msg,out) )
CHECK_BREAK( send(out) )
}
Run Code Online (Sandbox Code Playgroud)
如上所述,真正的无赖是关于构造者.在这种情况下你不能拥有"普通"的构造函数.
也许你可以使用boost::optional,如果你不能,我真的建议重复功能.将其与系统工厂功能结合起来代替施工人员,你就可以去了:
boost::optional<MyObject> obj = MyObject::Build(1, 2, 3);
if (!obj) return KO;
obj->foo();
Run Code Online (Sandbox Code Playgroud)
看起来很像一个指针,除了它的堆栈分配,因此涉及接近零开销.
如果你不能throw例外,那么替代方案是return(或者到return false或类似的错误代码).
无论是抛出还是返回,您仍然使用C++确定性析构函数来释放资源.
你不能轻易地"返回"的一件事是构造函数.如果你在构造函数中有一个不可恢复的错误,那么这是一个抛出的好时机; 但是如果你不被允许扔掉,那么你必须返回,在这种情况下你需要一些其他的方式来表示施工失败:
bool initialize()在构造函数之后调用一个单独的方法(并且不要忘记调用initialize并且不要忘记检查它的返回值).但是,在我们的编码标准中(在Google的C ++标准BTW中)也禁止使用异常。有没有我可以考虑的替代设计?
简短的答案是否定的。
长答案是:)。您可以使所有函数返回错误代码(类似于Microsoft的COM平台的实现。
这种方法的主要缺点是:
您必须明确处理所有例外情况
您的代码大小急剧增加
代码变得更加难以阅读。
代替:
initialize();
while (true) {
Message msg,out;
recieve(msg);
process(msg,out);
//no global state is saved between loop iterations!
send(out);
}
Run Code Online (Sandbox Code Playgroud)
你有:
if( !succeedded( initialize() ) )
return SOME_ERROR;
while (true) {
Message msg,out;
if( !succeeded( RetVal rv = recieve(msg) ) )
{
SomeErrorHandler(rv);
break;
}
if( !succeeded( RetVal rv = process(msg,out) ) )
{
SomeErrorHandler(rv);
break;
}
//no global state is saved between loop iterations!
if( !succeeded( RetVal rv = send(out) ) )
{
SomeErrorHandler(rv);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
此外,所有函数的实现都必须做同样的事情:在每个函数调用前都加上一个if。
在上面的示例中,您还必须确定每次迭代中的rv值是否构成当前函数的错误,并(最终)直接从中返回while,或者在出现任何错误时中断while并返回该值。
简而言之,除了可能在代码和模板中使用RAII(是否允许使用它们?)外,您最终都会接近“使用C ++编译器的C代码”。
您的代码将每个函数从两行转换为八行,依此类推。您可以通过使用额外的函数和#defined宏来改善此问题,但是宏必须具有自己的特质,因此必须非常小心。
简而言之,您的编码标准使您的代码不必要地更长,更容易出错,更难以理解且更难以维护。
这是向您公司中负责编码标准的任何人介绍的好案例:(
编辑:您也可以使用信号来实现这一点,但是它们是异常的坏替代品:它们与异常的作用相同,只是它们还完全禁用了RAII,使您的代码更不优雅,更容易出错。
| 归档时间: |
|
| 查看次数: |
3847 次 |
| 最近记录: |