我在 C++ API 中看到的通知错误的方法是通过特殊的返回代码和函数返回最后一个错误字符串,如果方法返回错误代码,可以检查该字符串。
有时这样做是有充分理由的,但更常见的是,当您看到 C++ 库包装了较旧的 C 库、由更熟悉 C 的人编写、为更熟悉 C 的客户端编码人员编写或为互操作性而编写时与 C.
从函数返回一个整数,整个整数值范围对返回值都有效,所以不能返回错误码。
选项包括:
例外情况
以更宽的类型返回(例如,getc() 返回 int,-1 表示 EOF)。
在值旁边返回一个成功标志,包裹在boost::optional、pair、tuple 或struct
至少具有调用者拥有的成功标志和/或值之一,并作为非const 引用或指针参数指定给函数
这是 C++ 中可接受的异常用法吗?
听起来不错,但艺术在于权衡利弊,我们不知道对于调用您的函数的客户端代码是否最方便和健壮。了解他们的关键期望,这将部分基于他们的整体 C++ 经验,但也来自您的 API 的其余部分和与您一起提供的任何其他 API,甚至来自他们可能使用的其他库的其他 API相同的应用程序等。
还要考虑函数的调用者是否可能希望在调用上下文中处理该函数的成功或失败,与其他可能的失败分开。例如,有时客户端代码更容易使用返回布尔成功值的函数:
if (fn(1) && !fn(2))
fn(3);
try
{
fn(1);
try
{
fn2();
}
catch (const ExpectedExceptionFromFn2Type&)
{
fn3();
}
}
catch (const PossibleExceptionFromFn1Type&)
{
// that's ok - we don't care...
}
但有时例外情况会更容易:
try
{
My_X x { get_X(99) };
if (x.is_happy(42))
x += next_prime_after(x.to_int() * 3);
}
catch (std::exception& e)
{
std::cerr << "oops\n";
}
...相比...
bool success;
My_X x;
if (get_X(&x, 99)) {
if (x.is_valid() {
bool happy;
if (x.can_get_happy(&happy, 42) && happy) {
int my_int;
if (x.can_convert_to_int(&my_int)) {
if (!x.add(next_prime_after(x.to_int() * 3))) {
std::cerr << "blah blah\n";
return false;
} else { cerr / return false; }
} else { cerr / return false; }
} else { cerr / return false; }
} else { cerr / return false; }
} else { cerr / return false; }
(到底有多糟糕取决于函数是否支持报告错误,或者是否可以信任它总是可以工作。这也很困难,因为发生了某些事情使得函数可能开始失败(例如,它开始使用可能丢失的数据文件),如果客户端代码尚未接受并检查错误代码或捕获异常,则一旦识别出潜在的错误,则可能需要重新处理该客户端代码。对于异常而言,情况并非如此,这- 如果你很幸运 - 可能会传播到一些已经合适的客户端 catch 语句,但另一方面,如果至少不关注客户端代码,这是一个冒险的假设。)
一旦您考虑了您所了解的有关客户端使用的一切,可能仍会怀疑哪种方法最好:通常您可以在整个 API 中选择并坚持使用它,但有时您可能希望提供多个版本一个函数,例如:
bool can_authenticate();
void authenticate_or_throw();
...或...
enum Errors_By { Return_Value, Exception };
bool authenticate(Errors_By e) { ... if (e == Exception) throw ...; return ...; }
...或...
template <class Error_Policy>
struct System
{
bool authenticate() { ... Error_Policy::return_or_throw(...); }
...
}
另外,在我的 API 中的某些函数(例如authenticate())中,我有两个选择。
如上所述,您有两个以上的选择。无论如何,一致性非常很重要。听起来例外是合适的。
提到使用 C++ 异常来控制程序流是不好的
这正是异常的作用和它们的全部用途,但我明白你的意思。最终,取得适当的平衡是使用了许多其他软件的艺术,考虑到您的客户将与您一起使用的其他库等。也就是说,如果某事在某种意义上是“错误”,那至少是合理考虑例外情况。