gpt4 book ai didi

c++ - 如何从异常中获取用户错误消息

转载 作者:太空狗 更新时间:2023-10-29 21:14:48 31 4
gpt4 key购买 nike

当我们在学校学习时,用户数据验证通常被公然忽略。我自学了使用异常机制来验证数据,但是当我尝试打印有用的用户消息时,从异常中做到这一点感觉非常笨拙。我尝试得越多,我就越觉得异常是“代码内部”的意思,它们不应该“逃逸”给最终用户。

举个简单的例子。用户需要输入命令。格式可以是:cmd i jcmd with cmd 一组预定义的命令以及 i j 数字。然后我们执行该命令。

我所拥有的,从下到上是这样的:

// utility function, converts string to enum
str_to_enum(string str, map<enum, string> s) -> enum
// may throw std::invalid_argument{"cannot convert " + str + " to enum"}

parse_line(string line)
tokens = split(line)

Cmd cmd = tokens[0]; // calls str_to_enum, lets the exception pass by

if (tokens.empty())
throw std::invalid_argument{"empty string as command"s};

if (...)
throw std::invalid_argument{cmd.spelling() + " does not take any argument."};

if (...)
throw std::invalid_argument{cmd.spelling() + " takes exactly 2 arguments."};

str_to_int(tokens[1])
str_to_int(tokens[2]) // throws std::out_of_range


main_loop() {
get_line(line);

try {
Full_cmd full_cmd = line; // calls parse_line, may throw

if (...)
throw std::invalid_argument{"Coordinates out of range"};

try {
execute_cmd(full_cmd); // may throw
}
catch (std::exception& e) {
cerr << "error executing command:" << endl;
cerr << e.what() << endl << endl;
}
}
catch (const std::exception& e) {
cerr << "Invalid command '"s << line << "': " << endl;
cerr << e.what() << endl;
}
}

我认为上面的逻辑很容易遵循。

现在,如果我只想显示神秘的“无效命令”和“执行命令时出错”,那么一切都会很简单。但我想要有意义的消息,就像我在上面尝试过的那样。一个问题是 e.what 对此感觉不合适。它包含简洁的,通常是技术细节。另一个问题是,例如错误“无法将 <input_string> 转换为枚举”到达用户。虽然他得到了他输入的真实字符串,但枚举部分是实现细节,对他来说很重要。

我看到的是 2 种解决方案:

  1. 使用 std::exception 错误。

    • 几乎可以在任何步骤中捕获错误并重新抛出重新处理的消息。例如。从“无法转换为枚举”到“找不到命令”。
    • 在最顶层捕获 std::exception 并基本上打印 e.what()
  2. 为每种类型的错误创建异常类(例如 invalid_commandinvalid_no_args 等。

  • 如有必要,向其中添加更多信息(其他数据成员)以获得错误的完整上下文
  • 一旦抛出异常,它就会冒泡到最顶层
  • 然后在最顶层,捕获每个自定义异常类型并相应地打印。

我显然采用了第一种方法。最大的缺点是用户收到的消息不完善。

对于第二个,我觉得付出与收获不成比例。仅仅为了创建自定义异常类就会有很多无聊的冗余工作。我尝试了一次,在第 7 个几乎相同的类之后放弃了每个完全配备的自定义数据成员和构造函数以及 what 方法(设置和使用这些成员)

此外,我忍不住觉得自己在重新发明轮子。

我的问题是:异常仅仅是向最终用户传达错误消息的好工具吗?如果是这样,正确的做法是什么?如果不行,怎么办?

最佳答案

第一种方法不是正确的方法,原因有二:

  1. 如您所见,它代表着巨大的工作量。

  2. 它假定程序员可能设法将可能对用户有意义的错误消息编码到异常对象中。他们不能;它不会。如果不是出于任何其他原因,那么至少因为从统计上讲,程序员编写消息所用的语言不太可能是用户可以理解的语言。 (我的意思主要是从语言的角度来看,尽管“技术水平”的角度也值得考虑。)

第二种方法方向正确,但有一些修改:

  1. 通过先发制人地检查错误并显示错误消息(如“我不会允许你这样做”)而不是“你做的很糟糕,它失败了”。

  2. 通过让每个异常都简单地代表“未能[做任何这个级别试图做的事情],显着减少您必须在系统的每个级别定义的异常类的数量因为某某异常被下面的级别抛出”。通过将“因果”异常的引用存储到新异常中,您不必在每个级别编写大量新异常,以及大量成员变量等。

  3. 意识到异常中人类可读的错误消息是完全无用的:永远不要写这样的消息,也永远不要向用户显示这样的消息。 异常的消息是异常的类名。如果您有 RTTI,(Run-Time-Type-Information,) 使用它。否则,只需确保每个异常对象都知道它自己的类的名称。这仅供您观看,当您看到它时,您明确知道它是哪个异常。

  4. 在您系统的顶层,向用户显示有关您可以测试的异常的错误消息。是的,这意味着您必须能够设置您的系统,以便实际抛出异常,以便您的测试代码可以确保捕获到异常并发出正确的错误消息。

  5. 对于您无法测试的异常,甚至不必费心显示消息。只需显示一个通用的“出错了”错误(可能还带有一个幽默的图片),并将尽可能多的信息附加到您的应用程序日志中,以便稍后进行取证分析。

关于c++ - 如何从异常中获取用户错误消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39840119/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com