www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - assigning to multiple fields in a struct

reply Lukanian <nanaipenero forum.com> writes:
I would like to simplify a situation when i need to change values 
of some fields of my struct but keep other fields unchanged. 
Standard situation looks like this.
```d
struct MyStruct {
     int    field1;
     double field2;
     string field3;
     int    field4;
     string field5;
}

MyStruct myStruct = //...

// i need to change some values
myStruct.field1 = 10;
myStruct.field2 = 0.5;
myStruct.field5 = "hello";

// and it gets even uglier if struct is inside some complex 
structure i.e.:
myArray[0].someOtherStruct.myStruct.field1 = 10;
myArray[0].someOtherStruct.myStruct.field2 = 0.5;
myArray[0].someOtherStruct.myStruct.field5 = "hello";
```

I propose two solutions:
- 1. automatically generate setter for every field which returns 
reference to the struct
- 2. automatically generate an assign function with all fields as 
optional params, but assign only those which are given by user 
(no idea how to implement this)

My attempt to implement solution 1.
```d
myStruct
   .setfield1(10)
   .setfield2(0.5)
   .setfield5("hello");

// or just make it in one line
myStruct.setfield1(10).setfield2(0.5).setfield5("hello");

```
I tried to implement it with mixin template:
```d

mixin template GenerateSetters(T) {
     enum fieldNames = FieldNameTuple!T;
     alias fieldTypes = FieldTypeTuple!T;
	enum typeName = fullyQualifiedName!T;

     string generateFunctions() {
         string param;
		string fuctions;
         foreach (i, fieldName; fieldNames) {
             param = fieldTypes[i].stringof ~ " " ~ fieldName;

	    fuctions ~="ref "~typeName~" set"~fieldName~"(return ref 
"~typeName~" a, "
                 ~ param ~ ") {\n"~
                 "   a."~fieldName~"  = "~fieldName~";\n"~
                 "   return a;\n"~
                 "}\n";
         }

         return fuctions;
     }

	//pragma(msg, generateFunction());
     mixin(generateFunctions());
}

struct MyStructTest {
     int field1;
     double field2;
     string field3;
	string field4;
	string field5;
}

mixin GenerateSetters!(MyStructTest); // you need to call this 
after struct definition

```
It works, but if it was built into the language it could be more 
elegant than this.


The **second solution** could look like this, but i don't have an 
implementation:
```d
myStruct.setFields(field1:10, field2:0.5, field5:"hello");

```
Jun 05
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:
 I would like to simplify a situation when i need to change 
 values of some fields of my struct but keep other fields 
 unchanged. Standard situation looks like this.
 ```d
 struct MyStruct {
     int    field1;
     double field2;
     string field3;
     int    field4;
     string field5;
 }

 MyStruct myStruct = //...

 // i need to change some values
 myStruct.field1 = 10;
 myStruct.field2 = 0.5;
 myStruct.field5 = "hello";

 // and it gets even uglier if struct is inside some complex 
 structure i.e.:
 myArray[0].someOtherStruct.myStruct.field1 = 10;
 myArray[0].someOtherStruct.myStruct.field2 = 0.5;
 myArray[0].someOtherStruct.myStruct.field5 = "hello";
 ```

 I propose two solutions:
 - 1. automatically generate setter for every field which 
 returns reference to the struct
 - 2. automatically generate an assign function with all fields 
 as optional params, but assign only those which are given by 
 user (no idea how to implement this)

 My attempt to implement solution 1.
 ```d
 myStruct
   .setfield1(10)
   .setfield2(0.5)
   .setfield5("hello");

 // or just make it in one line
 myStruct.setfield1(10).setfield2(0.5).setfield5("hello");

 ```
 I tried to implement it with mixin template:
 ```d

 mixin template GenerateSetters(T) {
     enum fieldNames = FieldNameTuple!T;
     alias fieldTypes = FieldTypeTuple!T;
 	enum typeName = fullyQualifiedName!T;

     string generateFunctions() {
         string param;
 		string fuctions;
         foreach (i, fieldName; fieldNames) {
             param = fieldTypes[i].stringof ~ " " ~ fieldName;

 	    fuctions ~="ref "~typeName~" set"~fieldName~"(return ref 
 "~typeName~" a, "
                 ~ param ~ ") {\n"~
                 "   a."~fieldName~"  = "~fieldName~";\n"~
                 "   return a;\n"~
                 "}\n";
         }

         return fuctions;
     }

 	//pragma(msg, generateFunction());
     mixin(generateFunctions());
 }

 struct MyStructTest {
     int field1;
     double field2;
     string field3;
 	string field4;
 	string field5;
 }

 mixin GenerateSetters!(MyStructTest); // you need to call this 
 after struct definition

 ```
 It works, but if it was built into the language it could be 
 more elegant than this.
```d struct MyStruct { int field1; double field2; string field3; int field4; string field5; } unittest{ MyStruct myStruct; with(myStruct){ field1=1; field2=3.14; field3="foo"; }} ``` you will likely be told to do this
 The **second solution** could look like this, but i don't have 
 an implementation:
 ```d
 myStruct.setFields(field1:10, field2:0.5, field5:"hello");

 ```
disagree that this is possible, templated name arguments doesnt have the tools yet
Jun 05
prev sibling next sibling parent Basile B. <b2.temp gmx.com> writes:
On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:
 [...]
 The **second solution** could look like this, but i don't have 
 an implementation:
 ```d
 myStruct.setFields(field1:10, field2:0.5, field5:"hello");

 ```
Builtin tuples + syntax sugar + AST lowering could help here. The sugar in the grammar is ```ebnf DotTupleExp ::= Expression "." TupleExpression ``` for example ```d a.(member1, member2) = (0,1); ``` would be lowered to ```d auto __noSideEffect = a; (__noSideEffect.member1, __noSideEffect.member2) = (0,1); ```
Jun 06
prev sibling next sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:
 I would like to simplify a situation when i need to change 
 values of some fields of my struct but keep other fields
So you are not happy with: ```d MyStruct myStruct = //... // i need to change some values with (myStruct) { field1 = 10; field2 = 0.5; field5 = "hello"; } ``` Right? Is this not simple enough for you? If it is so, then the only solution I can think of is to implement your own multi-setter method. People keep forgetting the with statement and come up with fluent APIs as solution, sigh...
Jun 06
parent Lukanian <nanaipenero forum.com> writes:
On Friday, 6 June 2025 at 16:15:36 UTC, Dejan Lekic wrote:
 On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:
 I would like to simplify a situation when i need to change 
 values of some fields of my struct but keep other fields
So you are not happy with: ```d MyStruct myStruct = //... // i need to change some values with (myStruct) { field1 = 10; field2 = 0.5; field5 = "hello"; } ``` Right? Is this not simple enough for you? If it is so, then the only solution I can think of is to implement your own multi-setter method. People keep forgetting the with statement and come up with fluent APIs as solution, sigh...
Thank you for reminding me about "with" I almost forgot about it. It may be handy sometimes, but I still think that my version can have some semantic advantages, mainly because it can be used as a rvalue: ```d // passing result to a funcion myFunction(myStruct.setf1(1).setf3(3)); // or myFunction(myStruct.setFields(f1:1, f3:3)); // using it in a chain with oter calls myStructmyStruct.setf1(1).setf3(3).calculateProduct().writeln(); myStruct.setFields(f1:1, f3:3).calculateProduct().writeln(); ```
Jun 07
prev sibling parent kinke <noone nowhere.com> writes:
This is also an interesting generic approach: 
https://github.com/dlang/dmd/blob/8762d1eaee42119269b82a1b1b7063c89c1e6a69/compiler/src/build.d#L2195-L2205

Sketch:
```D
struct Builder(T)
{
     T obj;

     ref Builder opDispatch(string 
name)(typeof(__traits(getMember, T, name)) arg)
     {
         __traits(getMember, obj, name) = arg;
         return this;
     }
}

struct S
{
     int x;
     double y;
     string z;
}

void main() {
     const s = Builder!S()
         .x(1)
         .y(2.0)
         .z("z")
         .obj;
     assert(s == S(1, 2.0, "z"));
}
```
Jun 07