I'm using the the C++/Qt print function qDebug,
but sometimes I would like to control how ", space and newline is appended
and not use the default qDebug.
Let's take a simple example:
QString var1("some string");
int var2 = 1;
qDebug() << var1 << "=" << var2;
This will print
"some string" = 1
But Let's say that I don't like the appended " and space
and would like the print to look like
some string=1
How to I then call qDebug?
Note: There is a function in qDebug called nospace, but it will remove the spaces.
But the " is still there.
If I use this:
qDebug().nospace() << var1 << "=" << var2;
I get:
"some string"=1
But please note that I have still not found a way to get rid of the ending newline.
/Thanks
It would be best to understand how QDebug works internally. That way you can easily modify it to suit your needs. Whenever you use the qDebug() function, it returns a QDebug object. By default QDebug always outputs a space after any use of operator <<.
The QDebug class internally contains a QString. Every time you use operator << you are appending to that internal QString. This QString is printed via qt_message_output(QtMsgType, char*) when the QDebug object is destroyed.
By default qt_message_output always prints the string followed by a newline.
Normal Output
qDebug() << "Var" << 1;
This will output Var 1. This is because qDebug will create a QDebug object which appends a space after each call to operator <<. So that will be Var + + 1 + .
Without Spaces
You can use QDebug::nospace to tell QDebug not to append a space after each call to operator <<.
qDebug().nospace() << "Var" << 1;
This will output Var1 as that QDebug object is no longer printing spaces.
Without New Lines
Not adding the \n at the end of the string is a little bit harder. Since QDebug internally only passes the string to qt_message_output when it is destroyed, you can delay the destruction of that QDebug object -
QDebug deb = qDebug();
deb << "One" << "Two";
deb << "Three";
This will print One Two Three and then append a new line.
If you never want a new line to be printed, you will have to change the behaviour of qt_message_output. This can be done by installing a custom handler.
void customHandler(QtMsgType type, const char* msg) {
fprintf(stderr, msg);
fflush(stderr);
}
// Somewhere in your program
qInstallMsgHandler(customHandler);
qDebug() << "One" << "Two";
qDebug().noSpace() << "Three" << "Four";
This will print One Two ThreeFour.
Be warned that this will affect all of the qDebug statements in your program. If you want to remove the custom handler, you should call qInstallMsgHandler(0).
qDebug(const char* msg, ...)
As indicated by the other answers you can also use the qDebug function to print strings in a format similar to that of printf. This way you can avoid the extra spaces that are appended by QDebug.
However, qDebug internally still uses qt_message_output, so you will still get a newline at the end unless you install your own handler.
Try this format: qDebug("%s=%d", "string", 1);
In this case qDebug uses printf formatting
P.S. Adapted for your example: qDebug("%s=%d", var1.toStdString().c_str(), var2);
Since Qt 5.4 you can also write:
qDebug().nospace().noquote() << var1;
Combining some of the above answers you can use
qDebug() << qPrintable(var1);
to eliminate the surrounding quotes.
I also experienced the quotes problem. The solution is to not pipe QString() into the stream but instead QString(...).toStdString().c_str().
I've built myself a small convenience macro to easily get around this:
#define Q(string) (string).toStdString().c_str()
Now everytime you use a QString, do it like that:
qDebug() << Q(var1) << "=" << var2;
The file $(QTDIR)/src/corelib/io/qdebug.h contains almost all definitions for the debug output methods. One of them is:
inline QDebug &operator<<(const QString & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); }
So there is no "official" way to suppress the quotes, but you can of course change qdebug.h or use your own copy or a modified and renamed copy of the QDebug class.
Another way is to use your own message handler.
Hope this helps.
Instantiate a QDebug object and output to it:
QDebug dbg = qDebug().nospace().noquote();
dbg << var1 << "=" << var2;
Yields:
some string=1
Output to the dbg object all you want -- there won't be a newline until it goes out of scope. For example:
char var1[] = "some string";
int var2 = 1;
{
QDebug dbg = qDebug().nospace().noquote();
dbg << var1 << "=" << var2;
// keep using "dbg"; there's no newline ('\n') until it destructs
dbg << "...";
for (int i = 5; i <=9; ++i)
dbg << i;
}
Outputs:
some string=1...56789
Related
I'm making an OpenGL game in C++. I'm fairly inexperinced in C++ as opposed to other languages. Anyway, I create a stringstream with the "base" directory for some images. I then pass this stringstream as a function parameter to a constructor. The constructor appends an image file name, then attempts to load the resulting path. However...
D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\ <-- before appending the filename
Ship01.tgacks Projects\SnakeRoid\bin\Debug\Texts\ <-- After.
Obviously not correct! The result should be D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\Ship01.tga
The relevant parts of my code:
std::stringstream concat;
std::string txtFullPath = "Path here";
...
concat.str(""); //Reset value (because it was changed in ...)
concat << texFullPath; //Restore the base path
PS = new PlayerShip(&TexMan, concat); //Call the constructor
The constructor's code
PlayerShip::PlayerShip(TextureManager * TexMan, std::stringstream &path)
{
texId = 2;
std::cout << path.str(); //First path above
path << "Ship01.tga";
std::cout << path.str(); //Second - this is the messed up one
//Do more fun stuff
}
Anyone have any idea why its "overwriting" what's already in the stringstream?
why its "overwriting" what's already in the stringstream
Because output places characters at the "put pointer" position in the output buffer. A freshly-constructed stream has the put pointer set to zero (except for file output streams opened in append mode), thus your output overwrites the characters already in the buffer.
If you really need to append strings this way, you need to move the put pointer to the end of the buffer:
std::cout << p.str(); //First path above
std::stringstream path;
path.str(p.str());
path.seekp(0, std::ios_base::end); // <-- add this
path << "Ship01.tga";
std::cout << "Loading player ship from " << path.str();
EDIT: The question has been edited and the code after the edit works, because it no longer uses path.str(p.str()); to create the output buffer without using an output operation (and without advancing the put pointer): see ideone for differences.
In any case, strings themselves can be concatenated, which would make the code easier to follow:
std::string p = path.str() + "Ship01.tga";
std::cout << p;
Not to mention that for dealing with files and pathnames, we have boost.filesystem.
Currently I only know of two methods to insert values into a C++ string or C string.
The first method I know of is to use std::sprintf() and a C-string buffer (char array).
The second method is to use something like "value of i: " + to_string(value) + "\n".
However, the first one needs the creation of a buffer, which leads to more code if you just want to pass a string to a function. The second one produces long lines of code, where a string gets interrupted every time a value is inserted, which makes the code harder to read.
From Python I know the format() function, which is used like this:
"Value of i: {}\n".format(i)
The braces are replaced by the value in format, and further .format()'s can be appended.
I really like Python's approach on this, because the string stays readable, and no extra buffer needs to be created. Is there any similar way of doing this in C++?
Idiomatic way of formatting data in C++ is with output streams (std::ostream reference). If you want the formatted output to end up in a std::string, use an output string stream:
ostringstream res;
res << "Value of i: " << i << "\n";
Use str() member function to harvest the resultant string:
std::string s = res.str();
This matches the approach of formatting data for output:
cout << "Value of i: " << i << "\n";
Is there any easy way to get the following work? I mean is there any helper class in Qt which prepares the string for qDebug?
QString s = "value";
qDebug("abc" + s + "def");
You can use the following:
qDebug().nospace() << "abc" << qPrintable(s) << "def";
The nospace() is to avoid printing out spaces after every argument (which is default for qDebug()).
No really easy way I am aware of. You can do:
QByteArray s = "value";
qDebug("abc" + s + "def");
or
QString s = "value";
qDebug("abc" + s.toLatin1() + "def");
According to Qt Core 5.6 documentation you should use qUtf8Printable() from <QtGlobal> header to print QString with qDebug.
You should do as follows:
QString s = "some text";
qDebug("%s", qUtf8Printable(s));
or shorter:
QString s = "some text";
qDebug(qUtf8Printable(s));
See:
http://doc.qt.io/qt-5/qtglobal.html#qPrintable
http://doc.qt.io/qt-5/qtglobal.html#qUtf8Printable
Option 1: Use qDebug's default mode of a C-string format and variable argument list (like printf):
qDebug("abc%sdef", s.toLatin1().constData());
Option 2: Use the C++ version with overloaded << operator:
#include <QtDebug>
qDebug().nospace() << "abc" << qPrintable(s) << "def";
Reference: https://qt-project.org/doc/qt-5-snapshot/qtglobal.html#qDebug
Just rewrite your code like this:
QString s = "value";
qDebug() << "abc" << s << "def";
I know this question is a bit old, but it appears nearly on top when searching for it in the web. One can overload the operator for qDebug (more specific for QDebug) to make it accept std::strings like this:
inline QDebug operator<<(QDebug dbg, const std::string& str)
{
dbg.nospace() << QString::fromStdString(str);
return dbg.space();
}
This thing is for years in all of my projects, I nearly forget it is still not there by default.
After that, usage of << for qDebug() is a lot more usable imho. You can even mix QString and std::string. Some additional(but not really intended) feature is, that you sometimes can throw in integers or other types that allow implicit conversion to std::string .
I have a QByteArray to store data received from a GPS, which is part binary and part ASCII. I want to know for debug proposals know what's being received, so I'm writing a qDebug like this:
//QByteArray buffer;
//...
qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer;
And I get messages like this at console:
GNSS msg ( 1774 ): "ygnnsdgk...(many data)..PR085hlHJGOLH
(more data into a new line, which is OK because it is a new GNSS sentence and
probably has a \n at the end of each one) blablabla...
But suddenly I get a new print iteration. Data has not been erased yet, it has been appended. So new message size its for example 3204, bigger than the previous print obviously. But it prints exactly the same (but with the new size 3204 between brackets). No new data is printed, just the same as the previous message had:
GNSS msg ( 3204 ): "ygnnsdgk...(many data)..PR085hlHJGOLH
(more data into a new line, which is OK because it is a new GNSS sentence and
probably has a \n at the end of each one) blablabla...
I guess qDebug stops printing because it has a limit, or because it reaches a terminating character or something like that, but I'm only guessing.
Any help or explanation for this behaviour?
Solution / workaround:
Indeed, the qDebug() output of QByteArray gets truncated at a '\0' character. This doesn't have something to do with the QByteArray; you even can't ever output a '\0' character using qDebug(). For an explanation see below.
QByteArray buffer;
buffer.append("hello");
buffer.append('\0');
buffer.append("world");
qDebug() << "GNSS msg (" << buffer.size() << "): " << buffer;
Output:
GNSS msg ( 11 ): "hello
Even any following arguments are ignored:
qDebug() << "hello" << '\0' << "world";
Output:
hello
You can work around this "problem" by replacing the special characters in your byte array before debugging them:
QByteArray dbg = buffer; // create a copy to not alter the buffer itself
dbg.replace('\\', "\\\\"); // escape the backslash itself
dbg.replace('\0', "\\0"); // get rid of 0 characters
dbg.replace('"', "\\\""); // more special characters as you like
qDebug() << "GNSS msg (" << buffer.size() << "): " << dbg; // not dbg.size()!
Output:
GNSS msg ( 11 ): "hello\0world"
So why is this happening? Why can't I output a '\0' using qDebug()?
Let's dive into the Qt internal code to find out what qDebug() does.
The following code snippets are from the Qt 4.8.0 source code.
This method is called when you do qDebug() << buffer:
inline QDebug &operator<<(const QByteArray & t) {
stream->ts << '\"' << t << '\"'; return maybeSpace();
}
The stream->ts above is of type QTextStream, which converts
the QByteArray into a QString:
QTextStream &QTextStream::operator<<(const QByteArray &array)
{
Q_D(QTextStream);
CHECK_VALID_STREAM(*this);
// Here, Qt constructs a QString from the binary data. Until now,
// the '\0' and following data is still captured.
d->putString(QString::fromAscii(array.constData(), array.length()));
return *this;
}
As you can see, d->putString(QString) is called (the type of d is the internal private class of the text stream), which calls write(QString) after doing some padding for constant-width fields. I skip the code of putString(QString) and directly jump into d->write(QString), which is defined like this:
inline void QTextStreamPrivate::write(const QString &data)
{
if (string) {
string->append(data);
} else {
writeBuffer += data;
if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE)
flushWriteBuffer();
}
}
As you can see, the QTextStreamPrivate has a buffer. This buffer is of type QString. So what happens when the buffer is finally printed on the terminal? For this, we have to find out what happens when your qDebug() statement finishes and the buffer is passed to the message handler, which, per default, prints the buffer on the terminal. This is happening in the destructor of the QDebug class, which is defined as follows:
inline ~QDebug() {
if (!--stream->ref) {
if(stream->message_output) {
QT_TRY {
qt_message_output(stream->type, stream->buffer.toLocal8Bit().data());
} QT_CATCH(std::bad_alloc&) { /* We're out of memory - give up. */ }
}
delete stream;
}
}
So here is the non-binary-safe part. Qt takes the textual buffer, converts it to "local 8bit" binary representation (until now, AFAIK we should still have the binary data we want to debug).
But then passes it to the message handler without the additional specification of the length of the binary data. As you should know, it is impossible to find out the length of a C-string which should also be able to hold '\0' characters. (That's why QString::fromAscii() in the code above needs the additional length parameter for binary-safety.)
So if you want to handle the '\0' characters, even writing your own message handler will not solve the problem, as you can't know the length. Sad, but true.
I'm making an OpenGL game in C++. I'm fairly inexperinced in C++ as opposed to other languages. Anyway, I create a stringstream with the "base" directory for some images. I then pass this stringstream as a function parameter to a constructor. The constructor appends an image file name, then attempts to load the resulting path. However...
D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\ <-- before appending the filename
Ship01.tgacks Projects\SnakeRoid\bin\Debug\Texts\ <-- After.
Obviously not correct! The result should be D:\CodeBlocks Projects\SnakeRoid\bin\Debug\Texts\Ship01.tga
The relevant parts of my code:
std::stringstream concat;
std::string txtFullPath = "Path here";
...
concat.str(""); //Reset value (because it was changed in ...)
concat << texFullPath; //Restore the base path
PS = new PlayerShip(&TexMan, concat); //Call the constructor
The constructor's code
PlayerShip::PlayerShip(TextureManager * TexMan, std::stringstream &path)
{
texId = 2;
std::cout << path.str(); //First path above
path << "Ship01.tga";
std::cout << path.str(); //Second - this is the messed up one
//Do more fun stuff
}
Anyone have any idea why its "overwriting" what's already in the stringstream?
why its "overwriting" what's already in the stringstream
Because output places characters at the "put pointer" position in the output buffer. A freshly-constructed stream has the put pointer set to zero (except for file output streams opened in append mode), thus your output overwrites the characters already in the buffer.
If you really need to append strings this way, you need to move the put pointer to the end of the buffer:
std::cout << p.str(); //First path above
std::stringstream path;
path.str(p.str());
path.seekp(0, std::ios_base::end); // <-- add this
path << "Ship01.tga";
std::cout << "Loading player ship from " << path.str();
EDIT: The question has been edited and the code after the edit works, because it no longer uses path.str(p.str()); to create the output buffer without using an output operation (and without advancing the put pointer): see ideone for differences.
In any case, strings themselves can be concatenated, which would make the code easier to follow:
std::string p = path.str() + "Ship01.tga";
std::cout << p;
Not to mention that for dealing with files and pathnames, we have boost.filesystem.