Possibility for fstring-like code in C++ using meta-programming

391 views Asked by At

The introduction of std::format into C++20 is very nice and can even be used (from fmtlib itself) in iterations before C++20. It certainly allows for less verbose code than the iostream equivalent code that came before it.

Contrast, for example, the following two statements where the latter seems clearer and more succinct (to me, anyway):

std::cout << "Your name is " << name << " and your licence number is "
    << std::setw(9) << std:setfill('0') << num << ".\n";
fmt::print("Your name is {} and your licence number is {:09}.\n", name, num);

However, the iostream method still has one small advantage in that the things that are to be printed are in the sequence in which they're listed, so you can read them from left to right. Well, provided you can ignore all the "noise" in the statement (<<, setw, and the like) :-)

In other words, you don't really have to look (too far) ahead to the arguments to see what's being printed.

In that sense, std::format is more like Python's str.format() (and C's printf) method as they both list a format string with special markers (like {} or %09d), followed by arguments which are slotted in to where those markers are.

I was wondering whether it would be possible to use C++'s recent meta-programming features to provide true f-strings, something like Python's:

print("Your name is {name} and your licence number is {licence_num:09}.\n");

This inlining of the expressions to output would be much easier to read in the code. I know rather little of the code generation capabilities of C++20, so I wonder if it's plausible to take a statement like that above and generate a new statement in the "accepted" form.

This would be done as part of the compilation process itself (not some pre-processing tool that likely introduces extra opportunity for problems). That way, you would never actually see the modified line, it would just magically be made from the more readable form, compiled, and thrown away.

In other words, an expression such as:

magic::fstring("Your name is {name} and your licence number is {num:09}.\n");

would become this for compilation:

fmt::format("Your name is {} and your licence number is {:09}.\n", name, num);

I've read the Sutton and Childers P1717R0 "Compile-time Metaprogramming in C++" document and tried to do some other research, but I'm not entirely sure that the consteval and meta things provide enough power to break apart a string like that above at compile time.

Can anyone more knowledgeable about these features in C++20 indicate whether this would be a viable approach and, if so, how it would be done?

Or, alternatively, there may be another way of doing this, I'd be grateful for any suggestions on that front.

3

There are 3 answers

0
Aykhan Hagverdili On

That syntax is not possible without a change to the language itself, but how about something like this:

#include <fmt/format.h>

#include <iostream>
#include <iterator>
#include <string>

template <class... Args>
auto fstring(Args&&... args) {
    std::string str;
    (fmt::format_to(back_inserter(str), "{}", std::forward<Args>(args)), ...);
    return str;
}

int main() {
    auto const name = "Foo Bar";
    auto const age = 24;

    std::cout << fstring("My name is ", name, " and I am ", fmt::format("{:#x}", age), " years old\n");
}
9
user17732522 On

No, this is fundamentally impossible. There is no way to reference an entity by its name from a string value, not even at compile-time.

There is currently no reflection support in C++ that would allow for this. The paper you refer to is a proposal that hasn't made it into the standard. There seemed to have been a stall in work on reflection support for a while, but just recently there was a paper P2996 targeting C++26, although I think what it proposes would still not allow for what you are asking for here.

There has also been an initial paper P1819 which specifically adds support for this behavior in string literals without depending on reflection and code synthesis support. However, it hasn't seen any progress in the last year and it is currently up to the paper's author to present a proposal based on the feedback.


To clarify:

If the functionality in P1717 was added to C++20, then it would probably allow for what you want, although I haven't verified this. However, P1717 is just a proposal that never was incorporated into a standard.

There is a also a TR "Extensions for reflection" (ISO/IEC TS 23619:2021) which extends C++20, but it follows a different approach to reflection than P1717 and I haven't checked whether it would allow to implement what you want, but I think it did not because it lacks code synthesis.

1
vitaut On

It is not possible to do in C++23 without hacks such as a custom preprocessor. There are two proposals to add string interpolation to C++:

So it might be added in future versions of the standard.

fstrings in Rust show that at least some limited form of interpolation using metaprogramming is possible in principle. But not with what we have now in the standard and not even with P2996 which is the most promising reflection proposal at the moment.