3 ms·
Ah, the problem of detecting the last (or the first) element and treating it specially: bool first = true; [:expand(nonstatic_data_members_of(^
by Joker_vD 2y ago
Ah, the problem of detecting the last (or the first) element and treating it specially:
bool first = true;
[:expand(nonstatic_data_members_of(^^T)):] >> [&]<auto nsdm>{
if (not first) {
*out++ = ',';
*out++ = ' ';
}
first = false;
out = std::format_to(out,
".{}={}",
identifier_of(nsdm), m.[:nsdm:]);
};
I really wish there were a for/for-each variant that straightforwardly supported interspersed actions, something like this:
[:expand(nonstatic_data_members_of(^^T)):] >> std::make_tuple([&]<auto nsdm>{
out = std::format_to(out,
".{}={}",
identifier_of(nsdm), m.[:nsdm:]);
}, [&]{
*out++ = ',';
*out++ = ' ';
});
An actual for-loop could also get a simple extension (which is quite easy to compile efficiently) but I can't figure out an intuitive enough syntax:
for (int i = 0; i < 101; i++) {
std::cout << i;
} then {
std::cout << ", ";
}
is a bit fishy because the "then" block (can't call it "else", obviously) technically happens before the main loop body (i.e. inside it, the values of i would be 1, 2, ..., 100) but writing it before the main body would be even more confusing. Any suggestions? I'd really like to figure this feature out because I personally find it more useful than the "else block, but for the loops" feature.
- saurik 2y agoIt happens before the main loop body but it doesn't happen before the first loop, and it happens after the increment, so I frankly read it initially with skeptical eyes but then felt like it was more intuitive than it was being made out to be? (And FWIW, I am someone who has used for-else on occasion, yet have never found it intuitive.)
- wrs 2y agoMy favorite way of handling this particular case (in any language) is to express the elements as a sequence (preferably a lazy one that doesn’t actually allocate) and use whatever the stdlib calls “join” (my favorite name is Haskell’s “intercalate”) to insert the delimiters. I haven’t used C++ in decades, but Rust typically makes this pretty easy to write, and low-cost.
- pbsd 2y agoNormal for loops can't really make that work, they're too general, but range for loops plausibly could. Something like for(auto&& e : range) { std::print("{}" e); join { std::print(", "); } } where join {} is effectively syntax sugar for if(std::next(__first) != __last) {}. The fmt library makes this sort of task easier by providing the join adaptor; this example would become fmt::print("{}", fmt::join(range, ", "));
- leni536 2y agoint i = 0; while (true) { cout << i; ++i; if (i == 101) { break; } cout << ", "; }
- Joker_vD 2y agoThat's too much branching. int i = 0; goto main_part; do { cout << ", "; main_part: cout << i; label_for_continue: // yeah, what about "continue"? i++; } while (i <= 100); That's what most for-loops get transformed to during the translation anyhow (with the condition check on the bottom), but writing this by hand? Ugh. Edit: actually, looking at Godbolt, it seems that gcc compiles your suggestion into exactly this shape, while clang splits the first iteration out, into something equivalent to cout << 0; for (int i = 1; i < 101; i++) { cout << ", "; cout << i; } Hmm.