www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Simplify some C-style code

reply sfp <sfp hush.ai> writes:
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
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
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
parent reply sfp <sfp hush.ai> writes:
On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:
 static foreach, traits and mixin
I 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
next sibling parent reply user1234 <user1234 12.de> writes:
On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:
 On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:
 static foreach, traits and mixin
I 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") } ```
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.
Dec 25 2024
next sibling parent sfp <sfp hush.ai> writes:
On Wednesday, 25 December 2024 at 17:20:01 UTC, user1234 wrote:
 On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:
 On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:
 static foreach, traits and mixin
I 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") } ```
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.
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.
Dec 25 2024
prev sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
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
parent reply sfp <sfp hush.ai> writes:
On Wednesday, 25 December 2024 at 17:46:01 UTC, monkyyy wrote:
 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
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?
Dec 25 2024
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
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
prev sibling parent Jim Balter <Jim Balter.name> writes:
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
prev sibling next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:
 On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:
 static foreach, traits and mixin
I 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") } ```
``` 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; } ```
Dec 25 2024
prev sibling parent reply Jim Balter <jqbalter gmail.com> writes:
On Wednesday, 25 December 2024 at 16:41:05 UTC, sfp wrote:
 On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:
 static foreach, traits and mixin
I 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") } ```
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.
Dec 25 2024
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
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
next sibling parent Jim Balter <Jim Balter.name> writes:
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:
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.
Defensive testing isn't inconsistent with learning and understanding the language rather than just randomly guessing what might work.
Dec 25 2024
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
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
parent reply monkyyy <crazymonkyyy gmail.com> writes:
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 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?
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 explicit
Dec 28 2024
next sibling parent reply Andy Valencia <dont spam.me> writes:
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 explicit
On 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
parent monkyyy <crazymonkyyy gmail.com> writes:
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:
 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 
 explicit
On the other hand, I'd characterize the language as very nicely designed,
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.
 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
prev sibling parent Nick Treleaven <nick geany.org> writes:
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
prev sibling parent sfp <sfp hush.ai> writes:
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:
 On Wednesday, 25 December 2024 at 07:57:04 UTC, monkyyy wrote:
 static foreach, traits and mixin
I 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") } ```
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.
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.
Dec 25 2024
prev sibling next sibling parent bauss <jacobbauss gmail.com> writes:
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
prev sibling next sibling parent sfp <sfp hush.ai> writes:
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
prev sibling parent reply Christian =?UTF-8?B?S8O2c3RsaW4=?= <christian.koestlin gmail.com> writes:
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
parent sfp <sfp hush.ai> writes:
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 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
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).
Jan 06