forgetting -betterC means no runtime bounds checking?
I was playing around with betterC, when I discovered, that if i accidently forget to provide -betterC to the compiler, it will still compile this, but, there will be no runtime bounds checking occuring. My question is: why is there no bounds checking occurring if I forget to use -betterC? module test; extern(C) void main() { import core.stdc.stdio : printf; int[5] arr = [ 0, 1, 2, 3, 4]; for (int i = 0; i < 10; i++) // oops! { printf("%d\n", arr[i]); } }
Re: forgetting -betterC means no runtime bounds checking?
On Thursday, 5 January 2023 at 09:10:00 UTC, areYouSureAboutThat wrote: btw. the output (when you forget to use -betterC): 0 1 2 3 4 src/rt/dwarfeh.d:330: uncaught exception reached top of stack This might happen if you're missing a top level catch in your fiber or signal handler core.exception.ArrayIndexError@test.d(25): index [5] exceeds array of length 5 Aborted (core dumped)
Re: forgetting -betterC means no runtime bounds checking?
On 05/01/2023 10:17 PM, areYouSureAboutThat wrote: src/rt/dwarfeh.d:330: uncaught exception reached top of stack This might happen if you're missing a top level catch in your fiber or signal handler core.exception.ArrayIndexError@test.d(25): index [5] exceeds array of length 5 Aborted (core dumped) Looks like an exception to me, which is what the default for bounds check on error will do. Because you are not using the druntime entry point, its not being caught and made to look nice to you.
Re: forgetting -betterC means no runtime bounds checking?
On Thursday, 5 January 2023 at 09:17:28 UTC, areYouSureAboutThat wrote: core.exception.ArrayIndexError@test.d(25): index [5] exceeds array of length 5 Aborted (core dumped) This is bounds checking happening.
Re: forgetting -betterC means no runtime bounds checking?
On Thursday, 5 January 2023 at 09:10:00 UTC, areYouSureAboutThat wrote: I was playing around with betterC, when I discovered, that if i accidently forget to provide -betterC to the compiler, it will still compile this, but, there will be no runtime bounds checking occuring. My question is: why is there no bounds checking occurring if I forget to use -betterC? module test; extern(C) void main() { import core.stdc.stdio : printf; int[5] arr = [ 0, 1, 2, 3, 4]; for (int i = 0; i < 10; i++) // oops! { printf("%d\n", arr[i]); } } Works on run.dlang.io for me: ```sh 0 1 2 3 4 dmd_runJFfSJW: onlineapp.d:12: Assertion 'array index out of bounds' failed. Error: program killed by signal 6 ``` On my personal ldc 1.27.1: ```sh (base) [tejasgarhewal@fedora ~]$ ldc2 -betterC -run ./D/oob.d 0 1 2 3 4 oob-06265c: ./D/oob.d:9: Assertion 'array overflow' failed. Error: /tmp/oob-06265c failed with status: -2 message: Aborted (core dumped) Error: program received signal 2 (Interrupt) (base) [tejasgarhewal@fedora ~]$ ```
How to avoid variable capturing in `foreach` loop with lambdas?
I am using CSFML D bindings and I have created my own sort of UI library for drawing elements onto the screen. One of the classes I've created is a `Button` class, which contains a delegate called `onButtonClick` which is called when the button is clicked on by the user. Up until now, everything has worked fine. I want to add a couple of buttons side by side to represent an element of a list and assign them each a unique lambda expression for that particular element in the list, this is the current code I have: ```D foreach (BoardSize boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; button.onButtonClick(); _boardSizeRow.addChild(button); } ``` Running this code, I had expected that everything would work fine. Unfortunately upon running the code, tapping each of the buttons returned only the largest `boardSize` value, the one which is gets iterated last. Note that I am still not totally sure why this is the case. At first, I was confused, but then I suspected that after the variable gets sent as a parameter to the `settingsWindow_onBoardSizeButtonClick` function, it gets changed again in the next iteration creating a sort of chain effect -- I may be wrong, but this is my suspicion. Some of the things I tried was creating a new object each time, although it didn't work. I might have not done this properly as I am a beginner to D language. I saw someone else ask a similar question as to why this is happening but that was for C#, not D, so it wasn't that much of a use to me. Help would be appreciated!
Re: How to avoid variable capturing in `foreach` loop with lambdas?
Update some time later: the only way (oof!) around this seems to be using a `static foreach` with arrays: ```D Button[3] b; static foreach (indx, BoardSize boardSize; arr) { b[indx] = new Button(); b[indx].text = format("%sx%s", boardSize[0], boardSize[1]); b[indx].onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; _boardSizeRow.addChild(b[indx]); } ``` Any other ways of fixing this annoying issue?
Error "Outer Function Context is Needed" when class declared in unittest
I've run into an unexpected problem that only seems to happen in unittests, but not outside of them. Consider the following example: ``` unittest { class Ab { int a; string b; static class Builder { int _a; string _b; Builder a(int a) { _a = a; return this; } Builder b(string b) { _b = b; return this; } Ab build() { Ab t = new Ab(); t.a = _a; t.b = _b; return t; } } } Ab ab = new Ab.Builder() .a(1) .b("ham") .build(); assert(ab.a == 1); assert(ab.b == "ham"); } ``` This fails to compile with the following error: ``` Generating test runner configuration 'builder-test-library' for 'library' (library). Starting Performing "unittest" build using /usr/bin/dmd for x86_64. Building builder ~master: building configuration [builder-test-library] source/builder.d(58,16): Error: outer function context of `builder.__unittest_L41_C1` is needed to `new` nested class `builder.__unittest_L41_C1.Ab` Error /usr/bin/dmd failed with exit code 1. ``` However, if I move the class definition outside of the unittest block, then everything works fine: ``` class Ab { int a; string b; static class Builder { int _a; string _b; Builder a(int a) { _a = a; return this; } Builder b(string b) { _b = b; return this; } Ab build() { Ab t = new Ab(); t.a = _a; t.b = _b; return t; } } } unittest { Ab ab = new Ab.Builder() .a(1) .b("ham") .build(); assert(ab.a == 1); assert(ab.b == "ham"); } ``` ``` Generating test runner configuration 'builder-test-library' for 'library' (library). Starting Performing "unittest" build using /usr/bin/dmd for x86_64. Building builder ~master: building configuration [builder-test-library] Linking builder-test-library Running builder-test-library 2 modules passed unittests ``` Why is this error only found when declaring a class in the unittest?
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Thursday, 5 January 2023 at 11:55:33 UTC, thebluepandabear wrote: ```D foreach (BoardSize boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; button.onButtonClick(); _boardSizeRow.addChild(button); } ``` Running this code, I had expected that everything would work fine. Unfortunately upon running the code, tapping each of the buttons returned only the largest `boardSize` value, the one which is gets iterated last. The problem is twofold: 1. Closures in D capture their environment by reference. 2. D (incorrectly, in my opinion) considers loop-local variables to have the same identity across each iteration of the loop within a single function call. So, `boardSize` in your event handler is a reference to a single variable whose value is overwritten on each iteration of the loop. As the event handlers are (I presume) never called until after the loop has terminated, the only value they will ever see is whichever was set by the final iteration of the loop in that function call. There are at least two possible solutions: 1. Use a `struct` to explicitly capture `boardSize` by value, instead of by reference: ```D static struct ClickHandler { // If eventHandler is not a global variable of some sort, add another field for it: BoardSize iteration_boardSize; this(BoardSize iteration_boardSize) { this.iteration_boardSize = iteration_boardSize; } void opCall() { eventHandler.settingsWindow_onBoardSizeButtonClick(iteration_boardSize); } } foreach (BoardSize loop_boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", loop_boardSize[0], loop_boardSize[1]); button.onButtonClick = &(new ClickHandler(loop_boardSize)).opCall; button.onButtonClick(); _boardSizeRow.addChild(button); } ``` 2. Use a nested function call with a `boardSize` parameter to create a copy of `boardSize` with a unique identity on each iteration of the loop: ```D foreach (BoardSize loop_boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", loop_boardSize[0], loop_boardSize[1]); button.onButtonClick = (BoardSize iteration_boardSize) { return { eventHandler.settingsWindow_onBoardSizeButtonClick(iteration_boardSize); }; }(loop_boardSize); button.onButtonClick(); _boardSizeRow.addChild(button); } ``` These two solutions should compile to approximately the same runtime code, with optimizations enabled. So, it's really down to personal preference; the former is more explicit about what the computer is to do, while the latter is more concise.
Re: Error "Outer Function Context is Needed" when class declared in unittest
On Thursday, 5 January 2023 at 13:27:23 UTC, Vijay Nayar wrote: Why is this error only found when declaring a class in the unittest? A unittest is just a special function, it can run code and have local variables. classes and structs declared inside it have access to those local contexts, which it calls the outer function context. Make the outer class `static` too to lift it out of this and your error should go away.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Thursday, 5 January 2023 at 13:05:46 UTC, thebluepandabear wrote: Update some time later: the only way (oof!) around this seems to be using a `static foreach` with arrays: ```D Button[3] b; static foreach (indx, BoardSize boardSize; arr) { b[indx] = new Button(); b[indx].text = format("%sx%s", boardSize[0], boardSize[1]); b[indx].onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; _boardSizeRow.addChild(b[indx]); } ``` This is semantically equivalent to copying and pasting the loop body `arr.length` number of times, substituting the expression `arr[indx]` for `boardSize`, and the literal value of `indx` for `indx`. Unlike with a runtime (non-`static`) loop, the event handler closure does not capture a reference to `boardSize` or `indx`, because they don't actually exist at runtime and so cannot be referenced. Instead, `arr.length` different event handler functions are created at compile time, with each having the appropriate `indx` literal substituted. So, while it does work as long as `arr.length` is known at compile time, it will cause a ton of needless code bloat unless `arr.length` is very small. Any other ways of fixing this annoying issue? Yes, see [my earlier reply](https://forum.dlang.org/post/fwnccjunvnffmtaho...@forum.dlang.org) for an explanation of how to get your original run time loop working as intended.
Re: forgetting -betterC means no runtime bounds checking?
On Thursday, 5 January 2023 at 09:10:00 UTC, areYouSureAboutThat wrote: My question is: why is there no bounds checking occurring if I forget to use -betterC? module test; extern(C) void main() Note that whether bounds checking is performed depends on [compiler switches](https://dlang.org/dmd-windows.html#switch-boundscheck), and that there is a mode where `@safe` functions are checked, but `@system` functions (which your `main` appears to be) are not. This mode is the default for `-release` builds: ``` -boundscheck=[on|safeonly|off ] Controls if bounds checking is enabled. on: Bounds checks are enabled for all code. This is the default. safeonly: Bounds checks are enabled only in @safe code. This is the default for -release builds. off: Bounds checks are disabled completely (even in @safe code). This option should be used with caution and as a last resort to improve performance. Confirm turning off @safe bounds checks is worthwhile by benchmarking. ``` So, if you want bounds checking, make sure you either have `-boundscheck=on`, or use an `@safe` function together with `-boundscheck=safeonly`.
Re: Is there a way to enforce UFCS?
On Wednesday, 4 January 2023 at 14:21:46 UTC, bauss wrote: ```d class Foo { int bar; void setBar(Foo foo, int value) { foo.bar = value; } } void main() { foo.setBar(100); // Not UFCS - just method call to the class foo.setBar = 100; // Not UFCS - simply a setter function call (equal to the above) setBar(foo, 100); // Error } ``` I think this is really another usecase for @property: we should forbid the function call syntax for them (so one needs to use them like a variable). This is useful to enable library authors to enforce this, so that if a property is replaced by a variable (e.g. during refactoring), it's not a braking change for the users of the library. Else it could be that setters or getters are directly called, which would not compile anymore with a variable (that doesn't have getters or setters). @properties are intended to be used like variables - the only differences (and the reason why they exist) is the read or write protection they provide, and that they may be calculated on the fly (not stored in memory at all). That they are realized with a construct that looks similar to (a pair of) functions should be completely transparent for the user of a library. Properties are not functions. If you want a function, use a function. If @properties would be the same as functions, they are superfluous garbage. Either make something useful out of them or remove them.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Thursday, 5 January 2023 at 11:55:33 UTC, thebluepandabear wrote: I am using CSFML D bindings and I have created my own sort of UI library for drawing elements onto the screen. One of the classes I've created is a `Button` class, which contains a delegate called `onButtonClick` which is called when the button is clicked on by the user. Up until now, everything has worked fine. I want to add a couple of buttons side by side to represent an element of a list and assign them each a unique lambda expression for that particular element in the list, this is the current code I have: ```D foreach (BoardSize boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; button.onButtonClick(); _boardSizeRow.addChild(button); } ``` Running this code, I had expected that everything would work fine. Unfortunately upon running the code, tapping each of the buttons returned only the largest `boardSize` value, the one which is gets iterated last. Note that I am still not totally sure why this is the case. At first, I was confused, but then I suspected that after the variable gets sent as a parameter to the `settingsWindow_onBoardSizeButtonClick` function, it gets changed again in the next iteration creating a sort of chain effect -- I may be wrong, but this is my suspicion. Some of the things I tried was creating a new object each time, although it didn't work. I might have not done this properly as I am a beginner to D language. I saw someone else ask a similar question as to why this is happening but that was for C#, not D, so it wasn't that much of a use to me. Help would be appreciated! Ah, I think you ran into that delegate bug # 🥲 Try this code in it's place: ```d foreach (BoardSize boardSize; arr) (){ // notice the brackets Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; button.onButtonClick(); _boardSizeRow.addChild(button); }() // notice the brackets ``` This is not your fault, it's an "optimization" that's being performed by dmd so that "the user doesn't get surprised" when they realize that capturing every single variable emitted in a `foreach` will allocate a new heap closure Have fun reading this : https://issues.dlang.org/show_bug.cgi?id=21929
Re: Error "Outer Function Context is Needed" when class declared in unittest
On Thursday, 5 January 2023 at 13:47:24 UTC, Adam D Ruppe wrote: On Thursday, 5 January 2023 at 13:27:23 UTC, Vijay Nayar wrote: Why is this error only found when declaring a class in the unittest? A unittest is just a special function, it can run code and have local variables. classes and structs declared inside it have access to those local contexts, which it calls the outer function context. Make the outer class `static` too to lift it out of this and your error should go away. That's very informative, I didn't realize that `unittest` is actually a function. It raises another question one step deeper, what does it mean to define a non-static class within a function? Does that class inherit the scope of the function it is inside, similar to how an inner class does with an outer class?
Re: Error "Outer Function Context is Needed" when class declared in unittest
On Thursday, 5 January 2023 at 16:38:49 UTC, Vijay Nayar wrote: Does that class inherit the scope of the function it is inside, similar to how an inner class does with an outer class? yup. They can see the local variables from the function.
Re: Error "Outer Function Context is Needed" when class declared in unittest
On Thursday, 5 January 2023 at 16:41:32 UTC, Adam D Ruppe wrote: On Thursday, 5 January 2023 at 16:38:49 UTC, Vijay Nayar wrote: Does that class inherit the scope of the function it is inside, similar to how an inner class does with an outer class? yup. They can see the local variables from the function. Glad to learn that. Having worked many years in the Java world, where basically "class" and "scope" are nearly synonymous, I just assumed that classes could only get the scope of other classes, it never occurred to me that it could get a scope from a function. Thanks for the explanation!
Re: Address of a class object
On Thu, Jan 05, 2023 at 06:32:47AM +, areYouSureAboutThat via Digitalmars-d-learn wrote: [...] > Second, to be sure your getting the correct results, it would be nice > if there was a 'category of type' in std.traits for: > > isAllocatedOnStack > isAllocatedOnHeap > > As it is, your just guessing (although the addresses printed will > clear this up for you anyway I guess). In general, it's not possible to determine whether an arbitrary reference is pointing to the stack or the heap, because it could have come from anywhere. You can only determine this if you are privy to the internal implementation details of the allocator or code that created the reference (GC, other allocator, or whatever took an address of a stack-allocated object), or the platform-specific details of your runtime environment (range of stack addresses). > Also, I cannot read hex, [...] IMNSHO, anyone who claims to be a programmer should at least know that much. As Knuth once said, People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird. -- D. Knuth T -- "Real programmers can write assembly code in any language. :-)" -- Larry Wall
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Thu, Jan 05, 2023 at 11:55:33AM +, thebluepandabear via Digitalmars-d-learn wrote: [...] > ```D > foreach (BoardSize boardSize; arr) { > Button button = new Button(); > button.text = format("%sx%s", boardSize[0], boardSize[1]); > button.onButtonClick = { > eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); > }; > button.onButtonClick(); > _boardSizeRow.addChild(button); > } > ``` This is a classic D trap: the loop variable is only allocated once, and the closure captures the single location where the loop variable resides, thus every delegate from every loop iteration will see the same value when they are run later, i.e., the last value of the loop index. Do this instead: ```D foreach (BoardSize boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); BoardSize size = boardSize; // force separate capture button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(size); }; button.onButtonClick(); _boardSizeRow.addChild(button); } ``` This is arguably a language bug (I can't imagine any sane use case where somebody would actually want the current semantics). But we have not be successful in convincing Walter about this... T -- Don't throw out the baby with the bathwater. Use your hands...
Re: Is there a way to enforce UFCS?
On Thu, Jan 05, 2023 at 02:32:17PM +, Dom DiSc via Digitalmars-d-learn wrote: [...] > I think this is really another usecase for @property: we should forbid the > function call syntax for them (so one needs to use them like a variable). [...] > Properties are not functions. If you want a function, use a function. If > @properties would be the same as functions, they are superfluous garbage. > Either make something useful out of them or remove them. We have been talking about deprecating and removing @property for years now. Somebody just has to bite the bullet and push it through the deprecation process... ... OR come up with a DIP that implements @property in a sane, fully worked out way, not the half-hearted, incomplete, leaky implementation that it is today. // In my own code, I've stopped bothering with @property for the most part. Parentheses are optional for argumentless functions in general anyway, so there's really no need to write @property on anything. This works: struct S { private int _x; int x() { return _x; } } S s; int y = s.x; The only significant thing @property does right now is to add confusion when the unary & operator is used or when the property function returns a delegate. Not worth the trouble, I say. Just don't use @property at all, plain old member functions work just fine. T -- Some ideas are so stupid that only intellectuals could believe them. -- George Orwell
Re: Address of a class object
On Thursday, 5 January 2023 at 05:59:26 UTC, Ali Çehreli wrote: While we're here, you can force the class objects to be on the stack as well: scope MyClassVar1 = new MyClass(); I replaced 'auto' with 'scope'. Ali Very interesting. Thanks Ali.
GC interaction with malloc/free
Suppose there is a D main program (not marked anywhere with @nogc), that _both_ A: Calls one or more C functions that themselves call malloc/free; and also B: Calls one or more D functions that themselves call malloc/free via `import core.stdc.stdlib;` Assuming the malloc/free's are used correctly, does this situation risk crashing the D main program? Best regards
Re: GC interaction with malloc/free
On Thu, Jan 05, 2023 at 07:49:38PM +, DLearner via Digitalmars-d-learn wrote: > Suppose there is a D main program (not marked anywhere with @nogc), > that _both_ > > A: Calls one or more C functions that themselves call malloc/free; and > also > B: Calls one or more D functions that themselves call malloc/free via > `import core.stdc.stdlib;` > > Assuming the malloc/free's are used correctly, does this situation > risk crashing the D main program? [...] core.stdc.stdlib.{malloc,free} *is* the exact same malloc/free that C uses, it has nothing to do with the GC. The allocated memory is taken from the malloc/free part of the heap, which is disjoint from the heap memory managed by the GC. So, it should not cause any crashes. T -- Маленькие детки - маленькие бедки.
Re: GC interaction with malloc/free
On Thursday, 5 January 2023 at 19:54:01 UTC, H. S. Teoh wrote: On Thu, Jan 05, 2023 at 07:49:38PM +, DLearner via Digitalmars-d-learn wrote: Suppose there is a D main program (not marked anywhere with @nogc), that _both_ A: Calls one or more C functions that themselves call malloc/free; and also B: Calls one or more D functions that themselves call malloc/free via `import core.stdc.stdlib;` Assuming the malloc/free's are used correctly, does this situation risk crashing the D main program? [...] core.stdc.stdlib.{malloc,free} *is* the exact same malloc/free that C uses, it has nothing to do with the GC. The allocated memory is taken from the malloc/free part of the heap, which is disjoint from the heap memory managed by the GC. So, it should not cause any crashes. T That's comforting, but there is a reference in: https://dlang.org/blog/2017/09/25/go-your-own-way-part-two-the-heap/ '...Given that it’s rarely recommended to disable the GC entirely, most D programs allocating outside the GC heap will likely also be using memory from the GC heap in the same program. In order for the GC to properly do its job, it needs to be informed of any non-GC memory that contains, or may potentially contain, references to memory from the GC heap.' Followed by things that have to be done (GC.addRange) to avoid interaction effects?
Re: GC interaction with malloc/free
On Thu, Jan 05, 2023 at 08:18:42PM +, DLearner via Digitalmars-d-learn wrote: > On Thursday, 5 January 2023 at 19:54:01 UTC, H. S. Teoh wrote: [...] > > core.stdc.stdlib.{malloc,free} *is* the exact same malloc/free that > > C uses, it has nothing to do with the GC. The allocated memory is > > taken from the malloc/free part of the heap, which is disjoint from > > the heap memory managed by the GC. > > > > So, it should not cause any crashes. [...] > That's comforting, but there is a reference in: > > https://dlang.org/blog/2017/09/25/go-your-own-way-part-two-the-heap/ > > '...Given that it’s rarely recommended to disable the GC entirely, > most D programs allocating outside the GC heap will likely also be > using memory from the GC heap in the same program. In order for the GC > to properly do its job, it needs to be informed of any non-GC memory > that contains, or may potentially contain, references to memory from > the GC heap.' > > Followed by things that have to be done (GC.addRange) to avoid > interaction effects? You only need to do this if you will be storing pointers to GC-allocated objects inside malloc-allocated objects. E.g., if you malloc a struct that contains a reference to a GC-allocated class object. The reason for this precaution is because the GC needs to know all the root pointers that eventually may point to a prospective object to be garbage-collected. If there are pointers to an object outside of the areas the GC is aware of, e.g., in the malloc heap, then the GC may not be able to correctly determine that there's still a reference to the object, and may collect it prematurely, leading to a crash when you next try to dereference the pointer to the object. If there are no references from the malloc heap to the GC heap, then you do not need to use GC.addRange. T -- Build a man a fire, and he is warm for a night. Set a man on fire, and he is warm for the rest of his life.
Re: Address of a class object
On Thursday, 5 January 2023 at 17:23:39 UTC, H. S. Teoh wrote: On Thu, Jan 05, 2023 at 06:32:47AM +, areYouSureAboutThat Also, I cannot read hex, [...] IMNSHO, anyone who claims to be a programmer should at least know that much. ?? Well, like all, I learnt this at uni. .. as well as lot of other useless stuff. But in all my years as a well paid and productive 'programmer' ;-) .. I cannot recall ever, a consistent need to read hex (using by brain, that is). As such, those neuron connections needed to read hex do not exist anymore. Even the neurons in my brain are smarted than Knuth.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Thursday, 5 January 2023 at 17:36:55 UTC, H. S. Teoh wrote: On Thu, Jan 05, 2023 at 11:55:33AM +, thebluepandabear via Digitalmars-d-learn wrote: [...] ```D foreach (BoardSize boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; button.onButtonClick(); _boardSizeRow.addChild(button); } ``` This is a classic D trap: the loop variable is only allocated once, and the closure captures the single location where the loop variable resides, thus every delegate from every loop iteration will see the same value when they are run later, i.e., the last value of the loop index. Do this instead: ```D foreach (BoardSize boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); BoardSize size = boardSize; // force separate capture button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(size); }; button.onButtonClick(); _boardSizeRow.addChild(button); } ``` This is arguably a language bug (I can't imagine any sane use case where somebody would actually want the current semantics). But we have not be successful in convincing Walter about this... T Your code with the variable capture doesn't seem to work.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
Have fun reading this : https://issues.dlang.org/show_bug.cgi?id=21929 Thanks for the code suggestion although it still doesn't fix the bug. I am curious as to what those brackets do as well.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
``` These two solutions should compile to approximately the same runtime code, with optimizations enabled. So, it's really down to personal preference; the former is more explicit about what the computer is to do, while the latter is more concise. Thanks! Works great.
Re: Is there a way to enforce UFCS?
them or remove them. I agree, forbidding function call syntax would be a great usecase for `@property`. It will probably never get implemented though.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Thu, Jan 05, 2023 at 10:46:07PM +, thebluepandabear via Digitalmars-d-learn wrote: > On Thursday, 5 January 2023 at 17:36:55 UTC, H. S. Teoh wrote: [...] > > ```D > > foreach (BoardSize boardSize; arr) { > > Button button = new Button(); > > button.text = format("%sx%s", boardSize[0], boardSize[1]); > > BoardSize size = boardSize; // force separate capture > > button.onButtonClick = { > > eventHandler.settingsWindow_onBoardSizeButtonClick(size); > > }; > > button.onButtonClick(); > > _boardSizeRow.addChild(button); > > } > > ``` [...] > Your code with the variable capture doesn't seem to work. Argh, apparently locals inside the loop body are subject to the same quirky behaviour. >:-( Here's a workaround that works: foreach (BoardSize boardSize; arr) { Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); void wowThisIsDumb(BoardSize size) { button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(size); }; } wowThisIsDumb(boardSize); button.onButtonClick(); _boardSizeRow.addChild(button); } A nested function (or perhaps an inline lambda) is needed to force the allocation of a dynamic context for the capture. This is an embarrassment. Why hasn't this been fixed yet? :-( T -- Being able to learn is a great learning; being able to unlearn is a greater learning.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Thursday, 5 January 2023 at 22:49:01 UTC, thebluepandabear wrote: Have fun reading this : https://issues.dlang.org/show_bug.cgi?id=21929 Thanks for the code suggestion although it still doesn't fix the bug. I am curious as to what those brackets do as well. Okay, my bad for writing the wrong code, the correct one is: ```d foreach (BoardSize boardSizeIter; arr) (Boardsize boardSize){ // notice the brackets and I changed the variable names a little Button button = new Button(); button.text = format("%sx%s", boardSize[0], boardSize[1]); button.onButtonClick = { eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize); }; button.onButtonClick(); _boardSizeRow.addChild(button); }(boardSizeIter) // notice the brackets and that I changed the name of you first foreach argument ``` This will do the same thing as HS Teoh and Tsbockman's code did : create a copy of the value that is currently present in one particular iteration of the `foreach` by creating a function literal that takes your `struct` as a paramter. This works because structs are value types, so passing it to the function creates a copy.