www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How do I generate `setX` methods for all private mutable variables in

reply Ki Rill <rill.ki yahoo.com> writes:
How do I generate `setX` methods for all private mutable 
variables in my class? Do I need to use `__traits`?

I need this for my [tiny-svg](https://github.com/rillki/tiny-svg) 
project to generate `setX` methods for all Shapes.

Example:
```D
class Rectangle {
     private immutable ...;
     private Color fillColor;
     private Color strokeColor;
     private uint strokeWidth;

     this(int x, int y) {...}

     mixin(???);

     // I need this:
     Rectangle setFillColor(Color color) {...}
     Rectangle setStrokeColor(Color color) {...}
     Rectangle setStrokeWidth(uint color) {...}
}
```

Usage:
```D
new Rectangle(10, 10)
     .setFillColor(Colors.white)
     .setStrokeColor(Colors.black)
     .setStrokeWidth(3);
```
Jun 05 2023
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 5 June 2023 at 13:57:20 UTC, Ki Rill wrote:
 How do I generate `setX` methods for all private mutable 
 variables in my class? Do I need to use `__traits`?

 I need this for my 
 [tiny-svg](https://github.com/rillki/tiny-svg) project to 
 generate `setX` methods for all Shapes.

 Example:
 ```D
 class Rectangle {
     private immutable ...;
     private Color fillColor;
     private Color strokeColor;
     private uint strokeWidth;

     this(int x, int y) {...}

     mixin(???);

     // I need this:
     Rectangle setFillColor(Color color) {...}
     Rectangle setStrokeColor(Color color) {...}
     Rectangle setStrokeWidth(uint color) {...}
 }
 ```

 Usage:
 ```D
 new Rectangle(10, 10)
     .setFillColor(Colors.white)
     .setStrokeColor(Colors.black)
     .setStrokeWidth(3);
 ```
You need to put an user attribute on the fieldd then to use static introspection to generate the setters. Very basically that works like that ```d enum Set; class Color {} auto generateSetters(T)() { string result; import std.traits; static foreach (m; __traits(allMembers, T)) {{ alias member = __traits(getMember, T, m); static if (hasUDA!(member, Set)) result ~= "void set" ~ m ~ "(" ~ typeof(member).stringof ~ "){}\n"; }} return result; } class Rectangle { Set private Color fillColor; Set private Color strokeColor; Set private uint strokeWidth; mixin(generateSetters!(Rectangle)()); } void main() { with (new Rectangle) { setstrokeWidth(0); setstrokeColor(null); setfillColor(null); } } ``` although I did not spent time on the setter body... I suppose the question was more about the metprogramming technic, and that you don't want a pre-mashed solution ;)
Jun 05 2023
parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 5 June 2023 at 15:13:43 UTC, Basile B. wrote:
 On Monday, 5 June 2023 at 13:57:20 UTC, Ki Rill wrote:
 How do I generate `setX` methods for all private mutable
although I did not spent time on the setter body... I suppose the question was more about the metprogramming technic, and that you don't want a pre-mashed solution ;)
By the way...an other solution is to use [opDispatch](https://dlang.org/spec/operatoroverloading.html#dispatch): ```d class Color {} class Rectangle { private Color fillColor; private Color strokeColor; private uint strokeWidth; auto opDispatch(string member, T)(auto ref T t) { static if (member == "setStrokeWidth") {} else static if (member == "setStrokeColor") {} else static if (member == "setFillColor") {} else static assert(0, "cannot set " ~ member); return this; } } void main() { (new Rectangle) .setStrokeWidth(0) .setStrokeColor(null) .setFillColor(null); } ``` Sure the first solution takes advantage of D features but to `property`, etc.) On top of that I tend to prefer the later solution because self-introspection based on `__traits` has corner issues, especially when typecons-like structures are used for the members (e.g sumtype, nullable, etc.).
Jun 05 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/5/23 11:33 AM, Basile B. wrote:
 On Monday, 5 June 2023 at 15:13:43 UTC, Basile B. wrote:
 On Monday, 5 June 2023 at 13:57:20 UTC, Ki Rill wrote:
 How do I generate `setX` methods for all private mutable
although I did not spent time on the setter body... I suppose the question was more about the metprogramming technic, and that you don't want a pre-mashed solution ;)
By the way...an other solution is to use [opDispatch](https://dlang.org/spec/operatoroverloading.html#dispatch): ```d class Color {} class Rectangle {     private Color fillColor;     private Color strokeColor;     private uint strokeWidth;     auto opDispatch(string member, T)(auto ref T t)     {              static if (member == "setStrokeWidth") {}         else static if (member == "setStrokeColor") {}         else static if (member == "setFillColor") {}         else static assert(0, "cannot set " ~ member);         return this;     } } void main() {     (new Rectangle)         .setStrokeWidth(0)         .setStrokeColor(null)         .setFillColor(null); } ```
Ugh, don't do it that way. Always give opDispatch a template constraint or it will suck to use. Also, given the problem constraints, you can build the method automatically using the string. ```d auto opDispatch(string member, T)(auto ref T t) if(member.startsWith("set")) { mixin(toLower(m[3]), m[4 .. $], " = t;"); } ``` -Steve
Jun 06 2023
parent reply Basile B. <b2.temp gmx.com> writes:
On Tuesday, 6 June 2023 at 14:23:59 UTC, Steven Schveighoffer 
wrote:
 On 6/5/23 11:33 AM, Basile B. wrote:
 [...]
Ugh, don't do it that way. Always give opDispatch a template constraint or it will suck to use. Also, given the problem constraints, you can build the method automatically using the string. ```d auto opDispatch(string member, T)(auto ref T t) if(member.startsWith("set")) { mixin(toLower(m[3]), m[4 .. $], " = t;"); } ``` -Steve
yeah I know that opDispatch is disliked because it is tried in a SFINAE fashion, as citicized by Adam. But on the other side it's the best opover.
Jun 06 2023
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 6/6/23 09:13, Basile B. wrote:

 yeah I know that opDispatch is disliked because it is tried in a SFINAE
 fashion, as citicized by Adam. But on the other side it's the best 
opover. I like how it helped in my current project: user.someShellCommand("-foo", "-bar"); opDispatch makes a command string, passes it to executeShell, takes care of the return code and dumps its output if there was an error. Ali
Jun 06 2023
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 5 June 2023 at 13:57:20 UTC, Ki Rill wrote:
 How do I generate `setX` methods for all private mutable 
 variables in my class? Do I need to use `__traits`?

 I need this for my 
 [tiny-svg](https://github.com/rillki/tiny-svg) project to 
 generate `setX` methods for all Shapes.

 Example:
 ```D
 class Rectangle {
     private immutable ...;
     private Color fillColor;
     private Color strokeColor;
     private uint strokeWidth;

     this(int x, int y) {...}

     mixin(???);

     // I need this:
     Rectangle setFillColor(Color color) {...}
     Rectangle setStrokeColor(Color color) {...}
     Rectangle setStrokeWidth(uint color) {...}
 }
 ```
Is there a reason you can't just make these fields `public`? D supports property accessors, so you can always go back and make them `private` later on if it turns out you need to. For example: ```d // Before class MyClass1 { public int data; } void example1(MyClass1 c) { c.data = 123; // set int n = c.data; // get } // After class MyClass2 { private int data_; int data() { return data; } void data(int value) { data = value; } } void example2(MyClass2 c) { // Usage is exactly the same c.data = 123; // set int n = c.data; // get } ```
Jun 05 2023
next sibling parent Basile B. <b2.temp gmx.com> writes:
On Monday, 5 June 2023 at 15:28:34 UTC, Paul Backus wrote:
 Is there a reason you can't just make these fields `public`?
My bet is that OP actually wants to generate something like ```d void setStrokeWidth(uint value) { if (value = strokeWidth) return; strokeWidth = value; redraw(); // or maybe... // needRedraw = true; } ``` that's a common pattern in 2D graphics libraries
Jun 06 2023
prev sibling parent Basile B. <b2.temp gmx.com> writes:
On Monday, 5 June 2023 at 15:28:34 UTC, Paul Backus wrote:
 Is there a reason you can't just make these fields `public`?
My bet is that OP actually wants to generate something like ```d void setStrokeWidth(uint value) { if (value = strokeWidth) return; strokeWidth = value; redraw(); // or maybe... // needRedraw = true; } ``` that's a common pattern in 2D graphic libraries.
Jun 06 2023
prev sibling parent reply cc <cc nevernet.com> writes:
On Monday, 5 June 2023 at 13:57:20 UTC, Ki Rill wrote:
 How do I generate `setX` methods for all private mutable 
 variables in my class? Do I need to use `__traits`?
```d mixin template GenerateSetters() { static foreach (idx, field; typeof(this).tupleof) static if (__traits(getVisibility,field) == "private") { mixin(q{ void %SETTER(typeof(this.tupleof[idx]) _) { %NAME = _; } } .replace("%NAME", field.stringof) .replace("%SETTER", "set"~toUpper(field.stringof[0..1]) ~ field.stringof[1..$]) ); } } class Rectangle { private Color fillColor; private Color strokeColor; private uint strokeWidth; this(int x, int y) {} mixin GenerateSetters; } void main() { auto rect = new Rectangle(0, 0); rect.setStrokeWidth(4); assert(rect.strokeWidth == 4); } ```
Jun 05 2023
next sibling parent Ki Rill <rill.ki yahoo.com> writes:
On Monday, 5 June 2023 at 18:54:30 UTC, cc wrote:
 On Monday, 5 June 2023 at 13:57:20 UTC, Ki Rill wrote:
 How do I generate `setX` methods for all private mutable 
 variables in my class? Do I need to use `__traits`?
```d mixin template GenerateSetters() { static foreach (idx, field; typeof(this).tupleof) static if (__traits(getVisibility,field) == "private") { mixin(q{ void %SETTER(typeof(this.tupleof[idx]) _) { %NAME = _; } } .replace("%NAME", field.stringof) .replace("%SETTER", "set"~toUpper(field.stringof[0..1]) ~ field.stringof[1..$]) ); } } class Rectangle { private Color fillColor; private Color strokeColor; private uint strokeWidth; this(int x, int y) {} mixin GenerateSetters; } void main() { auto rect = new Rectangle(0, 0); rect.setStrokeWidth(4); assert(rect.strokeWidth == 4); } ```
Thank you! That is exactly what I needed. Although another solution provided by Basile B. using attributes opens a door for other posibilities... I don't usually use metaprogramming or code generation much, this is an eye-opening experience.
Jun 05 2023
prev sibling parent reply Ki Rill <rill.ki yahoo.com> writes:
On Monday, 5 June 2023 at 18:54:30 UTC, cc wrote:
 [...]
Is there a way to check for mutability as well? I have both immutable and mutable fields. I would like to generate setters for mutable fields only.
Jun 06 2023
parent Basile B. <b2.temp gmx.com> writes:
On Tuesday, 6 June 2023 at 21:49:58 UTC, Ki Rill wrote:
 On Monday, 5 June 2023 at 18:54:30 UTC, cc wrote:
 [...]
Is there a way to check for mutability as well? I have both immutable and mutable fields. I would like to generate setters for mutable fields only.
that's why a user attribute on the field is somewhat better than very complex introspection. It justs tells you "for that member do that".
Jun 06 2023