implicit QChar constructors

Oswald Buddenhagen kde-optimize@mail.kde.org
Thu, 23 Jan 2003 03:29:32 +0100


On Wed, Jan 22, 2003 at 01:18:23AM +0100, Oswald Buddenhagen wrote:
> just an optimization hint,
>
and another one: don't use QString::mid() if you merely want to compare
the resulting string (or do something else short-lived and read-only
with it, like indexing a qmap (yes matthias, i was enlightened ...
finally :})). in such cases, use a construct like
if (QConstString(str.unicode() + pos, len).string() == <value>)
(instead of  if (str.mid(pos, len) == <value>)  ).
the left() and right() equality cases are covered by startsWith() and
endsWith() - i'm wondering, why there is no subStrIs() (midsWith() 
is sort of a dumb name *g*)?


now let's discuss the opposite direction: concatenation.
 str += foo + bar;
vs.
 ( str += foo ) += bar; // more readable: str.append(foo).append(bar);
the first form is mostly the same as
 QString tstr(foo);
 tstr += bar;
 str += tstr;
the second form avoids creating a temporary object and is therefore
favorable ... unless str was already quite long: resizing a long string
is an expensive operation. therefore the cost of composing a short
"appendage" in a temporary string and then appending it at once is
smaller than appending two small chunks. typical tradeoff ...
note that this recursively applies to creating the "appendage":
 str += foo + ( bar + baz );
will be faster than
 str += ( foo + bar ) += baz;
if foo happens to be long.

to have something fast, trolltech could provide a function like

#include <stdarg.h> // i'd prefer varargs.h, but hey ...
const QString &
QString::append(const QString &a1, ...)
{
  if (!a1)
    return *this;
  va_list ap;
  uint clen = length();
  uint len = clen + a1->length();
  const QString *tp;
  va_start(ap, a1);
  while ((tp = va_arg(ap, const QString *)))
    len += tp->length();
  va_end(ap);
  setLength(len);
  memcpy((void*)(unicode() + clen), a1->unicode(), a1->length() * sizeof(QChar));
  clen += a1->length();
  va_start(ap, a1);
  while ((tp = va_arg(ap, const QString *))) {
    memcpy((void*)(unicode() + clen), tp->unicode(), tp->length() * sizeof(QChar));
    clen += tp->length();
  }
  va_end(ap);
  return *this;
}

which could be used like
 QString a1("trolltech rocks");
 QString a2(" (sometimes)");
 str.append( &a1, &a2, 0L );

it's a bit ugly, because a) you have no type safety and b) you have to
explicitly create temporary objects (otherwise you get a compiler
warning).
the alternative would be to provide 10 overloaded const QString &
variants, duplicate tons of code and to hope nobody tries to add more
than 10 elements. :)

if you repeatedly append small chunks to an already long string (like,
say, you're reading a file line by line and appending the processed
results to a target string), theoretically you'd be better off
(speed-wise, not memory-wise) storing the lines in a QStringList and
then join()ing it at the end - too bad that qt's join() implementation
plainly sucks - it's a simple loop with incremental concat, instead of
a two-pass construct like the above function.

btw, QString::sprintf could also be made work in two passes, but i have
some doubts that doing all conversions twice is a gain over resizing a
(usually short) QString a few (usually ...) times.  the third way is of
course storing all substrings in a QStringList and joining them with the
new, optimized ;) join() function - however, creating tons of small
objects is probably no gain over a few resizes, either. the last
possibility that comes to mind is what the setLength docu says:
preallocate a bigger string and truncate it later. the problem here is
only, how to predict the size halfways reasonably ...

ergo: simply try to avoid using strings at all. :)


i hope i confused everybody sufficiently. good night. :)

greetings

-- 
Hi! I'm a .signature virus! Copy me into your ~/.signature, please!
--
Chaos, panic, and disorder - my work here is done.