Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to format strings in C++ like python's f-strings?

In Python it's possible to format strings conveniently using f-strings:

a = 42
print(f"a = {a}") # prints "a = 42"

Is it possible to do something like this in C++ at compile time, or is format("a = {}", a); the closest thing to f-strings?

EDIT: I'm not saying that that these formatting methods should work at runtime. Can't compiler just look up variable visible in current scope by it's name at compile time? Like, if it encounters string f"foo = {foo}" it could just substitute this string to std::format("foo = {}", foo) in code

like image 818
Bamberghh Avatar asked Jul 07 '20 17:07

Bamberghh


People also ask

Can you format a string in Python?

Python uses C-style string formatting to create new, formatted strings. The "%" operator is used to format a set of variables enclosed in a "tuple" (a fixed size list), together with a format string, which contains normal text together with "argument specifiers", special symbols like "%s" and "%d".

Is F-string faster than format?

f-strings are faster than both %-formatting and str. format() . At runtime, each expression inside the curly braces gets evaluated within its own scope, and then it's put together into the final string.

What is f {} in Python?

The f-string was introduced(PEP 498). In short, it is a way to format your string that is more readable and fast. Example: The f or F in front of strings tells Python to look at the values inside {} and substitute them with the values of the variables if exist.

What is the difference between F-string and format in Python?

Summary: f-string is more readable and easier to implement than % and . format() string formatting styles. Furthermore, using f-strings is suggested for Python 3.6 and above while . format() is best suited for Python 2.6 and above.


4 Answers

C++ doesn't have reflection. As a result, it is impossible to offer a reference to a variable by its name.

But as @NicolBolas points out, even if reflection was present, there would be more needed. Not only would it require huge language support (telling the called function what the variables were in the calling scope), it would also prevent tons of optimization. The compiler could not remove any variable, as the format string (when not known at compile time) could end up referring to any variable in-scope.

So, no. std::format is the closest.

like image 98
Jeffrey Avatar answered Oct 27 '22 18:10

Jeffrey


C++ has no f-string mechanism. String interpolation is not possible in the language as it stands. Even with compile-time function calls, you would also need to have such a function "reach out" into the caller's scope to retrieve the contents of those variables and format them appropriately. That would mean that the behavior of a function-based string interpolation mechanism would have to be different, not just based on what string it takes, but based on where it gets called.

That's not a thing C++ really allows.

like image 6
Nicol Bolas Avatar answered Oct 27 '22 17:10

Nicol Bolas


There's no good way do that.

With some heavy macros you could achieve syntax like F(a = ARG(a)) (where F and ARG are macros), and it will kind of work, but with major limitations (see below).

Here's an example implementation relying on libfmt:

#define F(...) F_LOW( (str, __VA_ARGS__ ) )
#define ARG(...) )(expr, __VA_ARGS__ )(str,

#define F_LOW(...) ::fmt::format(F_END(F_STR_LOOP_A __VA_ARGS__) F_END(F_ARG_LOOP_A __VA_ARGS__))

#define F_END(...) F_END_(__VA_ARGS__)
#define F_END_(...) __VA_ARGS__##_END

#define F_CAT(a, b) F_CAT_(a, b)
#define F_CAT_(a, b) a##b

#define F_STR_LOOP_A(...) F_STR_LOOP_BODY(__VA_ARGS__) F_STR_LOOP_B
#define F_STR_LOOP_B(...) F_STR_LOOP_BODY(__VA_ARGS__) F_STR_LOOP_A
#define F_STR_LOOP_A_END
#define F_STR_LOOP_B_END
#define F_STR_LOOP_BODY(tag, ...) F_CAT(F_STR_LOOP_BODY_, tag)(__VA_ARGS__)
#define F_STR_LOOP_BODY_str(...) #__VA_ARGS__
#define F_STR_LOOP_BODY_expr(...) "{}"

#define F_ARG_LOOP_A(...) F_ARG_LOOP_BODY(__VA_ARGS__) F_ARG_LOOP_B
#define F_ARG_LOOP_B(...) F_ARG_LOOP_BODY(__VA_ARGS__) F_ARG_LOOP_A
#define F_ARG_LOOP_A_END
#define F_ARG_LOOP_B_END
#define F_ARG_LOOP_BODY(tag, ...) F_CAT(F_ARG_LOOP_BODY_, tag)(__VA_ARGS__)
#define F_ARG_LOOP_BODY_str(...)
#define F_ARG_LOOP_BODY_expr(...) , __VA_ARGS__

For example, with those macros F(a=ARG(a), b=ARG(b)) will be equivalent to pythonic f"a={a}, b={b}", and will expand to

::fmt::format("a=" "{}" ", b=" "{}" "", a, b)

which is equivalent to

::fmt::format("a={}, b={}", a, b)

Looks good so far? It can even handle escape sequences (somehow)!

And here are the mentioned major drawbacks:

  • Any sequence of whitespace characters is replaced with a single space.

    • Additionally, any leading and trailing whitespace, and any whitespace surrounding ARG is removed.
  • Any macros in the string are expanded.

  • The string can't contain unbalanced parentheses.

  • ARG(...) can't appear inside of parentheses.

IMO, this makes it unusable for anything except debug output.

Possible workarounds include:

  • Changing the syntax to one of:

    • F("a = " ARG(a)) (I don't like how it looks)
    • F("a = ", (a)) (looks decent, but requires Boost.Preprocessor or equivalent generated macros)
    • F("a = ", ARG a) (same)
    • F("a = ", ()a) (same)
  • Adding a sub-macro (similar to ARG), let's say STR("..."), to include string literals verbatim. This will let you include any whitespace/parentheses/unexpanded macros/... you want.

    Example: F(a = ARG(A)) gives you a =42. If you want a space after =, you do F(a = STR(" ") ARG(A)).

    The syntax is way too obscure, so implementing this is left as an exercise to the reader.

  • ...


Bonus: with the right flags (or #pragmas), most compilers will let you include $ in a macro name. It means you could use $(expr) instead of ARG(expr), if you wanted to...

like image 5
HolyBlackCat Avatar answered Oct 27 '22 17:10

HolyBlackCat


No, there is no string interpolation in C++. And the only work in this area I've found is a "very very rough draft of a proposal" Interpolated String Literals from google back in 2015.

We just got the formatting library in C++20 and even that is does not appear to be yet implemented in any standard library implementation (as of July 2020). String interpolation would require language support and the standard committee is always more reluctant to change the language compared to changing the library.

like image 4
bolov Avatar answered Oct 27 '22 19:10

bolov