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
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".
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.
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.
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.
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.
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.
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.
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 #pragma
s), most compilers will let you include $
in a macro name. It means you could use $(expr)
instead of ARG(expr)
, if you wanted to...
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With