【问题标题】:Validating input parameters验证输入参数
【发布时间】:2017-06-27 05:30:48
【问题描述】:

我正在尝试决定是否应该在我的某些代码中使用异常、断言或边界检查。

该程序包含大量依赖于用户输入的计算。该程序在嵌入式系统上运行。

下面的代码是一个典型的例子。参数 weight_percent_salt 显然不应该是负数,也不应该超过 100。

float Conductivity(const float weight_percent_salt)
{

    float sigma_20C = 0.0;

    if(weight_percent_salt > 16.2)
    {
        sigma_20C = 19.4;
    }
    else
    {   
        sigma_20C = 2.0487 * weight_percent_salt - 0.2352 * pow(weight_percent_salt,2) + 0.0187 * pow(weight_percent_salt,3);
    }

    return sigma_20C;   

}

在这种特殊情况下,我可以将用户输入限制在 0 - 100% 之间并保持代码不变,但其他功能并不直接依赖于用户输入,而是依赖于其他计算的结果。

在这些类型的函数中处理错误输入参数的最佳方法是什么?

【问题讨论】:

    标签: c++ exception testing design-by-contract


    【解决方案1】:

    不要强制您班级的用户例外,而是允许他们愿意的话。

    • 如果xweight_percent_salt 的有效值,则提供一个返回truevalidWeightValue(x) 函数。

    • Conductivity 假定weight_percent_salt 作为其合同的一部分是有效的。

    bool isValidWeightPercentSalt(const float x) noexcept
    {
        return x >= 0.f && x <= 100.f;
    }
    
    float Conductivity(const float weight_percent_salt)
    {
        assert(isValidWeightPercentSalt(weight_percent_salt));
        // ...
    }
    

    用户现在可以在其一侧使用isValidWeightPercentSalt 来验证输入并引发异常。用户知道Conductivity 假定其参数满足isValidWeightPercentSalt 谓词。

    float calculateConductivityViaUserInput()
    {
        float percent;
        std::cin >> percent;
    
        if(!isValidWeightPercentSalt(percent))
        {
            throw InvalidPercentUserInput{percent};
        }
    
        return Conductivity(percent);
    }
    

    请注意,我不喜欢异常 - 如果您的设计允许,请返回 monadic 错误处理 类,例如 std::optionalstd::variantboost::expected,而不是抛出异常.或者,如果您没有与错误条件相关的任何额外状态,请考虑使用 返回代码enum class

    【讨论】:

    • 这看起来是个不错的方法。在我的情况下,用户输入将通过使用 Modbus RTU 的串行端口进行,因此如果输入数据超出指定值,我将只返回错误代码而不是引发异常。谢谢你的回复!
    • @Q-bertsuit:是的,如果你能侥幸逃脱,一定要避免例外。 std::optional、错误代码和类似的解决方案在我看来更加健壮,因为它们利用类型系统并强制用户处理它们。
    • 很好,我会研究 std::optional,尽管这个特定项目的编译器甚至不支持 C++11。
    【解决方案2】:

    断言: 这些通常仅对调试版本启用,并且会导致崩溃。由于它们通常在发布版本中被禁用,因此您可能需要跟进另一种引发错误的机制;因为他们的主要任务是向开发人员表明程序的状态是开发人员认为不可能的状态。 显然对于用户输入,一切皆有可能,这不是一个合适的选择。

    (几乎)其他任何事情: 为调试和发布版本完成;因此,对于用户输入而言,这是更可取的。 “其他任何事情”到底是什么,取决于您计划如何处理失败,这在很大程度上是一个见仁见智的问题。

    也就是说,异常应该是“异常的”,因为它们不仅不会经常发生;而且这可能不适用于用户输入解析。

    【讨论】:

    • 问题是不可能预测错误的用户输入数据的每一个组合,我必须有一些方法来报告哪里出了问题。如果我不能使用异常(我同意应该为内存不足等情况保留异常),我会留下返回码,它们往往会弄乱我的代码。
    • 鉴于 Vittorio 强调了异常 - 我只想介绍断言;因为它们完全不适合检查用户输入。关于 throws,没有人说“不要使用”——而是“不要强制”——即你可以制作一个具有返回码的类的版本——然后是一个检查返回码和 throws 的包装类。 (或者甚至只是在课堂上放一个标志并根据它抛出)
    猜你喜欢
    • 1970-01-01
    • 2011-08-03
    • 2021-06-03
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-20
    • 1970-01-01
    相关资源
    最近更新 更多