You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As there is no possibility of passing a reference to the anonymous function (aka. lambda), you need to capture a variable as a pointer. That makes a new idiom emerge from the code: &$* - capture variable by pointer (and use as a reference).
The following code:
main: () -> int = {
i := 42;
l := :() = {
std::cout << i&$* << std::endl;
};
l();
}
Will generate (skipping boilerplate):
[[nodiscard]]automain() -> int{
auto i {42};
auto l {[_0 = (&i)](){ // capture by pointer
std::cout << *cpp2::assert_not_null(_0) << std::endl; // dereference in place of use
}};
std::move(l)();
}
i& - address of i, (i&)$ - $ captures the value on the left (in that case, it is a pointer to i), ( (i&)$ )* - * dereference the value captured by $ (in that case, it is a pointer to i)
So, effectively i&$* is a reference to i that is captured by a lambda.
That makes me think, as there are no references in cpp2, how will we write functions that return a reference in cpp2? Like the [] operator for containers, or do we just start returning pointers in all cases?
Val also does not have references but they have projections which work by something called inversion of control, but I don't think those can be implemented at a transpiler level or if herb would even like to implement them.
So with the current state of cppfront, i means "local variable from inside the function body", i$ means "capture by value" and i&$* means "capture by reference"? I'm not sure I like that.
I wonder, would it make sense to have i$ capture by reference, and then you can capture that value by adding another capture as i$$? That doesn't quite fit with the rest of the Capture design notes, where a copy is captured "Postconditions: [[post: v.size() == v.size()$ + 1]] -- capture a copy of v.size() on function entry for use later when the whole postcondition is evaluated on function exit".
"capture" means "take an expression in this context and store a copy of its current value for use later."
On the other hand, i isn't even in this context yet until you add the $. It could be that the intention was that everything in the outer scope is implicitly in the function scope, with capture by reference, and $ makes it capture a copy of the value, but that seems unsafe compared to current behavior, as it means that you are implicitly capturing things that may go out of scope before they may be used. Maybe there's an intention of having lifetime analysis done for anything captured by reference.
I guess that's enough speculation for me until @hsutter can clarify his thoughts here.
@gregmarr just to clarify. i&$* does not mean capture by reference. It captures a pointer (auto l {[_0 = (&i)](){ ) and dereferences it. It behaves like a reference in that context (std::cout << *cpp2::assert_not_null(_0) << std::endl;)
@filipsajdak To answer your original question, yes that's the idiom. I do realize that &$* is an emergent idiom that comes from the design choice of "capture everything by value" + "in-place using $"... and I realize that it can look like line noise. (I had to look up "grawlix" :) ... that it looks like swearing could be a feature, rather than a bug, since it calls attention to what can be a dangerous construct...) I'm sitting back to see how it works out as I/we get experience using it in actual code.
Activity
filipsajdak commentedon Jan 31, 2023
The funny thing is that it looks like grawlix :D
AbhinavK00 commentedon Jan 31, 2023
That makes me think, as there are no references in cpp2, how will we write functions that return a reference in cpp2? Like the [] operator for containers, or do we just start returning pointers in all cases?
Val also does not have references but they have projections which work by something called inversion of control, but I don't think those can be implemented at a transpiler level or if herb would even like to implement them.
filipsajdak commentedon Jan 31, 2023
@AbhinavK00, that is an excellent question. It is already covered by the
forwardreturn type passing style.Generates (skipping boilerplate):
filipsajdak commentedon Jan 31, 2023
cppfront syntax concentrates on intentions, not implementation details (reference here is an implementation detail). You can read more here: https://github.com/hsutter/708/blob/main/708.pdf (@hsutter paper about
Parameter passing)AbhinavK00 commentedon Jan 31, 2023
Thanks for the answer! Now we just need something similar for lambdas lol
gregmarr commentedon Jan 31, 2023
So with the current state of cppfront,
imeans "local variable from inside the function body",i$means "capture by value" andi&$*means "capture by reference"? I'm not sure I like that.I wonder, would it make sense to have
i$capture by reference, and then you can capture that value by adding another capture asi$$? That doesn't quite fit with the rest of the Capture design notes, where a copy is captured "Postconditions:[[post: v.size() == v.size()$ + 1]]-- capture a copy ofv.size()on function entry for use later when the whole postcondition is evaluated on function exit".I guess that's enough speculation for me until @hsutter can clarify his thoughts here.
filipsajdak commentedon Jan 31, 2023
@gregmarr just to clarify.
i&$*does not mean capture by reference. It captures a pointer (auto l {[_0 = (&i)](){) and dereferences it. It behaves like a reference in that context (std::cout << *cpp2::assert_not_null(_0) << std::endl;)gregmarr commentedon Jan 31, 2023
@filipsajdak I understand, the effect is the same.
filipsajdak commentedon Jan 31, 2023
As we discuss references here, I think it is worth mentioning @hsutter sentence he said when I mentioned that
std::optionalcould not hold references:More here: #106 (comment)
hsutter commentedon Mar 1, 2023
@filipsajdak To answer your original question, yes that's the idiom. I do realize that
&$*is an emergent idiom that comes from the design choice of "capture everything by value" + "in-place using$"... and I realize that it can look like line noise. (I had to look up "grawlix" :) ... that it looks like swearing could be a feature, rather than a bug, since it calls attention to what can be a dangerous construct...) I'm sitting back to see how it works out as I/we get experience using it in actual code.Emit anonymous functions as `constexpr` or `mutable` lambdas