简介
在Qt开发里,使用 qDebug() 来打印日志信息,是一件十分愉快的事情,很多类、容器都可以直接打印,十分方便,而且还实现了编码输出,在windows上使用msvc编译器,构建输出不会出现乱码,会觉得更加舒服。
但有时想要打印自己格式的信息,比如 打印一个数组时,qDebug() 会自动产生添加换行,把一个整体的信息给弄得支离破碎
int arr[] = {1,2,3};
for (int i = 0; i < sizeof(arr)/sizeof(int); ++i) {
qDebug() << arr[i];
}
---
1
2
3
如果想打印成 数组 [1,2,3]
这种形式, 又该怎么办?
int arr[] = {1,2,3};
QString buf;
buf.append("数组 [ ");
for (int i = 0; i < sizeof(arr)/sizeof(int); ++i) {
buf.append(std::to_string(arr[i])).append(", ");
}
buf.replace(buf.length()-2,2," ]");
qDebug() << buf;
用一个QString来包装下msg就ok了,这种方式也直接也明了,不过代码就不再简洁了,我只是想要看一下信息而已。
有人说用 printf 或者 std::cout 来打印,这又牵扯到编码问题,如果项目没引入Qt包,可以直接设置源码编码。
但如果使用了 Qt,就别想通过如下cmake语句设置执行编码,会报错
#################### 设置源码编码 ####################
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>")
# add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/execution-charset:GBK>")
---报错信息---
D:\Project\cppstl\test\Main.cpp:1: error: In included file: static assertion failed: Error in C++ Standard Library usage.
问题在于解决qDebug() 的换行问题,咋越来越复杂啦??
qDebug() 宏
现在来大致看一下源码,会发现qDebug其实是一个QDebug对象,一个 QDebug 对象最终会添加一个 '\n'
换行符,并且在对象销毁时才会向设备打印,大概的代码逻辑如下:
--------------------------------------------------------------------------------
//消息日志上下文的宏定义
#ifdef QT_MESSAGELOGCONTEXT
#define QT_MESSAGELOG_FILE static_cast<const char *>(__FILE__)
#define QT_MESSAGELOG_LINE __LINE__
#define QT_MESSAGELOG_FUNC static_cast<const char *>(Q_FUNC_INFO)
#else
#define QT_MESSAGELOG_FILE nullptr
#define QT_MESSAGELOG_LINE 0
#define QT_MESSAGELOG_FUNC nullptr
#endif
//--------------------------------------------------------------------------------
//qDebug 的宏定义
#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug
#define QT_MESSAGELOG_FILE nullptr
#define QT_MESSAGELOG_LINE 0
#define QT_MESSAGELOG_FUNC nullptr
//展开后 原来是构造出了一个 QMessageLogger 对象,并调用debug()方法
QMessageLogger(nullptr,0,nullptr).debug()
//QMessageLogger 构造函数 ---------------------------------------------------------
Q_DECL_CONSTEXPR QMessageLogger(const char *file, int line, const char *function)
: context(file, line, function, "default") {}
//QMessageLogger::debug() 方法 返回一个QDebug 对象-----------------------------------
QDebug QMessageLogger::debug() const
{
QDebug dbg = QDebug(QtDebugMsg); // 这儿构造了一个QDebug 对象 dbg
QMessageLogContext &ctxt = dbg.stream->context;
ctxt.copyContextFrom(context);
return dbg;
}
//信息类型枚举 ----------------------------------------------------------------
enum QtMsgType { QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtInfoMsg, QtSystemMsg = QtCriticalMsg };
//QDebug 构造函数 ----------------------------------------------------------------
inline QDebug(QtMsgType t) : stream(new Stream(t)) {}
//QDebug << 运算符 ---------------------------------------------------------------
inline QDebug &operator<<(const QString & t) { putString(t.constData(), uint(t.length())); return maybeSpace(); }
//QDebug::putString() 其实就是往内部的 QString buf里增加、转义、填充字符 -----------
void QDebug::putString(const QChar *begin, size_t length)
{
if (stream->testFlag(Stream::NoQuotes)) {
// no quotes, write the string directly too (no pretty-printing)
// this respects the QTextStream state, though
stream->ts.d_ptr->putString(begin, int(length));
} else {
// we'll reset the QTextStream formatting mechanisms, so save the state
QDebugStateSaver saver(*this);
stream->ts.d_ptr->params.reset();
putEscapedString(stream->ts.d_ptr.data(), reinterpret_cast<const ushort *>(begin), int(length));
}
}
//--------------------------------------------------------------------------------
// QDebug 对象被析构时,才会调用 qt_message_output
QDebug::~QDebug()
{
if (stream && !--stream->ref) {
if (stream->space && stream->buffer.endsWith(QLatin1Char(' ')))
stream->buffer.chop(1);
if (stream->message_output) {
qt_message_output(stream->type,
stream->context,
stream->buffer);
}
delete stream;
}
}
//qt_message_output 这个玩意又调用 qt_message_print---------------------------------
void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
{
qt_message_print(msgType, context, message);
if (isFatal(msgType))
qt_message_fatal(msgType, context, message);
}
//qt_message_print 才实际向io设备打印 编码后的信息,也解决了乱码问题 ----------------------
//让人头疼的换行问题也在这儿 ------------------------------------------------------------
static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
{
#ifndef QT_BOOTSTRAPPED
Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message);
// qDebug, qWarning, ... macros do not check whether category is enabledgc
if (msgType != QtFatalMsg && isDefaultCategory(context.category)) {
if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
if (!defaultCategory->isEnabled(msgType))
return;
}
}
#endif
// prevent recursion in case the message handler generates messages
// itself, e.g. by using Qt API
if (grabMessageHandler()) {
const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); });
auto oldStyle = msgHandler.loadAcquire();
auto newStye = messageHandler.loadAcquire();
// prefer new message handler over the old one
if (newStye || !oldStyle) {
(newStye ? newStye : qDefaultMessageHandler)(msgType, context, message);
} else {
(oldStyle ? oldStyle : qDefaultMsgHandler)(msgType, message.toLocal8Bit().constData());
}
} else {
fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
}
}
使用 QDebug 对象来实现不换行的操作
从上述代码可以看到 一个 QDebug 对象 会打印一个"\n", 除非你自己实现并安装一个QtMessageHandler函数,具体实现参考 QtLogging 的帮助文档,略过。
或者利用 QDebug <<
操作只是在缓存字符串,析构时才打印 msg+"\n"
的特性,简单实现不换行的技巧
{
// auto qdebug = qDebug(); // 这种兜兜转转,不利落
auto qdebug = QDebug(QtDebugMsg); // 这种直接一点,构造一个QDebug()对象
qdebug << "数组 [";
int arr[] = {1,2,3};
for (int i = 0; i < sizeof(arr)/sizeof(int); ++i) {
qdebug.nospace() << arr[i] << ",";
}
qdebug << "]";
// qdebug.~QDebug(); 或者主动调用析构,或者等qdebug退出作用域自动析构
}
// 输出
数组 [ 1,2,3,]