Идентификатор экземпляра класса ведения журнала C++

В моем проекте экземпляры некоторых классов создаются более одного раза. Каждый класс регистрирует некоторые события. Метод ведения журнала является общим, который используется во всем проекте, и в нем используется стандартный cout. Сообщение журнала содержит время, имя класса, имя метода, значение переменной и настраиваемое сообщение.

Недостатком является то, что журнал не привязан к конкретному экземпляру. Я не знаю, какой экземпляр класса написал журнал.

Есть ли хороший способ решить эту проблему без добавления дополнительных статических членов в качестве счетчиков экземпляров в классы? Я использую boost и C++ 11. Может быть, у boost есть что-то, что может помочь.

Единственное решение, которое я могу придумать, - это включить адрес экземпляра (this) в журнал.

Как вы сказали, «это» помогает различать экземпляры, но если вы хотите точно знать, какой экземпляр что делает (особенно между разными запусками программы), вам придется как-то называть их во время создания (-> больше членов ).

Jari Komppa 24.05.2018 08:13

Итак, вы хотите различать объекты одного класса. В чем заключается отличительная черта и почему он не является (нестатическим) членом класса?

Sid S 24.05.2018 08:23

Поможет ли вам просто распечатать адрес экземпляра? Таким образом вы можете сказать, что два экземпляра, пишущие одну и ту же строку, разные.

Carlos 24.05.2018 11:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
491
5

Ответы 5

Более простой способ решить проблему - распечатать адрес, чтобы указать, какой это объект. Как следующее:

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

Или вывести какие-то особенности.

Обычно у меня есть защищенный член класса, содержащий время создания экземпляра в корне всех моих иерархий классов, по крайней мере, на этапе отладки. Также очень полезно для многопоточных приложений.

Преобразуйте это время во все, что захотите - строку, миллисекунды, время эпохи - и у вас будет уникальный идентификатор для каждого экземпляра.

Вам нужно будет различать разные классы где-то, это несколько вариантов (все зависят от какого-то идентификатора):

  1. Добавьте дополнительную статическую переменную. Не вариант для вас, но, на мой взгляд, лучший вариант, если вы стремитесь к удобочитаемости.
  2. Используйте this, как вы предложили:
    Это, очевидно, генерирует уникальные идентификаторы, но они будут, как правило, нечитаемыми и трудно различимыми при увеличении длины адреса (поскольку в длинной адресной строке могут отличаться только один или два символа). Кроме того, адреса, вероятно, будут меняться между каждым запуском приложения (особенно при использовании 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. Сгенерируйте идентификатор во время строительства (например, передав некоторый идентификатор конструктору):
    На самом деле это не вариант, поскольку у вас нет доказательств или контроля создания уникального идентификатора для каждого класса - если вы не используете какую-то (абстрактную) фабрику с доступом к частному конструктору или какому-либо глобальному реестру (см. Ниже).
  2. Используйте некоторую вспомогательную утилиту, в которой ваши классы ведения журнала регистрируются и генерируются уникальные идентификаторы (что-то вроде класса регистрации). Для простых целей регистрации, ИМХО, это раздувание и не добавляет никакой ценности по сравнению с 1).

Заключение: Я бы выбрал вариант 1), если удобочитаемость для людей вызывает беспокойство, и 2), если вам просто нужно какое-то число, чтобы различать сообщения журнала (например, для конвейера и фильтрации).

Если будет всего несколько экземпляров класса, я бы посоветовал вам ввести строковый член и инициализировать его уникальным именем во время создания объекта. Это поможет вам при чтении логов. По умолчанию он может быть инициализирован адресом объекта.

Вы вполне можете без труда генерировать идентификаторы через адаптер CRTP (если вы можете немного изменить свои классы)

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;
}

Другие вопросы по теме