4

Is there any way to print a string as quoted using {fmt}?

Here is an example code showing what I want to achieve:

fmt::print("Hello {}!", "Terens");

I want the code to print Hello "Terens"! instead of just Hello Terens!.

EDIT: I want to use the API for printing different data not known beforehand (I am writing this for a library, so I specifically want a quoted output when the data is a std::string or std::string_view.

Terens Tare
  • 129
  • 1
  • 14
  • 5
    `"Hello \"{}\"!"` or `R"(Hello "{}"!)"`? – Jarod42 Mar 09 '21 at 14:20
  • I want to use it with different types, but want to surround the output with quotes if it is a string. – Terens Tare Mar 09 '21 at 14:21
  • 1
    You could change the "Terens" into "\"Terens\"" - and any other string you want output in quotes – ericpat Mar 09 '21 at 14:23
  • @ericpat I don't want people to quote their string by themselves. I think I will use a `is_string` trait and a ternary on the format string. – Terens Tare Mar 09 '21 at 14:25
  • If you know that the input is going to be a string - you could make a preprocessor macro to add quotes around their input like #define QUOTE(x) "\"" x "\"" and pass QUOTE(szInput) as the parameter to fmt::print – ericpat Mar 09 '21 at 14:27
  • You might create a wrapper class/function `template const T& Quote(const T& t) { return t; } std::string Quote(const std::string& s) { return "\"" + s + "\""; }` and then`template void hello(const T& arg) { fmt::print("hello {}!", Quote(arg)); }.` – Jarod42 Mar 09 '21 at 14:27
  • Or wrapping your own type, you might [customize](https://fmt.dev/latest/api.html#formatting-user-defined-types). – Jarod42 Mar 09 '21 at 14:33
  • @Jarod42 I'm aware of `fmt::formatter`, but I'm not sure I can customize it for `std::string` (AFAIK it is already specialised). Writing a `Quote` method looks good, but I think I will write a `is_string_like` trait and use an `if constexpr` for separating the cases. – Terens Tare Mar 09 '21 at 14:55
  • 1
    `Quote` method here **create** new string (with its overhead :-/), returning a `QuoteWrapper` would allow to have customization point with your type (and indeed you cannot (and should not even if it was possible) add (external) customization for foreign type, at least one should be your). – Jarod42 Mar 09 '21 at 15:01
  • How do you want your api to look like? – KamilCuk Mar 10 '21 at 13:37

2 Answers2

5

You can either wrap "{}" in quotes in the format string or use the ? specifier. For example (https://godbolt.org/z/nxajzs5so):

#include <fmt/core.h>

int main(){
  fmt::print("Hello {:?}!", "Terens");
}

Output:

Hello "Terens"!
vitaut
  • 49,672
  • 25
  • 199
  • 336
  • 2
    I totally forgot about `std::quoted`. Looks perfect for this case, thank you! – Terens Tare Mar 10 '21 at 20:30
  • Sadly, it looks like the version of `` adopted into C++20 doesn't contain `std::quoted` support; and (probably for that reason) version 9 of {fmt} removed support for `std::quoted` too. So now `fmt::format("Hello \"{}\"!", "Terens")` is the best you can do, unless I'm missing something. – Quuxplusone Apr 18 '23 at 18:45
  • @Quuxplusone, thanks for pointing this out. {fmt} now has built-in escaping support with ?. I updated the answer. – vitaut Apr 18 '23 at 19:00
2

I want to use the API for printing different data not known beforehand (I am writing this for a library, so I specifically want a quoted output when the data is a std::string or std::string_view.

The following code:

#include <fmt/core.h>

template<typename T>
const T& myprint_get(const T& t) {
    return t;
}
std::string myprint_get(const std::string& t) {
    return "\"" + t + "\"";
}

template<typename ...T>
void myprint(const char *fmt, T... t) {
    fmt::print(fmt, myprint_get(t)...);
}

int main() {
    myprint("{} {}", std::string("string"), 5);
}

outputs:

"string" 5

It should be enough to get you started.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Excellent solution. I think I will use `fmt::format` instead of `"\"" + str "\""` since I want it to work with `std::string_view` as well. – Terens Tare Mar 10 '21 at 20:53
  • `fmt::format` is a waste of cpu to just suffix/prefix with `"`, just `"\"" + std::string(str) + "\""` copy the string. – KamilCuk Mar 10 '21 at 21:13
  • @KamilCuk Are we certain that creating and appending to a temporary `string` is better than `fmt::format()`ting to one? For all I know, `fmt` does some magic to `reserve` and `append` more efficiently... – underscore_d Apr 17 '22 at 12:10