替代C++异常

Ela*_*ich 14 c++ exception

我正在编写一个反应软件,它反复接收输入,处理它并发出相关输出.主循环看起来像:

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++的异常非常好,我可以processtry/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++额外功能越少,代码就越快,便携性也就越高.

我可以考虑一种替代设计吗?

更新: 我感谢大家对愚蠢代码标准的回答.我唯一可以说的是,在大型组织中,你必须有严格的,有时不合逻辑的规则,以确保没有白痴会来,让你的好代码不可维护.标准更多的是人而不是技术性.是的,坏人可以让每个代码都变得一团糟,但是如果你给他额外的工具来完成任务会更糟糕.

我还在寻找技术上的答案.

Mat*_* M. 8

我整天都在编写这类服务,我理解你的问题.虽然我们的代码中确实存在异常,但我们不会将它们返回到调用它的外部库,而是我们有一个简单的"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)

看起来很像一个指针,除了它的堆栈分配,因此涉及接近零开销.


Chr*_*isW 6

如果你不能throw例外,那么替代方案是return(或者到return false或类似的错误代码).

无论是抛出还是返回,您仍然使用C++确定性析构函数来释放资源.

你不能轻易地"返回"的一件事是构造函数.如果你在构造函数中有一个不可恢复的错误,那么这是一个抛出的好时机; 但是如果你不被允许扔掉,那么你必须返回,在这种情况下你需要一些其他的方式来表示施工失败:

  • 有私有构造函数和静态工厂方法; 使工厂方法在施工失败时返回null; 不要忘记在调用工厂方法时检查null返回
  • 有一个"get_isConstructedOk()"属性,你在每个构造函数之后调用它(并且不要忘记在每个新构造的对象上调用/检查它)
  • 实现"两阶段"构造:在其中你说任何可能失败的代码都不能在构造函数中,而必须bool initialize()在构造函数之后调用一个单独的方法(并且不要忘记调用initialize并且不要忘记检查它的返回值).


utn*_*tim 5

但是,在我们的编码标准中(在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,使您的代码更不优雅,更容易出错。