Re: semi-final switch?
On Friday, 18 June 2021 at 04:24:19 UTC, jfondren wrote: On Thursday, 17 June 2021 at 21:41:28 UTC, Steven Schveighoffer wrote: A final switch on an enum complains if you don't handle all the enum's cases. I like this feature. ... Oh, and to throw a monkey wrench in here, the value is a string, not an integer. So I can't use std.conv.to to verify the enum is valid (plus, then I'm running a switch twice). Wanting to avoid more work than a switch means generating a switch. I think that's the real monkey wrench. Alternately, weave the check you want into your switch: ```d import std.traits : EnumMembers; import std.algorithm : map, canFind; import std.conv : to; enum C { ABC, XYZ } alias namesEnum(E) = s => [EnumMembers!E].map!(to!string).canFind(s); enum enumCount(E) = [EnumMembers!E].length; int example(string k) { switch (k) { case "ABC": static assert("ABC".namesEnum!C); return 1; case "XYZ": static assert("XYZ".namesEnum!C); return 2; default: static assert(2 == enumCount!C); return 0; } } unittest { example("force asserts"); } ``` but this repeats the keys (which could be mis-repeated), and it requires hand-counting the cases checked (which could be mis-counted). At least it's easier to check without reference to the enum. What I wanted to do was add "ABC" to a static string[] and confirm that it's a permutation of [EnumMembers!C].map!(to!string).
Re: semi-final switch?
On Thursday, 17 June 2021 at 21:41:28 UTC, Steven Schveighoffer wrote: A final switch on an enum complains if you don't handle all the enum's cases. I like this feature. However, sometimes the data I'm switching on is coming from elsewhere (i.e. a user), and while I want to enforce that the data is valid (it's one of the enum values), I don't want to crash the program if the incoming value is not correct. But final switch doesn't let me declare a default case (to throw an exception instead). If I use a non-final switch, then my code might forget to handle one of the cases. Oh, and to throw a monkey wrench in here, the value is a string, not an integer. So I can't use std.conv.to to verify the enum is valid (plus, then I'm running a switch twice). Any ideas on better ways to handle this? -Steve Well, if you receive an `enum` that have an out of bounds value, your problem lies in the caller, not the callee. You're breaking the most fundamental promise of a type, that is, the values it can take. And you obviously also break any `@safe` function by feeding it this value. So instead of thinking in terms of `enum`, I would say, think in them of the value, and generate the switch: ```D SWITCH: switch (myRawValue) { static foreach (EV; NoDuplicates!(EnumMembers!MyEnum)) { case EV: // Handle; break SWITCH; } default: throw new Exception("Invalid value: " ~ myRawValue); } ``` Note that this can be encapsulated in its own function, like `validateEnum (EnumType) (BaseType!EnumType value)` (not sure if we have a `BaseType` template, but you get the point).
Re: semi-final switch?
On Thursday, 17 June 2021 at 21:41:28 UTC, Steven Schveighoffer wrote: A final switch on an enum complains if you don't handle all the enum's cases. I like this feature. ... Oh, and to throw a monkey wrench in here, the value is a string, not an integer. So I can't use std.conv.to to verify the enum is valid (plus, then I'm running a switch twice). Wanting to avoid more work than a switch means generating a switch. I think that's the real monkey wrench. Something like: ```d T enumCases(T, E, T[E] cases)(string x) { import std.format : format; import std.algorithm : map, all, joiner; import std.array : array; import std.traits : EnumMembers; import std.conv : to; mixin("switch (x) {\n" ~ [EnumMembers!E].map!(e => format!"case %(%s%): return %(%s%);\n"([e.to!string], [cases[e]])) .joiner.array ~ "default: assert(0);\n}"); } unittest { enum C { ABC, XYZ } assert("x\tb" == enumCases!(string, C, [ C.ABC: "x\tb", // error to omit an enum value C.XYZ: "ab",// impossible to have a bad enum value ])("ABC")); // the first problem with this solution: the following is an error... // unless the preceding usage is commented out. /+assert(2 == enumCases!(int, C, [ C.ABC: 1, C.XYZ: 2, ])("XYZ"));+/ } ```
Re: Vibe.d diet templates
On Thursday, 17 June 2021 at 18:54:41 UTC, WebFreak001 wrote: On Thursday, 17 June 2021 at 16:26:57 UTC, JG wrote: [...] Thanks, this works. I would have thought this would be a common enough use case to have support in diet. Anyone else wanted this? Opened an issue here: https://github.com/rejectedsoftware/diet-ng/issues/91 Thanks for opening that issue.
Re: semi-final switch?
On Thursday, 17 June 2021 at 21:41:28 UTC, Steven Schveighoffer wrote: Any ideas on better ways to handle this? I've had such a situation before too where I want to switch over enums I read from an ELF file which can't be assumed to be correct, but I also don't want to forget one. For a tight numerical enum you simply check `if (i <= EnumType.max)`, but when there's gaps (or strings like in your case) that doesn't work. I got the idea for a DIP to allow a `default` statement in `final switch` to allow a custom error handler instead of the default `__switch_error`, but never pursued it further. I'm still in favor of it though.
Re: semi-final switch?
On Thu, Jun 17, 2021 at 05:41:28PM -0400, Steven Schveighoffer via Digitalmars-d-learn wrote: [.[..] > Oh, and to throw a monkey wrench in here, the value is a string, not > an integer. So I can't use std.conv.to to verify the enum is valid > (plus, then I'm running a switch twice). > > Any ideas on better ways to handle this? [...] Why not just: try { MyEnum value = input.to!MyEnum; } catch (Exception e) { stderr.writeln("Invalid input"); } ? T -- People demand freedom of speech to make up for the freedom of thought which they avoid. -- Soren Aabye Kierkegaard (1813-1855)
semi-final switch?
A final switch on an enum complains if you don't handle all the enum's cases. I like this feature. However, sometimes the data I'm switching on is coming from elsewhere (i.e. a user), and while I want to enforce that the data is valid (it's one of the enum values), I don't want to crash the program if the incoming value is not correct. But final switch doesn't let me declare a default case (to throw an exception instead). If I use a non-final switch, then my code might forget to handle one of the cases. Oh, and to throw a monkey wrench in here, the value is a string, not an integer. So I can't use std.conv.to to verify the enum is valid (plus, then I'm running a switch twice). Any ideas on better ways to handle this? -Steve
Re: Arrays of variants, C++ vs D
On 6/17/21 5:01 PM, Ali Çehreli wrote: What's the difference? In both cases an int is being converted to a Foo. I think the "working" case is against the design of D. Likely there is a subtlety that I am missing... The difference might be that construction has only one set of overloads -- the constructor of the item being created. With your example, there's the set of overloads to create a Foo and the set of overloads to call foo. Imagine this: struct Foo { int x; this(int y) { x = y; } } struct Bar { int x; this(int y) { x = y; } } void foo(Foo f){} void foo(Bar b){} void main() { foo(42); // which one? } But I don't know. I know that there are all kinds of subtle problems with C++ implicit conversions, and D is right to avoid all that. But this might be one case where the decision tree is easy. -Steve
Re: Arrays of variants, C++ vs D
On 6/17/21 1:46 PM, Steven Schveighoffer wrote: > Implicit construction is supported: > > struct Foo > { > int x; > this(int y) { x = y; } > } > > Foo f = 5; // ok implicit construction That's so unlike the rest of the language that I consider it to be a bug. :) Really, why? What if the expression is more complicated: int i() { return 1; } Foo f = i(); OK, that works as well. Wow! But the following doesn't and is what I think Walter has been trying to prevent: struct Foo { int x; this(int y) { x = y; } } void foo(Foo) { } void main() { foo(42);// COMPILATION ERROR } What's the difference? In both cases an int is being converted to a Foo. I think the "working" case is against the design of D. Likely there is a subtlety that I am missing... Ali
Re: Vibe.d diet templates
On 6/17/21 4:22 PM, kdevel wrote: On Thursday, 17 June 2021 at 19:14:28 UTC, Steven Schveighoffer wrote: On 6/17/21 12:26 PM, JG wrote: However, what I *have* wanted is to have attribute values support `Nullable!T` such that they are only included if the item is non-null. See [here](https://github.com/rejectedsoftware/diet-ng/issues/28). BTW: Is it possible to replace the diet generator in vibe.d with a template engine like moustache [1] which is agnostic wrt the code it produces? In the past 25 or so years I frequently encountered designed HTML pages where only some data had to be inserted here or there. If I got the vibe.d model right one would have to reimplement these HTML pages in the diet language first. [1] https://github.com/repeatedly/mustache-d Of course. Vibe's diet support is wholly based on the diet-ng project, and you don't have to use it. It's just there out of the box. when you do: ```d res.render!("sometemplate.dt", all, my, args); ``` It's just a UFCS call. You could replace this with: res.renderMustache(...) Where you have to write the adapter. It's possible to get a char output range out of HTTPServerResponse, which you then can write to and it's just sent back to the client. This seems like it would do the trick, you just have to pass the output range in as the sink: https://mustache-d.dpldocs.info/mustache.MustacheEngine.render.2.html -Steve
Re: Arrays of variants, C++ vs D
On 6/17/21 4:15 PM, H. S. Teoh wrote: On Thu, Jun 17, 2021 at 07:44:31PM +, JN via Digitalmars-d-learn wrote: [...] Foo[int] foos = [ 0: Foo("abc"), 1: Foo(5) ]; } ``` Why does D need the explicit declarations whereas C++ can infer it? Because D does not support implicit construction. The array literal is parsed as-is, meaning string[int] is inferred rather than Foo[int]. So the initialization fails because of a type mismatch. Implicit construction is supported: struct Foo { int x; this(int y) { x = y; } } Foo f = 5; // ok implicit construction What is happening here though is that the construction is being done as an AA literal. This works for *some* types, but not all. e.g.: Foo[int] f = [5 : 5]; // error double[int] f = [5 : 5]; // ok It really should work IMO. -Steve
Re: Vibe.d diet templates
On Thursday, 17 June 2021 at 19:14:28 UTC, Steven Schveighoffer wrote: On 6/17/21 12:26 PM, JG wrote: However, what I *have* wanted is to have attribute values support `Nullable!T` such that they are only included if the item is non-null. See [here](https://github.com/rejectedsoftware/diet-ng/issues/28). BTW: Is it possible to replace the diet generator in vibe.d with a template engine like moustache [1] which is agnostic wrt the code it produces? In the past 25 or so years I frequently encountered designed HTML pages where only some data had to be inserted here or there. If I got the vibe.d model right one would have to reimplement these HTML pages in the diet language first. [1] https://github.com/repeatedly/mustache-d
Re: Arrays of variants, C++ vs D
On Thu, Jun 17, 2021 at 07:44:31PM +, JN via Digitalmars-d-learn wrote: [...] > Foo[int] foos = [ > 0: Foo("abc"), > 1: Foo(5) > ]; > } > ``` > > Why does D need the explicit declarations whereas C++ can infer it? Because D does not support implicit construction. The array literal is parsed as-is, meaning string[int] is inferred rather than Foo[int]. So the initialization fails because of a type mismatch. Implicit construction has been asked for many times, but Walter has been adamant about not allowing implicit construction in D. T -- Music critic: "That's an imitation fugue!"
Arrays of variants, C++ vs D
This C++ code compiles: ```cpp #include #include #include int main() { using Foo = std::variant; std::map foos = {{0, "abc"}, {1, 5}}; } This code doesn't: ```d import std.variant; void main() { alias Foo = Algebraic!(int, string); Foo[int] foos = [ 0: "abc", 1: 5 ]; } ``` but this does: ```d import std.variant; void main() { alias Foo = Algebraic!(int, string); Foo[int] foos = [ 0: Foo("abc"), 1: Foo(5) ]; } ``` Why does D need the explicit declarations whereas C++ can infer it?
Re: Vibe.d diet templates
On 6/17/21 12:26 PM, JG wrote: Thanks, this works. I would have thought this would be a common enough use case to have support in diet. Anyone else wanted this? I haven't found a need for it, as I'm usually only dynamically configuring attribute values, not attribute names. But my web-fu is pretty weak. However, what I *have* wanted is to have attribute values support `Nullable!T` such that they are only included if the item is non-null. See [here](https://github.com/rejectedsoftware/diet-ng/issues/28). -Steve
Re: Vibe.d diet templates
On Thursday, 17 June 2021 at 16:26:57 UTC, JG wrote: [...] Thanks, this works. I would have thought this would be a common enough use case to have support in diet. Anyone else wanted this? Opened an issue here: https://github.com/rejectedsoftware/diet-ng/issues/91
Re: List of Dynamic Arrays
On 6/17/21 9:10 AM, Justin Choi wrote: >> DList!(int[])() ? > > Thanks I've mentally slapped myself a hundred times for this mistake :D Also, unless DList is really needed, why not: int[][] Ali
Re: Vibe.d diet templates
On Thursday, 17 June 2021 at 09:16:56 UTC, WebFreak001 wrote: On Thursday, 17 June 2021 at 08:23:54 UTC, JG wrote: Suppose I have an array of attributes and values v is there any way to apply these attributes to a tag? So that something like tag(#{v[0]0]}=#{v[0][1]},...}) becomes where v[0][0]="attribute0" and v[0][1]="value0"? I think there is nothing for this built-in in diet, so you have to manually emit raw HTML: ```diet - import std.xml : encode; - auto start = appender!string; - start ~= " Thanks, this works. I would have thought this would be a common enough use case to have support in diet. Anyone else wanted this?
Re: List of Dynamic Arrays
On Thursday, 17 June 2021 at 15:58:40 UTC, Adam D Ruppe wrote: On Thursday, 17 June 2021 at 15:57:46 UTC, Justin Choi wrote: I want to write something like `DList!int[]()` DList!(int[])() ? Thanks I've mentally slapped myself a hundred times for this mistake :D
List of Dynamic Arrays
If I wanted to create a DList (or any similar data structure) of multiple integers, how would I accomplish this? I want to write something like `DList!int[]()` but the best I can do for now is a format such as `DList!(Tuple(int, int))()` which confines me to a fixed number of integers.
Re: List of Dynamic Arrays
On Thursday, 17 June 2021 at 15:57:46 UTC, Justin Choi wrote: I want to write something like `DList!int[]()` DList!(int[])() ?
Re: Vibe.d diet templates
On Thursday, 17 June 2021 at 08:23:54 UTC, JG wrote: Suppose I have an array of attributes and values v is there any way to apply these attributes to a tag? So that something like tag(#{v[0]0]}=#{v[0][1]},...}) becomes where v[0][0]="attribute0" and v[0][1]="value0"? I think there is nothing for this built-in in diet, so you have to manually emit raw HTML: ```diet - import std.xml : encode; - auto start = appender!string; - start ~= "
Vibe.d diet templates
Suppose I have an array of attributes and values v is there any way to apply these attributes to a tag? So that something like tag(#{v[0]0]}=#{v[0][1]},...}) becomes where v[0][0]="attribute0" and v[0][1]="value0"?
Re: Can not get struct member addresses at compile time
On Wednesday, 16 June 2021 at 23:20:26 UTC, Ali Çehreli wrote: Thank you, both. It still rules out an address at "compile time" in general. For example, we cannot use such an address when instantiating a template, or static array length, etc. And if I understand it correctly, there must be a pointer *variable* for the linker to initialize. Fine then: That's how this usage works for C but not for D. :) Thank you, Ali Yes, there must be a pointer variable, which explains why we can not do compile time pointer arithmetic, which is fair and square (although I think it might be possible with some extra compiler effort). It does _not_ explain why we can't take addresses of struct members, though, since we have a pointer variable there! Coming back to the working part of my first example: ```d struct Foo{ int bar; } __gshared Foo foo; void *fooptr = ``` This works! And yields very similar relocations than the C version (plus some overhead and name-mangling): ``` [...] wO .data._D19TypeInfo_S4test3Foo6__initZ 0091 _D19TypeInfo_S4test3Foo6__initZ g O .bss 0004 _D4test3Foo6__initZ 0004 g O .bss 0004 _D4test3fooSQk3Foo g .tdata.0008 _D4test6fooptrPv g O .rodata 000d _D4test12__ModuleInfoZ [...] RELOCATION RECORDS FOR [.tdata.]: OFFSET TYPE VALUE R_X86_64_64 _D4test3fooSQk3Foo [...] ``` The only difference is that the compiler will not pass down relocations plus an offset (i.e. relocating to a member of a struct) down to the linker, and I don't quite see a specific reason why it should not.