C++日志记录类实例标识符

C++ logging class instance identifier

本文关键字:实例 标识符 记录 日志 C++      更新时间:2023-10-16

在我的项目中,有些类被实例化了不止一次。每个类记录一些事件。日志记录方法是在项目周围使用的通用方法,它使用标准cout。日志消息包含时间、类的名称、方法的名称、变量值和自定义消息。

缺点是日志不是特定于实例的。我不知道该类的哪个实例编写了日志。

有没有一种很好的方法来解决此问题,而无需向类添加额外的静态成员作为实例计数器?我正在使用提升和 C++11。也许提升有一些可能会有所帮助的东西。

我能想到的唯一解决方案是将实例地址(this(包含在日志中。

您需要在某处区分不同的类,以下是一些选项(都依赖于某种 ID(:

  1. 添加一些额外的静态变量.
    不是您的选择,但在我看来,如果您的目标是可读性,这是最好的方法。
  2. 按照您的建议使用this
    这显然会生成唯一的 ID,但它们往往不可读且难以随着地址长度的增加而区分(因为在冗长的地址字符串中可能只有一个或两个字符不同(。此外,地址可能会在每次运行应用程序之间更改(尤其是在使用 ASLR 时(,因此您将无法看到哪个确切的实例创建了哪一行输出(如果需要(。
  3. 使用this并对值进行哈希处理

老实说,我认为与 2 没有太大区别(,但它可能会引发一些进一步的想法。一些丑陋的黑客可能看起来像这样:

#include <iostream>
#include <functional>
#include <cstddef>
class Logger
{
public:
static void log(void* ptr)
{
using hash_type = std::uintptr_t;
std::cout << std::hash<hash_type>{}(reinterpret_cast<hash_type>(ptr))
<< " logged something..." << std::endl;
}
};

您还可以考虑为类专门使用std::hash,并在日志记录输出中使用它。如果实施得当,这将消除在不同运行之间更改地址的问题。

    在构造过程中生成一个
  1. ID(例如,通过将一些标识符传递给构造函数(:
    这不是一个真正的选择,因为您没有证据或控制为每个类生成唯一 ID - 除非您使用某种可以访问私有构造函数或某种全局注册表的(抽象(工厂(见下文(。
  2. 使用一些帮助程序实用程序,在其中注册日志记录类并生成唯一 ID(类似于注册类(.
    出于简单的日志记录目的,恕我直言,这是臃肿的,与 1 相比不会增加任何值(。

结论:我会选择选项 1( 如果人类的可读性是值得关注的,以及 2( 如果您只是想要一些数字来区分日志消息(例如用于管道和过滤(。

您可以通过这样的CRTP 适配器轻松生成 ID(如果您可以稍微修改一下类(

template<typename T>
struct enable_id
{
int id = global_id++;
private:
static int global_id;
};
template<typename T>
int enable_id<T>::global_id = 0;
class foo : public enable_id<foo>
{
};
template<typename T>
void log(const T& t)
{
// if is_base_of
std::cout << t.id << std::endl;
}

修复 problom 的更简单方法是打印地址以指示它是哪个对象。 如下:

cout<<static_cast<void*>(this)<<endl;

或者输出一些功能。

我通常有一个受保护的类成员,其中包含我所有类层次结构的根目录下的实例创建时间,至少在调试阶段是这样。对多线程应用程序也非常有帮助。

将此时间转换为您想要的任何时间 - 字符串、毫秒、纪元时间 - 并且每个实例都有一个唯一的 ID。

如果只有几个类实例,我建议您引入一个字符串成员,并在对象创建期间使用唯一名称对其进行初始化。它将帮助您读取日志。默认情况下,可以使用对象的地址对其进行初始化。