Is there an efficient way to pass a list of args to a QString, by avoiding copies?

67 views Asked by At

Lets say I have a list of N arguments which I want to pass to a QString. The number N is variable and determined at runtime.

If I do this:

QString myString = "Some text %1, some other text %2,..., some more text %n"
for(auto arg : list) {
  myString = myString.arg(arg);
}

I create a lot of string copies, as I can only change one .arg at a time, and need to save one copy of the string in each iteration. Is there a more efficient way? I looked into the documentation for something like QString::args(SomethingIteratable), but found nothing.

2

There are 2 answers

0
Jarod42 On BEST ANSWER

With some hard-coded limit (to transform runtime vector size to compile time one), you might use arg() overload taking several QString

std::variant<
    std::integral_constant<std:size_t, 1>,
    std::integral_constant<std:size_t, 2>
    std::integral_constant<std:size_t, 3>
    std::integral_constant<std:size_t, 4>
    std::integral_constant<std:size_t, 5>
    // Adapt to your limit
    >
as_integral_constant(std::size_t n) {
    switch (n) {
        case 1: return std::integral_constant<std:size_t, 1>{};
        case 2: return std::integral_constant<std:size_t, 2>{};
        case 3: return std::integral_constant<std:size_t, 3>{};
        case 4: return std::integral_constant<std:size_t, 4>{};
        case 5: return std::integral_constant<std:size_t, 5>{};
        // adapt to your limit...
    }
    throw std::runtime_error("Unsupported size");
}

And then, visit:

QString args(QString format, const std::vector<QString>& list)
{
    const auto size = as_integral_constant(list.size());
    return std::visit([&](auto N){
        return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
            return format.arg(list[Is]...);
        }(std::make_index_sequence<N()>());
    }, size);
}

Notice than arg in one pass might behave differently than multipass, especially if your inserted texts contain %1.

0
Botje On

Keep it simple, just reserve some memory up front and do the simplest thing possible:

QString myString;
myString.reserve(1'000'000);
{
  QTextStream ts(&myString);
  for (auto arg: list) {
    ts << arg << ", ";
  }
}
myString.resize(myString.size() - 2);
myString.squeeze();