使用boost.Test的try catch块

3

我有一个(类成员)函数,希望避免因歧义导致应用程序崩溃。为此,我已添加了下面所示的try catch块:

void getGene(unsigned int position){
     T val;
     try {
        val =  _genome.at(_isCircular ? position % _genome.size() : position);
     }
     catch (std::exception& e) {
         std::cerr << "Error in [" << __PRETTY_FUNCTION__ << "]: " 
                   << e.what()  << std::endl;
         exit(1);
    }
    return val;
}

现在,我希望添加一个Boost单元测试,我的想法是这样做:
BOOST_AUTO_TEST_CASE(nonCircularGenome_test){

   // set size to 10
   test.setSize(10);
   // set non circular
   test.setNonCircular();    

   // gene at site # 12 does not exist in a 10-site long genome, must throw an exception
   BOOST_CHECK_THROW(test.getGene(12), std::out_of_range);

问题是,我无法同时让这两个功能正常工作。在发布设置中,try-catch块可以很好地工作。然而,如果我删除try-catch块并让函数抛出异常,则该测试仅在此情况下运行。

最好的方法是如何使这两个功能都正常工作,以便用户可以即时获得正确的错误提示,而测试则可以明确检查调试?一种方法是使用#ifdef/#endif DEBUG 块,但我希望避免预处理器宏。

提前致谢,

Nikhil

1个回答

3

您好,您似乎误解了异常的范围和目的 - 也许是关于错误处理的一般情况。首先,您应该定义函数的前提条件:是否始终期望getGene()position是有效的?它是否期望其客户端永远不会提供无效的位置?如果是这样,即使客户端是测试例程,提供无效位置的客户端也会违反与getGene()的合同(特别是它违反了前提条件),而且破坏合同本身就是未定义行为。您不能测试未定义行为,因此应该删除您的测试。

另一方面,如果您的函数具有广泛的合同,即允许客户端传递任何位置(甚至是无效的位置),并且(a)在位置无效时抛出异常或(b)返回错误代码以通知失败,则exit(1)行不应存在,因为您正在退出程序,控制权不会转移到调用者。

一种可能性是在记录诊断信息后重新抛出异常:

T getGene(unsigned int position){
    T val;
    try {
       val =  _genome.at(_isCircular ? position % _genome.size() : position);
    }
    catch (std::exception& e) {
        std::cerr << "Error in [" << __PRETTY_FUNCTION__ << "]: " 
                  << e.what()  << std::endl;

        throw;
//      ^^^^^
    }
    return val;
}

如果您不需要打印诊断信息,只需让异常自然传播即可:

T getGene(unsigned int position){
    return _genome.at(_isCircular ? position % _genome.size() : position);
}

+1,但有些细节...您可以有时测试未定义的行为,这取决于什么是未定义的行为。按照Lakos的说法,您可以测试未定义的行为(即对库的超出合同调用),但不能测试未定义的行为(即在库和/或语言中已经触发了未定义的行为)。事实上,有一个提案(n3604)在语言层面上增加对此的一些支持,而BSL则有一个库级别的实现。 - David Rodríguez - dribeas
@DavidRodríguez-dribeas:我必须承认,我读了那个有趣的提案太久了,已经记不清它的内容了。现在我有点困惑,所以我想问你,如果违反前置条件是未定义行为,那么通过违反该前置条件调用该函数将触发未定义行为,这意味着我们的测试断言不能期望一致的结果。我是否误解了这一点?或者我们的测试应该在通常未定义行为现在是明确定义行为的情况下运行(例如抛出异常)? - Andy Prowl
重点是,在您的函数中,您可以通过使用assert宏之一在函数本身导致其他地方出现未定义行为之前测试先决条件。Bloomberg / BSL框架和提案使触发main(无论如何,在代码的单个点)的断言时可以配置发生什么。现在在测试套件中(您可以检查BSL测试),您可以将assert处理程序配置为唯一异常,以此方式,您可以添加验证代码能够检测自己接口上的UB的测试[...] - David Rodríguez - dribeas
总结一下:接口狭窄且在超出合同调用时具有未定义的行为并不意味着您不能在函数本身引起UB之前检查输入,也不意味着您需要通过为所有可能的输入提供明确定义的行为来扩大合同。 - David Rodríguez - dribeas
我想我会回到我的if()循环中进行显式检查。你的建议对我不起作用,因为让错误原样传播会导致运行崩溃,用户会困惑于出了什么问题。顺便说一下,谢谢 - 尼克尔 - Nikhil J Joshi
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接