%PDF-1.5
%äðíø
38 0 obj
<>
stream
xÚíZË+5Ýóý1¶ËO)Ê ®Ä4;Äbz2Ãüÿ*Ûe»Ý¤ûÂwÑítü8õpÕ©òLLjø¼Æÿåôò;~ûßÊçS÷ýþ¿ß=Mßþ &å
VÖLOorNhç¦ÂáO×_ÎR:
)¥Ñø¹J óåRùÍEÛs:´ærJ/-N7AJõÐôîzùõéÇéû§m`0EvKC
ñ|&BÁ¥_i!ÜÅÓWG`fÎO(hú£^.’
¿,Àq0B²’Zâc%§R”ZÛmÆÐú¸
óJH ]T5Óù_ZØȼ¸ï!op*SÛ$T²Ë²ß÷ÞÞ·M#ÓägBKÚHÞl ´®/#®eÆ5¤?¥QAt³/:óú-oeC=Wut°ä0½ÊÖß¹ö.wFmìC«Y]Ê»¬VR¾ÝáI¢F/moÎk$øò}é¦#÷ÒqüùSyøsÃÏX´x®<`Á·Í÷aá|ÕÖ`kûòLªIcC6B3'n
Ô8-)d%ÕÂèXtáØëÑÙGi/'YÉ{Yì«{'[{É3íßn(»ÚoôáÈYqPFRÎþ´TVe'e HS§ N**¤a>W¯ÕEÄþ»Ófðxq@xÍØâ¹Ú¤#-ÏÂç=+çáòxø³ë»y?0°Â6¯àJçØ4oä¢àß
ËzaçÏ×k62ëǻƵwòQϾá¿2ÅW8)¢;:0å½Äd. ¦VÒ
K´Ý±ê³ gvÎ:G´9ì÷Õ @)ЮG.G@A·iº IùÂ_Ë©¤óþ*À¹;ùgWï?»MÄÒ”öÁ× HBt~ba,aKtøF®ÂE@wdn³Ìò
àY¯eö4÷Cc+°mJ¿¡^4ñ@;3
gÑÏl¦cÿû’õÞ{<ÌíÈ ¨ÑvópvP%VR(Êt¹Ze´áÜ(aÏf[`ÉÓ{ß^¤x]y½ýòóÂÀ.?Ö*¨Â¨?~'O<í°§Z*ÇPJ¼ x*"ÀÅSsD
%§ û0àI$Ò&H
&Æ/¾Ð÷eö»ïà~ÝR0ZbÅ}ZóìudRnFO¦£3ô|¡p&~{¿3ÂÙ0Êòáóÿj¿üî®ßnô«v^ÀÔç¥ËÈÌ^+ܽMÌGt{kõtôµw»è¤ÅL¦2òÀÅ]N)ü&×î¹äðÄjB:%Ýô-Õ47ð¼^K¤
o×ÙKh¡ög^k¹øÍUG`RÇ0Íu4{21 0¦,G°A µ»¿È9b)ÎkG¥Ð×µ²5i?ôt¨ÝÐ^wÈ©<ÌÈoµ s)amVÍ$[-ÿô«ÑëÕ¹å^Þiù%ÐÑëÈ´;hó^þz³DË t£²%+îæ=GúálýüÜ_ëßëë,qßxAX<à6ÂBÞÖO°¹Z+]ð¾µl#¤¯W×e(î~¹§#z,0Y çÎAkMp¹
êÒ÷¢¼óͯôöµ°Ýa©ÿGÅ mks,9æó2ui´&Ýug¹áÚª¦Tn÷MÉ%¨µT·¥Á8N÷"M¨»u z¹éy+ÄеÆå" ÍÒn!ñû{«Ü½¡FÇÐ5«-ÔKK~)U¾f1jïÎe#|yÜý$ãb¡D®K,mó5âÀãêC)ë.e
DLöës¥útµµ³½[ÖgÒP§v·L³=ºMé[Ò>/ïâ6îÞG.ÌíÈí°§Z*ÇPJ¼ x*ÀÅSsD
ÌÈoµ s)amVÍ$[-ÿô«ÑëÕ¹å^Þiù%ÐÑëÈ´;hó^þz³DËà6ÂBÞÖO°¹Z+]ð¾µl#¤¯W×e(î~¹§#z,0Y çÎAkMp¹
êÒ÷¢¼óͯôöµ°Ýa©ÿGÅ mks,9æó2ui´&Ýug¹áÚª¦Tn÷MÉ%¨µT·¥Á8N÷M¨»u z¹éy+ÄеÆå>`Á·Í÷aá|ÕÖ`kûòLªIcC6B3n
Ô8-)d%ÕÂèXtáØëÑÙGiYÉ{Yì«{[{É3íßn(»ÚoôáÈYqPFRÎþ´TVee HS§>>
11 Comments
puffybuf
I'm pretty sure boost::format can do this, though not inline in the string. Do we really need more complexity in cpp? isn't it complex enough?
mkoubaa
Tangent: this sort of thing can be implemented without any change to libc++ (the runtime). Updates to compiler versions are sometimes postponed by users with big codebases that treat a libc++ change as something major.
Why don't we see gcc or clang or msvc back porting stuff like this to an older version with a sort of future tag. It's normal to see __future__ in the python ecosystem, for instance.
edflsafoiewq
So the f-string literal produces a basic_formatted_string, which is basically a reified argument list for std::format, instead of a basic_string. This allows eg. println to be overloaded to operate on basic_formatted_string without allocating an intermediate string
In exchange we have the following problems
There are two other proposals to fix these problems.
amluto
This links to a “decays_to” proposal:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p33…
And observes that this additional feature is needed to avoid dangling references. And, as a long time C++ programmer, this illustrates one of the things I dislike most about C++. In most languages, if you make a little mistake involving mixing up something that references something else with something that contains a copy, you end up with potential overhead or maybe accidental mutation. In Rust, you get a compiler error. In C++, you get use-after-free, and the code often even seems to work!
So now we expect people to type:
And those people expect s to act like a string. But the designers (reasonably!) do not want f to unconditionally produce an actual std::string for efficiency reasons, so there’s a proposal to allow f to produce a reference-like type (that’s a class value, not actually a reference), but for s to actually be std::string.
But, of course, more advanced users might know what they’re doing and want to bypass this hack, so:
Does what they programmer actually typed: s captures foo by reference.
What could possibly go wrong?
(Rust IMO gets this exactly right: shared xor mutable means plus disallowing code that would be undefined behavior means that the cases like this where the code might do the wrong thing don’t compile. Critically, none of this actually strictly requires Rust’s approach to memory management, although a GC’d version might end up with (deterministic) runtime errors instead unless some extra work is done to have stronger static checking. And I think other languages should learn from this.)
dgfitz
Somehow I manage to get by just fine with c++11. I have refactored more than a few codebases that use 17 or greater.
Strangely, the codebase became more maintainable afterwards.
efitz
When I saw the title I thought “F-strings” might be some novel variant of P—strings. I was disappointed that this is just about formatting. I really would prefer safer string handling in modern C/++
andyg_blog
I agree that we should have safe-by-default "decay" behavior to a plain ol std::string, but I'm also picking up that many aren't certain it's a useful syntactic sugar in top of the fmt lib? Many other languages have this same syntax and it quickly becomes your go-to way to concatenate variables into a string. Even if it didn't handle utf-8 out of the box, so what? The amount of utility is still worth it.
mixmastamyk
So, the f-string in Python is "spelled" that way because another leading character was the only ASCII syntax left for such a thing. It's odd that PRQL and now potentially C++ might copy it. In the PRQL case it was a new thing so they could have chosen anything, double quotes (like shell interpolation) or even backticks, that seem to make more sense.
Also the f- prefix was supposed to be short for format and pronounced that way. But "eff" caught on and now devs the world over are calling them "eff strings" … funny. :-D
neonsunset
Reinventing C#’s FormattableString and interpolated string handlers :)
ggm
I'm going to make an asinine prediction. We will be exploring F-strings in future languages in 100 years time, encountering the same problems and questions.
I still use printf semantics in Python3 despite trying to get with the program for symbolic string/template logic. I don't need to be told it's better, I need some Philip-K-Dick level brain re-wiring not to reach for
modes of thinking.
alterom
Yeah, I really missed ubiquitous C preprocessor macros in C++, so let's bring them back, but now inside string literals. Sweet.
Seriously, I just keep being amazed that people are running with the idea of having a full-blown untyped and unchecked formatting mini language (that's what libfmt, which became C++20 format, literally calls it) inside string literals — i.e., the part of the source code that you're specifically telling the compiler to not treat as code.
Think about it for a minute.