【问题标题】:How does QMessageLogger magic work?QMessageLogger 魔法是如何工作的?
【发布时间】:2016-10-31 22:11:19
【问题描述】:

我正在为 QT 应用程序开发一个记录器框架。由于理解和学习的目的,我没有直接使用QMessageLogger。关于QMessageLogger 的一个功能,我真的很想在我的记录器中拥有一件事,但我不知道它是如何工作的。让我们以qDebug 宏为例:

#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug

可以通过两种方式调用此函数: 第一种方式:

qDebug("abc = %u", abc);

第二种方式:

qDebug() << "abc = " << abc;

我正在查看库代码,但我不太明白它是如何实现的,可以通过使用va_args 以及一些流对象来使用QMessageLogger。 我怎样才能达到这样的效果?我非常感谢所有帮助,如果有一个例子,我将不胜感激。

这是我的print 方法体。我需要通过“流”方式实现类似的功能:

/*!
 * \brief Adds the log line to the print queue.
 * \param lvl: Log level of the line.
 * \param text: Formatted input for va_list.
 */
void CBcLogger::print(MLL::ELogLevel lvl, const char* text, ...)
{
    // check if logger initialized
    if (!m_loggerStarted)
        return;

    // check if log level sufficient
    if (lvl > m_setLogLvl)
        return;

    logLine_t logline;
    logline.loglvl = lvl;
    logline.datetime = QDateTime::currentDateTime();

    va_list argptr;
    va_start(argptr, text);

    char* output = NULL;
    if (vasprintf(&output, text, argptr))
    {
        logline.logstr = output;
        delete output;
    }

    va_end(argptr);
    emit addNewLogLine(logline);
}

【问题讨论】:

  • 旁注:如果您编写 C++11 并使用 C 的可变参数,那么您做错了。
  • 我应该怎么做才合适?
  • 不要使用可变参数。忘记它们的存在。参见例如this answer.
  • 它不是类型安全的,在 C++11 中是不必要的。您不妨问“C 有什么问题”。
  • 你链接到的文章探讨了这个问题,是的。

标签: c++ qt logging


【解决方案1】:

首先你要明白下面是什么

QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug

上面的行构造了一个QMessageLogger 实例并立即访问它的调试成员。因为它是一个宏,所以在它后面写的代码也很重要。

如果您查看QMessageLogger::debug 是什么,您会看到四个重载,其中前两个与您的问题相关:

void debug(const char *msg, ...) const Q_ATTRIBUTE_FORMAT_PRINTF(2, 3);
QDebug debug() const;
QDebug debug(const QLoggingCategory &cat) const;
QDebug debug(CategoryFunction catFunc) const;

现在事情应该很简单了。如果你调用qDebug("abc = %u", abc),你调用的是第一个重载,扩展宏如下:

QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug("abc = %u", abc)

或多或少等于

QMessageLogger temp(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC);
temp.debug("abc = %u", abc);

在第二种情况下,您正在调用返回 QDebug 对象的重载。 QDebug 已超载 operator&lt;&lt;。扩展宏如下:

QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug() << "abc = " << abc;

或多或少等于

QMessageLogger temp(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC);
QDebug anotherTemp = temp.debug();
anotherTemp << "abc = " << abc;

这是此类记录器的简单实现:

void addNewLogLine(char const* ptr){
    cout << "addNewLogLine: " << ptr << endl;
}
struct LoggerHelper
{
    std::stringstream s;

    explicit LoggerHelper()=default;
    LoggerHelper(LoggerHelper&&) = default;

    ~LoggerHelper(){
        auto str = s.str();
        addNewLogLine(str.c_str());
    }

    template<typename T>
    LoggerHelper& operator<<(T const& val){
        s << val;
        return *this;
    }
};

struct Logger
{
    void operator()(char const* fmt, ...) const {
        char* buf;
        va_list args;
        va_start(args, fmt);
        vasprintf(&buf, fmt, args);
        va_end(args);
        addNewLogLine(buf);
        free(buf);
    }

    LoggerHelper operator()() const {
        return LoggerHelper{};
    }
};

demo

几个注意事项:

  • 我坚持你的界面,但就个人而言,我会使用可变参数模板而不是 va_args
  • 你应该freevasprintf返回的缓冲区。 free 不能与 deletedelete[] 互换
  • 我使用了std::stringstream,但将其更改为QTextStream 或任何其他都应该足够简单
  • 如果您同意允许log &lt;&lt; "foo" &lt;&lt; "bar" 语法而不是log() &lt;&lt; "foo" &lt;&lt; "bar",则无需将帮助程序实现为单独的类

【讨论】:

  • 所以如果我的标准方法是:void print(MLL::ELogLevel lvl, const char* text, ...);,我是否应该添加一个重载,看起来像这样:QTextStream print(MLL::ELogLevel lvl) const;
  • @ŁukaszPrzeniosło 我有点错过了问题的第二部分。我会更新它。你可以做你所做的,但我只想在我的记录器中重载operator(),以及operator&lt;&lt;
  • 我将不胜感激。因为现在我不确定如何按照我展示的方式进行操作,因为在 pint 函数中,我检查记录器是否已初始化,如果没有则返回。但在这里我必须返回QTextStream,所以我不能返回即。 0.
  • @ŁukaszPrzeniosło pastebin.com/HV8hP2J4 第 73 行 fwd 声明,87 将 op() 更改为仅在类内声明,130 定义 op()
  • 好的,我想我做到了。我添加了一个覆盖打印功能,如下所示:LoggerHelper CBcLogger::print(MLL::ELogLevel lvl) const { return LoggerHelper(lvl); }。我不确定这是正确的方法,我确信我还没有完全理解这一点,但我很高兴它有效。非常感谢@krzaq
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多