Thread overview
Mixin programming foreach
Sep 27, 2021
eXodiquas
Sep 27, 2021
Dga123
Sep 27, 2021
eXodiquas
Sep 27, 2021
Dga123
Sep 27, 2021
Ali Çehreli
Sep 27, 2021
Tejas
Sep 27, 2021
Ali Çehreli
Sep 27, 2021
Tejas
Sep 27, 2021
eXodiquas
Sep 27, 2021
Tejas
September 27, 2021

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:

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 mixincall 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:

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:

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 Vectors as an argument?
For example I want to add two of those, the function would look something like this, or am I lost again?

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

September 27, 2021

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:

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

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.

September 27, 2021

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:

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

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

September 27, 2021

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:

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;
September 27, 2021
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

September 27, 2021

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:

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 mixincall 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:

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:

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 Vectors as an argument?
For example I want to add two of those, the function would look something like this, or am I lost again?

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(please don't use them unless absolutely necessary)

Now, to solve your problem:

struct Vector(int T){
    int[T] elems;
}

And that's it :D

Now, how to use it?

Here is an isolated code fragment:

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:

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;
}
September 27, 2021

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.

September 27, 2021
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


September 27, 2021

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 :/

September 27, 2021
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