www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Mixin programming foreach

reply eXodiquas <exodiquas gmail.com> writes:
Howdy ho everyone,

I found this forum very helpful for my (maybe) stupid questions, 
so I give it a try again because I don't understand what's 
happening here.

First of all, I'm not exactly sure what this code here, from the 
documentation at https://dlang.org/articles/mixin.html, does:

```d
template GenStruct(string Name, string M1)
{
     const char[] GenStruct = "struct " ~ Name ~ "{ int " ~ M1 ~ 
"; }";
}

mixin(GenStruct!("Foo", "bar"));
```

In my understanding we create a template called `GenStruct` with 
compile time arguments `Name` and `M1`. We then create the 
`char[] GenStruct` which holds the source code for the `struct`. 
Afterwards we do the `mixin`call and create the source code 
during compile time to have it ready to go. But I don't 
understand why the `char[] GenStruct` is there. Is this the name 
of the `mixin` or is the `template GenStruct` the name of what we 
pass to the `mixin`? Can we create more than one const char[] in 
one `template` scope and what would it do if we could? So I'm a 
bit confused here.

But nevertheless, I copied the code and changed it a bit to 
something like this:

```d
template Vector(int dimension) {
   string cps = "";
   foreach(d; 0..dimension) {
     cps ~= "x" ~ d ~ " = 0;\n";
   }
   const char[] Vector = "struct Vector {\n" ~ dimension.to!string 
~ cps ~ "}";
}
```
In my head a call to `mixin(Vector!5)` should result in something 
like this:

```d
struct Vector5 {
   int x0;
   int x1;
   int x2;
   int x3;
   int x4;
}
```
But it does not compile because `Error: declaration expected, not 
foreach` which I understand as "foreach is not possible during 
compile time", whereas I don't see any problem with compile time 
availability of anything in this template. Obviously I don't 
understand something here, so it would be awesome if someone 
could help me out here. :P

And as a final question. Let's say the above problems are all 
solved and I construct such a template. How could I build 
functions that take any of those `Vector`s as an argument?
For example I want to add two of those, the function would look 
something like this, or am I lost again?

```d
Vector!T add(Vector!T lhs, Vector!T rhs) {
   return ?;
}
```
How could I iterate over all components if such a `Vector`?

I hope I am not on the completely wrong track to tackle this 
problem.

Thanks in advance. :)

eXodiquas
Sep 27 2021
next sibling parent reply Dga123 <Dga123 123.fi> writes:
On Monday, 27 September 2021 at 16:23:50 UTC, eXodiquas wrote:
 Howdy ho everyone,

 I found this forum very helpful for my (maybe) stupid 
 questions, so I give it a try again because I don't understand 
 what's happening here.
 [...]
 But nevertheless, I copied the code and changed it a bit to 
 something like this:

 ```d
 template Vector(int dimension) {
   string cps = "";
   foreach(d; 0..dimension) {
     cps ~= "x" ~ d ~ " = 0;\n";
make a `string cps(){}` function. TemplateDeclaration can only contains DeclDefs, i.e declarations and no expressions. b.t.w `template Vector` can also be a string `Vector(int dimension){}` function. So finally to obtain something like this ```d import std; string vector(int dimension) { string cps; foreach(d; 0..dimension) { cps ~= "int x" ~ d.to!string ~ " = 0;\n"; } return "struct Vector" ~ dimension.to!string ~" {\n" ~ cps ~ "}"; } mixin(vector(5)); ``` function used in mixin are evaluated at compile time and are generally more perfomantly evaluated than eponymous template (that contains a single member named as the template). Also less constraint on what can be done. Have a great day, Dga.
Sep 27 2021
parent reply eXodiquas <exodiquas gmail.com> writes:
On Monday, 27 September 2021 at 16:49:15 UTC, Dga123 wrote:
 On Monday, 27 September 2021 at 16:23:50 UTC, eXodiquas wrote:
 Howdy ho everyone,

 I found this forum very helpful for my (maybe) stupid 
 questions, so I give it a try again because I don't understand 
 what's happening here.
 [...]
 But nevertheless, I copied the code and changed it a bit to 
 something like this:

 ```d
 template Vector(int dimension) {
   string cps = "";
   foreach(d; 0..dimension) {
     cps ~= "x" ~ d ~ " = 0;\n";
make a `string cps(){}` function. TemplateDeclaration can only contains DeclDefs, i.e declarations and no expressions. b.t.w `template Vector` can also be a string `Vector(int dimension){}` function. So finally to obtain something like this ```d import std; string vector(int dimension) { string cps; foreach(d; 0..dimension) { cps ~= "int x" ~ d.to!string ~ " = 0;\n"; } return "struct Vector" ~ dimension.to!string ~" {\n" ~ cps ~ "}"; } mixin(vector(5)); ``` function used in mixin are evaluated at compile time and are generally more perfomantly evaluated than eponymous template (that contains a single member named as the template). Also less constraint on what can be done. Have a great day, Dga.
I see, thank you very much for the quick answer. So the "macro magic" does not happen in `template`, it happens in the `mixin` call, now I understand. But my last question still stands, how do I build functions that can work with those vectors because the type of those vectors is created at compile time. But I can start working on the problem now. Thanks again for helping me out here. :) Have a great day aswell, eXodiquas
Sep 27 2021
parent Dga123 <Dga123 123.fi> writes:
On Monday, 27 September 2021 at 16:59:16 UTC, eXodiquas wrote:
 But my last question still stands, how do I build functions 
 that can work with those vectors because the type of those 
 vectors is created at compile time.
Here's another approach allowing to have a more obvious declaration name: ```d import std; string vectorBody(int dimension) { string result; foreach(d; 0..dimension) result ~= "int x" ~ d.to!string ~ " = 0;\n"; return result; } struct Vector(int dimension) { mixin(vectorBody(dimension)); } alias Vector5 = Vector!5; ```
Sep 27 2021
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 9/27/21 9:23 AM, eXodiquas wrote:

 I found this forum very helpful
Same here. :)
 for my (maybe) stupid questions
Often repeated but there are no stupid questions.
 First of all, I'm not exactly sure what this code here, from the
 documentation at https://dlang.org/articles/mixin.html, does:

 ```d
 template GenStruct(string Name, string M1)
 {
      const char[] GenStruct = "struct " ~ Name ~ "{ int " ~ M1 ~ "; }";
 }

 mixin(GenStruct!("Foo", "bar"));
 ```

 In my understanding we create a template called `GenStruct` with compile
 time arguments `Name` and `M1`. We then create the `char[] GenStruct`
 which holds the source code for the `struct`. Afterwards we do the
 `mixin`call and create the source code during compile time to have it
 ready to go.
All correct.
 But I don't understand why the `char[] GenStruct` is there.
That example is cofusing because it includes elements that has nothing to do with a string mixin. The following could be used without raising your question: string GenStruct(string Name, string M1) { return "struct " ~ Name ~ "{ int " ~ M1 ~ "; }"; } unittest { mixin(GenStruct("Foo", "bar")); auto foo = Foo(42); assert(foo.bar == 42); } void main() { }
 template is an
 Is this the name of the `mixin` or is the `template GenStruct` the name
 of what we pass to the `mixin`?
The original code uses an eponymous template, which can be defined as "given a template definition that includes a symbol same as the template's name, that symbol will mean the same thing as instances of that template." So, in this case, the template instance GenStruct!("Foo", "bar") happens to be the same thing as the GenStruct member of that template.
 Can we create more than one const char[]
 in one `template` scope
Yes.
 and what would it do if we could?
1) The template would take advantage of those other symbols in its implementation: template GenStruct(string Name, string M1) { // Note: I would define both of these as 'enum's; // just staying with the same code style. const char[] memberDef = "int " ~ M1 ~ ";"; const char[] GenStruct = "struct " ~ Name ~ "{ " ~ memberDef ~ " }"; } 2) Members of a template can be used directly: // Defines two symbols template Foo(string s) { import std.format : format; enum integerVarDef = format!"int %s_i;"(s); enum doubleVarDef = format!"double %s_d;"(s); } // Mixing in one or the other symbol void main() { mixin(Foo!"hello".integerVarDef); hello_i = 42; mixin(Foo!"world".doubleVarDef); world_d = 1.5; } 3) All symbols can be mixed in as a whole. (Note it's a template mixin in this case as opposed to a string mixin.) template MemberListFeature(T) { T[] theList; void addMember(T value) { theList ~= value; } T getMember(size_t index) { return theList[index]; } } struct S { mixin MemberListFeature!string; // Now this struct has one member array and two member functions. } unittest { auto s = S(); s.addMember("hello"); s.getMember(0) == "hello"; } void main() { } etc. :)
 But nevertheless, I copied the code and changed it a bit to something
 like this:

 ```d
 template Vector(int dimension) {
    string cps = "";
    foreach(d; 0..dimension) {
      cps ~= "x" ~ d ~ " = 0;\n";
    }
    const char[] Vector = "struct Vector {\n" ~ dimension.to!string ~ cps
 ~ "}";
 }
 ```
 In my head a call to `mixin(Vector!5)` should result in something like
 this:

 ```d
 struct Vector5 {
    int x0;
    int x1;
    int x2;
    int x3;
    int x4;
 }
 ```
 But it does not compile because `Error: declaration expected, not
 foreach` which I understand as "foreach is not possible during compile
 time",
Not true. foreach *is* available at compile time but not in the template scope. (On the other hand, 'static foreach' is available inside a template scope or module scope, etc.) Here is an almost direct implementation of your idea: template Vector(int dimension) { import std.conv : to; string makeMembers() { // Note: No need to initialize with "" string cps; foreach(d; 0..dimension) { // Note: Initial values are not needed in D. // (Already initialized.) cps ~= "int x" ~ d.to!string ~ ";\n"; } return cps; } // Note: The name Vector would be confusing; changing to // VectorStruct const char[] Vector = "struct VectorStruct {\n" ~ makeMembers() ~ "}"; } // Note: Visualizing the result: pragma(msg, Vector!5); // Also see the compiler switch -mixin mixin(Vector!3); void main() { auto v = VectorStruct(); v.x0 = 0; v.x1 = 1; v.x2 = 2; } However, Vector vs. VectorStruct points at a problem there. Instead, here is another example that uses 'static foreach' and uses not a 'template' that defines a string but a 'struct template' directly: struct Vector(size_t dimension) { static foreach (d; 0..dimension) { import std.format : format; mixin (format!" int x%s;"(d)); } } void main() { auto v = Vector!3(); v.x0 = 0; v.x1 = 1; v.x2 = 2; }
 And as a final question. Let's say the above problems are all solved and
 I construct such a template. How could I build functions that take any
 of those `Vector`s as an argument?
 For example I want to add two of those, the function would look
 something like this, or am I lost again?

 ```d
 Vector!T add(Vector!T lhs, Vector!T rhs) {
    return ?;
 }
 ```
T is a compile-time parameter there, which must be defined as such. I will stay with my size_t template parameter: struct Vector(size_t dimension) { static foreach (d; 0..dimension) { import std.format : format; mixin (format!" int x%s;"(d)); } } import std.stdio; void main() { auto a = Vector!2(1, 2); auto b = Vector!2(3, 4); auto c = add(a, b); writeln(c); } Vector!dim add(size_t dim)(Vector!dim lhs, Vector!dim rhs) { // As there are many different ways of implementing both Vector // and addition, I will just cheat and assume there is only // the x0 member: return Vector!dim(lhs.x0 + rhs.x0); }
 How could I iterate over all components if such a `Vector`?
It is possible with .tupleof but unlike your design, I would use an int[dimension] static array in the implementation like I do with the Polygon struct template here: http://ddili.org/ders/d.en/templates_more.html Still, assuming the 'c' variable from the last example above exists, here is how .tupleof might be used: foreach (i, member; c.tupleof) { writefln!"Member %s is %s"(i, member); } Ali
Sep 27 2021
next sibling parent reply Tejas <notrealemail gmail.com> writes:
On Monday, 27 September 2021 at 17:14:11 UTC, Ali Çehreli wrote:
 On 9/27/21 9:23 AM, eXodiquas wrote:

 [...]
Same here. :)
 [...]
Often repeated but there are no stupid questions. [...]
Sometimes I wonder if I should bother writing answers since one of you Old Guards make them redundant(and inadequate) :( I'm really surprised you went with his name-every-element-uniquely scheme though. Why didn't you recommend `static array`? Are there genuine use-cases where it makes more sense?You only mentioned it when it came to the traversal part.
Sep 27 2021
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 9/27/21 10:26 AM, Tejas wrote:
 On Monday, 27 September 2021 at 17:14:11 UTC, Ali =C3=87ehreli wrote:
 On 9/27/21 9:23 AM, eXodiquas wrote:

 [...]
Same here. :)
 [...]
Often repeated but there are no stupid questions. [...]
Sometimes I wonder if I should bother writing answers since one of you=
 Old Guards make them redundant(and inadequate) :(
Point taken.
 I'm really surprised you went with his name-every-element-uniquely
 scheme though. Why didn't you recommend `static array`? Are there
 genuine use-cases where it makes more sense?You only mentioned it when=
 it came to the traversal part.
I noticed that too. ;) I could have changed other things there as well.=20 For example, I wanted to replace 'int' with 'size_t', which I did=20 eventually. I think staying with the original code as much as possible is helpful=20 for the OP to follow the response. Also, sometimes I miss what the=20 original intent is. :) Further, the original post touched on many concepts like eponymous=20 templates that I wanted to expand on instead of a short answer. Ali
Sep 27 2021
parent Tejas <notrealemail gmail.com> writes:
On Monday, 27 September 2021 at 17:43:22 UTC, Ali Çehreli wrote:
 On 9/27/21 10:26 AM, Tejas wrote:
 On Monday, 27 September 2021 at 17:14:11 UTC, Ali Çehreli
wrote:
 On 9/27/21 9:23 AM, eXodiquas wrote:

 [...]
Same here. :)
 [...]
Often repeated but there are no stupid questions. [...]
Sometimes I wonder if I should bother writing answers since
one of you
 Old Guards make them redundant(and inadequate) :(
Point taken.
 I'm really surprised you went with his
name-every-element-uniquely
 scheme though. Why didn't you recommend `static array`? Are
there
 genuine use-cases where it makes more sense?You only
mentioned it when
 it came to the traversal part.
I noticed that too. ;) I could have changed other things there as well. For example, I wanted to replace 'int' with 'size_t', which I did eventually. I think staying with the original code as much as possible is helpful for the OP to follow the response. Also, sometimes I miss what the original intent is. :) Further, the original post touched on many concepts like eponymous templates that I wanted to expand on instead of a short answer. Ali
I was also already afraid of post length so I didn't write anything about `eponymous templates`... and instead threw in `partial template specialization`... Yeah I think you vets should still continue posting, I have a long way to go on making my posts accessible :/
Sep 27 2021
prev sibling parent eXodiquas <exodiquas gmail.com> writes:
On Monday, 27 September 2021 at 17:14:11 UTC, Ali Çehreli wrote:
 [...]
Woah, thanks everyone for the ridiculous precise and helpful answers I got here. This forum never disappoints. :P I now try to fully understand the answers and implement the solution to the problem in a cleaner way. The reason I wanted to hold on to the single members, is because I implemented Vector2 and Vector3 by hand and I personally did not need higher dimensional vectors, but because I write this library as open source I could imagine some other developer may need more than 3 dimensions and it would be great if all those vectors behave exactly the same way. For example, the components of my `Vector2` are accessible via `v.x` and `v.y`. Otherwise I have to do something like `Vector4.components[0]` which doesn't look that nice imo. But with the new insight I got, I should be able to cook something up. Thanks again everyone. Have a great day, eXodiquas
Sep 27 2021
prev sibling parent Tejas <notrealemail gmail.com> writes:
On Monday, 27 September 2021 at 16:23:50 UTC, eXodiquas wrote:
 Howdy ho everyone,

 I found this forum very helpful for my (maybe) stupid 
 questions, so I give it a try again because I don't understand 
 what's happening here.

 First of all, I'm not exactly sure what this code here, from 
 the documentation at https://dlang.org/articles/mixin.html, 
 does:

 ```d
 template GenStruct(string Name, string M1)
 {
     const char[] GenStruct = "struct " ~ Name ~ "{ int " ~ M1 ~ 
 "; }";
 }

 mixin(GenStruct!("Foo", "bar"));
 ```

 In my understanding we create a template called `GenStruct` 
 with compile time arguments `Name` and `M1`. We then create the 
 `char[] GenStruct` which holds the source code for the 
 `struct`. Afterwards we do the `mixin`call and create the 
 source code during compile time to have it ready to go. But I 
 don't understand why the `char[] GenStruct` is there. Is this 
 the name of the `mixin` or is the `template GenStruct` the name 
 of what we pass to the `mixin`? Can we create more than one 
 const char[] in one `template` scope and what would it do if we 
 could? So I'm a bit confused here.

 But nevertheless, I copied the code and changed it a bit to 
 something like this:

 ```d
 template Vector(int dimension) {
   string cps = "";
   foreach(d; 0..dimension) {
     cps ~= "x" ~ d ~ " = 0;\n";
   }
   const char[] Vector = "struct Vector {\n" ~ 
 dimension.to!string ~ cps ~ "}";
 }
 ```
 In my head a call to `mixin(Vector!5)` should result in 
 something like this:

 ```d
 struct Vector5 {
   int x0;
   int x1;
   int x2;
   int x3;
   int x4;
 }
 ```
 But it does not compile because `Error: declaration expected, 
 not foreach` which I understand as "foreach is not possible 
 during compile time", whereas I don't see any problem with 
 compile time availability of anything in this template. 
 Obviously I don't understand something here, so it would be 
 awesome if someone could help me out here. :P

 And as a final question. Let's say the above problems are all 
 solved and I construct such a template. How could I build 
 functions that take any of those `Vector`s as an argument?
 For example I want to add two of those, the function would look 
 something like this, or am I lost again?

 ```d
 Vector!T add(Vector!T lhs, Vector!T rhs) {
   return ?;
 }
 ```
 How could I iterate over all components if such a `Vector`?

 I hope I am not on the completely wrong track to tackle this 
 problem.

 Thanks in advance. :)

 eXodiquas
You are trying to write arbitrary statements inside a template, that will not work(if you insist on writing code that way, use [string mixins](http://ddili.org/ders/d.en/mixin.html)(please don't use them unless absolutely necessary) Now, to solve your problem: ```d struct Vector(int T){ int[T] elems; } ``` And that's it :D Now, how to use it? Here is an isolated code fragment: ```d auto Add(T: Vector!U,int U)(T a, T b){ /*Yes, you don't have to write different funcs for different instantiations of Vector :D*/ int[] c; c.length = U; for(size_t i = 0; i < U; i++){ c[i] = a.elems[i] + b.elems[i]; } return Vector!U(c); } ``` Full code: ```d import std; struct Vector(int t){ int[t] elems; size_t length = t; this(int[] input){ assert(input.length <= length); foreach(i,num; input){ elems[i] = num; } } this(int num){ elems = num; } void displayElems(){ import std.stdio; foreach(elem; elems){ writeln(elem); } } } auto Add(T: Vector!U,int U)(T a, T b){ int[] c; c.length = U; for(size_t i = 0; i < U; i++){ c[i] = a.elems[i] + b.elems[i]; } return Vector!U(c); } void main(){ auto first = Vector!10([7,896,864, 8976]); auto second = Vector!10([23,7890,9886]); auto third = Vector!10(5); auto result = Add(first, second); writeln(result); result.displayElems; third.displayElems; } ```
Sep 27 2021