0

I have a simple scene in C++ code. I want to get a long string containing some repeated std::string_view; is there a way to achieve this without copying the string?

#include <cstdio>
#include <string_view>

struct Logger {
    static inline constexpr std::string_view indent = "    ";
    static inline int level = 0;

    Logger() {
        level++;
    }

    // ...
    
    void log(const std::string &msg) {
        std::printf(/* repeat indent 'level' times */);
    }
};
Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Gtylcara
  • 87
  • 5

3 Answers3

3

C-Style Solution

std::printf allows you to dynamically specify the field width. For example:

#include <cstdio>

int main() {
    int indent_length = 4;
    int indent_depth = 3;

    // note: "" is passed as an argument for %s
    std::printf("%*shi!", indent_length * indent_depth, "");
}

This will output:

            hi!

We can use this trick because indentation always consist of one character, so we don't really have to repeat a full std::string_view. Obviously, this wouldn't work if you wanted indentation that is a mixture of multiple characters, but that would be highly unusual.

Old-School C++ Solution

However, that is a very C-style solution, and you could just use a loop to avoid copying:

void log(std::string_view msg) {
    for (int i = 0; i < level; ++i) {
        std::cout << indent;
    }
    std::cout << msg;
}

However, if the indent is not a mixture of different characters, we can do a similar trick using std::cout as above to avoid this loop:

void log(std::string_view msg) {
    std::cout << std::setw(indent_length * level) << "" << msg;
}

If you absolutely insist on using std::printf, we can still do:

// if msg is always null-terminated
std::printf("%s", msg.data());

// otherwise
std::printf("%.*s", int(msg.length()), msg.data());

Your indent string can be printed exactly the same way.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
  • What does the first snippet has to do with the question? – HolyBlackCat Jun 27 '23 at 09:38
  • 2
    @HolyBlackCat the purpose of the question is to `printf` indentation a variable amount of times. Since the indentation is always a single a single character, we don't really need to repeat a full `std::string_view`, but we can repeat a single character. – Jan Schultke Jun 27 '23 at 09:41
  • there is `fmt` library and `spdlog` based on it. Since C++23 there is `std::print` motivated by `fmt::print`. `fmt::print("{:{}}", "", indent);` – Marek R Jun 27 '23 at 09:46
  • Ah, sorry, I see it now. – HolyBlackCat Jun 27 '23 at 10:04
0

The Answer to you question is NO,

but std::views::repeat or std::ranges::repeat_view can help you write more concise and more readable code.

In alternative, if you know a priori the max indentation, you could initialize a std::array of string_view, where the dimension is the number of max indentation, where each string_view, have incremental repetion of the pattern.

Zig Razor
  • 3,381
  • 2
  • 15
  • 35
0

I would try do longest possible ident and then extract subview form it:

using namespace std::string_view_literals;

constexpr auto MaxIndentView = "                                            "sv;

constexpr std::string_view indentView(size_t n)
{
    if (n * 4 > MaxIndentView.size()) {
        throw std::invalid_argument{"Max indentation reached"};
    }
    return MaxIndentView.substr(0, n * 4);
}

But I think this is not what you actually need.

Marek R
  • 32,568
  • 6
  • 55
  • 140