Added macros that returns formatted value - #96
Conversation
|
Hi @JoyHak , thanks for your code. What exactly is the pain point you're trying to address here? The intent of icecream-cpp is to deliver a quick and practical way to inspect variable contents and code paths. After inspecting/debugging the code, icecream-cpp usage should be removed from the project before delivery. The output generated by icecream-cpp is meant to be consumed by the programmer during development, not by the end user. Those objectives differ from the {fmt} library, which generates messages to be printed to users, saved in log files, etc. In your examples, when you write: why is this better than just: I'm open to discussion and I'm open to being convinced otherwise. But for now, I think that if you're already using fmt::print("Pushing i: {:#x} to the v: {}", i, v) The only place where I can see that integrating icecream-cpp with {fmt} adds value is formatting ranges with Python-inspired slicing and printing structs with "Clang dump struct". But even there, the focus is on the printing itself, not on the variable name together with that printing. I imagine that a function like: that would return just the printed value (no prefix, no variable name) would solve the problem better. What do you think? |
I'm trying to extend the IceCream library, because it provides some uniq formatting that
For debug builds it can be useful to mix IceCream with some
I can't slice any range using
Furthermore IceCream provides structure and class dump (I'm using clang 21 right now), and that's really powerful. But all this helpful information can't be mixed with support text like
In addition, I think that IceCream requires at least some formatting that can simplify reading stdout: |
|
OK @JoyHak, I agree with you here. It would be nice to customize a message before showing the value. It would help the debugging adding context beyond the variable name. However, from a design perspective, I'm not comfortable with adding a new pair of macros returning a string instead of printing it. The problem is that if we have these two macros at the same logical level in the API as the other six (IC, IC_A, IC_V, IC_F, IC_FA, IC_FV), it will move the Icecream-cpp library uncomfortably close to a formatting library. From the eyes of an onlooker, that could put Icecream-cpp and {fmt} as having a bigger overlap in their problem domain. Something that I wish to avoid. If someone believes that Icecream-cpp works like a formatting library, and uses it to that purpose, it would lead them to a bad experience. We have design goals possibly at odds with the design goals of a good formatting library. Moreover, we use {fmt} (or STL formatting) internally. If we use {fmt} to generate a string, that the user will send to {fmt} again to print. I don't know, my spider-sense is telling me that this isn't a good design.
Sure, all that text to justify this: I'm inclined to go in the line you have suggested here. We can add the feature to print the additional text to Icecream itself. Some details can change while I think better about it, but a rough idea is this: we change the "output formatting" syntax to support it.
This is my rough solution idea. What do you think about it? Can you see any problems with it? Do you have any improvements? |
|
auto vec = std::vector<int>{10, 11, 12};
IC_F("{1:name} contains {1:[1:2]}", v); // vec contains [10, 11]
IC_F("{1:name} is the {1:type}", v); // vec is the vector<int>
int a = 1, int b = 2
IC_F("{1:name} at the {1:address}; {2:name} at the {2:address}", a, b); // a at the 000000FFFA..., b at the 000000CFFA... |
|
If multiple meta specifiers are present, they should be applied sequentially to each variable (one meta specifier per variable), similar to the format specifier that does not include a variable index: auto a = 1, auto b = 2
IC_F("{:d} != {:d}", a, b); // 1 != 2 - no index, apply format one by one
IC_F("{:name} and {:name}", a, b); // a and b - no index too, apply meta one by one
IC_F("{:name} is {:d} and {name} is {:d}", a, b); // a is 1 and b is 2 |
This is something I want to explicitly avoid, having more than one variable mapped to the same formatting string. Doing so would break Icecream-cpp internal logic, such as the ability to break long lines with proper indentation. The current design is: one variable produces one string, then we merge all strings together for output. Moreover, it would move us uncomfortably close to {fmt}, adding complexity we'd need to maintain. This is a subjective boundary: where do we draw the line past which we've gone too far into the {fmt} domain? I believe we should limit one formatting string to one variable. In a one-to-one mapping: And since the formatting strings are identical, this simplifies to: That said, I like the idea of relying on what the C++ standard provides for meta information. C++26's reflection support may offer some useful features, I will research about it. We just need take care to avoid potential ambiguities. For example, if we have: what if So we need a syntax where the "meta information id" and the "format specifier" are unambiguous. Since we map one variable to one output, we don't need numeric indices or any other variable selector. In this scheme, both the variable's "name" and its "value" are meta information ids. This suggests: This leads to the reasoning in my last message, where I suggested: The Alternatively, we could adopt a syntax like: So: I'll think more about these two syntax options. I'm currently split between them. Regardless, thank you very much for your discussion here! I believe it will inspire some very nice features. |
|
I thought the new characters like Or it can be something like It's simpler then pairs of formatting strings, but it doesn't allows to specify multiple meta specifiers, e.g. both
You mean the whole formatting string like
|
I believe that it may support meta specifiers without formatting, i.e. you can't specify The idea is to keep
|
|
My last message was confusing. Let me try to explain my idea in more precise terms.
In theory, this wouldn't be a big problem. We shouldn't break backward compatibility just for fun, but following the design principle:
If we are torn between backward compatibility and a better API, in general having a better API should win. However, I'm with you here. Having just the To fix the terminology, in this code line: The whole My point is that we should restrict each format string to a single variable. That will avoid adding complexity to the library when breaking long lines, parsing the string, etc. With this one-to-one mapping, we no longer need to specify (with indexes or whatever) which variable is being referenced by each replacement field. There is only one variable being printed to that format string. The syntax would be: Where A short string
A For now, no other The same This is my current thinking. I believe we are converging to something cool. |
Awesome, I like this design!
This big specification is a bit confusing for a newbie, so I want to suggest how to change it a little bit. Instead of strictly describing the specification for the new formatting, the main elements will be described using Possible documentationMacros like
To better understand this, let's take a look at example output depending on the given string: auto v = 1.1;
IC_F("{:#x}", v); // 0x1 - format as hexadecimal
IC_F("{:d}", v); // 1 - format as decimal
IC_F("{name} is {}", v); // v is 1.1As you can see, the output value changes (formatted) according to the given rules, for which we allow the following syntax: Here's your explanation about meta: Where For example, you can get information about what variable you are using in a given piece of code. This information can be useful for debugging variables with the same name in different scopes IC_F("{name} is {value}", v); // v is 1.1
IC_F("{name} is {type}", v); // v is int
IC_F("{name} is {value}", v); // v is 1.1
IC_F("{name} is {}", v); // v is 1.1 - same as aboveA IC_F("{{type}}:: {type}", v); // {type}: intIt's not clear to me: You can completely avoid IC_F("I don't have vars", v); // I don't have vars
// plain text, no formatting...I think I need to commit the changes to readme to see how new docs looks with the current docs. So far, I'm not sure which section(-s) needs to be updated to not scare the new user. |
I wanted to combine the formatting capabilities of IceCream with
{fmt}and be able to quickly output and format objects. Since IceCream does not support"{} {}"formatting, such formatting can be created withIC_R(return) andIC_FR(format and return):I'm trying to reuse IceCream formatting without rewriting anything. The macros passed local tests successfully, and I used them in my projects.