Как использовать Q_LOGGING_CATEGORY и ссылаться на категорию в шаблонной функции, не получая многократную ошибку реализации? - PullRequest
0 голосов
/ 04 апреля 2019

У меня проблема с одним из моих заголовочных файлов в проекте QT.

Обычно я использую следующую конструкцию:

global.h

#ifndef global
#define global
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(cat)
#endif

foo.h

#ifndef foo
#define foo
#include "global.h"
class foo{
 public:
   void bar();
};
#endif

foo.cpp

#include "foo.h"
Q_LOGGING_CATEGORY(cat, "awesomecategory")
void foo::bar(){
qCDebug(cat) << "helloworld";
}

Который прекрасно работает. Теперь у меня есть заголовочный файл, который реализует шаблонную функцию изнутри. Я хочу войти в мою категорию журналирования:

foo.h

#ifndef foo
#define foo

#include "global.h"

template <typename T>
bool bar(T, int val);

// Template definitions outside of header:
#include "foo.tpp"

#endif // foo

foo.tpp

#include <QLoggingCategory>
Q_LOGGING_CATEGORY(cat, "awesomecategory")
template <typename T>
bool bar(T, int val)
{
        T baz;
        // do stuff with baz
        qCDebug(cat) << "helloworld";
}

Что работает, если я вызываю шаблонную функцию в одном другом файле cpp (где я, следовательно, включаю foo.h, конечно), но как только я включаю foo.h в другой файл cpp, где я хочу вызвать функцию, я получаю многократную ошибку определения:

In function `ZN11QStringListD1Ev':
C:\path\to\foo.h:{n}: multiple definition of `cat()'
C:\path\to\foo.h:{n}: first defined here
collect2.exe: error: ld returned 1 exit status

Где я должен поместить свой макрос Q_LOGGING_CATEGORY, чтобы функция могла использовать его, и я все еще мог использовать заголовок в разных местах?

1 Ответ

2 голосов
/ 05 апреля 2019

Вы можете только Q_LOGGING_CATEGORY(cat, "awesomecategory") один раз, потому что это по существу создает «глобальную» функцию, откуда бы она ни вызывалась (см. Ниже).Также, когда вы #include "foo.tpp" просто помещаете содержимое этого файла в заголовок (это не «отдельная единица», как, например, исходный файл .cpp).

Если вы хотитеглобальная категория ведения журнала с именем cat, вы можете просто создать это прямо в вашем global.h.Я не думаю, что Q_LOGGING_CATEGORY(cat, "awesomecategory") будет работать непосредственно в заголовке, но это легко обойти.

Вот что делают эти макросы ... на самом деле это очень просто, и гораздо яснее увидеть, что происходит:

#define Q_DECLARE_LOGGING_CATEGORY(name) \
    extern const QLoggingCategory &name();

#define Q_LOGGING_CATEGORY(name, ...) \
    const QLoggingCategory &name() \
    { \
        static const QLoggingCategory category(__VA_ARGS__); \
        return category; \
    }

Так что DECLARE - это на самом деле просто предварительное объявление экспортируемой функции.С тем же успехом можно вместо этого поместить это в global.h (и полностью пропустить все макросы Q):

static const QLoggingCategory &cat()
{
    static const QLoggingCategory category("awesomecategory");
    return category;
}

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

#define qCDebug(category, ...) \
    for (bool qt_category_enabled = category().isDebugEnabled(); qt_category_enabled; qt_category_enabled = false) \
        QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC, category().categoryName()).debug(__VA_ARGS__)

Или, другими словами, созданная ранее функция cat() вызывается в единичном (или в нашем примере, глобальном) контексте.

Конечно, если вызывать подпрограммы журналирования стиля категории только из членов одного класса, то метод cat() может просто принадлежать этому классу.

Или сама функция cat() может находиться в пространстве имен или даже в своем собственном классе ...

class MyLoggers
{
  public:
    static const QLoggingCategory &cat()
    {
        static const QLoggingCategory category("awesomecategory");
        return category;
    }
}

// used somewhere else:
qCDebug(MyLoggers::cat)  << "Hello cats!";

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

HTH

ДОБАВЛЕНО: Кажется, что приведенный ниже конкретный пример работает без ошибок.

global.h

#ifndef GLOBAL_H
#define GLOBAL_H

#include <QLoggingCategory>

static const QLoggingCategory &cat()
{
    static const QLoggingCategory category("awesomecategory");
    return category;
}

#endif // GLOBAL_H

foo.h

#ifndef FOO_H
#define FOO_H

#include "global.h"

template <typename T>
bool bar(T, int)
{
    qCDebug(cat) << "helloworld";
    return true;
}

#endif // FOO_H

baz.h

#ifndef BAZ_H
#define BAZ_H

#include "global.h"

inline void baz()
{
    qCDebug(cat) << "baz() says meow.";
}

#endif // BAZ_H

main.cpp

#include "foo.h"
#include "baz.h"

int main(int argc, char *argv[])
{
    bar<int>(2, 2);
    baz();
    return 0;
}

Отпечатки:

awesomecategory: helloworld
awesomecategory: baz() says meow.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...