QMessageLogger魔术如何工作?

ŁukaszPrzeniosło

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

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

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

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

第二种方式:

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

我正在查看库代码,但我不太了解如何实现QMessageLogger使用va_args以及某些流对象的实现。我怎样才能达到这样的效果?我真的很感谢所有帮助,不胜感激。

这是我的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);
}
克尔扎格

首先,您需要了解以下内容

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<<展开的宏如下:

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

演示

几点注意事项:

  • 我坚持使用您的界面,但就个人而言,我将使用可变参数模板而不是va_args
  • 您应该使用free所返回的缓冲区vasprintffree不能与delete互换delete[]
  • 我曾经用过std::stringstream,但是将其更改为QTextStream或任何其他更改应该足够简单
  • 如果可以允许使用log << "foo" << "bar"语法而不是语法,则无需将helper作为单独的类实现log() << "foo" << "bar"

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章