digitalmars.D.learn - Simplify some C-style code
- sfp (46/46) Dec 24 2024 I have some code like this:
- monkyyy (2/48) Dec 24 2024 static foreach, traits and mixin
- sfp (7/8) Dec 25 2024 I was looking into this but I think I need some help getting off
- user1234 (6/14) Dec 25 2024 Mixins can only introduce certain type of nodes: Type,
- sfp (10/26) Dec 25 2024 Unfortunately, std.sumtype doesn't seem to work for my case. I
- monkyyy (5/8) Dec 25 2024 If he's struggling to define a enum with a mixin I assume he's
- sfp (13/21) Dec 25 2024 Thanks for fleshing out an example for me. That's a big help.
- monkyyy (24/27) Dec 25 2024 ```d
- Jim Balter (37/39) Dec 25 2024 It's not weird at all when you understand that it's an AST that
- monkyyy (51/59) Dec 25 2024 ```
- Jim Balter (6/14) Dec 25 2024 No, but `mixin("enum Test {", "A, B, C", "}");` does. There are
- monkyyy (5/7) Dec 25 2024 I suggest trying random things instead of reading the spec. The
- Jim Balter (4/12) Dec 25 2024 Defensive testing isn't inconsistent with learning and
- Nick Treleaven (2/5) Dec 27 2024 Can you please stop saying that the spec is intentionally false?
- monkyyy (6/11) Dec 28 2024 The spec would need to drastically improve before my opinion
- Andy Valencia (11/17) Dec 29 2024 On the other hand, I'd characterize the language as very nicely
- monkyyy (7/23) Dec 29 2024 Where that is true, I doubt the spec was involved. The spec
- Nick Treleaven (5/8) Jan 06 The spec may have flaws, as any complex technical spec is prone
- sfp (10/26) Dec 25 2024 Language specs aren't a good resource for learning. I'm also not
- bauss (37/83) Dec 25 2024 Here's how I would improve upon your code with mixin and static
- sfp (71/73) Dec 26 2024 Thanks again for all the helpful feedback. This is where I landed:
- Christian =?UTF-8?B?S8O2c3RsaW4=?= (10/56) Jan 05 What would speak against coming up with an interface
- sfp (15/77) Jan 06 I have another post here about virtual opBinary for interfaces.
I have some code like this: ``` enum DomainType { Ball, Box, CsgDiff } struct Domain(int Dim) { DomainType type; union { Ball!Dim ball; Box!Dim box; CsgDiff!Dim csgDiff; } this(Ball!Dim ball) { this.type = DomainType.Ball; this.ball = ball; } this(Box!Dim box) { this.type = DomainType.Box; this.box = box; } this(CsgDiff!Dim csgDiff) { this.type = DomainType.CsgDiff; this.csgDiff = csgDiff; } void doSomething() { switch (type) { case DomainType.Ball: ... break; case DomainType.Box: ... break; case DomainType.CsgDiff: ... break; } } } ``` Is there some way I can reduce the amount of boilerplate using D, i.e. generate most of this code at compile time? For what it's worth, I had checked out `SumType` as an alternative to this mess, but it doesn't play nicely with recursively defined types.
Dec 24 2024
On Wednesday, 25 December 2024 at 07:49:28 UTC, sfp wrote:I have some code like this: ``` enum DomainType { Ball, Box, CsgDiff } struct Domain(int Dim) { DomainType type; union { Ball!Dim ball; Box!Dim box; CsgDiff!Dim csgDiff; } this(Ball!Dim ball) { this.type = DomainType.Ball; this.ball = ball; } this(Box!Dim box) { this.type = DomainType.Box; this.box = box; } this(CsgDiff!Dim csgDiff) { this.type = DomainType.CsgDiff; this.csgDiff = csgDiff; } void doSomething() { switch (type) { case DomainType.Ball: ... break; case DomainType.Box: ... break; case DomainType.CsgDiff: ... break; } } } ``` Is there some way I can reduce the amount of boilerplate using D, i.e. generate most of this code at compile time? For what it's worth, I had checked out `SumType` as an alternative to this mess, but it doesn't play nicely with recursively defined types.static foreach, traits and mixin
Dec 24 2024
On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:static foreach, traits and mixinI was looking into this but I think I need some help getting off the ground... This doesn't compile: ``` enum Test { mixin("A, B, C") } ```
Dec 25 2024
On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:Mixins can only introduce certain type of nodes: Type, Declaration, Expression, and Statement. Honestly I dont see how would they help here... What you can do however is to use https://dlang.org/phobos/std_sumtype.html. instead of a the manually defined tagged union.static foreach, traits and mixinI was looking into this but I think I need some help getting off the ground... This doesn't compile: ``` enum Test { mixin("A, B, C") } ```
Dec 25 2024
On Wednesday, 25 December 2024 at 17:20:01 UTC, user1234 wrote:On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:Unfortunately, std.sumtype doesn't seem to work for my case. I want to be able to define several mutually recursive types. It seems the only way to define a recursive type using std.sumtype is the `This` parameter. For instance, this doesn't work: ``` alias Blah = SumType!(A); struct A { Blah *b; } ``` Throws an error about recursive template expansion or something.On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:Mixins can only introduce certain type of nodes: Type, Declaration, Expression, and Statement. Honestly I dont see how would they help here... What you can do however is to use https://dlang.org/phobos/std_sumtype.html. instead of a the manually defined tagged union.static foreach, traits and mixinI was looking into this but I think I need some help getting off the ground... This doesn't compile: ``` enum Test { mixin("A, B, C") } ```
Dec 25 2024
On Wednesday, 25 December 2024 at 17:20:01 UTC, user1234 wrote:What you can do however is to use https://dlang.org/phobos/std_sumtype.html. instead of a the manually defined tagged union.If he's struggling to define a enum with a mixin I assume he's very new and to get this code to work with sumtype would require a template map, which would make you a highly competent template wizard
Dec 25 2024
On Wednesday, 25 December 2024 at 17:46:01 UTC, monkyyy wrote:On Wednesday, 25 December 2024 at 17:20:01 UTC, user1234 wrote:Thanks for fleshing out an example for me. That's a big help. Understanding that `mixin` only works for certain AST nodes is useful... but kind of weird that it doesn't work inside `enum`. Seems like an obvious application... I'm an experienced programmer but new to D. I've done a decent amount of template metaprogramming in C++, but not really a wizard there either. Just enough to be dangerous (mostly to myself). What is a template map? I'd be interested in seeing this pattern and whether it's worth it to try to salvage std.sumtype. I also saw a thread about a language level (possibly?) sumtype. Is this feature going to resolve the `This` parameter grossness?What you can do however is to use https://dlang.org/phobos/std_sumtype.html. instead of a the manually defined tagged union.If he's struggling to define a enum with a mixin I assume he's very new and to get this code to work with sumtype would require a template map, which would make you a highly competent template wizard
Dec 25 2024
On Wednesday, 25 December 2024 at 18:25:57 UTC, sfp wrote:What is a template map? I'd be interested in seeing this pattern and whether it's worth it to try to salvage std.sumtype.```d import std; struct Ball(int i){} struct Box(int i){} struct CsgDiff(int i){} alias dimlist(int dim)=AliasSeq!(Ball!dim,Box!dim,CsgDiff!dim); alias dimedsumtype(int dim)=SumType!(dimlist!dim); alias genfunction(T)=(T t)=>t.writeln; void doSomething(int dim)(dimedsumtype!dim st){ st.match!( staticMap!(genfunction,dimlist!dim) ); } unittest{ dimedsumtype!1 foo; foo.doSomething!1; } ``` You will need to read the template book to even begin to understand, I had to work around what seems to me to be a compiler bug to get it to compile, and the number of people who can help you drop to like 5 https://github.com/PhilippeSigaud/D-templates-tutorial
Dec 25 2024
On Wednesday, 25 December 2024 at 18:25:57 UTC, sfp wrote:kind of weird that it doesn't work inside `enum`. Seems like an obvious application...It's not weird at all when you understand that it's an AST that is being mixed in, not just a string as if it were an C preprocessor macro. The insides of `enum { ... }` is not an expression so it makes no sense to try to mix in an expression. enums are indeed an obvious application for mixins, but that's just not the way you do it. Here's are some mixins I wrote just yesterday: ``` string membersListMixin(alias T)() { string s; foreach (member; __traits(allMembers, T)) { s ~= member ~ ", "; } return s; } enum NamedEnum { // named so I can generate its fields field1 = "string1", field2 = "string2", // ... } void foo() { foreach (field; mixin(`[`, membersListMixin!NamedEnum, `]`)) { // do something with field } } string anonymousEnumMixin(alias E)() { string s = "enum { "; foreach (member; __traits(allMembers, E)) { s ~= member ~ " = " ~ E.stringof ~ "." ~ member ~ ", "; } return s ~ "}"; } mixin(anonymousEnumMixin!NamedEnum); // now I can refer to field1, field2, etc. without qualifying them. ```
Dec 25 2024
On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:``` import std; enum string[] domains=["Ball","Box","CsgDiff"];//could be from traits but I think traits when strings will do is bad enum string[] lowerdomains=["ball","box","csgDiff"];//could be ctfe but... eh mixin("enum DomainType{"~domains.joiner(",").array~"}");//array makes allot of range code simpler to work with, at costs of speed string makeunion(){ string o; o~="union{"; foreach(i;0..domains.length){ o~=domains[i]~"!Dim "; o~=lowerdomains[i]~";"; } o~="}"; return o; } string makeconstructor(int i){ string o; o~="this("~domains[i]~"!Dim "~lowerdomains[i]~"){\n"; o~="this.type=DomainType."~domains[i]~";\n"; o~="this."~lowerdomains[i]~"="~lowerdomains[i]~";}"; return o; } unittest{ //makeconstructor(1).writeln; } struct Ball(int i){} struct Box(int i){} struct CsgDiff(int i){} struct Domain(int Dim){ DomainType type; mixin(makeunion); static foreach(i;0..domains.length){ mixin(makeconstructor(i)); } void doSomething(){ lable:switch(type){ static foreach(E;DomainType.min..DomainType.max){ case E: E.stringof.writeln; break lable; } default: }} } unittest{ auto foo=Domain!1(Ball!1()); foo.doSomething; } ```static foreach, traits and mixinI was looking into this but I think I need some help getting off the ground... This doesn't compile: ``` enum Test { mixin("A, B, C") } ```
Dec 25 2024
On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:No, but `mixin("enum Test {", "A, B, C", "}");` does. There are mixin statements, which that is, and there are mixin expressions, which you tried, but you can't put an expression there and A, B, C isn't one. I suggest reading the spec rather than just trying random things.static foreach, traits and mixinI was looking into this but I think I need some help getting off the ground... This doesn't compile: ``` enum Test { mixin("A, B, C") } ```
Dec 25 2024
On Wednesday, 25 December 2024 at 21:23:00 UTC, Jim Balter wrote:I suggest reading the spec rather than just trying random things.I suggest trying random things instead of reading the spec. The spec is full of lies and slander, and when your in template hell, defensively syntax test to find the compiler bugs before you glue all your code together.
Dec 25 2024
On Wednesday, 25 December 2024 at 21:34:00 UTC, monkyyy wrote:On Wednesday, 25 December 2024 at 21:23:00 UTC, Jim Balter wrote:Defensive testing isn't inconsistent with learning and understanding the language rather than just randomly guessing what might work.I suggest reading the spec rather than just trying random things.I suggest trying random things instead of reading the spec. The spec is full of lies and slander, and when your in template hell, defensively syntax test to find the compiler bugs before you glue all your code together.
Dec 25 2024
On Wednesday, 25 December 2024 at 21:34:00 UTC, monkyyy wrote:The spec is full of lies and slander,noun: lie; plural noun: lies an intentionally false statement.Can you please stop saying that the spec is intentionally false?
Dec 27 2024
On Friday, 27 December 2024 at 11:31:05 UTC, Nick Treleaven wrote:On Wednesday, 25 December 2024 at 21:34:00 UTC, monkyyy wrote:The spec would need to drastically improve before my opinion changes; Im also uninterested practicing withholding my opinions. There are many many ancient bugs, that you need to inherent implicit knowledge to navigate, either those a) should actually be fixed or b) the spec should make such knowledge explicitThe spec is full of lies and slander,noun: lie; plural noun: lies an intentionally false statement.Can you please stop saying that the spec is intentionally false?
Dec 28 2024
On Saturday, 28 December 2024 at 23:23:02 UTC, monkyyy wrote:The spec would need to drastically improve before my opinion changes; Im also uninterested practicing withholding my opinions. There are many many ancient bugs, that you need to inherent implicit knowledge to navigate, either those a) should actually be fixed or b) the spec should make such knowledge explicitOn the other hand, I'd characterize the language as very nicely designed, and the spec and Programming in D to be tremendously helpful. The responsiveness and quality of comments on this forum has always sufficed to fill in the gaps. Just recently a submitted a bug on Phobos, which was quickly and kindly narrowed down to something else (changed ldc2 default). The community is the real treasure. Can the spec be improved? No doubt. I hope you can find a way to get the flaws you've found incorporated. Andy
Dec 29 2024
On Sunday, 29 December 2024 at 15:15:28 UTC, Andy Valencia wrote:On Saturday, 28 December 2024 at 23:23:02 UTC, monkyyy wrote:Where that is true, I doubt the spec was involved. The spec bullet points look like compiler peusdo code or formalizing wontfix bugs while burying the lead.The spec would need to drastically improve before my opinion changes; Im also uninterested practicing withholding my opinions. There are many many ancient bugs, that you need to inherent implicit knowledge to navigate, either those a) should actually be fixed or b) the spec should make such knowledge explicitOn the other hand, I'd characterize the language as very nicely designed,Just recently a submitted a bug on Phobos, which was quickly and kindly narrowed down to something else (changed ldc2 default). I hope you can find a way to get the flaws you've found incorporated.At this point if I found a bug I needed fixed id complain to adr, and even then I assume ill find a bug with templates once a week after leaving the garden path.
Dec 29 2024
On Saturday, 28 December 2024 at 23:23:02 UTC, monkyyy wrote:The spec would need to drastically improve before my opinion changes; Im also uninterested practicing withholding my opinions.The spec may have flaws, as any complex technical spec is prone to. There is no reason why anyone on the core team would want the spec to intentionally be false, and you have no evidence of that. So if that is your opinion, then it is not based on evidence.
Jan 06
On Wednesday, 25 December 2024 at 21:23:00 UTC, Jim Balter wrote:On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:Language specs aren't a good resource for learning. I'm also not trying random things. I have a clear idea of what I want to do, and I'm in the process of learning how D does its thing, and whether its a reasonable choice for me. I've found an example which is simplified and closely related to something I want to do. It fails and the compiler errors aren't helpful. I've posted it here and gotten detailed responses from people who know what's going on. Extremely useful. Thanks in advance for your patience, since I will invariably be posting more of the same.On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:No, but `mixin("enum Test {", "A, B, C", "}");` does. There are mixin statements, which that is, and there are mixin expressions, which you tried, but you can't put an expression there and A, B, C isn't one. I suggest reading the spec rather than just trying random things.static foreach, traits and mixinI was looking into this but I think I need some help getting off the ground... This doesn't compile: ``` enum Test { mixin("A, B, C") } ```
Dec 25 2024
On Wednesday, 25 December 2024 at 07:49:28 UTC, sfp wrote:I have some code like this: ``` enum DomainType { Ball, Box, CsgDiff } struct Domain(int Dim) { DomainType type; union { Ball!Dim ball; Box!Dim box; CsgDiff!Dim csgDiff; } this(Ball!Dim ball) { this.type = DomainType.Ball; this.ball = ball; } this(Box!Dim box) { this.type = DomainType.Box; this.box = box; } this(CsgDiff!Dim csgDiff) { this.type = DomainType.CsgDiff; this.csgDiff = csgDiff; } void doSomething() { switch (type) { case DomainType.Ball: ... break; case DomainType.Box: ... break; case DomainType.CsgDiff: ... break; } } } ``` Is there some way I can reduce the amount of boilerplate using D, i.e. generate most of this code at compile time? For what it's worth, I had checked out `SumType` as an alternative to this mess, but it doesn't play nicely with recursively defined types.Here's how I would improve upon your code with mixin and static foreach. ``` enum DomainType { Ball, Box, CsgDiff } const DomainTypeShortNames = [ DomainType.Ball : "ball", DomainType.Box : "box", DomainType.CsgDiff : "csgDiff" ]; struct Domain(int Dim) { DomainType type; union { static foreach (domainType; EnumMembers!DomainType) { mixin(domainType.to!string ~ "!Dim " ~ DomainTypeShortNames[domainType] ~ ";"); } } static foreach (domainType; EnumMembers!DomainType) { mixin(q{ this(%1$s!Dim %2$s) { this.type = DomainType.%1$s; this.%2$s = %2$s; } }.format(domainType, DomainTypeShortNames[domainType])); } ... } ```
Dec 25 2024
On Wednesday, 25 December 2024 at 07:49:28 UTC, sfp wrote:I have some code like this: ...Thanks again for all the helpful feedback. This is where I landed: ``` import std.algorithm : map; import std.algorithm.iteration : joiner; import std.array : array; string uncap(string s) { import std.format; import std.uni; return format("%s%s", s[0].toLower, s[1..$]); } enum string[] domains = [ "Ball", "Box", "CsgDiff", "CsgIsect", "CsgUnion", "Rect", ]; auto getTypeString(string[] types) { return "enum Type {" ~ types.joiner(", ").array ~ "}"; } auto getUnionString(string[] types) { return ( ["union {"] ~ types.map!(s => s ~ "!Dim " ~ uncap(s) ~ ";").array ~ ["}"] ).joiner("\n").array; } string getCtorString(string s) { return "this(" ~ s ~ "!Dim " ~ uncap(s) ~ ") {\n" ~ " this.type = Type." ~ s ~ ";\n" ~ " this." ~ uncap(s) ~ " = " ~ uncap(s) ~ ";\n" ~ "}\n"; } auto getCtorStrings(string[] types) { return types.map!(getCtorString).array; } struct Domain(int Dim) { mixin(getTypeString(domains)); Type type; mixin(getUnionString(domains)); static foreach (ctorString; getCtorStrings(domains)) mixin(ctorString); Rect!Dim getBoundingBox() const { final switch (type) static foreach (s; domains) case mixin("Type." ~ s): return mixin(uncap(s) ~ ".getBoundingBox()"); } Domain!Dim intersect(const ref Domain!Dim other) const { final switch (type) static foreach (s1; domains) case mixin("Type." ~ s1): final switch (other.type) static foreach (s2; domains) case mixin("Type." ~ s2): return mixin(uncap(s1) ~ ".intersect(other." ~ uncap(s2) ~ ")"); } } ``` This is enough to get me unstuck so I can write more actual code, but it still seems less than ideal: 1. It would be nice to be able to define the list of domains in terms of the actual types rather than strings. 2. As an added bonus, if I could validate that the types are all compatible with each other as templates (e.g., in this case they're all template classes with a single integer parameter), that would be useful. 3. It would be nice to come up with an abbreviated syntax for `getBoundingBox` and `intersect`, so that this starts looking a little more like a DSL for stubbing out interfaces.
Dec 26 2024
On Wednesday, 25 December 2024 at 07:49:28 UTC, sfp wrote:I have some code like this: ``` enum DomainType { Ball, Box, CsgDiff } struct Domain(int Dim) { DomainType type; union { Ball!Dim ball; Box!Dim box; CsgDiff!Dim csgDiff; } this(Ball!Dim ball) { this.type = DomainType.Ball; this.ball = ball; } this(Box!Dim box) { this.type = DomainType.Box; this.box = box; } this(CsgDiff!Dim csgDiff) { this.type = DomainType.CsgDiff; this.csgDiff = csgDiff; } void doSomething() { switch (type) { case DomainType.Ball: ... break; case DomainType.Box: ... break; case DomainType.CsgDiff: ... break; } } } ``` Is there some way I can reduce the amount of boilerplate using D, i.e. generate most of this code at compile time? For what it's worth, I had checked out `SumType` as an alternative to this mess, but it doesn't play nicely with recursively defined types.What would speak against coming up with an interface (https://dlang.org/spec/interface.html) the only includes this `doSomething` method and then having `Ball`, `Box` and `CsgDiff` implementing that. Almost no boilerplate involved, no manual union and type storing, but a virtual function call for `doSomething`. It's "very" OOP, which gets less and less popular, but why not? Kind regards, Christian
Jan 05
On Sunday, 5 January 2025 at 16:02:47 UTC, Christian Köstlin wrote:On Wednesday, 25 December 2024 at 07:49:28 UTC, sfp wrote:I have another post here about virtual opBinary for interfaces. Main two reasons at this point: - I don't want to be tied into D's OO + GC impl unnecessarily. - I've figured out how to do multiple dispatch using mixin templates at this point and I'm basically happy with the result (there is room for improvement but I'm confident in my ability to make those improvements now). But I agree that interface is a good choice for many things. It would unfortunately lead to quite a lot of boilerplate for what I'm working on and it's unclear whether the performance tradeoff is acceptable (I have seen other projects make a similar tradeoff bit fail becsuse they were unable to tame their exploding memory use).I have some code like this: ``` enum DomainType { Ball, Box, CsgDiff } struct Domain(int Dim) { DomainType type; union { Ball!Dim ball; Box!Dim box; CsgDiff!Dim csgDiff; } this(Ball!Dim ball) { this.type = DomainType.Ball; this.ball = ball; } this(Box!Dim box) { this.type = DomainType.Box; this.box = box; } this(CsgDiff!Dim csgDiff) { this.type = DomainType.CsgDiff; this.csgDiff = csgDiff; } void doSomething() { switch (type) { case DomainType.Ball: ... break; case DomainType.Box: ... break; case DomainType.CsgDiff: ... break; } } } ``` Is there some way I can reduce the amount of boilerplate using D, i.e. generate most of this code at compile time? For what it's worth, I had checked out `SumType` as an alternative to this mess, but it doesn't play nicely with recursively defined types.What would speak against coming up with an interface (https://dlang.org/spec/interface.html) the only includes this `doSomething` method and then having `Ball`, `Box` and `CsgDiff` implementing that. Almost no boilerplate involved, no manual union and type storing, but a virtual function call for `doSomething`. It's "very" OOP, which gets less and less popular, but why not? Kind regards, Christian
Jan 06