Re: Deprecation: foreach: loop index implicitly converted from size_t to int
A horrible alternative would be to use `alias` on `size_t` to make up a new pseudo-type that is more aligned with the code logic. ``` alias integer = size_t; import std.stdio : writefln; void main() { auto arr = [ [5, 15], // 20 [2, 3, 2, 3], // 10 [3, 6, 2, 9], // 20 ]; foreach (integer i, row; arr) { double total = 0.0; foreach (e; row) total += e; auto avg = total / row.length; writefln("AVG [row=%d]: %.2f", i, avg); } } ```
Re: Deprecation: foreach: loop index implicitly converted from size_t to int
Well all these proposals to `int` index like `size_t` and `const typeof(arr.length)` are cryptic and less readable and less straightforward in comparison to how it used to be. Feels like horrible decision if the language is suppose to be somewhat futureproof. The `int` was simple, straighforward and great. These suggestions feel like some `C++` all over again.
Re: Deprecation: foreach: loop index implicitly converted from size_t to int
On Friday, 3 May 2024 at 15:19:13 UTC, user1234 wrote: On Friday, 3 May 2024 at 14:59:57 UTC, BoQsc wrote: On Friday, 3 May 2024 at 13:18:02 UTC, user1234 wrote: [...] So how would you update this example, what is the right index type here to choose? ``` import std.stdio : writefln; void main() { auto arr = [ [5, 15], // 20 [2, 3, 2, 3], // 10 [3, 6, 2, 9], // 20 ]; foreach (i, row; arr) { double total = 0.0; foreach (e; row) total += e; auto avg = total / row.length; writefln("AVG [row=%d]: %.2f", i, avg); } } ``` Example taken from https://tour.dlang.org/tour/en/basics/foreach Isn't that obvious ? ```d foreach (const size_t i, row; arr) ``` `arr` is not a static array, it is a dynamic one, consequently its `.length` type is `size_t`, even if you have the feeling that, in the present situation, `int` bitwidth would be sufficient. even better: ```d foreach (const typeof(arr.length) i, row; arr) ``` Otherwise I respect your POV, it's just that here I have no problem with the way that works. I dont see any issue with the type system. D type system is static, strong, but optionally inferred. And that's it.
Re: Deprecation: foreach: loop index implicitly converted from size_t to int
On Friday, 3 May 2024 at 14:59:57 UTC, BoQsc wrote: On Friday, 3 May 2024 at 13:18:02 UTC, user1234 wrote: On Friday, 3 May 2024 at 10:50:03 UTC, BoQsc wrote: [...] **You can specify the index type, just choose the right one.** For now there's a deprecation message but after some while you'll get a proper error message, e.g _"index type for arr must be of type T because arr.length type is T"_. What's is happening now is to help people updating their code and prevent abrupt breakages. So how would you update this example, what is the right index type here to choose? ``` import std.stdio : writefln; void main() { auto arr = [ [5, 15], // 20 [2, 3, 2, 3], // 10 [3, 6, 2, 9], // 20 ]; foreach (i, row; arr) { double total = 0.0; foreach (e; row) total += e; auto avg = total / row.length; writefln("AVG [row=%d]: %.2f", i, avg); } } ``` Example taken from https://tour.dlang.org/tour/en/basics/foreach Isn't that obvious ? ```d foreach (const size_t i, row; arr) ``` `arr` is not a static array, it is a dynamic one, consequently its `.length` type is `size_t`, even if you have the feeling that, in the present situation, `int` bitwidth would be sufficient.
Re: Deprecation: foreach: loop index implicitly converted from size_t to int
On Friday, 3 May 2024 at 13:18:02 UTC, user1234 wrote: On Friday, 3 May 2024 at 10:50:03 UTC, BoQsc wrote: Why am I forced to visit this D Lang thread, why this deprecation warning still appears in my console window in the latest version of DMD. Does not make any sense from the developer's perspective to show this warning and pollute the already polluted logging entries of the compiler. How am I suppose to program anything effectively if half of the screen are some nonsensical deprecation warnings without guidance or sane explanations. This is not better ``` foreach (i, row; arr) ``` than ``` foreach (int i, row; arr) ``` Hides the datatype and makes the D language appear in-explicit and annoying. What is this language becoming. A completely weak typed language or something? I would use JavaScript if I would want that. How are we suppose to make whole sane Operating Systems with such syntaxes. Do everyone just enjoy having bugs with some implicit size_t, or do everyone just enjoy deprecation warnings in their logging systems when there are way more important problems to solve, that are actually project related. **You can specify the index type, just choose the right one.** For now there's a deprecation message but after some while you'll get a proper error message, e.g _"index type for arr must be of type T because arr.length type is T"_. What's is happening now is to help people updating their code and prevent abrupt breakages. So how would you update this example, what is the right index type here to choose? ``` import std.stdio : writefln; void main() { auto arr = [ [5, 15], // 20 [2, 3, 2, 3], // 10 [3, 6, 2, 9], // 20 ]; foreach (i, row; arr) { double total = 0.0; foreach (e; row) total += e; auto avg = total / row.length; writefln("AVG [row=%d]: %.2f", i, avg); } } ``` Example taken from https://tour.dlang.org/tour/en/basics/foreach
Re: Deprecation: foreach: loop index implicitly converted from size_t to int
On Friday, 3 May 2024 at 10:50:03 UTC, BoQsc wrote: Why am I forced to visit this D Lang thread, why this deprecation warning still appears in my console window in the latest version of DMD. Does not make any sense from the developer's perspective to show this warning and pollute the already polluted logging entries of the compiler. How am I suppose to program anything effectively if half of the screen are some nonsensical deprecation warnings without guidance or sane explanations. This is not better ``` foreach (i, row; arr) ``` than ``` foreach (int i, row; arr) ``` Hides the datatype and makes the D language appear in-explicit and annoying. What is this language becoming. A completely weak typed language or something? I would use JavaScript if I would want that. How are we suppose to make whole sane Operating Systems with such syntaxes. Do everyone just enjoy having bugs with some implicit size_t, or do everyone just enjoy deprecation warnings in their logging systems when there are way more important problems to solve, that are actually project related. You can specify the index type, just choose the right one. For now there's a deprecation message but after some while you'll get a proper error message, e.g _"index type for arr must be of type T because arr.length type is T"_. What's is happening now is to help people updating their code and prevent abrupt breakages.
Re: Deprecation: foreach: loop index implicitly converted from size_t to int
Why am I forced to visit this D Lang thread, why this deprecation warning still appears in my console window in the latest version of DMD. Does not make any sense from the developer's perspective to show this warning and pollute the already polluted logging entries of the compiler. How am I suppose to program anything effectively if half of the screen are some nonsensical deprecation warnings without guidance or sane explanations. This is not better ``` foreach (i, row; arr) ``` than ``` foreach (int i, row; arr) ``` Hides the datatype and makes the D language appear in-explicit and annoying. What is this language becoming. A completely weak typed language or something? I would use JavaScript if I would want that. How are we suppose to make whole sane Operating Systems with such syntaxes. Do everyone just enjoy having bugs with some implicit size_t, or do everyone just enjoy deprecation warnings in their logging systems when there are way more important problems to solve, that are actually project related.
Re: is the array literal in a loop stack or heap allocated?
On 10/10/23 10:54 PM, mw wrote: Hi, I want to confirm: in the following loop, is the array literal `a` vs. `b` stack or heap allocated? and how many times? ask the compiler: ```d void main() @nogc { int[2] a; int[] b; int i; while(++i <=100) { a = [i, i+1]; // array literal //b = [i, i+1]; // yes, this allocates, had to comment it out } } ``` -Steve
Re: is the array literal in a loop stack or heap allocated?
On Wednesday, 11 October 2023 at 02:54:53 UTC, mw wrote: Hi, I want to confirm: in the following loop, is the array literal `a` vs. `b` stack or heap allocated? and how many times? void main() { int[2] a; int[] b; int i; While(++i <=100) { a = [i, i+1]; // array literal b = [i, i+1]; } } Thanks. profile=gc
Re: is the array literal in a loop stack or heap allocated?
On Wednesday, 11 October 2023 at 03:15:30 UTC, H. S. Teoh wrote: On Wed, Oct 11, 2023 at 02:54:53AM +, mw via Digitalmars-d-learn wrote: Hi, I want to confirm: in the following loop, is the array literal `a` vs. `b` stack or heap allocated? and how many times? void main() { int[2] a; This is stack-allocated. Once per call to the function. int[] b; This is an empty slice. It can refer to either stack or heap memory, depending on what's assigned to it. int i; While(++i <=100) { a = [i, i+1]; // array literal `a` is overwritten in-place once per loop. How about the temporary array literal on the right hand side? It's stack / heap allocated? Or it's not in the language specification, but up to the (optimizing) compiler to decide? b = [i, i+1]; [...] A new array consisting of 2 elements is allocated, once per loop, and assigned to b each time. Any arrays from previous iterations will be collected by the GC eventually. T
Re: is the array literal in a loop stack or heap allocated?
On Wednesday, 11 October 2023 at 02:54:53 UTC, mw wrote: Hi, I want to confirm: in the following loop, is the array literal `a` vs. `b` stack or heap allocated? and how many times? void main() { int[2] a; int[] b; int i; While(++i <=100) { a = [i, i+1]; // array literal b = [i, i+1]; } } Thanks. a is a static array, therefore it won't allocate any, it's a memcpy b will be heap allocated, and it'll do an allocate at each iteration ```D void test_b() { int[] a; int i; while (++i <= 100) { a = [i, i + 1]; printf("%p\n", a.ptr); } } ``` You can run this, and it'll print a different address each time If you add `[]` it'll do range based copy (memcpy), but since the array is not initialized, it has a length of 0, so you only need to allocate once (either with GC or with malloc) ```D void test_b() { int[] a; int i; a.length = 2; // initialize the heap allocated array here // or with malloc: // auto ptr = malloc(int.sizeof * 2); // a = cast(int[]) ptr[0 .. int.sizeof * 2]; while (++i <= 100) { a[] = [i, i + 1]; printf("%p\n", a.ptr); } } ``` Otherwise you'd get: ``core.exception.RangeError@onlineapp.d(18): Range violation`` I don't use D with the GC, so my memory about it is probably foggy, but i'm pretty sure what i said is right, please anyone correct me if i'm wrong
Re: is the array literal in a loop stack or heap allocated?
On Wed, Oct 11, 2023 at 02:54:53AM +, mw via Digitalmars-d-learn wrote: > Hi, > > I want to confirm: in the following loop, is the array literal `a` vs. > `b` stack or heap allocated? and how many times? > > void main() { > > int[2] a; This is stack-allocated. Once per call to the function. > int[] b; This is an empty slice. It can refer to either stack or heap memory, depending on what's assigned to it. > int i; > While(++i <=100) { > > a = [i, i+1]; // array literal `a` is overwritten in-place once per loop. > b = [i, i+1]; [...] A new array consisting of 2 elements is allocated, once per loop, and assigned to b each time. Any arrays from previous iterations will be collected by the GC eventually. T -- They pretend to pay us, and we pretend to work. -- Russian saying
is the array literal in a loop stack or heap allocated?
Hi, I want to confirm: in the following loop, is the array literal `a` vs. `b` stack or heap allocated? and how many times? void main() { int[2] a; int[] b; int i; While(++i <=100) { a = [i, i+1]; // array literal b = [i, i+1]; } } Thanks.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
On Monday, 9 January 2023 at 00:19:39 UTC, thebluepandabear wrote: Fixing this will improve the quality of the language for newcomers such as myself greatly. This not only confuses newcomers but it gave a false illusion of a bug within the code :/ It doesn't confuse newcomers only, also people who have used D for over a decade like me :) Mostly because it's not a thing in most (if not all) other languages. I think D is unique in that aspect. It should be fixed however, I don't think the current behavior is correct or expected by anyone.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
: 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. Thanks, your solution seems to be the most elegant one yet, I recently came across the similar problem and I realized that the other answer was too long and verbose.
Re: How to avoid variable capturing in `foreach` loop with lambdas?
Fixing this will improve the quality of the language for newcomers such as myself greatly. This not only confuses newcomers but it gave a false illusion of a bug within the code :/
Re: How to avoid variable capturing in `foreach` loop with lambdas?
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 I agree that this needs to get fixed immediately, it seems to be bugging me another time as well. I believe I saw a similar StackOverflow post about this issue for C#, and Microsoft did fix it, hopefully D can do the same. Fixing this will improve the quality of the language for newcomers such as myself greatly.
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.
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?
``` 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: 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?
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?
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: 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: 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: 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: 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?
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: Disk write in a "for" loop with RwMutex never happens
On 30/08/2022 8:16 AM, Gavin Ray wrote: It must have been the "writing at end of file" bit? I don't know. It read like it should work. The offsets were correct, it just didn't work *shrug*.
Re: Disk write in a "for" loop with RwMutex never happens
On Monday, 29 August 2022 at 15:52:31 UTC, rikki cattermole wrote: After a bunch of playing around I managed to determine that it is as simple as the mode. exists(dbFileName) ? "r+" : "w+" Will fix it. Of course you shouldn't delete the file like that method is doing. It should probably reinitialize the FILE* descriptor. D'oh! I didn't even think about the mode: > `a+ or ab+ or a+b` > "Append; open or create file for update, writing at end-of-file." It must have been the "writing at end of file" bit? Thanks Rikki. /embarrassed
Re: Disk write in a "for" loop with RwMutex never happens
On Monday, 29 August 2022 at 16:21:53 UTC, ag0aep6g wrote: You never change `pageId`. So as far as I can tell, you're always `seek`-ing to the same position, and you just overwrite the same piece of the file again and again. Whoops. I guess I missed the point of the question there.
Re: Disk write in a "for" loop with RwMutex never happens
On Sunday, 28 August 2022 at 22:46:17 UTC, Gavin Ray wrote: I've put the code, stripped to a minimal example here: - https://ldc.godbolt.org/z/fzsx3Tnnn [...] But if the same code is placed inside of a `for` loop, suddenly no writes occur: [...] Does anyone know what is happening here? It's really puzzling. Relevant pieces of the code: ```d class DiskManager { void writePage(PageId pageId, ubyte[PAGE_SIZE] pageData) { synchronized (dbIOMutex.writer) { dbFile.seek(pageId * PAGE_SIZE); dbFile.rawWrite(pageData); } } } void singleReadWrite() { PageId pageId = 0; diskManager.writePage(pageId, pageData); } void multiReadWrite() { PageId pageId = 0; foreach (i; 0 .. 10) { diskManager.writePage(pageId, pageData); } } ``` You never change `pageId`. So as far as I can tell, you're always `seek`-ing to the same position, and you just overwrite the same piece of the file again and again.
Re: Disk write in a "for" loop with RwMutex never happens
After a bunch of playing around I managed to determine that it is as simple as the mode. exists(dbFileName) ? "r+" : "w+" Will fix it. Of course you shouldn't delete the file like that method is doing. It should probably reinitialize the FILE* descriptor.
Re: Disk write in a "for" loop with RwMutex never happens
On Monday, 29 August 2022 at 07:04:49 UTC, bauss wrote: Does anyone know what is happening here? It's really puzzling. You probably need to flush the output. That's a good idea. I gave it a shot, and the following doesn't seem to change anything unfortunately: ```d void writePage(PageId pageId, ubyte[PAGE_SIZE] pageData) { synchronized (dbIOMutex.writer) { dbFile.seek(pageId * PAGE_SIZE); dbFile.rawWrite(pageData); dbFile.flush(); dbFile.sync(); } } ```
Re: Disk write in a "for" loop with RwMutex never happens
On Sunday, 28 August 2022 at 22:46:17 UTC, Gavin Ray wrote: I've put the code, stripped to a minimal example here: - https://ldc.godbolt.org/z/fzsx3Tnnn You can see that the single write + read version of the code works just fine: ``` pageData[0..4] = [1, 2, 3, 4] readData[0..4] = [1, 2, 3, 4] ``` Where here, `pageData` is the data to be written to a file, and `readData` is the result of trying to read the freshly written file data. But if the same code is placed inside of a `for` loop, suddenly no writes occur: ```d pageData[0..4] = [0, 0, 0, 0] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 1] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 2] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 3] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 4] readData[0..4] = [0, 0, 0, 0] // ... ``` Does anyone know what is happening here? It's really puzzling. You probably need to flush the output.
Disk write in a "for" loop with RwMutex never happens
I've put the code, stripped to a minimal example here: - https://ldc.godbolt.org/z/fzsx3Tnnn You can see that the single write + read version of the code works just fine: ``` pageData[0..4] = [1, 2, 3, 4] readData[0..4] = [1, 2, 3, 4] ``` Where here, `pageData` is the data to be written to a file, and `readData` is the result of trying to read the freshly written file data. But if the same code is placed inside of a `for` loop, suddenly no writes occur: ```d pageData[0..4] = [0, 0, 0, 0] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 1] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 2] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 3] readData[0..4] = [0, 0, 0, 0] pageData[0..4] = [0, 0, 0, 4] readData[0..4] = [0, 0, 0, 0] // ... ``` Does anyone know what is happening here? It's really puzzling.
Re: Main foreach loop fails when another foreach is added
On Monday, 8 August 2022 at 02:45:54 UTC, Steven Schveighoffer wrote: And now, you tried to read it again! Which means you are trying to read more data from an empty stream. You need to either a) reopen the file, or b) do both in the same loop. -Steve Steve! You are Legend! **Thank you**. It is obvious now the second `foreach` is reading from an empty stream! Regards, ikel
Re: Main foreach loop fails when another foreach is added
On 8/7/22 10:11 PM, ikelaiah wrote: Hi, I'm writing a program that reads a text file and launch my work URLs in it. It worked fine, and very happy. Then I added another `foreach` loop to count total number of lines. After this, the main `foreach` won't work. Does anyone know as to why this happens? I might have missed something very obvious. Thank you. ```d ... // Open input file for reading size_t line_count = 0; File input_file = File(input_filename); Here you open the file auto file_range = input_file.byLine(KeepTerminator.no, std.ascii.newline); And then wrap it in a byline range. // if this foreach not commented out // the last foreach won't run. foreach (char[] line; file_range) { line_count += 1; } For this foreach loop, you read every line in the file, exhausting the file (the input stream for the file is empty). // the MAIN foreach loop // -- won't run if above foreach is enabled foreach (char[] line; file_range) { if (!line.empty) { writeln("Open URL: " ~ line); Thread.sleep(dur!("seconds")(2)); browse(line); } } And now, you tried to read it again! Which means you are trying to read more data from an empty stream. You need to either a) reopen the file, or b) do both in the same loop. ``` -Steve
Main foreach loop fails when another foreach is added
Hi, I'm writing a program that reads a text file and launch my work URLs in it. It worked fine, and very happy. Then I added another `foreach` loop to count total number of lines. After this, the main `foreach` won't work. Does anyone know as to why this happens? I might have missed something very obvious. Thank you. Regards, ikel ```d module openurls; import std.stdio : writeln, File, KeepTerminator; import std.file; import std.range; import std.ascii; import std.getopt : getopt, GetoptResult, defaultGetoptPrinter; import std.process : browse; import core.thread.osthread; import core.time; void main(string[] args) { string input_filename = ""; // Get command line option GetoptResult user_args = getopt(args, "input|i", "A list of URLs separated by new lines.", _filename); // Does user need help? if (user_args.helpWanted) { defaultGetoptPrinter("Command line options for Open URLs.", user_args.options); } // Is input file specified? if (input_filename.length == 0) { writeln("Open URL: No file specified.\n"); defaultGetoptPrinter("Command line option for Open URLs.", user_args.options); return; } // Does the file exist? if (!input_filename.exists) { writeln("Open URL: input file --" ~ input_filename ~ "-- does not exist.\n"); return; } else { writeln("Open URL: reading --" ~ input_filename ~ "--"); } // Open input file for reading size_t line_count = 0; File input_file = File(input_filename); auto file_range = input_file.byLine(KeepTerminator.no, std.ascii.newline); // if this foreach not commented out // the last foreach won't run. foreach (char[] line; file_range) { line_count += 1; } // the MAIN foreach loop // -- won't run if above foreach is enabled foreach (char[] line; file_range) { if (!line.empty) { writeln("Open URL: " ~ line); Thread.sleep(dur!("seconds")(2)); browse(line); } } writeln("Open URL: completed"); writeln("Closing " ~ input_filename); input_file.close(); } ```
Re: vectorization of a simple loop -- not in DMD?
On Thursday, 14 July 2022 at 13:00:24 UTC, ryuukk_ wrote: On Thursday, 14 July 2022 at 05:30:58 UTC, Siarhei Siamashka wrote: On Tuesday, 12 July 2022 at 13:23:36 UTC, ryuukk_ wrote: I wonder if DMD/LDC/GDC have built in tools to profile and track performance Linux has a decent system wide profiler: https://perf.wiki.kernel.org/index.php/Main_Page And there are other useful tools, such as callgrind. To take advantage of all these tools, DMD/LDC/GDC only need to provide debugging symbols in the generated binaries, which they already do. Profiling applications to identify performance bottlenecks is very easy nowadays. I am not talking about linux, and i am not talking about 3rd party tools I am talking about the developers of DMD/LDC/GDC, do they profile the compilers, do they provide ways to monitor/track performance? do they benchmark specific parts of the compilers? I am not talking about the output of valgrind Zig also has: https://ziglang.org/perf/ (very slow to load) Having such thing is more useful than being able to plug valgrind god knows how into the compiler and try to decipher what does what and what results correspond to what internally, and what about a graph over time to catch regressions? DMD is very fast at compiling code, so i guess Walter doing enough work to monitor all of that LDC on the other hand.. they'd benefit a lot by having such thing in place Running valgrind on the compiler is completely trivial. Builtin profilers are often terrible. LDC and GDC and dmd all have instrumenting profilers builtin, of varying quality. gprof in particular is somewhat infamous. dmd isn't particularly fast, it does a lot of unnecessary work. LDC is slow because LLVM is slow. We need a graph over time, yes.
Re: vectorization of a simple loop -- not in DMD?
On Thursday, 14 July 2022 at 05:30:58 UTC, Siarhei Siamashka wrote: On Tuesday, 12 July 2022 at 13:23:36 UTC, ryuukk_ wrote: I wonder if DMD/LDC/GDC have built in tools to profile and track performance Linux has a decent system wide profiler: https://perf.wiki.kernel.org/index.php/Main_Page And there are other useful tools, such as callgrind. To take advantage of all these tools, DMD/LDC/GDC only need to provide debugging symbols in the generated binaries, which they already do. Profiling applications to identify performance bottlenecks is very easy nowadays. I am not talking about linux, and i am not talking about 3rd party tools I am talking about the developers of DMD/LDC/GDC, do they profile the compilers, do they provide ways to monitor/track performance? do they benchmark specific parts of the compilers? I am not talking about the output of valgrind Zig also has: https://ziglang.org/perf/ (very slow to load) Having such thing is more useful than being able to plug valgrind god knows how into the compiler and try to decipher what does what and what results correspond to what internally, and what about a graph over time to catch regressions? DMD is very fast at compiling code, so i guess Walter doing enough work to monitor all of that LDC on the other hand.. they'd benefit a lot by having such thing in place
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 18:15:16 UTC, Ivan Kazmenko wrote: Hi. I'm looking at the compiler output of DMD (-O -release), LDC (-O -release), and GDC (-O3) for a simple array operation: ``` void add1 (int [] a) { foreach (i; 0..a.length) a[i] += 1; } ``` Here are the outputs: https://godbolt.org/z/GcznbjEaf From what I gather at the view linked above, DMD does not use XMM registers for speedup, and does not unroll the loop either. Switching between 32bit and 64bit doesn't help either. However, I recall in the past it was capable of at least some of these optimizations. So, how do I enable them for such a function? Ivan Kazmenko. No, not in DMD. DMD generates what looks like 32 bit code adapted to x86_64. LDC may optimize this kind of loop with a tri-way branch depending on how many array elements remain. but it can both generate very good loop code(particularly when AVX-512 is available and the struct/data arrangement in memory is unfavorable for SIMD) and very questionable code. You may be losing performance for obscure reasons that look like gnomes decided to steal your precious cpu cycles and when that happens there is no way to fix it other than manually going in with a disassembler/debugger, changing defect optimizations in hot code paths to something faster then save back to executable file.(yikes, i know.)
Re: vectorization of a simple loop -- not in DMD?
On Tuesday, 12 July 2022 at 13:23:36 UTC, ryuukk_ wrote: I wonder if DMD/LDC/GDC have built in tools to profile and track performance Linux has a decent system wide profiler: https://perf.wiki.kernel.org/index.php/Main_Page And there are other useful tools, such as callgrind. To take advantage of all these tools, DMD/LDC/GDC only need to provide debugging symbols in the generated binaries, which they already do. Profiling applications to identify performance bottlenecks is very easy nowadays. Rust have this: https://perf.rust-lang.org/ Maybe we need to do something similar What is this website? Are they tracking performance differences between different versions of Rust or something? Like ensuring that the compile time does not regress without them noticing this immediately?
Re: vectorization of a simple loop -- not in DMD?
On Tuesday, 12 July 2022 at 12:47:26 UTC, bauss wrote: Of course if you're alone it doesn't matter, but if it's a larger project that will have multiple maintainers then it will never work and will tarnish the project entirely. That's true, i work solo on my project so it doesn't bother me It definitely is something hard to balance But one sure thing is that's something you have to monitor every so often, if you don't, then you end up with poor build speed that's harder to fix I wonder if DMD/LDC/GDC have built in tools to profile and track performance Rust have this: https://perf.rust-lang.org/ Maybe we need to do something similar
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 21:46:10 UTC, IGotD- wrote: Just depreciate the the DMD backend, it's just not up to the task anymore. Just deprecate LDC and GDC. They compile slowly and are unlikely to ever deliver fast compile times, due to their design. Some people say they like it because it is fast, yes it is fast because it doesn't do much. If it produces code that's fast enough, there is zero benefit to using a different compiler. If you use a development workflow that's heavy on compilation, stay away from LDC or GDC until you're done - and even then, you might not have any motivation to use either.
Re: vectorization of a simple loop -- not in DMD?
On Tuesday, 12 July 2022 at 10:32:36 UTC, ryuukk_ wrote: How do i achieve fast compile speed (results above were on windows, on linux i get much faster results): I maintain healthy project management: This - Imports of std: i simply don't, and i copy/paste functions i need - I avoid dub packages, instead i prefer import/src path, and i chery pick what i need And this. Can be argued to not be healthy project management. Of course if you're alone it doesn't matter, but if it's a larger project that will have multiple maintainers then it will never work and will tarnish the project entirely.
Re: vectorization of a simple loop -- not in DMD?
How do i achieve fast compile speed (results above were on windows, on linux i get much faster results): I maintain healthy project management: - Templates ONLY when necessary and when the cost is worth the time saved in the long term - this is why i try to lobby for builtin tagged union instead of std.sumtype - Dependencies, only dependency WITHOUT dependencies, and i keep them at the bare minimum! - Imports of std: i simply don't, and i copy/paste functions i need - I avoid dub packages, instead i prefer import/src path, and i chery pick what i need
Re: vectorization of a simple loop -- not in DMD?
On Tuesday, 12 July 2022 at 07:58:44 UTC, bauss wrote: You don't think this difference is huge? DMD is over 2x as fast. I think that DMD having more than 10x faster compilation speed in ryuukk_'s project shows that there is likely either a misconfiguration in DUB build setup or some other low hanging fruit for LDC. This looks like an opportunity to easily improve something in a major way.
Re: vectorization of a simple loop -- not in DMD?
On Tuesday, 12 July 2022 at 07:06:37 UTC, Siarhei Siamashka wrote: ``` real0m34.371s user0m32.883s sys 0m1.488s ``` ``` real0m14.078s user0m12.941s sys 0m1.129s ``` Is there an open source DUB package, which can be used to reproduce a huge build time difference between LDC and DMD? You don't think this difference is huge? DMD is over 2x as fast.
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 22:16:05 UTC, ryuukk_ wrote: LDC clean full rebuild ``` $ time dub build -f --compiler=ldc2 Performing "debug" build using ldc2 for x86_64. game ~master: building configuration "desktop"... Linking... real0m18.033s user0m0.000s sys 0m0.015s ``` DMD clean full rebuild ``` $ time dub build -f --compiler=dmd Performing "debug" build using dmd for x86_64. game ~master: building configuration "desktop"... Linking... real0m1.348s user0m0.031s sys 0m0.015s ``` BTW, I'm very curious about investigating the reason for such huge build time difference, but can't reproduce it on my computer. For example, compiling the DUB source code itself via the same DUB commands only results in DMD showing roughly twice faster build times (which is great, but nowhere close to ~13x difference): ``` $ git clone https://github.com/dlang/dub.git $ cd dub ``` ``` $ time dub build -f --compiler=ldc2 Performing "debug" build using ldc2 for x86_64. dub 1.29.1+commit.38.g7f6f024f: building configuration "application"... Serializing composite type Flags!(BuildRequirement) which has no serializable fields Serializing composite type Flags!(BuildOption) which has no serializable fields Linking... real0m34.371s user0m32.883s sys 0m1.488s ``` ``` $ time dub build -f --compiler=dmd Performing "debug" build using dmd for x86_64. dub 1.29.1+commit.38.g7f6f024f: building configuration "application"... Serializing composite type Flags!(BuildRequirement) which has no serializable fields Serializing composite type Flags!(BuildOption) which has no serializable fields Linking... real0m14.078s user0m12.941s sys 0m1.129s ``` Is there an open source DUB package, which can be used to reproduce a huge build time difference between LDC and DMD?
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 22:16:05 UTC, ryuukk_ wrote: I use D because DMD compiles my huge project in ~1 second (full clean rebuild) It is a competitive advantage that many languages doesn't have The other programming languages typically use an interpreter for quick iterations and rapid development. For example, Python programming language has CPython interpreter, PyPy Just-in-Time compiler and Cython optimizing static compiler (not perfect right now, but shows a lot of promise). D still has a certain advantage over interpreters, because DMD generated code is typically only up to twice slower than LDC generated code. If the x86 architecture stops being dominant in the future and gets displaced by ARM or RISC-V, then this may become a problem for DMD. But we'll cross that bridge when we get there.
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 21:46:10 UTC, IGotD- wrote: On Monday, 11 July 2022 at 18:19:41 UTC, max haughton wrote: The dmd backend is ancient, it isn't really capable of these kinds of loop optimizations. I've said it several times before. Just depreciate the the DMD backend, it's just not up to the task anymore. This is not criticism against the original purpose of it as back in the 90s and early 2000s it made sense to create your own backend. Time has moved on and we have LLVM and GCC backends with a lot of CPU support that the D project could never achieve themselves. The D project should just can the DMD backend in order to free up resources for more important tasks. Some people say they like it because it is fast, yes it is fast because it doesn't do much. I use D because DMD compiles my huge project in ~1 second (full clean rebuild) It is a competitive advantage that many languages doesn't have LDC clean full rebuild ``` $ time dub build -f --compiler=ldc2 Performing "debug" build using ldc2 for x86_64. game ~master: building configuration "desktop"... Linking... real0m18.033s user0m0.000s sys 0m0.015s ``` LDC incremental ``` $ time dub build --compiler=ldc2 Performing "debug" build using ldc2 for x86_64. game ~master: building configuration "desktop"... Linking... real0m17.215s user0m0.000s sys 0m0.000s ``` DMD clean full rebuild ``` $ time dub build -f --compiler=dmd Performing "debug" build using dmd for x86_64. game ~master: building configuration "desktop"... Linking... real0m1.348s user0m0.031s sys 0m0.015s ``` DMD incremental ``` $ time dub build --compiler=dmd Performing "debug" build using dmd for x86_64. game ~master: building configuration "desktop"... Linking... real0m1.249s user0m0.000s sys 0m0.000s ``` The day DMD gets removed is the day i will good a different language I want to thank Walter for maintaining DMD the compiler, and making it incredibly fast at compiling code Release perf can't beat LLVM and its amount of optimizations, but the advantage is it allows VERY FAST and QUICK iteration time, it is ESSENTIAL for developing software
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 18:19:41 UTC, max haughton wrote: The dmd backend is ancient, it isn't really capable of these kinds of loop optimizations. I've said it several times before. Just depreciate the the DMD backend, it's just not up to the task anymore. This is not criticism against the original purpose of it as back in the 90s and early 2000s it made sense to create your own backend. Time has moved on and we have LLVM and GCC backends with a lot of CPU support that the D project could never achieve themselves. The D project should just can the DMD backend in order to free up resources for more important tasks. Some people say they like it because it is fast, yes it is fast because it doesn't do much.
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 18:15:16 UTC, Ivan Kazmenko wrote: Hi. I'm looking at the compiler output of DMD (-O -release), LDC (-O -release), and GDC (-O3) for a simple array operation: ``` void add1 (int [] a) { foreach (i; 0..a.length) a[i] += 1; } ``` Here are the outputs: https://godbolt.org/z/GcznbjEaf From what I gather at the view linked above, DMD does not use XMM registers for speedup, and does not unroll the loop either. [snip] Specifying a SIMD capable target will reveal an even wider gap in capability. (LDC -mcpu=x86-64-v3 or gdc -march=x86-64-v3).
Re: vectorization of a simple loop -- not in DMD?
On Monday, 11 July 2022 at 18:15:16 UTC, Ivan Kazmenko wrote: Hi. I'm looking at the compiler output of DMD (-O -release), LDC (-O -release), and GDC (-O3) for a simple array operation: ``` void add1 (int [] a) { foreach (i; 0..a.length) a[i] += 1; } ``` Here are the outputs: https://godbolt.org/z/GcznbjEaf From what I gather at the view linked above, DMD does not use XMM registers for speedup, and does not unroll the loop either. Switching between 32bit and 64bit doesn't help either. However, I recall in the past it was capable of at least some of these optimizations. So, how do I enable them for such a function? Ivan Kazmenko. How long ago is the past? The godbolt.org dmd is quite old. The dmd backend is ancient, it isn't really capable of these kinds of loop optimizations.
vectorization of a simple loop -- not in DMD?
Hi. I'm looking at the compiler output of DMD (-O -release), LDC (-O -release), and GDC (-O3) for a simple array operation: ``` void add1 (int [] a) { foreach (i; 0..a.length) a[i] += 1; } ``` Here are the outputs: https://godbolt.org/z/GcznbjEaf From what I gather at the view linked above, DMD does not use XMM registers for speedup, and does not unroll the loop either. Switching between 32bit and 64bit doesn't help either. However, I recall in the past it was capable of at least some of these optimizations. So, how do I enable them for such a function? Ivan Kazmenko.
Re: vibe.d requestHTTP in static this causes infinite loop?
On Friday, 20 May 2022 at 01:41:59 UTC, Ali Çehreli wrote: On 5/19/22 16:44, Vijay Nayar wrote: > If I remove the call from `static this()`, then the web call works as > normal. Any idea why calling vibe.d's `requestHTTP` function inside of a > module's static construction would cause an infinite loop? I am not experienced with vibe.d. 'static this' is executed per thread. If requestHTTP starts a new thread, then I can see how you would be in an infinite loop. I wonder whether it should be 'shared static this' (which is executed once per program, not per thread). Ali Very clever, you were exactly right. I had not thought of that, but that is indeed what was happening.
Re: vibe.d requestHTTP in static this causes infinite loop?
On Friday, 20 May 2022 at 01:41:59 UTC, Ali Çehreli wrote: On 5/19/22 16:44, Vijay Nayar wrote: > If I remove the call from `static this()`, then the web call works as > normal. Any idea why calling vibe.d's `requestHTTP` function inside of a > module's static construction would cause an infinite loop? I am not experienced with vibe.d. 'static this' is executed per thread. If requestHTTP starts a new thread, then I can see how you would be in an infinite loop. I wonder whether it should be 'shared static this' (which is executed once per program, not per thread). Ali Static constructor shouldn't be there at all in this case, since it's not constructing anything. He shouldn't really use anything but main in this case.
Re: vibe.d requestHTTP in static this causes infinite loop?
On 5/19/22 16:44, Vijay Nayar wrote: > If I remove the call from `static this()`, then the web call works as > normal. Any idea why calling vibe.d's `requestHTTP` function inside of a > module's static construction would cause an infinite loop? I am not experienced with vibe.d. 'static this' is executed per thread. If requestHTTP starts a new thread, then I can see how you would be in an infinite loop. I wonder whether it should be 'shared static this' (which is executed once per program, not per thread). Ali
vibe.d requestHTTP in static this causes infinite loop?
I've encountered an unusual behavior that I have no good explanation for. Consider the following code example: ```d import vibe.core.log : logInfo; import vibe.http.client : requestHTTP, HTTPClientRequest, HTTPClientResponse; import vibe.http.common : HTTPMethod; import vibe.stream.operations : readAllUTF8; void doThing() { requestHTTP("http://example.com/;, (scope HTTPClientRequest req) { logInfo("Setting request method."); req.method = HTTPMethod.GET; }, (scope HTTPClientResponse res) { logInfo("Log point 1"); string data = res.bodyReader.readAllUTF8(); logInfo("Log point 2: %s", data); }); } static this() { logInfo("--- static this() ---"); doThing(); } void main(string[] args) { logInfo("--- main() ---"); doThing(); } ``` The output of this program is actually an infinite loop with output of the form: ``` [Eventcore DNS Lookup() INF] --- static this() --- [Eventcore DNS Lookup() INF] --- static this() --- ``` If I remove the call from `static this()`, then the web call works as normal. Any idea why calling vibe.d's `requestHTTP` function inside of a module's static construction would cause an infinite loop?
Re: While loop on global variable optimised away?
On Wednesday, 11 May 2022 at 10:01:18 UTC, Johan wrote: Any function call (inside the loop) for which it cannot be proven that it never modifies your memory variable will work. That's why I'm pretty sure that mutex lock/unlock will work. I think the common semantics ought to be that everything written by thread A before it releases the mutex will be visible to thread B when it aquires the same mutex, and any assumptions beyond this are nonportable?
Re: While loop on global variable optimised away?
On Wednesday, 11 May 2022 at 10:01:18 UTC, Johan wrote: Any function call (inside the loop) for which it cannot be proven that it never modifies your memory variable will work. That's why I'm pretty sure that mutex lock/unlock will work. Thank you, in C I would not have been surprised. It was the default thread local storage that made me question it. Your remark about the mutex lock/unlock is very helpful, I was starting to get worried I would need to keep a very careful watch on those __gshareds [well, more than usual :) ]
Re: While loop on global variable optimised away?
On Wednesday, 11 May 2022 at 09:34:20 UTC, ichneumwn wrote: Hi Forum, I have a snippet of code as follows: ``` extern(C) extern __gshared uint g_count; // inside a class member function: while(g_count) <= count) {} ``` This is from a first draft of the code without proper thread synchronisation. The global variable g_count is updated from a bit of C++ code. As soon as I turn the optimiser on, the code never gets passed this point, leading me to suspect it gets turned into ``` while(true) {} ``` If modify the code in the following way: ``` import core.volatile : volatileLoad; while(volatileLoad(_count) <= count) {} ``` it works again. My question is, have I hit a compiler bug (ldc 1.28.1, aarch64 [Raspberry Pi]) or is this part of the language design. I would have thought since D use thread-local storage by default, that for a __gshared variable it would be understood that it can get modified by another thread. This is part of the language spec. The language assumes that there is a single thread running, and any thread synchronization must be done by the user. This is well known from C and C++, from which D (implicitly afaik) borrows the memory model. Example: imagine loading a struct with 2 ulongs from shared memory: `auto s = global_struct_variable;`. Loading the data into local storage `s` - e.g. CPU registers - would happen in two steps, first member1, then member2 (simplified, let's assume it spans across a cache boundary, etc..). During that load sequence, another thread might write to the struct. If the language must have defined behavior in that situation (other thread write), then a global mutex lock/unlock must be done before/after _every_ read and write of shared data. That'd be a big performance impact on multithreading. Instead, single-thread execution is assumed, and thus the optimization is valid. Your solution with `volatileLoad` is correct. Access through atomic function would prevent the compiler from optimising this away as well, but if I were to use a Mutex inside the loop, there is no way for the compiler to tell *what* that Mutex is protecting and it might still decide to optimise the test away (assuming that is what is happening, did not attempt to look at the assembler code). Any function call (inside the loop) for which it cannot be proven that it never modifies your memory variable will work. That's why I'm pretty sure that mutex lock/unlock will work. On Wednesday, 11 May 2022 at 09:37:26 UTC, rikki cattermole wrote: Compiler optimizations should not be defined by a programming language specification. This is not true. Compiler optimizations are valid if and only if they can be proven by the programming language specification. A compiler optimization can never change valid program behavior. If an optimization does change behavior, then either the program is invalid per the language spec, or the optimization is bugged (or the observed behavior change is outside the language spec, such as how long a program takes to execute). -Johan
Re: While loop on global variable optimised away?
Compiler optimizations should not be defined by a programming language specification. This will be on LLVM.
While loop on global variable optimised away?
Hi Forum, I have a snippet of code as follows: ``` extern(C) extern __gshared uint g_count; // inside a class member function: while(g_count) <= count) {} ``` This is from a first draft of the code without proper thread synchronisation. The global variable g_count is updated from a bit of C++ code. As soon as I turn the optimiser on, the code never gets passed this point, leading me to suspect it gets turned into ``` while(true) {} ``` If modify the code in the following way: ``` import core.volatile : volatileLoad; while(volatileLoad(_count) <= count) {} ``` it works again. My question is, have I hit a compiler bug (ldc 1.28.1, aarch64 [Raspberry Pi]) or is this part of the language design. I would have thought since D use thread-local storage by default, that for a __gshared variable it would be understood that it can get modified by another thread. Access through atomic function would prevent the compiler from optimising this away as well, but if I were to use a Mutex inside the loop, there is no way for the compiler to tell *what* that Mutex is protecting and it might still decide to optimise the test away (assuming that is what is happening, did not attempt to look at the assembler code). Cheers
Re: Using getSymbolsByUDA in a static foreach loop
On Thursday, 20 January 2022 at 01:14:51 UTC, Adam Ruppe wrote: On Thursday, 20 January 2022 at 00:55:33 UTC, Jack Stouffer wrote: static foreach(member; __traits(allMembers, Manager)) member here is a string, not the member. I prefer to call it memberName. Then you __traits(getMember, Manager, memberName) to actually get the alias you can pass to getAttributes. Thanks, that fixed it. Final working version for anyone who finds this thread: ```d import std.traits; import std.stdio; enum Runnable; struct SubSystem { void run() { writeln("SubSystem ran"); } } struct Manager { @Runnable SubSystem subsystem; void run() { static foreach(memberName; __traits(allMembers, Manager)) { static foreach (attribute; __traits(getAttributes, __traits(getMember, Manager, memberName))) { static if (is(attribute == Runnable)) { __traits(getMember, Manager, memberName).run(); } } } } } void main() { Manager m; m.run(); } ```
Re: Using getSymbolsByUDA in a static foreach loop
On Thursday, 20 January 2022 at 00:55:33 UTC, Jack Stouffer wrote: static foreach(member; __traits(allMembers, Manager)) member here is a string, not the member. I prefer to call it memberName. Then you __traits(getMember, Manager, memberName) to actually get the alias you can pass to getAttributes.
Re: Using getSymbolsByUDA in a static foreach loop
On Wednesday, 19 January 2022 at 21:49:12 UTC, Adam D Ruppe wrote: I never use most of std.traits, they just complicate things. Bleh idk, I wouldn't bother with it and loop through the __traits instead. Unless I'm missing something obvious this has to be a DMD bug, because this prints nothing: ```d import std.traits; import std.stdio; enum Runnable; struct SubSystem { void run() { writeln("SubSystem ran"); } } struct Manager { @Runnable SubSystem subsystem; void run() { static foreach(member; __traits(allMembers, Manager)) { static foreach (attribute; __traits(getAttributes, member)) { static if (attribute == Runnable) { __traits(child, Manager, member).run(); } } } } } void main() { Manager m; m.run(); } ``` The `__traits(getAttributes, member)` call always returns an empty tuple. Calling `__traits(getAttributes, Manager.subsystem)` manually works as expected.
Re: Using getSymbolsByUDA in a static foreach loop
On Wednesday, 19 January 2022 at 21:44:57 UTC, Jack Stouffer wrote: The error is actually coming from trying to use the result of getSymbolsByUDA in the right part of the `static foreach` huh.. I never use most of std.traits, they just complicate things. Bleh idk, I wouldn't bother with it and loop through the __traits instead.
Re: Using getSymbolsByUDA in a static foreach loop
On Wednesday, 19 January 2022 at 20:53:29 UTC, Adam D Ruppe wrote: So you want to `__traits(child, system, this).run()` and it should work - the traits child will re-attach a this value. The error is actually coming from trying to use the result of getSymbolsByUDA in the right part of the `static foreach`, not the call to the `run` function. Which was odd to me because I thought it just returned a `AliasSeq`. Here's a link to the erroring code with your traits change: https://run.dlang.io/is/gO84ox
Re: Using getSymbolsByUDA in a static foreach loop
On Wednesday, 19 January 2022 at 20:46:17 UTC, Jack Stouffer wrote: static foreach(system; getSymbolsByUDA!(Manager, Runnable)) { system.run(); onlineapp.d(16): Error: value of `this` is not known at compile time The getSymbols returns aliases, meaning you hit what I wrote about a few days ago: http://dpldocs.info/this-week-in-d/Blog.Posted_2022_01_10.html#tip-of-the-week The `this` is a runtime value and all the other `static` things work on compile time info. So you want to `__traits(child, system, this).run()` and it should work - the traits child will re-attach a this value.
Using getSymbolsByUDA in a static foreach loop
I'm trying to use getSymbolsByUDA in order to loop over all of the members in a struct with a certain UDA, and then call a function on the member. The plan is to use this to avoid looping over an array of function pointers. However, the compiler is giving a strange error and the documentation of getSymbolsByUDA is unhelpful, as there are no practical use-case examples. Here's a very simplified version of my code ```d import std.traits; enum Runnable; struct SubSystem { void run(); } struct Manager { @Runnable SubSystem subsystem; void run() { static foreach(system; getSymbolsByUDA!(Manager, Runnable)) { system.run(); } } } void main() { Manager m; m.run(); } ``` Result: ``` onlineapp.d(16): Error: value of `this` is not known at compile time ``` This seems to me to be the logical way to write this code. What am I missing?
Re: How to loop through characters of a string in D language?
On Thursday, 23 December 2021 at 16:13:49 UTC, Stanislav Blinov wrote: You're comparing apples and oranges. When benchmarking, at least look at the generated assembly first. I looked now and you're right. Insomuch that it should be eggplant not apple, banana not orange...:) Because it's an irrelevant benchmarking! Thank you all...
Re: How to loop through characters of a string in D language?
On Thursday, 23 December 2021 at 07:14:35 UTC, Salih Dincer wrote: It seems faster than algorithms in Phobos. We would love to see this in our new Phobos. Replace: 436 msecs Malloc : 259 msecs */ It seems because MallocReplace is cheating a lot: - it is not called through another function like replace is called; - accesses directly the constant str; - assumes that it has a single character to replace; - assumes that the character will be deleted not replaced with something; - assumes that the character is always ';' - assumes that the replacing string is not bigger than the replaced one, so it knows exactly how much space to allocate; - does not have any parameter, at least on x86 this means that there is no arg pushes when it's called. - does not return a string, just compares its result with another constant; Since we already know all this stuff, we can go further :) ```d string superFast() { enum r = str.replace(";", ""); return r; } ``` Replace: 436 msecs Malloc : 259 msecs SuperFast: 0 msecs
Re: How to loop through characters of a string in D language?
On Thursday, 23 December 2021 at 07:14:35 UTC, Salih Dincer wrote: It seems faster than algorithms in Phobos. We would love to see this in our new Phobos. ```d void mallocReplace() void normalReplace() string result = str.replace(';',""); }/* Console Out: Replace: 436 msecs Malloc : 259 msecs */ ``` You're comparing apples and oranges. When benchmarking, at least look at the generated assembly first. replace is not in Phobos, it's a D runtime vestige. It's not getting inlined even in release builds with lto, whereas that manual version would. Also, benchmark with runtime strings, not literals, otherwise the compiler might even swallow the thing whole. What you're benchmarking is, basically, inlined optimized search in a literal versus a function call.
Re: How to loop through characters of a string in D language?
On Monday, 13 December 2021 at 09:36:57 UTC, Ola Fosheim Grøstad wrote: ```d @safe: string prematureoptimizations(string s, char stripchar) @trusted { import core.memory; immutable uint flags = GC.BlkAttr.NO_SCAN|GC.BlkAttr.APPENDABLE; char* begin = cast(char*)GC.malloc(s.length+1, flags); char* end = begin + 1; foreach(c; s) { immutable size_t notsemicolon = c != stripchar; // hack: avoid conditional by writing semicolon outside buffer *(end - notsemicolon) = c; end += notsemicolon; } immutable size_t len = end - begin - 1; begin = cast(char*)GC.realloc(begin, len, flags); return cast(string)begin[0..len]; } void main() { import std.stdio; string str = "abc;def;ab"; writeln(prematureoptimizations(str, ';')); } ``` It seems faster than algorithms in Phobos. We would love to see this in our new Phobos. ```d enum str = "abc;def;gh"; enum res = "abcdefgh"; void main() { void mallocReplace() { import core.memory; immutable uint flags = GC.BlkAttr.NO_SCAN| GC.BlkAttr.APPENDABLE; char* begin = cast(char*)GC.malloc(str.length+1, flags); char* end = begin + 1; foreach(c; str) { immutable size_t f = c != ';'; *(end - f) = c; end += f; } immutable size_t len = end - begin - 1; begin = cast(char*)GC.realloc(begin, len, flags); assert(begin[0..len] == res); } void normalReplace() { import std.string; string result = str.replace(';',""); assert(result == res); } void delegate() t1 = void delegate() t2 = import std.stdio : writefln; import std.datetime.stopwatch : benchmark; auto bm = benchmark!(t1, t2)(1_000_000); writefln("Replace: %s msecs", bm[0].total!"msecs"); writefln("Malloc : %s msecs", bm[1].total!"msecs"); }/* Console Out: Replace: 436 msecs Malloc : 259 msecs */ ```
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 18:47:53 UTC, Stanislav Blinov wrote: Threshold could be relative for short strings and absolute for long ones. Makes little sense reallocating if you only waste a couple bytes, but makes perfect sense if you've just removed pages and pages of semicolons ;) Like this? ``` @safe: string prematureoptimizations(string s, char stripchar) @trusted { import core.memory; immutable uint flags = GC.BlkAttr.NO_SCAN|GC.BlkAttr.APPENDABLE; char* begin = cast(char*)GC.malloc(s.length+1, flags); char* end = begin + 1; foreach(c; s) { immutable size_t notsemicolon = c != stripchar; // hack: avoid conditional by writing semicolon outside buffer *(end - notsemicolon) = c; end += notsemicolon; } immutable size_t len = end - begin - 1; begin = cast(char*)GC.realloc(begin, len, flags); return cast(string)begin[0..len]; } void main() { import std.stdio; string str = "abc;def;ab"; writeln(prematureoptimizations(str, ';')); } ```
Re: How to loop through characters of a string in D language?
On Monday, 13 December 2021 at 05:46:06 UTC, forkit wrote: On Saturday, 11 December 2021 at 09:25:37 UTC, Ola Fosheim Grøstad wrote: ```putchar(…)``` is too slow! On planet Mars maybe, but here on earth, my computer can do about 4 billion ticks per second, and my entire program (using putchar) takes only 3084 ticks. Can I borrow a couple of your ticks?
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 09:25:37 UTC, Ola Fosheim Grøstad wrote: ```putchar(…)``` is too slow! On planet Mars maybe, but here on earth, my computer can do about 4 billion ticks per second, and my entire program (using putchar) takes only 3084 ticks.
Re: How to loop through characters of a string in D language?
Of course, since it is easy to mess up and use ranges in the wrong way, you might want to add ```assert```s. That is most likely *helpful* to newbies that might want to use your kickass library function: ``` auto helpfuldeatheater(char stripchar)(string str) { struct voldemort { immutable(char)* begin, end; bool empty(){ return begin == end; } char front(){ assert(!empty); return *begin; } char back()@trusted{ assert(!empty); return *(end-1); } void popFront()@trusted{ assert(!empty); while(begin != end){begin++; if (*begin != stripchar) break; } } void popBack()@trusted{ assert(!empty); while(begin != end){end--; if (*(end-1) != stripchar) break; } } this(string s)@trusted{ begin = s.ptr; end = s.ptr + s.length; while(begin!=end && *begin==stripchar) begin++; while(begin!=end && *(end-1)==stripchar) end--; } } return voldemort(str); } ```
Re: How to loop through characters of a string in D language?
On Sunday, 12 December 2021 at 08:58:29 UTC, Ola Fosheim Grøstad wrote: this(string s)@trusted{ begin = s.ptr; end = s.ptr + s.length; } } Bug, it fails if the string ends or starts with ';'. Fix: ``` this(string s)@trusted{ begin = s.ptr; end = s.ptr + s.length; while(begin!=end && *begin==stripchar) begin++; while(begin!=end && *(end-1)==stripchar) end--; } ```
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 19:50:55 UTC, russhy wrote: you need to import a 8k lines of code module that itself imports other modules, and then the code is hard to read I agree. ``` @safe: auto deatheater(char stripchar)(string str) { struct voldemort { immutable(char)* begin, end; bool empty(){ return begin == end; } char front(){ return *begin; } char back()@trusted{ return *(end-1); } void popFront()@trusted{ while(begin != end){begin++; if (*begin != stripchar) break; } } void popBack()@trusted{ while(begin != end){end--; if (*(end-1) != stripchar) break; } } this(string s)@trusted{ begin = s.ptr; end = s.ptr + s.length; } } return voldemort(str); } void main() { import std.stdio; string str = "abc;def;ab"; foreach(c; deatheater!';'(str)) write(c); writeln(); foreach_reverse(c; deatheater!';'(str)) write(c); } ```
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 18:51:12 UTC, Rumbu wrote: On Saturday, 11 December 2021 at 14:42:53 UTC, russhy wrote: Here is mine - 0 allocations - configurable - let's you use it how you wish - fast You know that this is already in phobos? ``` "abc;def;ghi".splitter(';').joiner ``` you need to import a 8k lines of code module that itself imports other modules, and then the code is hard to read https://github.com/dlang/phobos/blob/v2.098.0/std/algorithm/iteration.d#L2917
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 14:42:53 UTC, russhy wrote: Here is mine - 0 allocations - configurable - let's you use it how you wish - fast You know that this is already in phobos? ``` "abc;def;ghi".splitter(';').joiner ```
Re: How to loop through characters of a string in D language?
Here is mine - 0 allocations - configurable - let's you use it how you wish - fast ```D import std; void main() { string a = "abc;def;ab"; writeln("a => ", a); foreach(item; split(a, ';')) writeln("\t", item); string b = "abc;def ;ab"; writeln("a => ", b); foreach(item; split(b, ';', SplitOption.TRIM)) writeln("\t", item); string c= "abc;; ;def ;ab"; writeln("a => ",c); foreach(item; split(c, ';', SplitOption.TRIM | SplitOption.REMOVE_EMPTY)) writeln("\t", item); } SplitIterator!T split(T)(const(T)[] buffer, const(T) delimiter, SplitOption option = SplitOption.NONE) { return SplitIterator!T(buffer, delimiter, option); } struct SplitIterator(T) { const(T)[] buffer; const(T) delimiter; SplitOption option; int index = 0; int count() { int c = 0; foreach(line; this) { c++; } index = 0; return c; } const(T) get(int index) { return buffer[index]; } int opApply(scope int delegate(const(T)[]) dg) { auto length = buffer.length; for (int i = 0; i < length; i++) { if (buffer[i] == '\0') { length = i; break; } } int result = 0; for (int i = index; i < length; i++) { int entry(int start, int end) { // trim only if we got something if ((end - start > 0) && (option & SplitOption.TRIM)) { for (int j = start; j < end; j++) if (buffer[j] == ' ') start += 1; else break; for (int k = end; k >= start; k--) if (buffer[k - 1] == ' ') end -= 1; else break; // nothing left if(start >= end) return 0; } //printf("%i to %i :: %i :: total: %lu\n", start, end, index, buffer.length); return dg(buffer[start .. end]) != 0; } auto c = buffer[i]; if (c == delimiter) { if (i == index && (option & SplitOption.REMOVE_EMPTY)) { // skip if we keep finding the delimiter index = i + 1; continue; } if ((result = entry(index, i)) != 0) break; // skip delimiter for next result index = i + 1; } // handle what's left if ((i + 1) == length) { result = entry(index, i + 1); } } return result; } // copy from above, only replace if above has changed int opApply(scope int delegate(int, const(T)[]) dg) { auto length = buffer.length; for (int i = 0; i < length; i++) { if (buffer[i] == '\0') { length = i; break; } } int n = 0; int result = 0; for (int i = index; i < length; i++) { int entry(int start, int end) { // trim only if we got something if ((end - start > 0) && (option & SplitOption.TRIM)) { for (int j = start; j < end; j++) if (buffer[j] == ' ') start += 1; else break; for (int k = end; k >= start; k--) if (buffer[k - 1] == ' ') end -= 1; else break; // nothing left if(start >= end) return 0; } //printf("%i to %i :: %i :: total: %lu\n", start, end, index, buffer.length); return dg(n++, buffer[start .. end]) != 0; } auto c = buffer[i]; if (c == delimiter) { if (i == index && (option & SplitOption.REMOVE_EMPTY)) { // skip if we keep finding the delimiter index = i + 1; continue; } if ((result = entry(index, i)) != 0) break; // skip delimiter for next result index = i + 1;
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 09:40:47 UTC, Stanislav Blinov wrote: On Saturday, 11 December 2021 at 09:34:17 UTC, Ola Fosheim Grøstad wrote: void donttrythisathome(string s, char stripchar) @trusted { import core.stdc.stdlib; char* begin = cast(char*)alloca(s.length); A function with that name, and calling alloca to boot, cannot be @trusted ;) :-) But I am very trustworthy person! PROMISE!!!
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 09:34:17 UTC, Ola Fosheim Grøstad wrote: void donttrythisathome(string s, char stripchar) @trusted { import core.stdc.stdlib; char* begin = cast(char*)alloca(s.length); A function with that name, and calling alloca to boot, cannot be @trusted ;)
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 09:34:17 UTC, Ola Fosheim Grøstad wrote: @system Shouldn't be there. Residual leftovers… (I don't want to confuse newbies!)
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 08:46:32 UTC, forkit wrote: On Saturday, 11 December 2021 at 08:05:01 UTC, Ola Fosheim Grøstad wrote: Using libraries can trigger hidden allocations. ok. fine. no unnecessary, hidden allocations then. // -- module test; import core.stdc.stdio : putchar; nothrow @nogc void main() { string str = "abc;def;ab"; ulong len = str.length; for (ulong i = 0; i < len; i++) { if (cast(int) str[i] != ';') putchar(cast(int) str[i]); } } // -- ```putchar(…)``` is too slow! ``` @safe: extern (C) long write(long, const void *, long); void donttrythisathome(string s, char stripchar) @trusted { import core.stdc.stdlib; char* begin = cast(char*)alloca(s.length); char* end = begin; foreach(c; s) if (c != stripchar) *(end++) = c; write(0, begin, end - begin); } @system void main() { string str = "abc;def;ab"; donttrythisathome(str, ';'); }
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 09:26:06 UTC, Stanislav Blinov wrote: What you're showing is... indeed, don't do this, but I fail to see what that has to do with my suggestion, or the original code. You worry too much, just have fun with differing ways of expressing the same thing. (Recursion can be completely fine if the compiler supports it well. Tail recursion that is, not my example.) Again, that is a different algorithm than what I was responding to. Slightly different, but same idea. Isn't the point of this thread to present N different ways of doing the same thing? :-)
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 23:53:47 UTC, Ola Fosheim Grøstad wrote: ```d char[] dontdothis(string s, int i=0, int skip=0){ if (s.length == i) return new char[](i - skip); if (s[i] == ';') return dontdothis(s, i+1, skip+1); auto r = dontdothis(s, i+1, skip); r[i-skip] = s[i]; return r; } ``` That is about 500% not what I meant. At all. Original code in question: - duplicates string unconditionally as mutable storage - uses said mutable storage to gather all non-semicolons - duplicates said mutable storage (again) as immutable I suggested to make the second duplicate conditional, based on amount of space freed by skipping semicolons. What you're showing is... indeed, don't do this, but I fail to see what that has to do with my suggestion, or the original code. Scanning short strings twice is not all that expensive as they will stay in the CPU cache > when you run over them a second time. ```d import std.stdio; @safe: string stripsemicolons(string s) @trusted { int i,n; foreach(c; s) n += c != ';'; // premature optimization auto r = new char[](n); foreach(c; s) if (c != ';') r[i++] = c; return cast(string)r; } ``` Again, that is a different algorithm than what I was responding to. But sure, short strings - might as well. So long as you do track the distinction somewhere up in the code and don't simply call this on all strings.
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 08:46:32 UTC, forkit wrote: On Saturday, 11 December 2021 at 08:05:01 UTC, Ola Fosheim Grøstad wrote: Using libraries can trigger hidden allocations. ok. fine. no unnecessary, hidden allocations then. // -- module test; import core.stdc.stdio : putchar; nothrow @nogc void main() { string str = "abc;def;ab"; ulong len = str.length; for (ulong i = 0; i < len; i++) { if (cast(int) str[i] != ';') putchar(cast(int) str[i]); } } // -- ```putchar(…)``` is too slow! ``` @safe: extern (C) long write(long, const void *, long); void donttrythisathome(string s, char stripchar) @trusted { import core.stdc.stdlib; char* begin = cast(char*)alloca(s.length); char* end = begin; foreach(c; s) if (c != stripchar) *(end++) = c; write(0, begin, end - begin); } @system void main() { string str = "abc;def;ab"; donttrythisathome(str, ';'); }
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 08:05:01 UTC, Ola Fosheim Grøstad wrote: Using libraries can trigger hidden allocations. ok. fine. no unnecessary, hidden allocations then. // -- module test; import core.stdc.stdio : putchar; nothrow @nogc void main() { string str = "abc;def;ab"; ulong len = str.length; for (ulong i = 0; i < len; i++) { if (cast(int) str[i] != ';') putchar(cast(int) str[i]); } } // --
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 18:47:53 UTC, Stanislav Blinov wrote: Threshold could be relative for short strings and absolute for long ones. Makes little sense reallocating if you only waste a couple bytes, but makes perfect sense if you've just removed pages and pages of semicolons ;) Scanning short strings twice is not all that expensive as they will stay in the CPU cache when you run over them a second time. ``` import std.stdio; @safe: string stripsemicolons(string s) @trusted { int i,n; foreach(c; s) n += c != ';'; // premature optimization auto r = new char[](n); foreach(c; s) if (c != ';') r[i++] = c; return cast(string)r; } int main() { writeln(stripsemicolons("abc;def;ab")); return 0; } ```
Re: How to loop through characters of a string in D language?
On Saturday, 11 December 2021 at 00:39:15 UTC, forkit wrote: On Friday, 10 December 2021 at 22:35:58 UTC, Arjan wrote: "abc;def;ghi".tr(";", "", "d" ); I don't think we have enough ways of doing the same thing yet... so here's one more.. "abc;def;ghi".substitute(";", ""); Using libraries can trigger hidden allocations. ``` import std.stdio; string garbagefountain(string s){ if (s.length == 1) return s == ";" ? "" : s; return garbagefountain(s[0..$/2]) ~ garbagefountain(s[$/2..$]); } int main() { writeln(garbagefountain("abc;def;ab")); return 0; } ```
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 22:35:58 UTC, Arjan wrote: "abc;def;ghi".tr(";", "", "d" ); I don't think we have enough ways of doing the same thing yet... so here's one more.. "abc;def;ghi".substitute(";", "");
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 18:47:53 UTC, Stanislav Blinov wrote: Oooh, finally someone suggested to preallocate storage for all these reinventions of the wheel :D ``` import std.stdio; char[] dontdothis(string s, int i=0, int skip=0){ if (s.length == i) return new char[](i - skip); if (s[i] == ';') return dontdothis(s, i+1, skip+1); auto r = dontdothis(s, i+1, skip); r[i-skip] = s[i]; return r; } int main() { string s = "abc;def;ab"; string s_new = cast(string)dontdothis(s); writeln(s_new); return 0; } ```
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 06:24:27 UTC, Rumbu wrote: On Wednesday, 8 December 2021 at 11:23:45 UTC, BoQsc wrote: Let's say I want to skip characters and build a new string. The character I want to skip: `;` Expected result: ``` abcdefab ``` Since it seems there is a contest here: ```d "abc;def;ghi".split(';').join(); ``` :) ```d "abc;def;ghi".tr(";", "", "d" ); ```
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 12:15:18 UTC, Rumbu wrote: I thought it's a beauty contest. Well, if it's a beauty contest, then i got a beauty.. char[("abc;def;ab".length - count("abc;def;ab", ";"))] b = "abc;def;ab".replace(";", "");
Re: How to loop through characters of a string in D language?
Yes it will. You can use lazy templates instead, like splitter and joiner, which splits and joins lazily, respectively. LDC can optimize those templates fairly well and avoid too much lazy calls and pretty much constructs the logic equivalent to for loop. On 10 December 2021 11:06:21 WET, IGotD- via Digitalmars-d-learn wrote: >On Friday, 10 December 2021 at 06:24:27 UTC, Rumbu wrote: > >> >> Since it seems there is a contest here: >> >> ```d >> "abc;def;ghi".split(';').join(); >> ``` >> >> :) > >Would that become two for loops or not?