Long live QString::asprintf()!

asprintf() is a GNU extension that prints into a string it
allocates internally. Arguably, that's a better name for
QString::sprintf() since it also allocates memory internally.

The main problem with QString::sprintf() isn't that it's
dangerous to use (it is), but that it's not static. It also
returns a reference instead of by-value, breaking RVO.

There is a comment about removing this function completely
in Qt 6.0, but it remains the only printf-style function
in Qt that can allocate the target string, so it's vital
for logging, e.g., and the recommended replacement code
(http://linux.die.net/man/3/vsnprintf) is a nightmare.

So this patch adds static (v)asprintf() methods to replace it.

Further patches will fix up all in-tree callers and finally
deprecate the old (v)sprintf().

Test coverage is provided through the existing tests of
sprintf(), which is implemented in terms of asprintf().

Arguably, the in-tree callers show that QByteArray would
benefit from having an asprintf(), too, as most of the
in-tree code works around its lack with calls
to to{Latin1,Local8Bit}() after using the QString version.

[ChangeLog][QtCore][QString] Added asprintf(), vasprintf().

Change-Id: I8510f8d67c22230653ec0f1c252c01bc95f3c386
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Marc Mutz 2012-02-20 13:54:26 +01:00
parent 63114f4d3c
commit d251cae7b7
4 changed files with 41 additions and 18 deletions

View File

@ -5661,9 +5661,22 @@ QString QString::toUpper_helper(QString &str)
return QUnicodeTables::convertCase<QUnicodeTables::UppercaseTraits>(str);
}
/*!
\obsolete Use asprintf(), arg() or QTextStream instead.
*/
QString &QString::sprintf(const char *cformat, ...)
{
va_list ap;
va_start(ap, cformat);
*this = vasprintf(cformat, ap);
va_end(ap);
return *this;
}
// ### Qt 6: Consider whether this function shouldn't be removed See task 202871.
/*!
\since 5.5
Safely builds a formatted string from the format string \a cformat
and an arbitrary list of arguments.
@ -5680,7 +5693,7 @@ QString QString::toUpper_helper(QString &str)
a \c{wchar_t*}, and might also produce compiler warnings on platforms
where the size of \c {wchar_t} is not 16 bits.
\warning We do not recommend using QString::sprintf() in new Qt
\warning We do not recommend using QString::asprintf() in new Qt
code. Instead, consider using QTextStream or arg(), both of
which support Unicode strings seamlessly and are type-safe.
Here's an example that uses QTextStream:
@ -5695,32 +5708,42 @@ QString QString::toUpper_helper(QString &str)
\sa arg()
*/
QString &QString::sprintf(const char *cformat, ...)
QString QString::asprintf(const char *cformat, ...)
{
va_list ap;
va_start(ap, cformat);
QString &s = vsprintf(cformat, ap);
const QString s = vasprintf(cformat, ap);
va_end(ap);
return s;
}
/*!
Equivalent method to sprintf(), but takes a va_list \a ap
instead a list of variable arguments. See the sprintf()
\obsolete Use vasprintf(), arg() or QTextStream instead.
*/
QString &QString::vsprintf(const char *cformat, va_list ap)
{
return *this = vasprintf(cformat, ap);
}
/*!
\fn QString::vasprintf(const char *cformat, va_list ap)
\since 5.5
Equivalent method to asprintf(), but takes a va_list \a ap
instead a list of variable arguments. See the asprintf()
documentation for an explanation of \a cformat.
This method does not call the va_end macro, the caller
is responsible to call va_end on \a ap.
\sa sprintf()
\sa asprintf()
*/
QString &QString::vsprintf(const char* cformat, va_list ap)
QString QString::vasprintf(const char *cformat, va_list ap)
{
if (!cformat || !*cformat) {
// Qt 1.x compat
*this = fromLatin1("");
return *this;
return fromLatin1("");
}
// Parse cformat
@ -6036,9 +6059,7 @@ QString &QString::vsprintf(const char* cformat, va_list ap)
result.append(subst.rightJustified(width));
}
*this = result;
return *this;
return result;
}
/*!
@ -7167,7 +7188,7 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f
First, \c arg(i) replaces \c %1. Then \c arg(total) replaces \c
%2. Finally, \c arg(fileName) replaces \c %3.
One advantage of using arg() over sprintf() is that the order of the
One advantage of using arg() over asprintf() is that the order of the
numbered place markers can change, if the application's strings are
translated into other languages, but each arg() will still replace
the lowest numbered unreplaced place marker, no matter where it

View File

@ -300,6 +300,8 @@ public:
QString &vsprintf(const char *format, va_list ap) Q_ATTRIBUTE_FORMAT_PRINTF(2, 0);
QString &sprintf(const char *format, ...) Q_ATTRIBUTE_FORMAT_PRINTF(2, 3);
static QString vasprintf(const char *format, va_list ap) Q_ATTRIBUTE_FORMAT_PRINTF(1, 0);
static QString asprintf(const char *format, ...) Q_ATTRIBUTE_FORMAT_PRINTF(1, 2);
int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;

View File

@ -58,10 +58,10 @@ QT_BEGIN_NAMESPACE
platforms, you should not rely on the return value or on the fact
that you will always get a 0 terminated string back.
Ideally, you should never call this function but use QString::sprintf()
Ideally, you should never call this function but use QString::asprintf()
instead.
\sa qsnprintf(), QString::sprintf()
\sa qsnprintf(), QString::asprintf()
*/
int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap)
@ -107,9 +107,9 @@ int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap)
\warning Call this function only when you know what you are doing
since it shows different behavior on certain platforms.
Use QString::sprintf() to format a string instead.
Use QString::asprintf() to format a string instead.
\sa qvsnprintf(), QString::sprintf()
\sa qvsnprintf(), QString::asprintf()
*/
int qsnprintf(char *str, size_t n, const char *fmt, ...)

View File

@ -975,7 +975,7 @@ QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVar
return qvariant_cast<QPixmap>(variant);
}
// hacky but faster version of "QString::sprintf("%d-%d", i, enabled)"
// hacky but faster version of "QString::asprintf("%d-%d", i, enabled)"
static QString qPixmapSerial(quint64 i, bool enabled)
{
ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', ushort('0' + enabled) };