Re: template/mixin magic for to! auto inferring type from variable
On Saturday, 3 February 2024 at 02:20:13 UTC, Paul Backus wrote: On Friday, 2 February 2024 at 23:25:37 UTC, Chris Katko wrote: The auto solution won't work for a struct however which I'm using: ```D struct procTable{ //contains all the fields inside a file I'm parsing uint time; int priority; string name; // etc } ``` Maybe you can use `typeof` in that case? ```d procTable pt; pt.time = to!(typeof(pt.time))(data[1]); // etc ``` ...although I guess then you're repeating the field name, which isn't great either. ```d struct ProcTable2 { uint time; int priority; string name; this (string [this.tupleof.length] initializers) { import std.conv; static foreach (i, field; this.tupleof) field = initializers [i].to!(typeof (field)); } } unittest { string [3] initializers = ["100", "-5", "foobar"]; auto pt2 = ProcTable2 (initializers); with (pt2) { assert(time == 100); assert(priority == -5); assert(name == "foobar"); } } ```
Re: template/mixin magic for to! auto inferring type from variable
On Friday, 2 February 2024 at 23:25:37 UTC, Chris Katko wrote: The auto solution won't work for a struct however which I'm using: ```D struct procTable{ //contains all the fields inside a file I'm parsing uint time; int priority; string name; // etc } ``` Maybe you can use `typeof` in that case? ```d procTable pt; pt.time = to!(typeof(pt.time))(data[1]); // etc ``` ...although I guess then you're repeating the field name, which isn't great either. You could avoid the repetition by wrapping the above pattern up in a helper function, though: ```d void convertAssign(Dest, Value)(ref Dest dest, Value value) { import std.conv; dest = to!Dest(value); } void main() { string[3] data = ["100", "-5", "foobar"]; uint time; int priority; string name; time.convertAssign(data[0]); priority.convertAssign(data[1]); name.convertAssign(data[2]); assert(time == 100); assert(priority == -5); assert(name == "foobar"); } ```
Re: template/mixin magic for to! auto inferring type from variable
On Friday, 2 February 2024 at 21:01:53 UTC, Paul Backus wrote: No, D only does bottom-up type inference, not top down. If you want to avoid repeating the type, use `auto` on the left side: ```d auto time = to!uint(data[1]); auto priority = to!int(data[2]); ``` Okay thanks. It finally clicked what bottom-up/top-down type interference is. The auto solution won't work for a struct however which I'm using: ```D struct procTable{ //contains all the fields inside a file I'm parsing uint time; int priority; string name; // etc } ```
Re: template/mixin magic for to! auto inferring type from variable
On Friday, 2 February 2024 at 07:43:09 UTC, Chris Katko wrote: Is there some way to do: ```D string[3] data; //strings from some file input, some are ints, uints, etc. auto into!(T)(T value){return to!???(value); } // ??? uint time = into!(data[1]); // We already know this is uint int priority = into!(data[2]); ``` instead of: ```D uint time = to!uint(data[1]); // specifying type twice int priority = to!int(data[2]) ``` No, D only does bottom-up type inference, not top down. If you want to avoid repeating the type, use `auto` on the left side: ```d auto time = to!uint(data[1]); auto priority = to!int(data[2]); ```
template/mixin magic for to! auto inferring type from variable
Is there some way to do: ```D string[3] data; //strings from some file input, some are ints, uints, etc. auto into!(T)(T value){return to!???(value); } // ??? uint time = into!(data[1]); // We already know this is uint int priority = into!(data[2]); ``` instead of: ```D uint time = to!uint(data[1]); // specifying type twice int priority = to!int(data[2]) ```
Re: 'auto' keyword
On 3/12/23 06:07, DLearner wrote: > 1. As a shorthand to make the type of the variable being declared the > same as the type on the right hand side of an initial assignment. As Adam explained, D already has type inference without a special keyword. However, some places where 'auto' (or 'const', etc.) appear is not only for "shorthand" but for necessity. Some types cannot be spelled-out at all: auto foo() { struct S {} return S(); } void main() { pragma(msg, typeof(foo())); auto s = foo(); } The name 'S' is available only inside 'foo', so code outside has no choice but to use 'auto' (or 'const', etc.) Having said that, it is still possible to alias the returned type, which may be cumbersome in some cases because you may have to come up with a clean expression for attempting to call the function. In this case it's trivial because foo does not take any parameter: alias T = typeof(foo()); T t;// <-- There: I did not need to use 'auto' Ali
Re: 'auto' keyword
On Sunday, 12 March 2023 at 13:27:05 UTC, Adam D Ruppe wrote: [...] *any* storage class will work for type inference. [...] After heaving read [1] I immediately thought of this: void main () { deprecated i = 3; i = 4; } $ dmd test.d test.d(4): Deprecation: variable `test.main.i` is deprecated Does that make sense??? [1] https://issues.dlang.org/show_bug.cgi?id=7432 Issue 7432 - DMD allows variables to be declared as pure
Re: 'auto' keyword
On Sunday, 12 March 2023 at 15:31:07 UTC, Salih Dincer wrote: Moreover, `auto ref` or `ref auto` is needed in functions. That's because `ref` isn't part of the argument or return value's type, so it isn't covered by **type** inference. Instead, D has a totally separate feature for "`ref` inference".
Re: 'auto' keyword
On Sunday, 12 March 2023 at 13:07:58 UTC, DLearner wrote: Is it correct that this _single_ keyword is used to indicate _two_ quite different things: 1. As a shorthand to make the type of the variable being declared the same as the type on the right hand side of an initial assignment. The auto keyword is really helpful for shortening it. But in at least 2 cases (one of which is interfaces) it should help the compiler. For example, contrary to expected, it is dynamic array: ```d auto arr = [ 1, 2, 3 ]; ``` Moreover, `auto ref` or `ref auto` is needed in functions. SDB@79
Re: 'auto' keyword
On Sunday, 12 March 2023 at 13:07:58 UTC, DLearner wrote: Is it correct that this _single_ keyword is used to indicate _two_ quite different things: No, it only actually does #2 in your thing. The type is optional meaning *any* storage class will work for type inference. `auto` is not special in variable declarations. (it can be special in other contexts though). auto A = 5; static A = 5; const A = 5; all the same.
'auto' keyword
Is it correct that this _single_ keyword is used to indicate _two_ quite different things: 1. As a shorthand to make the type of the variable being declared the same as the type on the right hand side of an initial assignment. Example: ```auto A = 5;``` makes A an int. 2. To indicate storage class of variable. Example: ```auto int A;``` (I guess) makes A have automatic storage, ie contents lost when control goes out of scope, unlike static. Best regards
Re: Is "auto t=T();" not the same as "T t;"?
Looks like explicitly initialized variable in this case allocates array literal. Uninitialized variable is initialized with init pattern. This may be correct as uninitialized variable isn't guaranteed to hold a value most useful for you, it's only guaranteed to hold a defined value.
Re: Is "auto t=T();" not the same as "T t;"?
On Wednesday, 26 October 2022 at 04:40:17 UTC, Salih Dincer wrote: On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote: Does the second piece of code shows a bug or my expectation is not correct (and why if so)? As a result, if this is a bug, Andrey has the right to report it. Bug tracking system doesn't work with gmail emails so I'm not able to report.
Re: Is "auto t=T();" not the same as "T t;"?
On Wednesday, 26 October 2022 at 02:34:24 UTC, Ali Çehreli wrote: On 10/25/22 19:25, Salih Dincer wrote: > with static in main(): If 'static' makes a difference on your side as well, it is your turn to create a bug report. :) (Last time you discovered a bug, I was too quick to report it. :/) On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote: Does the second piece of code shows a bug or my expectation is not correct (and why if so)? As a result, if this is a bug, Andrey has the right to report it. Unlike what Andrey did, I haven't tried it with a nested struct. There is also be in the heap or be in the stack issue... SDB@79
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 19:25, Salih Dincer wrote: > with static in main(): If 'static' makes a difference on your side as well, it is your turn to create a bug report. :) (Last time you discovered a bug, I was too quick to report it. :/) Ali
Re: Is "auto t=T();" not the same as "T t;"?
On Wednesday, 26 October 2022 at 00:44:45 UTC, H. S. Teoh wrote: If you'll excuse some ASCII art, here's the situation you have: STACK GLOBAL DATA x[0] { Y[] y; -+> [ Y.init ] } | x[1] { | Y[] y; -' } Thanks for these detailed explanations, especially the ASCII art On Wednesday, 26 October 2022 at 00:58:33 UTC, Ali Çehreli wrote: On 10/25/22 17:16, Salih Dincer wrote: I tested: If you make X a 'static struct', then you see the same output. It occurred to me too, to use a static struct. I also tried the following example because it can work with static in main(): import std; void main() { //static struct X { static struct Y { //... }} static struct Bar { string s; string toString() { return s; } } auto list = "sixtwoone".chunks(3); list.map!(c => c.to!string) .map!Bar.array.writeln; // [six, two, one] //... } Thank you... @SDB79
Re: auto scope question?
typeof(screen.output.findSplit("")) s; Perfect. That was the "essence" of my question. But thanks to Ali, I don't have to use such esoteric syntax. D is a wonderful language, but I seem to shoot myself in the foot :)
Re: auto scope question?
On 10/25/22 6:07 PM, WhatMeWorry wrote: I'm naturally getting a undefined identifier `s` error in the return. Is there some way to refactor my code? I tried to declare s outside of the else brackets like: auto screen = executeShell(cmdLine); auto s; ... { s = screen.output.findSplit("REG_SZ"); } but that doesn't compile either. string[] getPath(string cmdLine) { auto screen = executeShell(cmdLine); if (screen.status != 0) { writeln(cmdLine, " failed"); } else { writeln("screen.output = ", screen.output); auto s = screen.output.findSplit("REG_SZ"); writeln("s[0] = ", s[0]); writeln("s[1] = ", s[1]); writeln("s[2] = ", s[2]); } return (s.split(';')); // Error: undefined identifier `s` } As Ali mentioned, your logic is faulty -- you seem to write that the command failed, but then return something anyway. Also, you are returning `string[]`, so just declaring `string[] s;` should be enough. However, I did want to mention that if you do want to hoist a difficult to name type outside where it is declared with an auto, you can use `typeof`: ```d typeof(screen.output.findSplit("")) s; ``` In this case, it's just `string[]`, but the idea here is you can name a type without naming it, by using `typeof` on the expression you would have called. Hope this helps further your D knowledge ;) -Steve
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 17:16, Salih Dincer wrote: > Excuse me, but they still write in purple prose about dynamic > array literature here! I've heard positive things about D's arrays from people who use D in production. Especially slices... > I've known D for more than 10 years, but the topic we're talking about > still seems strange to me. Your example makes it more complicated and potentially exposes a bug. > The explanations given are not enough for > me, I'm sorry. There may be a number of different concepts to list but I don't think there is anything inherently complicated with these topics (again, your example is more complicated). > Can anyone tell me what happens when I change the location of the > structure? What you mean is, you see different behaviour depending on struct X is nested or not. The difference is, nested structs carry a context pointer. This may be related to a bug for the different outputs that we see. > So the X structure must be in the stack when it is in > main(), and the following must be in the heap, right? To nit-pick: The struct is just a definition. Not the struct but its objects can be on the stack or on the heap. But yes, all objects you have are on the stack. > //void main() { So when you uncomment that line and comment-out the following main() line in the program, you see a different output. I tested: If you make X a 'static struct', then you see the same output. I think the difference is due to a bug. Ali
Re: Is "auto t=T();" not the same as "T t;"?
On Wed, Oct 26, 2022 at 12:16:55AM +, Salih Dincer via Digitalmars-d-learn wrote: [...] > I've known D for more than 10 years, but the topic we're talking about > still seems strange to me. The explanations given are not enough for > me, I'm sorry. > > Can anyone tell me what happens when I change the location of the > structure? So the X structure must be in the stack when it is in > main(), and the following must be in the heap, right? > > ```d > import std; > > //void main() { > > struct X > { > struct Y { > int i = 10; > alias i this; > } > Y[] y = [Y.init]; This declares y to be a slice of some memory somewhere (it can be either the stack or the heap), and initializes it to point to a 1-element array containing Y.init. Here's an important quirk in D: whenever you initialize an aggregate array member with a literal, ALL instances of the aggregate will point to the *same* underlying array (see below). If you want to avoid the confusing situation below, my recommendation is that you initialize .y inside the ctor instead. Then it will be more explicit what exactly is going on. > string toString() { > return y.format!"%s"; > } > } > > void main() { > > X[2] x; This declares a static array of 2 elements, each of which is an X. Each instance of X contains a member y that is a slice that points to the array [Y.init]. Each instance of X sits on the stack; however, their members y point somewhere else, in this case, to the array [Y.init]. And here's the important point: as mentioned above, [Y.init] here is the SAME ARRAY that's being referred to by two different slices: x[0].y and x[1].y. If you'll excuse some ASCII art, here's the situation you have: STACK GLOBAL DATA x[0] { Y[] y; -+> [ Y.init ] } | x[1] { | Y[] y; -' } > x.writeln; // [[10], [10]] > > x[0].y[0] = 0; // [[0], [0]] This line in essence says, "assign 0 to the first element of the array in the slice y, in the first element of array x". Since both x[0].y and x[1].y point to the same underlying array, modifying it via x[0].y will also cause x[1].y to see the change. IOW, the revised ASCII art diagram now looks like this: STACK GLOBAL DATA x[0] { Y[] y; -+> [ 0 ] } | x[1] { | Y[] y; -' } That is why x[0].y[0] == 0 and also x[1].y[0] == 0. This is because x[0].y.ptr == x[1].y.ptr. > x.writeln; > > x[0].y.length++; This line tries to increase the length of the array pointed to by x[0].y. Since it was declared as a 1-element literal, which is allocated in program global data area, there isn't room to expand it. So at this point, in order to honor the request to lengthen the array, druntime will allocate a new array on the heap and copy the contents of the old array over, then expand its length to 2. The situation now looks like this: STACK GLOBAL DATA HEAP x[0] { Y[] y; > [ 0, 0 ] } x[1] { Y[] y; --> [ 0 ] } Key point: x[0].y and x[1].y now point to two different arrays, in two different places. Changes in one will no longer reflect in the other. The array [ 0 ] shown above is the same array that x[0].y *used* to point to, but no longer does because druntime has made a copy of it in the heap and updated x[0].y to point to the copy instead of the original. x[1].y, however, continues to point to the original array. > x[0].y[1] = 1; This modifies the second element of x[0].y to 1. Situation now looks like this: STACK GLOBAL DATA HEAP x[0] { Y[] y; > [ 0, 1 ] } x[1] { Y[] y; --> [ 0 ] } > x[1].y[0] = 2; This modifies the first element of x[1].y to 2. Situation: STACK GLOBAL DATA HEAP x[0] { Y[] y; > [ 0, 1 ] } x[1] { Y[] y; --> [ 2 ] } > x.writeln; // [[0, 1], [2]] Which reflects the situation shown above. T -- Why ask rhetorical questions? -- JC
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 20:36:28 UTC, matheus wrote: On int[] a = [1]; int[] b = a.dup; assert([0] !is [0]); // different memory ``` This is interesting, I understand the point of "reference vs copy", and I'm OK with this design choice of, but I wonder in the case of newcomers if this is a case the generate more problem understanding this rules, like we are having here. Matheus. Huh, I do NOT understand and I DO agree with you! Excuse me, but they still write in purple prose about dynamic array literature here! I've known D for more than 10 years, but the topic we're talking about still seems strange to me. The explanations given are not enough for me, I'm sorry. Can anyone tell me what happens when I change the location of the structure? So the X structure must be in the stack when it is in main(), and the following must be in the heap, right? ```d import std; //void main() { struct X { struct Y { int i = 10; alias i this; } Y[] y = [Y.init]; string toString() { return y.format!"%s"; } } void main() { X[2] x; x.writeln; // [[10], [10]] x[0].y[0] = 0; // [[0], [0]] x.writeln; x[0].y.length++; x[0].y[1] = 1; x[1].y[0] = 2; x.writeln; // [[0, 1], [2]] } /* Output of other state: [[10], [10]] [[0], [0]] [[2, 1], [2]] */ ``` SDB@79
Re: auto scope question?
On 10/25/22 15:07, WhatMeWorry wrote: > auto screen = executeShell(cmdLine); > auto s; That can't work because there is no information to infer the type of 's'. Judging from the return type of getPath, perhaps it's string[]: string[] s; This is the question we should answer first: What should happen when executeShell fails? a) It is an error; the program should not continue. Then we can use 'enforce' (this is my way of coding): string[] getPath(string cmdLine) { import std.exception : enforce; auto screen = executeShell(cmdLine); enforce(screen.status == 0, format!"%s failed:\n%s"(cmdLine, screen.output)); writeln("screen.output = ", screen.output); auto s = screen.output.findSplit("REG_SZ"); writeln("s[0] = ", s[0]); writeln("s[1] = ", s[1]); writeln("s[2] = ", s[2]); return (s.split(';')); } b) It is not an error; getPath() should return empty array: string[] getPath(string cmdLine) { string[] result; auto screen = executeShell(cmdLine); if (screen.status != 0) { writeln(cmdLine, " failed"); return null; // <-- HERE (null converts to any array type) } else { // ... // Now, 'return' is where 's' is defined: return (s.split(';')); } } Ali
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 13:36, matheus wrote: > On Tuesday, 25 October 2022 at 20:12:25 UTC, Paul Backus wrote: >> Static arrays are value types. What that means is, when we say float[3], there are just 3 floats without any overhead. >> Dynamic arrays are reference types. That phrase can be confusing because we often use the terms "dynamic array" and "slice" interchangably. I find the following simpler to understand (can still be confusing): - Dynamic arrays are expandable arrays that are owned by the D runtime (the GC). Dynamic arrays don't have names. - The names (symbols) that we see in source code are slices that are references to elements of arrays. Such arrays can be static or dynamic. When we add an element to a slice that has no room (.capacity <= .length) then a fresh dynamic array is created from the copies of the elements of the slice. > if this is a case the generate more problem understanding this rules, I think all these array complexities are inherent. Other examples from two other languages that I know: - In C, arrays that are members of user-defined types are value types. Arrays that are parameters are reference types (pointer to the first element). Confusing. - In C++, std::vector is a value type, which would be copied if passed by value. So there came std::array, std::string_view, std::span, etc. to address value versus reference complexities. I don't think D could do anything better with arrays. They seem to work pretty well and are among the favorite features of many D programmers. Ali
auto scope question?
I'm naturally getting a undefined identifier `s` error in the return. Is there some way to refactor my code? I tried to declare s outside of the else brackets like: auto screen = executeShell(cmdLine); auto s; ... { s = screen.output.findSplit("REG_SZ"); } but that doesn't compile either. string[] getPath(string cmdLine) { auto screen = executeShell(cmdLine); if (screen.status != 0) { writeln(cmdLine, " failed"); } else { writeln("screen.output = ", screen.output); auto s = screen.output.findSplit("REG_SZ"); writeln("s[0] = ", s[0]); writeln("s[1] = ", s[1]); writeln("s[2] = ", s[2]); } return (s.split(';')); // Error: undefined identifier `s` }
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 20:27:18 UTC, Ali Çehreli wrote: On 10/25/22 13:12, Paul Backus wrote: > In order to create a copy of a static array Although .dup works for static arrays as well, you meant "dynamic array" and everyones knows it. :) Yes; thank you for the correction. :)
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 20:12:25 UTC, Paul Backus wrote: On Tuesday, 25 October 2022 at 17:54:16 UTC, Salih Dincer wrote: On Tuesday, 25 October 2022 at 17:18:35 UTC, Paul Backus wrote: It's not a bug. They're pointing to the exact same instance of `A` in memory: I don't understand? So I don't understand why it causes problems with dynamic arrays! So why is there nothing wrong with the static array in the example below? Static arrays are value types. When you copy a static array, the copy's data is stored in a separate block of memory from the original: ```d int[1] a = [1]; int[1] b = a; assert([0] !is [0]); // different memory ``` Dynamic arrays are reference types. When you copy a dynamic array, both copies point to the same block of memory: ```d int[] a = [1]; int[] b = a; assert([0] is [0]); // same memory ``` In order to create a copy of a static array with its own block of memory, separate from the original, you have to use the built-in `.dup` method: ```d int[] a = [1]; int[] b = a.dup; assert([0] !is [0]); // different memory ``` This is interesting, I understand the point of "reference vs copy", and I'm OK with this design choice of, but I wonder in the case of newcomers if this is a case the generate more problem understanding this rules, like we are having here. Matheus.
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 13:12, Paul Backus wrote: > In order to create a copy of a static array Although .dup works for static arrays as well, you meant "dynamic array" and everyones knows it. :) > with its own block of > memory, separate from the original, you have to use the built-in `.dup` > method: > > ```d > int[] a = [1]; > int[] b = a.dup; Ali
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 17:54:16 UTC, Salih Dincer wrote: On Tuesday, 25 October 2022 at 17:18:35 UTC, Paul Backus wrote: It's not a bug. They're pointing to the exact same instance of `A` in memory: I don't understand? So I don't understand why it causes problems with dynamic arrays! So why is there nothing wrong with the static array in the example below? Static arrays are value types. When you copy a static array, the copy's data is stored in a separate block of memory from the original: ```d int[1] a = [1]; int[1] b = a; assert([0] !is [0]); // different memory ``` Dynamic arrays are reference types. When you copy a dynamic array, both copies point to the same block of memory: ```d int[] a = [1]; int[] b = a; assert([0] is [0]); // same memory ``` In order to create a copy of a static array with its own block of memory, separate from the original, you have to use the built-in `.dup` method: ```d int[] a = [1]; int[] b = a.dup; assert([0] !is [0]); // different memory ```
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 11:23, Steven Schveighoffer wrote: >> Why do I say incorrect things like that? :) > You were right actually. As always! Now I'm confused. :o) Ali
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 2:03 PM, Ali Çehreli wrote: On 10/25/22 11:01, Ali Çehreli wrote: > static arrays don't have .ptr to point > to any member. Why do I say incorrect things like that? :) Of course static arrays have .ptr as well but that always point to their own body of N elements. They own their elements... I move away from the keyboard now... :) You were right actually. The `arr.ptr` is a shortcut for the address of the first element. A dynamic array is equivalent to a struct with a `ptr` and `length` fields. A static array does not have fields, it's just the data itself. The properties are not real fields, but lowerings so they can be used like dynamic arrays. -Steve
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 11:01, Ali Çehreli wrote: > static arrays don't have .ptr to point > to any member. Why do I say incorrect things like that? :) Of course static arrays have .ptr as well but that always point to their own body of N elements. They own their elements... I move away from the keyboard now... :) > Static arrays are "just elements side by side." I still like that description though. Ali
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 10:54, Salih Dincer wrote: > So I don't understand why it causes problems with > dynamic arrays! So why is there nothing wrong with the static array in > the example below? The same rules as other uses of dynamic arrays... > //A[] a = [A.init];/* In that case, there is a single instance of [A.init]. All A.a members point to it through their .ptr .length members. > A[1] a = [A.init];//*/ In that case, there is still a single instance of [A.init], which gets copied to each A.a member because static arrays don't have .ptr to point to any member. Static arrays are "just elements side by side." In other words, there is no option but to copy to static arrays; they can't point to elements and they don't have any problem in this case. Ali
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 17:18:35 UTC, Paul Backus wrote: It's not a bug. They're pointing to the exact same instance of `A` in memory: I don't understand? So I don't understand why it causes problems with dynamic arrays! So why is there nothing wrong with the static array in the example below? ```d import std.stdio, std.format; void main() { struct B { struct A { int i = 10; } //A[] a = [A.init];/* A[1] a = [A.init];//*/ string toString() { return a[0].i.format!"%s"; } } B[2] b; b.writeln; // [10, 10] b[0].a[0].i = 0; b.writeln; // [0, 10] b[1].a[0].i = 1; b.writeln; // [0, 1] } ```
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 08:50, Andrey Zherikov wrote: > I'd like to tune default ctor but structs can't have custom one. > Adding a ctor with parameter seems a hack to me There is static opCall, which may be seen as a hack as well. :) The following all print the same thing now: import std.stdio; struct A { int[] i; } struct B { A[] a = [A.init]; static B opCall() { return B.init; } } void main() { auto b1 = B.init; b1.writeln; B b2 = B(); b2.writeln; B b3; b3.writeln; } > This fails in run time Not anymore with the above code. Ali
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 16:52:48 UTC, Salih Dincer wrote: On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote: Does the second piece of code shows a bug or my expectation is not correct (and why if so)? This is a bug: ```d void main() { struct B { struct A { int i = 10; } A[] a = [A.init]; } B[2] b; assert(b[0].a[0].i == 10); assert(b[1].a[0].i == 10); b[0].a[0].i = 1; assert(b[0].a[0].i == 1); // ok... assert(b[1].a[0].i == 1); // must be 10 !!! } ``` It's not a bug. They're pointing to the exact same instance of `A` in memory: ```d void main() { struct B { struct A { int i = 10; } A[] a = [A.init]; } B[2] b; assert(b[0].a.ptr is b[1].a.ptr); } ``` As explained in [Adam's reply][1], what happens here is that there is a single, global `A[]` allocated at compile time, which is shared between all instances of `B.init`. It's the same as if you'd written ```d struct B { struct A { int i = 10; } static A[] globalArray = [A.init]; A[] a = globalArray; } ``` [1]: https://forum.dlang.org/post/yznhocajstphrozpn...@forum.dlang.org
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote: Does the second piece of code shows a bug or my expectation is not correct (and why if so)? This is a bug: ```d void main() { struct B { struct A { int i = 10; } A[] a = [A.init]; } B[2] b; assert(b[0].a[0].i == 10); assert(b[1].a[0].i == 10); b[0].a[0].i = 1; assert(b[0].a[0].i == 1); // ok... assert(b[1].a[0].i == 1); // must be 10 !!! } ``` SDB@79
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 14:53:50 UTC, Adam D Ruppe wrote: On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote: A[] a = [A.init]; This is a problem - this is referring to a static array instance, shared across all copies of B. You almost certainly don't want this. That B.a[0] is the *same object* across different default-constructed Bs... unless the optimizer hits it or something. Is it a bad idea to trigger copy on write before modification of B.a so it behaves as below? ```d B b1; b1.a = b1.a.dup; // copy on write b1.a[0].i ~= '1'; b1.a ~= A.init; b1.a[0].i ~= '2'; b1.a[1].i ~= '3'; b1.writeln;// B([A("12"), A("3")]) B b2; b2.writeln;// B([A("")]) ```
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 14:53:50 UTC, Adam D Ruppe wrote: But just don't do this. Only basic values and immutable strings are good to initialize this way. With the array or class objects, you're liable to get some shared thing. If you change this to be initialized in a constructor (which will require an argument in D) or factory function, you'll get far more consistent behavior as each instance will have its own array. I'd like to tune default ctor but structs can't have custom one. Adding a ctor with parameter seems a hack to me - and compiler still allows default construction even when default ctor is disabled: ```d struct A { int[] i; } struct B { A[] a; @disable this(); this(bool) { A[] a = [A.init]; } } void main() { auto b1 = B.init; b1.a[0].i ~= 1; b1.a ~= A.init; b1.a[0].i ~= 11; b1.a[1].i ~= 12; b1.writeln; auto b2 = B.init; b2.writeln; } ``` This fails in run time, not compile time: ``` core.exception.ArrayIndexError@onlineapp.d(19): index [0] is out of bounds for array of length 0 ??:? _d_arraybounds_indexp [0x55b558b8ec55] ./onlineapp.d:19 _Dmain [0x55b558b6bd5f] ``` As for why B() and B.init are different here... i don't know, probably some subtly of the compiler's implementation. Actually `auto b = B.init;` behaves the same way as `auto b = B();` but they don't do the same as `B b;`
Re: Is "auto t=T();" not the same as "T t;"?
On 10/25/22 07:53, Adam D Ruppe wrote: > On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote: >> A[] a = [A.init]; > > This is a problem - this is referring to a static array instance, shared > across all copies of B. You almost certainly don't want this. Agreed. It should be fine when the elements are immutable as I've experimented with recently[1]: struct Xml { string beg; string end = "/>"; // Shared by all instances; // no allocation for some objects. // ... } > As for why B() and B.init are different here... i don't know, probably > some subtly of the compiler's implementation. I think it's a bug. Ali [1] https://youtu.be/0JL9uT_XGZE?t=4260s
Re: Is "auto t=T();" not the same as "T t;"?
On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote: A[] a = [A.init]; This is a problem - this is referring to a static array instance, shared across all copies of B. You almost certainly don't want this. That B.a[0] is the *same object* across different default-constructed Bs... unless the optimizer hits it or something. But just don't do this. Only basic values and immutable strings are good to initialize this way. With the array or class objects, you're liable to get some shared thing. If you change this to be initialized in a constructor (which will require an argument in D) or factory function, you'll get far more consistent behavior as each instance will have its own array. As for why B() and B.init are different here... i don't know, probably some subtly of the compiler's implementation.
Is "auto t=T();" not the same as "T t;"?
I have the following types (simplified version of my code): ```d struct A { int[] i; } struct B { A[] a = [A.init]; } ``` This code: ```d auto b1 = B.init; b1.a[0].i ~= 1; b1.a ~= A.init; b1.a[0].i ~= 11; b1.a[1].i ~= 12; b1.writeln; auto b2 = B(); b2.writeln; ``` prints this as expected: ``` B([A([1, 11]), A([12])]) B([A([])]) ``` But this code: ```d B b1; // auto b1 = B.init; b1.a[0].i ~= 1; b1.a ~= A.init; b1.a[0].i ~= 11; b1.a[1].i ~= 12; b1.writeln; B b2; // auto b2 = B(); b2.writeln; ``` prints this which is not expected: ``` B([A([1, 11]), A([12])]) B([A([1])]) ``` Does the second piece of code shows a bug or my expectation is not correct (and why if so)?
Re: auto ref function parameter causes that non copyable struct is copied?
On Tuesday, 9 November 2021 at 19:30:20 UTC, tchaloupka wrote: On Monday, 8 November 2021 at 23:26:39 UTC, tchaloupka wrote: Bug or feature? :) I've reported it in https://issues.dlang.org/show_bug.cgi?id=22498. It doesn't copy. It just destructs the same object twice... Ouch. Change the destructor to `~this() { printf("~this(%d)\n", n); n = 12; }` and you'll see. I have been looking at LDC's IR of this. `unwrap` takes a lvalue, but the compiler puts the object from `gen` on main's stack and gives a pointer to unwrap instead. `unwrap` is now the sole owner of the object, and calls the destructor before returning a ref to one of its fields. I haven't figured out the interplay of features and optimizations here, but it looks like a leak. I would have expected @safe to catch this. As you said in the bug, NVRO solves it (not sure why). As well as not passing a lvalue into `unwrap` in the first place.
Re: auto ref function parameter causes that non copyable struct is copied?
On Monday, 8 November 2021 at 23:26:39 UTC, tchaloupka wrote: Bug or feature? :) I've reported it in https://issues.dlang.org/show_bug.cgi?id=22498.
Re: auto ref function parameter causes that non copyable struct is copied?
On Tuesday, 9 November 2021 at 02:43:55 UTC, jfondren wrote: On Tuesday, 9 November 2021 at 02:41:18 UTC, jfondren wrote: The expectation is probably that `f.move` set `f` to `Foo.init`, but the docs say: Posted too fast. Foo qualifies with its @disable: ```d struct A { int x; } struct B { int x; @disable this(this); } unittest { import core.lifetime : move; auto a = A(5); auto b = B(5); a.move; b.move; assert(a == A(5)); assert(b == B(0)); } ``` Yes it should qualify so it should be cleaned out when moved. When I change: ```D auto ref unwrap(EX)(auto ref EX res) { printf("unwrap()\n"); return res.get(); } ``` I get: ``` ~this(0) ~this(0) ~this(0) unwrap() ~this(42) ~this(0) ~this(42) ``` So the destructor isn't called from `gen()` -> there is [NRVO](https://dlang.org/glossary.html#nrvo) in play. Also `pragma(msg, __traits(isRef, res));` prints false in unwrap in this case (which is expected), but how it gets there as a value when it's not moved and instead copied even though it has disabled copy constructor? `isCopyable!(Value!Foo)` returns false as expected too.
Re: auto ref function parameter causes that non copyable struct is copied?
On Tuesday, 9 November 2021 at 02:41:18 UTC, jfondren wrote: The expectation is probably that `f.move` set `f` to `Foo.init`, but the docs say: Posted too fast. Foo qualifies with its @disable: ```d struct A { int x; } struct B { int x; @disable this(this); } unittest { import core.lifetime : move; auto a = A(5); auto b = B(5); a.move; b.move; assert(a == A(5)); assert(b == B(0)); } ```
Re: auto ref function parameter causes that non copyable struct is copied?
On Tuesday, 9 November 2021 at 02:19:28 UTC, Stanislav Blinov wrote: On Monday, 8 November 2021 at 23:26:39 UTC, tchaloupka wrote: ``` auto gen() { Foo f; // <--- this one f.n = 42; return value(f.move()); } void main() { Foo f; f = gen().unwrap.move; } ``` ~this(0) ~this(0) ~this(0) ~this(42) <- this is a copy (that shouldn't exist) being destroyed ~this(0) ~this(42) Is it a copy? I think the first destructor call is one of `f` in `gen` (marked by the comment above) The expectation is probably that `f.move` set `f` to `Foo.init`, but the docs say: "If T is a struct with a destructor or postblit defined, source is reset to its .init value after it is moved into target, otherwise it is left unchanged." ```d struct A { int x; } struct B { int x; this(this) { } } unittest { import core.lifetime : move; auto a = A(5); auto b = B(5); a.move; b.move; assert(a == A(5)); assert(b == B(0)); } ```
Re: auto ref function parameter causes that non copyable struct is copied?
On Monday, 8 November 2021 at 23:26:39 UTC, tchaloupka wrote: ``` auto gen() { Foo f; // <--- this one f.n = 42; return value(f.move()); } void main() { Foo f; f = gen().unwrap.move; } ``` ~this(0) ~this(0) ~this(0) ~this(42) <- this is a copy (that shouldn't exist) being destroyed ~this(0) ~this(42) Is it a copy? I think the first destructor call is one of `f` in `gen` (marked by the comment above)
auto ref function parameter causes that non copyable struct is copied?
Lets have this code: ```D import core.lifetime : forward; import core.stdc.stdio; import std.algorithm : move; struct Value(T) { private T storage; this()(auto ref T val) { storage = forward!val; } ref inout(T) get() inout { return storage; } } Value!T value(T)(auto ref T val) { return Value!T(forward!val); } auto ref unwrap(EX)(auto ref EX res) { return res.get(); } struct Foo { int n; @disable this(this); ~this() { printf("~this(%d)\n", n); } } auto gen() { Foo f; f.n = 42; return value(f.move()); } void main() { Foo f; f = gen().unwrap.move; } ``` As I understand it unwrap in `f = gen().unwrap.move` can't be called by ref (as gen() returns rvalue) so it would force the `Value` to be copied but as it holds non copyable struct, it can't be and so it should end up with a compiler error. But the code outputs: ``` ~this(0) ~this(0) ~this(0) ~this(42) <- this is a copy (that shouldn't exist) being destroyed ~this(0) ~this(42) ``` This could cause unwanted resource cleanup on a seemingly non copyable structs. Bug or feature? :)
Re: Return values from auto function
On Friday, 6 November 2020 at 10:51:20 UTC, Andrey Zherikov wrote: struct Result(T) { struct Success { static if(!is(T == void)) { T value; } } struct Failure { string error; } Algebraic!(Success, Failure) result; this(Success value) { result = value; } this(Failure value) { result = value; } } auto success(T)(T value) { return Result!T(Result!T.Success(value)); } auto failure(string error) { return Result!void(Result!void.Failure(error)); } auto f(int i) { return i > 0 ? success(i) : failure("err text"); // Error: incompatible types for (success(i)) : (failure("err text")): Result!int and Result!void } I'd go with: struct Result(T) { Nullable!string error; static if(!is(T == void)) { T value; static Result!T success(T value) { return Result!T(Nullable!string.init, value); } static Result!T failure(string error) { return Result!T(nullable(error), T.init); } } else { static Result!T success() { return Result!T(Nullable!string.init); } static Result!T failure(string error) { return Result!T(nullable(error)); } } bool isSuccess() { return error.isNull(); } } // Demo Result!int intFun(int i) { return i > 0 ? Result!int.success(i) : Result!int.failure("Be positive!"); } Result!void voidFun(int i) { return i > 0 ? Result!void.success() : Result!void.failure("Be positive!"); } void main() { auto intFunRes = intFun(5); if(intFunRes.isSuccess) writefln("Result: %d", intFunRes.value); else writefln("ERROR: %s", intFunRes.error.get); } Does this satisfy your needs?
Re: Return values from auto function
On Saturday, 7 November 2020 at 15:49:13 UTC, James Blachly wrote: ``` return i > 0 ? cast(Result) Success!int(i) : cast(Result) Failure("Sorry"); ``` I don't know about the SumType but I would expect you could use a construction instead of cast. import std; alias Result = Algebraic!(int, string) ; void main() { auto x = true? Result("fish") : Result(6); }
Re: Return values from auto function
On Saturday, 7 November 2020 at 15:49:13 UTC, James Blachly wrote: ``` retval = i > 0 ? Success!int(i) : Failure("Sorry"); ``` casting each to `Result` compiles, but is verbose: ``` return i > 0 ? cast(Result) Success!int(i) : cast(Result) Failure("Sorry"); ``` ** Could someone more knowledgeable than me explain why implicit conversion does not happen with the ternary op, but works fine with if/else? Presumably, it is because the op returns a single type and implicit conversion is performed after computing the expression's return type? If this somehow worked, it would make the SumType package much more ergonomic ** It's just that tenary requires the same type in both branches. It was already so in C. return i > 0 ? (retval = Success!int(i)) : (retval = Failure("Sorry")); should work
Re: Return values from auto function
On 11/6/20 5:51 AM, Andrey Zherikov wrote: I have auto function 'f' that might return either an error (with some text) or a result (with some value). The problem is that the type of the error is not the same as the type of result so compilation fails. ... How can I make the original code compilable without templatizing `failure` function? Paul Backus has already replied to you so I am surprised he did not plug is own package "SumType". Maybe it is modesty? This seems like an ideal time to use this. All of that being said, I tried to solve this for you using Paul's SumType package and myself was initially stymied by the fact that the implicit conversion of a `Success(T)` or `Failure` type did not work within the ternary op expression*. I rewrote it with if/else which works great and is only slightly more verbose than with ternary op. ```D alias Result = SumType!(Success!int, Failure); auto f(int i) { Result retval; if (i > 0) retval = Success!int(i); else retval = Failure("Sorry!"); return retval; } ``` the above relies on suitable definition of `Success(T)` and `Failure` structs, obviously. * This fails due to different types within the same expression: ``` retval = i > 0 ? Success!int(i) : Failure("Sorry"); ``` casting each to `Result` compiles, but is verbose: ``` return i > 0 ? cast(Result) Success!int(i) : cast(Result) Failure("Sorry"); ``` ** Could someone more knowledgeable than me explain why implicit conversion does not happen with the ternary op, but works fine with if/else? Presumably, it is because the op returns a single type and implicit conversion is performed after computing the expression's return type? If this somehow worked, it would make the SumType package much more ergonomic **
Re: Return values from auto function
On Saturday, 7 November 2020 at 01:50:15 UTC, Jesse Phillips wrote: On Friday, 6 November 2020 at 15:06:18 UTC, Andrey Zherikov wrote: On Friday, 6 November 2020 at 14:58:40 UTC, Jesse Phillips wrote: On Friday, 6 November 2020 at 14:20:40 UTC, Andrey Zherikov wrote: This issue seems hit the inability to implicitly convert custom types. May be it makes more sense to ask in a separate thread. The return type must be the same for all execution paths. Result!void is a different type from Result!int. You aren't passing a 'Result' because that doesn't exist as a type. To clarify my statement: Yes, Result!void and Result!int are different types but I couldn't find a way to implicitly convert one to another. I'm curious what your semantics would be. # Result!void => Result!int How does the type know it can convert to int, or string, or Foo? What does it mean for a void to be an int? # Result!int => Result!void If you have something, what does it mean to go to not having that something. Would you really want to implicitly lose that something? Ideally error type shouldn't depend on what type the operation returns but language has a limitation that function must return single type. Technically Failure struct can be moved out from Result(T) but the issue remains the same: how to convert Failure to Result!T for any T.
Re: Return values from auto function
On Friday, 6 November 2020 at 15:06:18 UTC, Andrey Zherikov wrote: On Friday, 6 November 2020 at 14:58:40 UTC, Jesse Phillips wrote: On Friday, 6 November 2020 at 14:20:40 UTC, Andrey Zherikov wrote: This issue seems hit the inability to implicitly convert custom types. May be it makes more sense to ask in a separate thread. The return type must be the same for all execution paths. Result!void is a different type from Result!int. You aren't passing a 'Result' because that doesn't exist as a type. To clarify my statement: Yes, Result!void and Result!int are different types but I couldn't find a way to implicitly convert one to another. Putting aside D not providing implicit conversion to custom types. I'm curious what your semantics would be. # Result!void => Result!int How does the type know it can convert to int, or string, or Foo? What does it mean for a void to be an int? # Result!int => Result!void If you have something, what does it mean to go to not having that something. Would you really want to implicitly lose that something?
Re: Return values from auto function
On Friday, 6 November 2020 at 20:05:36 UTC, Ferhat Kurtulmuş wrote: On Friday, 6 November 2020 at 10:51:20 UTC, Andrey Zherikov wrote: I have auto function 'f' that might return either an error (with some text) or a result (with some value). The problem is that the type of the error is not the same as the type of result so compilation fails. [...] Sounds like Andrei's "Expected". Here is an implementation for d. https://github.com/tchaloupka/expected Hmmm, I wonder how that is different from the idea of 'option'
Re: Return values from auto function
On Friday, 6 November 2020 at 10:51:20 UTC, Andrey Zherikov wrote: I have auto function 'f' that might return either an error (with some text) or a result (with some value). The problem is that the type of the error is not the same as the type of result so compilation fails. [...] Sounds like Andrei's "Expected". Here is an implementation for d. https://github.com/tchaloupka/expected
Re: Return values from auto function
On Friday, 6 November 2020 at 15:06:18 UTC, Andrey Zherikov wrote: To clarify my statement: Yes, Result!void and Result!int are different types but I couldn't find a way to implicitly convert one to another. You can't. Structs do not implicitly convert to each other, templated or otherwise.
Re: Return values from auto function
On Friday, 6 November 2020 at 14:58:40 UTC, Jesse Phillips wrote: On Friday, 6 November 2020 at 14:20:40 UTC, Andrey Zherikov wrote: This issue seems hit the inability to implicitly convert custom types. May be it makes more sense to ask in a separate thread. The return type must be the same for all execution paths. Result!void is a different type from Result!int. You aren't passing a 'Result' because that doesn't exist as a type. To clarify my statement: Yes, Result!void and Result!int are different types but I couldn't find a way to implicitly convert one to another.
Re: Return values from auto function
On Friday, 6 November 2020 at 14:20:40 UTC, Andrey Zherikov wrote: On Friday, 6 November 2020 at 12:03:01 UTC, Paul Backus wrote: You can't. Both return values have to have the same type, which means the failure function has to be able to return more than one type, which means it has to be a template. This issue seems hit the inability to implicitly convert custom types. May be it makes more sense to ask in a separate thread. The return type must be the same for all execution paths. Result!void is a different type from Result!int. You aren't passing a 'Result' because that doesn't exist as a type. Hopefully one of these captures a misunderstanding.
Re: Return values from auto function
On Friday, 6 November 2020 at 12:03:01 UTC, Paul Backus wrote: You can't. Both return values have to have the same type, which means the failure function has to be able to return more than one type, which means it has to be a template. This issue seems hit the inability to implicitly convert custom types. May be it makes more sense to ask in a separate thread.
Re: Return values from auto function
On Friday, 6 November 2020 at 13:59:58 UTC, gbram wrote: On Friday, 6 November 2020 at 12:03:01 UTC, Paul Backus wrote: On Friday, 6 November 2020 at 10:51:20 UTC, Andrey Zherikov wrote: How can I make the original code compilable without templatizing `failure` function? You can't. Both return values have to have the same type, which means the failure function has to be able to return more than one type, which means it has to be a template. Being pedantic, he can, by templatising 'f' instead. Could you share how?
Re: Return values from auto function
On Friday, 6 November 2020 at 12:03:01 UTC, Paul Backus wrote: On Friday, 6 November 2020 at 10:51:20 UTC, Andrey Zherikov wrote: How can I make the original code compilable without templatizing `failure` function? You can't. Both return values have to have the same type, which means the failure function has to be able to return more than one type, which means it has to be a template. Being pedantic, he can, by templatising 'f' instead.
Re: Return values from auto function
On Friday, 6 November 2020 at 10:51:20 UTC, Andrey Zherikov wrote: I can make it work if I add a type to `failure` function but this remove brevity in usage: - auto failure(T)(string error) { return Result!T(Result!T.Failure(error)); } auto f(int i) { return i > 0 ? success(i) : failure!int("err text"); // no error } - How can I make the original code compilable without templatizing `failure` function? You can't. Both return values have to have the same type, which means the failure function has to be able to return more than one type, which means it has to be a template.
Return values from auto function
I have auto function 'f' that might return either an error (with some text) or a result (with some value). The problem is that the type of the error is not the same as the type of result so compilation fails. Here is my code: -- struct Result(T) { struct Success { static if(!is(T == void)) { T value; } } struct Failure { string error; } Algebraic!(Success, Failure) result; this(Success value) { result = value; } this(Failure value) { result = value; } } auto success(T)(T value) { return Result!T(Result!T.Success(value)); } auto failure(string error) { return Result!void(Result!void.Failure(error)); } auto f(int i) { return i > 0 ? success(i) : failure("err text"); // Error: incompatible types for (success(i)) : (failure("err text")): Result!int and Result!void } - I can make it work if I add a type to `failure` function but this remove brevity in usage: - auto failure(T)(string error) { return Result!T(Result!T.Failure(error)); } auto f(int i) { return i > 0 ? success(i) : failure!int("err text"); // no error } - How can I make the original code compilable without templatizing `failure` function?
Re: How auto convert Variant to required function arguments?
On Friday, 9 October 2020 at 16:10:03 UTC, Ferhat Kurtulmuş wrote: On Friday, 9 October 2020 at 15:49:31 UTC, Ferhat Kurtulmuş wrote: On Friday, 9 October 2020 at 00:19:20 UTC, Marcone wrote: How auto convert Variant to required function arguments? import std.variant; import std.stdio; Variant a; int mul2(Variant b){ int c = *b.peek!(int); return 2*c; } int mul3(int b){ return 3*b; } void main() { a = 5; writeln(mul2(a)); Variant b = 3; writeln(mul3(*b.peek!int)); } Uh, probably this is not what you want. I found this thread: https://forum.dlang.org/thread/ebylgcrslkelgrvnt...@forum.dlang.org You man need to use Algebraic. may*
Re: How auto convert Variant to required function arguments?
On Friday, 9 October 2020 at 15:49:31 UTC, Ferhat Kurtulmuş wrote: On Friday, 9 October 2020 at 00:19:20 UTC, Marcone wrote: How auto convert Variant to required function arguments? import std.variant; import std.stdio; Variant a; int mul2(Variant b){ int c = *b.peek!(int); return 2*c; } int mul3(int b){ return 3*b; } void main() { a = 5; writeln(mul2(a)); Variant b = 3; writeln(mul3(*b.peek!int)); } Uh, probably this is not what you want. I found this thread: https://forum.dlang.org/thread/ebylgcrslkelgrvnt...@forum.dlang.org You man need to use Algebraic.
Re: How auto convert Variant to required function arguments?
On Friday, 9 October 2020 at 00:19:20 UTC, Marcone wrote: How auto convert Variant to required function arguments? import std.variant; import std.stdio; Variant a; int mul2(Variant b){ int c = *b.peek!(int); return 2*c; } int mul3(int b){ return 3*b; } void main() { a = 5; writeln(mul2(a)); Variant b = 3; writeln(mul3(*b.peek!int)); }
How auto convert Variant to required function arguments?
How auto convert Variant to required function arguments?
Re: Annotating SortedRange.empty const: Best way to auto-deduce annotations?
On Wednesday, 2 September 2020 at 21:40:59 UTC, Steven Schveighoffer wrote: What they can do is template the `this` parameter. Then if the underlying range supports calling that way, it will work, otherwise it won't. using `template this` should be compatible with the existing code I would think. Thanks! I've never looked much into template this -- at first glance, it offers what inout already offers -- but it sounds like the right tool for the job. (inout would straightaway disallow calls to mutable methods.) https://dlang.org/spec/template.html#template_this_parameter I'll experiment with it! -- Simon
Re: Annotating SortedRange.empty const: Best way to auto-deduce annotations?
On 9/2/20 5:23 PM, SimonN wrote: Hi, About this issue in Phobos: https://issues.dlang.org/show_bug.cgi?id=21216 SortedRange.empty should be const, .front should be inout Just adding const/inout to SortedRange's methods won't be enough; if we add const/inout here, then many other Phobos ranges need to become const/inout-correct to keep the tests passing. Before I dive deeper into annotating their methods, I would like to verify my assumptions on how template function attribute deduction works: 1) I know that templated functions deduce their own attributes on instantiation. But front()/empty() are non-templated methods within the templated struct SortedRange. Will attribute deduction happen here? mutability attributes are not deducted for templates. wrapping ranges such as SortedRange can't really specify const or inout on their methods via introspection of the underlying range. What they can do is template the `this` parameter. Then if the underlying range supports calling that way, it will work, otherwise it won't. using `template this` should be compatible with the existing code I would think. -Steve
Annotating SortedRange.empty const: Best way to auto-deduce annotations?
Hi, About this issue in Phobos: https://issues.dlang.org/show_bug.cgi?id=21216 SortedRange.empty should be const, .front should be inout Just adding const/inout to SortedRange's methods won't be enough; if we add const/inout here, then many other Phobos ranges need to become const/inout-correct to keep the tests passing. Before I dive deeper into annotating their methods, I would like to verify my assumptions on how template function attribute deduction works: 1) I know that templated functions deduce their own attributes on instantiation. But front()/empty() are non-templated methods within the templated struct SortedRange. Will attribute deduction happen here? 2) Is it sensible/possible to force attribute deduction by declaring empty() in SortedRange as a zero-argument template? I.e.: @property bool empty()() { return this._input.empty; } 3) Should I rather annotate the non-templated SortedRange.empty/.front manually? But with what, then? empty() should probably be const, but it's never @nogc if annotated manually, even if the wrapped range offers empty() @nogc. -- Simon
Re: safety and auto vectorization
On Monday, 3 August 2020 at 19:42:51 UTC, Steven Schveighoffer wrote: On 8/3/20 3:22 PM, Bruce Carneal wrote: Thanks Steve (and Chad). Summary: underspecified, varying behavior across versions, buggy. Steve, what's the best way for me to report this? Are spec issues lumped in with the other bugzilla reports? Yep. You can file under dlang.org with the spec keyword. Although this looks like it's not exactly a spec issue. I'd start it as a dmd bug (I don't know the exact interaction with the compiler and the library). It might also be a druntime bug. -Steve FWIW ..using gdc Debian 8.3.0-6, dst[]= generates a range error auto a = [1, 2, 3]; auto b = [4, 5, 6]; int[] dst = new int[4]; // note the extra element dst[] = a[] + b[]; writeln(dst[3]);
Re: safety and auto vectorization
On 8/3/20 3:22 PM, Bruce Carneal wrote: Thanks Steve (and Chad). Summary: underspecified, varying behavior across versions, buggy. Steve, what's the best way for me to report this? Are spec issues lumped in with the other bugzilla reports? Yep. You can file under dlang.org with the spec keyword. Although this looks like it's not exactly a spec issue. I'd start it as a dmd bug (I don't know the exact interaction with the compiler and the library). It might also be a druntime bug. -Steve
Re: safety and auto vectorization
On Monday, 3 August 2020 at 18:55:36 UTC, Steven Schveighoffer wrote: On 8/2/20 1:31 PM, Bruce Carneal wrote: import std; void f0(int[] a, int[] b, int[] dst) @safe { dst[] = a[] + b[]; } [snip of auto-vectorization example] I was surprised that f0 ran just fine with a.length and b.length geq dst.length. Is that a bug or a feature? First, I think this is a bug. A regression in fact. As of 2.077 this works, and before it did not. There is nothing in the spec that says the behavior is defined for this case. Second, it's more than just that. This also runs currently: void main() { auto a = [1, 2, 3]; auto b = [4, 5, 6]; int[] dst = new int[4]; // note the extra element dst[] = a[] + b[]; writeln(dst[3]); } Prior to 2.077, this fails with array length problems. After that it prints (at the moment): 402653184 If I up the size to 5, it fails with a range violation. I strongly suspect some off-by-one errors, but this looks unsafe. -Steve Thanks Steve (and Chad). Summary: underspecified, varying behavior across versions, buggy. Steve, what's the best way for me to report this? Are spec issues lumped in with the other bugzilla reports?
Re: safety and auto vectorization
On 8/2/20 1:31 PM, Bruce Carneal wrote: import std; void f0(int[] a, int[] b, int[] dst) @safe { dst[] = a[] + b[]; } void f1(int[] a, int[] b, int[] dst) @trusted { const minLen = min(a.length, b.length, dst.length); dst[0..minLen] = a[0..minLen] + b[0..minLen]; assert(dst.length == minLen); } I was surprised that f0 ran just fine with a.length and b.length geq dst.length. Is that a bug or a feature? Assuming it's a feature, are f0 and f1 morally equivalent? I ask because f1 auto-vectorizes in ldc while f0 does not. Not sure why. As a guess I'd say that the front end doesn't hoist bounds checks in f0 or at least doesn't convey the info to the back end in a comprehensible fashion. Non-guesses welcome. First, I think this is a bug. A regression in fact. As of 2.077 this works, and before it did not. There is nothing in the spec that says the behavior is defined for this case. Second, it's more than just that. This also runs currently: void main() { auto a = [1, 2, 3]; auto b = [4, 5, 6]; int[] dst = new int[4]; // note the extra element dst[] = a[] + b[]; writeln(dst[3]); } Prior to 2.077, this fails with array length problems. After that it prints (at the moment): 402653184 If I up the size to 5, it fails with a range violation. I strongly suspect some off-by-one errors, but this looks unsafe. -Steve
Re: safety and auto vectorization
On Sunday, 2 August 2020 at 17:31:45 UTC, Bruce Carneal wrote: import std; void f0(int[] a, int[] b, int[] dst) @safe { dst[] = a[] + b[]; } void f1(int[] a, int[] b, int[] dst) @trusted { const minLen = min(a.length, b.length, dst.length); dst[0..minLen] = a[0..minLen] + b[0..minLen]; assert(dst.length == minLen); } I was surprised that f0 ran just fine with a.length and b.length geq dst.length. Is that a bug or a feature? Assuming it's a feature, are f0 and f1 morally equivalent? I ask because f1 auto-vectorizes in ldc while f0 does not. Not sure why. As a guess I'd say that the front end doesn't hoist bounds checks in f0 or at least doesn't convey the info to the back end in a comprehensible fashion. Non-guesses welcome. I don't know what's going on auto-vectorization-wise, but to address the behavioral issues, the next thing I would do if I were in your shoes is something like this: import std.stdio; int[100] a, b, dst; a[] = 2; b[] = 3; dst[] = 42; f0(a[0..$], b[0..$], dst[0..50]); // Notice: dst is a smaller slice. writefln("dst[49] == %d", dst[49]); // Should be 5. writefln("dst[50] == %d", dst[50]); // 42 or 5? On my machine (Linux 64-bit DMD v2.093.0) it prints this: dst[49] == 5 dst[50] == 42 Which suggests that it is doing the minimum-length calculation, as the dst[] values outside of the lesser-sized slice were untouched. This was DMD, so it's going to be worth trying on your compiler to see what you get.
safety and auto vectorization
import std; void f0(int[] a, int[] b, int[] dst) @safe { dst[] = a[] + b[]; } void f1(int[] a, int[] b, int[] dst) @trusted { const minLen = min(a.length, b.length, dst.length); dst[0..minLen] = a[0..minLen] + b[0..minLen]; assert(dst.length == minLen); } I was surprised that f0 ran just fine with a.length and b.length geq dst.length. Is that a bug or a feature? Assuming it's a feature, are f0 and f1 morally equivalent? I ask because f1 auto-vectorizes in ldc while f0 does not. Not sure why. As a guess I'd say that the front end doesn't hoist bounds checks in f0 or at least doesn't convey the info to the back end in a comprehensible fashion. Non-guesses welcome.
Re: Finding out ref-ness of the return of an auto ref function
On Saturday, 13 June 2020 at 09:13:36 UTC, Arafel wrote: If, however, you're wrapping a function template, however, you won't know until you actually instantiate it, which is basically going back to Paul Backus' solution. So the compiler doesn't always have all the information :) Well, the compiler can know `typeof (return)`, so at that point and under the same circumstances it has know (and thus it could expose) the ref-ness. And it does, for functions, via the aforementioned trait. It can't for templates until those templates are instantiated, which you can only do correctly by actually forwarding arguments to it (because a function template may have auto ref parameters). Steven's issue report doesn't account for templates, nor overloading, and correctly testing the latter would pretty much require you to match argument types and ref-ness by hand anyway (yuck).
Re: Finding out ref-ness of the return of an auto ref function
On 12/6/20 20:34, Stanislav Blinov wrote: On Friday, 12 June 2020 at 17:50:43 UTC, Arafel wrote: All in all, I still think something like `__traits(isRef,return)` would still be worth adding! After all the compiler already has all the information, so it's just about exposing it. I'm trying to think of a library solution, but I find it very hard to express "the hypothetical result of calling the current function with the current parameters in the current context". A. If you're wrapping a function you can use a 'getFunctionAttributes' trait [1], which would contain a "ref" if that function returns ref. If, however, you're wrapping a function template, however, you won't know until you actually instantiate it, which is basically going back to Paul Backus' solution. So the compiler doesn't always have all the information :) [1] https://dlang.org/spec/traits.html#getFunctionAttributes Well, the compiler can know `typeof (return)`, so at that point and under the same circumstances it has know (and thus it could expose) the ref-ness. Also there could be a better and more straightforward way of checking if an expression would be an l-value... it seems it's not the first time it has appeared: https://issues.dlang.org/show_bug.cgi?id=15634 The forum thread linked in the bug report is also quite interesting.
Re: Finding out ref-ness of the return of an auto ref function
On Friday, 12 June 2020 at 17:50:43 UTC, Arafel wrote: All in all, I still think something like `__traits(isRef,return)` would still be worth adding! After all the compiler already has all the information, so it's just about exposing it. I'm trying to think of a library solution, but I find it very hard to express "the hypothetical result of calling the current function with the current parameters in the current context". A. If you're wrapping a function you can use a 'getFunctionAttributes' trait [1], which would contain a "ref" if that function returns ref. If, however, you're wrapping a function template, however, you won't know until you actually instantiate it, which is basically going back to Paul Backus' solution. So the compiler doesn't always have all the information :) [1] https://dlang.org/spec/traits.html#getFunctionAttributes
Re: Finding out ref-ness of the return of an auto ref function
On 12/6/20 18:15, Paul Backus wrote: I think I have something that works: ref int foo(); int bar(); enum isLvalue(string expr) = q{ __traits(compiles, (auto ref x) { static assert(__traits(isRef, x)); }(} ~ expr ~ q{)) }; pragma(msg, mixin(isLvalue!"foo()")); // true pragma(msg, mixin(isLvalue!"bar()")); // false Basically, you can pass the result of the function call to a function with an `auto ref` parameter and check whether that parameter is inferred as ref or not. Thanks a lot! I have to say, it works, it's really, really clever... but it's also ugly as hell, and feels like a kludge. I had already tried something similar with an identity function, but I couldn't make it compile-time... I was missing the "compiles"+"static assert" trick. Also, it can become quite hard to check from within the function itself... in my case it's more or less doable because it's basically a forwarding, so I'm essentially doing a simple mixin, but in a more complex case (with perhaps even static ifs and whatever) I can see it becoming essentially unmanageable. All in all, I still think something like `__traits(isRef,return)` would still be worth adding! After all the compiler already has all the information, so it's just about exposing it. I'm trying to think of a library solution, but I find it very hard to express "the hypothetical result of calling the current function with the current parameters in the current context". A.
Re: Finding out ref-ness of the return of an auto ref function
On Friday, 12 June 2020 at 14:56:41 UTC, Arafel wrote: Hi all, I'm hitting a problem that it's making crazy... is there any way to find out if the return of an `auto ref` function is actually ref or not? So, according to the documentation [1] it depends on the return expressions... however in my case I'm implementing `opDispatch` in a wrapper type (and trying to carry over the ref-ness), so I don't know how can I check it. Now, the whole point of this wrapper is to act differently based on whether the return is a reference or not (it already checks for `hasIndirections`, which btw doesn't help here either). I've tried to use `__traits(isRef, ??? ) but I haven't been able to find out how to use it, it seems to be meant for parameters. Perhaps it would make sense to have something like `traits(isRef, return)`? Also I have tried making two different overloads, with and without ref, but it didn't work either... A. [1]: https://dlang.org/spec/function.html#auto-ref-functions I think I have something that works: ref int foo(); int bar(); enum isLvalue(string expr) = q{ __traits(compiles, (auto ref x) { static assert(__traits(isRef, x)); }(} ~ expr ~ q{)) }; pragma(msg, mixin(isLvalue!"foo()")); // true pragma(msg, mixin(isLvalue!"bar()")); // false Basically, you can pass the result of the function call to a function with an `auto ref` parameter and check whether that parameter is inferred as ref or not.
Finding out ref-ness of the return of an auto ref function
Hi all, I'm hitting a problem that it's making crazy... is there any way to find out if the return of an `auto ref` function is actually ref or not? So, according to the documentation [1] it depends on the return expressions... however in my case I'm implementing `opDispatch` in a wrapper type (and trying to carry over the ref-ness), so I don't know how can I check it. Now, the whole point of this wrapper is to act differently based on whether the return is a reference or not (it already checks for `hasIndirections`, which btw doesn't help here either). I've tried to use `__traits(isRef, ??? ) but I haven't been able to find out how to use it, it seems to be meant for parameters. Perhaps it would make sense to have something like `traits(isRef, return)`? Also I have tried making two different overloads, with and without ref, but it didn't work either... A. [1]: https://dlang.org/spec/function.html#auto-ref-functions
Re: I want Sublime 3 D auto import !
On Thursday, 4 June 2020 at 04:48:22 UTC, bauss wrote: On Wednesday, 3 June 2020 at 11:54:57 UTC, Виталий Фадеев wrote: On Tuesday, 2 June 2020 at 20:08:09 UTC, bauss wrote: What happens if you have the same symbol in multiple modules? Ex. two libraries that implement symbols with same name. Is there a way to be selective? I want it too! :) Updated: - implemented simple way to be selective !
Re: I want Sublime 3 D auto import !
On Wednesday, 3 June 2020 at 11:54:57 UTC, Виталий Фадеев wrote: On Tuesday, 2 June 2020 at 20:08:09 UTC, bauss wrote: What happens if you have the same symbol in multiple modules? Ex. two libraries that implement symbols with same name. First module will inserted. Is there a way to be selective? I want it too! :) And what about keyboard shortcut? A specially for you ! Sublime 3 / Preferences / Key bindings: [ { "keys": ["alt+a"], "command": "dlang_auto_import" }, ] Thanks, that's great!
Re: I want Sublime 3 D auto import !
On Tuesday, 2 June 2020 at 20:08:09 UTC, bauss wrote: What happens if you have the same symbol in multiple modules? Ex. two libraries that implement symbols with same name. First module will inserted. Is there a way to be selective? I want it too! :) And what about keyboard shortcut? A specially for you ! Sublime 3 / Preferences / Key bindings: [ { "keys": ["alt+a"], "command": "dlang_auto_import" }, ]
Re: I want Sublime 3 D auto import !
On Monday, 1 June 2020 at 17:28:16 UTC, Johannes Loher wrote: On Monday, 1 June 2020 at 16:18:44 UTC, Виталий Фадеев wrote: [...] Demanding stuff usually doesn't work in this community. The usual answer is something like this: If you care about this, implement it yourself or pay somebody to do it. Depends on how you interpret it.
Re: I want Sublime 3 D auto import !
On Tuesday, 2 June 2020 at 06:00:10 UTC, Виталий Фадеев wrote: On Monday, 1 June 2020 at 18:55:03 UTC, Paul Backus wrote: On Monday, 1 June 2020 at 16:18:44 UTC, Виталий Фадеев wrote: I do it! https://github.com/vitalfadeev/SublimeDlangAutoImport What happens if you have the same symbol in multiple modules? Ex. two libraries that implement symbols with same name. Is there a way to be selective? And what about keyboard shortcut?
Re: I want Sublime 3 D auto import !
On Tuesday, 2 June 2020 at 06:00:10 UTC, Виталий Фадеев wrote: On Monday, 1 June 2020 at 18:55:03 UTC, Paul Backus wrote: On Monday, 1 June 2020 at 16:18:44 UTC, Виталий Фадеев wrote: I do it! https://github.com/vitalfadeev/SublimeDlangAutoImport Cool. I dont use classe but I see how this kind of functionality might be useful for other symbols.
Re: I want Sublime 3 D auto import !
On Tuesday, 2 June 2020 at 06:00:10 UTC, Виталий Фадеев wrote: On Monday, 1 June 2020 at 18:55:03 UTC, Paul Backus wrote: On Monday, 1 June 2020 at 16:18:44 UTC, Виталий Фадеев wrote: I do it! https://github.com/vitalfadeev/SublimeDlangAutoImport Great! You should make a post about it in the Announce forum, so that other Sublime Text users will see it.
Re: I want Sublime 3 D auto import !
On Monday, 1 June 2020 at 18:55:03 UTC, Paul Backus wrote: On Monday, 1 June 2020 at 16:18:44 UTC, Виталий Фадеев wrote: I do it! https://github.com/vitalfadeev/SublimeDlangAutoImport
Re: I want Sublime 3 D auto import !
On Monday, 1 June 2020 at 16:18:44 UTC, Виталий Фадеев wrote: I want Sublime D auto import ! When typing code like this: class Uno : IStylable { // } I want will be auto added "import IStylable" at begin of file. Like this: import ui.istylable : IStylable; class Uno : IStylable { // } 1. I want for plugin will scan all files in project, and grep for "module ". 2. Then "module " replaced to "import ". 3. Then "import " inserted in text. At top. After line "module ..." if it exist, else just at top. 4. Check for "module " not exists before insert. Your best bet for getting IDE-like features like this is probably to use serve-d [1] together with a language server client plugin for your editor [2]. [1] https://github.com/Pure-D/serve-d [2] https://github.com/sublimelsp/LSP
Re: I want Sublime 3 D auto import !
On Monday, 1 June 2020 at 16:18:44 UTC, Виталий Фадеев wrote: I want Sublime D auto import ! When typing code like this: class Uno : IStylable { // } I want will be auto added "import IStylable" at begin of file. Like this: import ui.istylable : IStylable; class Uno : IStylable { // } 1. I want for plugin will scan all files in project, and grep for "module ". 2. Then "module " replaced to "import ". 3. Then "import " inserted in text. At top. After line "module ..." if it exist, else just at top. 4. Check for "module " not exists before insert. Demanding stuff usually doesn't work in this community. The usual answer is something like this: If you care about this, implement it yourself or pay somebody to do it.
I want Sublime 3 D auto import !
I want Sublime D auto import ! When typing code like this: class Uno : IStylable { // } I want will be auto added "import IStylable" at begin of file. Like this: import ui.istylable : IStylable; class Uno : IStylable { // } 1. I want for plugin will scan all files in project, and grep for "module ". 2. Then "module " replaced to "import ". 3. Then "import " inserted in text. At top. After line "module ..." if it exist, else just at top. 4. Check for "module " not exists before insert.
Re: Is there a way to tell if an auto ref parameter is by ref or by value?
On Sunday, 10 May 2020 at 01:15:58 UTC, Anonymouse wrote: On Sunday, 10 May 2020 at 00:33:07 UTC, NaN wrote: Ie something like.. auto Foo(T)(auto ref T x) { static if (isByRef(x)) { } else { } } __traits(isRef, x) Thanks :)
Re: Is there a way to tell if an auto ref parameter is by ref or by value?
On Sunday, 10 May 2020 at 00:33:07 UTC, NaN wrote: Ie something like.. auto Foo(T)(auto ref T x) { static if (isByRef(x)) { } else { } } __traits(isRef, x)
Is there a way to tell if an auto ref parameter is by ref or by value?
Ie something like.. auto Foo(T)(auto ref T x) { static if (isByRef(x)) { } else { } }
Re: How make DMD auto link imported modules?
On Tuesday, 21 April 2020 at 21:36:57 UTC, Marcone wrote: When I create a module, for exemple mymodule.d and import im my main program using "import mymodule" I need add mymodule.d in DMD command line manually. How can make it automatic? dmd -i
How make DMD auto link imported modules?
When I create a module, for exemple mymodule.d and import im my main program using "import mymodule" I need add mymodule.d in DMD command line manually. How can make it automatic?
Re: auto vectorization notes
On Saturday, 28 March 2020 at 18:01:37 UTC, Crayo List wrote: On Saturday, 28 March 2020 at 06:56:14 UTC, Bruce Carneal wrote: On Saturday, 28 March 2020 at 05:21:14 UTC, Crayo List wrote: On Monday, 23 March 2020 at 18:52:16 UTC, Bruce Carneal wrote: [snip] Explicit SIMD code, ispc or other, isn't as readable or composable or vanilla portable but it certainly is performance predictable. This is not true! The idea of ispc is to write portable code that will vectorize predictably based on the target CPU. The object file/binary is not portable, if that is what you meant. Also, I find it readable. There are many waypoints on the readability <==> performance axis. If ispc works for you along that axis, great! I find SIMT code readability better than SIMD but a little worse than auto-vectorizable kernels. Hugely better performance though for less effort than SIMD if your platform supports it. Again I don't think this is true. Unless I am misunderstanding you, SIMT and SIMD are not mutually exclusive and if you need performance then you must use both. Also based on the workload and processor SIMD may be much more effective than SIMT.j SIMD might become part of the solution under the hood for a number of reasons including: ease of deployment, programmer familiarity, PCIe xfer overheads, kernel launch overhead, memory subsystem suitability, existing code base issues, ... SIMT works for me in high throughput situations where it's hard to "take a log" on the problem. SIMD, in auto-vectorizable or more explicit form, works in others. Combinations can be useful but most of the work I've come in contact with splits pretty clearly along the memory bandwidth divide (SIMT on one side, SIMD/CPU on the other). Others need a plus-up in arithmetic horsepower. The more extreme the requirements, the more attractive SIMT appears. (hence my excitement about dcompute possibly expanding the dlang performance envelope with much less cognitive load than CUDA/OpenCL/SycL/...) On the readability front, I find per-lane programming, even with the current thread-divergence caveats, to be easier to reason about wrt correctness and performance predictability than other approaches. Apparently your mileage does vary. When you have chosen SIMD, whether ispc or other, over SIMT what drove the decision? Performance? Ease of programming to reach a target speed?