Reputation: 529
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
Upvotes: 35
Views: 50158
Reputation: 965
I think the simplest method to use something like an "f-string" in c++ is the accepted answer to this other question C++: how to get fprintf results as a std::string w/o sprintf
Upvotes: 0
Reputation: 67
If you just want to print a string with variables in it, you could always use the ancient printf() function.
char my_name[] = "Michael";
int my_age = 107;
printf("Hello my name is %s. I am %d years old.", my_name, my_age);
sprintf() is a variant which allows you to save the output as a string (char array) and use it again, or send it to a serial port in the case of embedded systems.
I am far more familiar with C than python, so if I am missing something, please do explain.
Upvotes: 4
Reputation: 96925
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...
Upvotes: 6
Reputation: 11440
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.
Upvotes: 20
Reputation: 474476
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.
Upvotes: 5
Reputation: 75924
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.
Upvotes: 4