16

I am new to C++17 and to std::string_view. I learned that they are not null terminated and must be handled with care.

Is this the right way to printf() one?

#include<string_view>
#include<cstdio>

int main()
{
    std::string_view sv{"Hallo!"};
    printf("=%*s=\n", static_cast<int>(sv.length()), sv.data());
    return 0;
}

(or use it with any other printf-style function?)

11
  • 8
    Why you want to use printf in c++ at all?
    – Marek R
    Commented Jun 10, 2022 at 14:00
  • 6
    I learned that they are not null terminated That's not entirely correct. A string_view can be non-null terminated, but only if you create it as such. "Hallo" has a null terminator, so sv will as well. Commented Jun 10, 2022 at 14:01
  • 3
    Is this what you are asking? Using printf with a non-null terminated string Commented Jun 10, 2022 at 14:03
  • I believe it should be "=%.*s=\n"... Just checked, that's correct.
    – john
    Commented Jun 10, 2022 at 14:05
  • 4
    Consider use fmt library it should easy to transform code which uses printf.
    – Marek R
    Commented Jun 10, 2022 at 14:30

4 Answers 4

19

This is strange requirement, but it is possible:

std::string_view s{"Hallo this is longer then needed!"};
auto sub = s.substr(0, 5);
printf("=%.*s=\n", static_cast<int>(sub.length()), sub.data());

https://godbolt.org/z/nbeMWo1G1

As you can see you were close to solution.

8
  • casting to an int can lead to UB. Does printf support passing a size_t for the length of the string? Commented Jun 10, 2022 at 14:11
  • 1
    @NathanOliver No, only int is supported.
    – Ted Lyngmo
    Commented Jun 10, 2022 at 14:14
  • 1
    @kuga No, it's not wrong. The * is what receives the precision. So, '.5s', sv.data() or '.*s', (int)sv.size(), sv.data() does the same thing (as long as size() doesn't overflow the int).
    – Ted Lyngmo
    Commented Jun 10, 2022 at 14:24
  • 1
    printf requires int value there so cast is needed. Star parameter doesn't have any modifiers to change expected type. It is possible to implement safe casting to int, but IMO it would be overkill.
    – Marek R
    Commented Jun 10, 2022 at 14:25
  • 1
    I was more thinking to implement template which could be called clip_cast.
    – Marek R
    Commented Jun 10, 2022 at 14:35
10

You can use:

assert(sv.length() <= INT_MAX);
std::printf(
    "%.*s",
    static_cast<int>(sv.length()),
    sv.data());
0

If using google's Abseil library (https://abseil.io/) the absl::PrintF function can safely print a string view (either std::string_view or absl::string_view).

From the Abseil documentation of absl::PrintF at https://github.com/abseil/abseil-cpp/blob/master/absl/strings/str_format.h

std::string_view s = "Ulaanbaatar";
absl::PrintF("The capital of Mongolia is %s", s);
-4

The thing to remember about string_view is that it will never modify the underlying character array. So, if you pass a C-string to the string_view constructor, the sv.data() method will always return the same C-string.

So, this specific case will always work:

#include <string_view>
#include <cstdio>

int main() {
    std::string_view sv {"Hallo!"};
    printf("%s\n", sv.data());
}
4
  • 7
    I'd warn against doing this in the general case, since not all views are null-terminated. Commented Jun 11, 2022 at 5:12
  • 1
    @holyBlackCat – i was very specific that it's only for C-string constructed string_view objects. Commented Jun 12, 2022 at 17:52
  • not sure why this is "-2" since in the above requested case the string_view is null-term. see ctor #4 in string_view in cppref. Yes, it's a view that is not null term, but I'd change the original question to be more general.
    – Kobi
    Commented Aug 3, 2022 at 17:20
  • Access to basic_string_view::operator[](size()) (the nul terminator in this case) has undefined behaviour. You can use this trick because it works, but you should feel dirty every time, I do :-(
    – Sandy
    Commented May 3, 2023 at 0:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.