更多调试策略

使调试语句在整个程序中更容易禁用和启用的一种方法是使用预处理器指令将调试语句设置为条件式的。除了使用预处理器进行条件化调试之外,另一种方法是将调试信息发送到日志中。

在上一课中,我们开始探索如何手动调试问题。在本课程中,我们将继续探讨一些更多调试策略。

条件化你的调试代码

例如以下包含一些调试语句的程序:

#include <iostream>

int getUserInput()
{
std::cerr << "getUserInput() called\n";
	std::cout << "Enter a number: ";
	int x{};
	std::cin >> x;
	return x;
}

int main()
{
std::cerr << "main() called\n";
    int x{ getUserInput() };
    std::cout << "You entered: " << x << '\n';

    return 0;
}

完成调试语句后,您需要删除它们或将它们注释掉。然后,如果您稍后再次需要它们,则必须将它们添加回来,或取消注释它们。

使调试语句在整个程序中更容易禁用和启用的一种方法是使用预处理器指令将调试语句设置为条件式的:

#include <iostream>

#define ENABLE_DEBUG // 注释掉以禁用调试

int getUserInput()
{
#ifdef ENABLE_DEBUG
std::cerr << "getUserInput() called\n";
#endif
	std::cout << "Enter a number: ";
	int x{};
	std::cin >> x;
	return x;
}

int main()
{
#ifdef ENABLE_DEBUG
std::cerr << "main() called\n";
#endif
    int x{ getUserInput() };
    std::cout << "You entered: " << x << '\n';

    return 0;
}

现在,我们可以通过注释/取消注释 #define ENABLE_DEBUG 来启用调试。这使我们可以重用先前添加的调试语句,然后在完成调试时将其禁用,而无需实际从代码中删除它们。如果这是一个多文件程序,#define ENABLE_DEBUG 将放在一个包含在所有代码文件中的头文件中,这样我们可以在单个位置注释/取消注释 #define,并使其传播到所有代码文件。

这解决了必须删除调试语句以及这样做的风险的问题,但以更多的代码混乱为代价。这种方法的另一个缺点是,如果出现拼写错误(例如,拼错了 “DEBUG”)或忘记将头文件包含到代码文件中,某些或所有调试可能未启用。因此,尽管这比无条件化版本更好,但仍有改进的空间。

使用记录器

除了使用预处理器进行条件化调试之外,另一种方法是将调试信息发送到日志中。日志是事件的顺序记录,通常具有时间戳。生成日志的过程称为记录。通常,日志被写入磁盘上的文件(称为日志文件),以便稍后进行查看。大多数应用程序和操作系统都会写入日志文件,以帮助诊断出现的问题。

日志文件具有一些优势。由于写入日志文件的信息与程序的正常输出分开,因此可以避免由于混合正常输出和调试输出而导致的混乱。日志文件也可以轻松地发送给其他人以进行诊断 – 因此,如果使用您的软件的某人遇到问题,您可以要求他们将日志文件发送给您,这可能有助于为您提供线索,以确定问题所在。

C++ 包含一个名为 std::clog 的输出流,旨在用于写入日志信息。但是,默认情况下,std::clog 写入标准错误流(与 std::cerr 相同)。虽然您可以将其重定向到文件,但这是一个通常更好地使用现有的许多第三方记录工具之一的领域。您可以自行选择要使用的记录工具。

为了说明目的,我们将展示使用 plog 记录器输出的样子。Plog 是作为一组头文件实现的,因此您可以轻松地在需要的任何位置包含它,而且它轻巧且易于使用。

#include <plog/Log.h> // 第 1 步:包含记录器标头
#include <plog/Initializers/RollingFileInitializer.h>
#include <iostream>

int getUserInput()
{
	PLOGD << "getUserInput() called"; // PLOGD is defined by the plog library

	std::cout << "Enter a number: ";
	int x{};
	std::cin >> x;
	return x;
}

int main()
{
	plog::init(plog::debug, "Logfile.txt"); // 第二步:初始化记录器

	PLOGD << "main() called"; // 第三步:像写入控制台一样输出到日志

	int x{ getUserInput() };
	std::cout << "You entered: " << x << '\n';

	return 0;
}

以下是上述记录器的输出(在 Logfile.txt 文件中):

2018-12-26 20:03:33.295 DEBUG [4752] [main@14] main() called
2018-12-26 20:03:33.296 DEBUG [4752] [getUserInput@4] getUserInput() called

如何包含、初始化和使用记录器将根据您选择的特定记录器而有所不同。

请注意,使用这种方法也不需要条件编译指令,因为大多数记录器都有一种减少/消除写入日志的方法。这使得代码更容易阅读,因为条件编译行会添加大量杂乱。使用 plog,可以通过将初始化语句更改为以下内容来临时禁用日志记录:

plog::init(plog::none , "Logfile.txt"); // plog::none eliminates writing of most messages, essentially turning logging off

如果您想自己编译上面的示例,或者在自己的项目中使用 plog,您可以按照以下说明进行安装:

首先,获取最新的 plog 版本:

  • 访问 plog 存储库。
  • 单击右上角的绿色代码按钮,然后选择“下载 zip”

接下来,将整个存档解压缩到硬盘上的某个位置。

最后,对于每个项目,将 Something\plog-master\include\ 目录设置为 IDE 内的包含目录。

原创文章,作者:jkhxw,如若转载,请注明出处:https://www.jkhxw.com/more-debugging-tactics/

(0)
上一篇 3天前
下一篇 2天前

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注