Thread overview
[Template] Mixins and foreach
Oct 01, 2017
Nicholas Wilson
Oct 01, 2017
Nicholas Wilson
Oct 01, 2017
Jonathan M Davis
Oct 01, 2017
Nicholas Wilson
Oct 01, 2017
Jonathan M Davis
Oct 01, 2017
Nicholas Wilson
Oct 01, 2017
Nicholas Wilson
Oct 01, 2017
Jonathan M Davis
October 01, 2017
struct MyType
{
    void* raw;
    static struct Info
    {
        @(42) int foo;
    }
    mixin generateGetInfo!MyTypeGetInfo;
}

extern(C) void MyTypeGetInfo(void*,int,size_t,void*size_t*);
mixin template generateGetInfo(alias func)
{
    foreach(field; typeof(this).Info.tupleof)
    {
        mixin("@property " ~ typeof(field).stringof ~ " " ~ field.stringof ~ "()" ~
               " { " ~
               "    typeof(return) ret;" ~
               func.stringof ~ "(this.tupleof," ~ __traits(getAttributes, field)[0] ~ ",ret.sizeof,&ret,null);" ~
              "return ret; }");
    }
}

Fails with
Error: declaration expected, not 'foreach'
Error: no identifier for declarator typeof(this).Info.tupleof
Error: declaration expected, not ')'

I also tried a double mixin, one for the foreach and one for the mixin to no avail. How do I do a foreach in a [template] mixin to procedurally inject multiple declarations ? Static foreach is not an option as I am using LDC.


October 01, 2017
On Sunday, 1 October 2017 at 01:05:56 UTC, Nicholas Wilson wrote:
> struct MyType
> {
>     void* raw;
>     static struct Info
>     {
>         @(42) int foo;
>     }
>     mixin generateGetInfo!MyTypeGetInfo;
> }
>
> extern(C) void MyTypeGetInfo(void*,int,size_t,void*size_t*);
> mixin template generateGetInfo(alias func)
> {
>     foreach(field; typeof(this).Info.tupleof)
>     {
>         mixin("@property " ~ typeof(field).stringof ~ " " ~ field.stringof ~ "()" ~
>                " { " ~
>                "    typeof(return) ret;" ~
>                func.stringof ~ "(this.tupleof," ~ __traits(getAttributes, field)[0] ~ ",ret.sizeof,&ret,null);" ~
>               "return ret; }");
>     }
> }
>
> Fails with
> Error: declaration expected, not 'foreach'
> Error: no identifier for declarator typeof(this).Info.tupleof
> Error: declaration expected, not ')'
>
> I also tried a double mixin, one for the foreach and one for the mixin to no avail. How do I do a foreach in a [template] mixin to procedurally inject multiple declarations ? Static foreach is not an option as I am using LDC.

Hmm, generating the mixed in code as all one string and then mixing it in should work I think. Rather ugly though.
September 30, 2017
On Sunday, October 01, 2017 02:07:26 Nicholas Wilson via Digitalmars-d-learn wrote:
> On Sunday, 1 October 2017 at 01:05:56 UTC, Nicholas Wilson wrote:
> > struct MyType
> > {
> >
> >     void* raw;
> >     static struct Info
> >     {
> >
> >         @(42) int foo;
> >
> >     }
> >     mixin generateGetInfo!MyTypeGetInfo;
> >
> > }
> >
> > extern(C) void MyTypeGetInfo(void*,int,size_t,void*size_t*);
> > mixin template generateGetInfo(alias func)
> > {
> >
> >     foreach(field; typeof(this).Info.tupleof)
> >     {
> >
> >         mixin("@property " ~ typeof(field).stringof ~ " " ~
> >
> > field.stringof ~ "()" ~
> >
> >                " { " ~
> >                "    typeof(return) ret;" ~
> >                func.stringof ~ "(this.tupleof," ~
> >
> > __traits(getAttributes, field)[0] ~ ",ret.sizeof,&ret,null);" ~
> >
> >               "return ret; }");
> >
> >     }
> >
> > }
> >
> > Fails with
> > Error: declaration expected, not 'foreach'
> > Error: no identifier for declarator typeof(this).Info.tupleof
> > Error: declaration expected, not ')'
> >
> > I also tried a double mixin, one for the foreach and one for the mixin to no avail. How do I do a foreach in a [template] mixin to procedurally inject multiple declarations ? Static foreach is not an option as I am using LDC.
>
> Hmm, generating the mixed in code as all one string and then mixing it in should work I think. Rather ugly though.

I would have thought that it would be pretty straightforward to just write a recursive, eponymous template to solve the problem and have it recursively build a single string to mix in for everything.

In general though, without static foreach, you're either going to be using templates to operate in a functional manner rather than a procedural one, or you're going to be writing procedural code that generates a string to mix in. The latter doesn't really work though when the arguments are an AliasSeq of fields like you get from tupleof.

This looks like it would be pretty straightforward to do with a recursive template though.

- Jonathan M Davis

October 01, 2017
On Sunday, 1 October 2017 at 02:29:57 UTC, Jonathan M Davis wrote:
> I would have thought that it would be pretty straightforward to just write a recursive, eponymous template to solve the problem and have it recursively build a single string to mix in for everything.
>
> In general though, without static foreach, you're either going to be using templates to operate in a functional manner rather than a procedural one, or you're going to be writing procedural code that generates a string to mix in. The latter doesn't really work though when the arguments are an AliasSeq of fields like you get from tupleof.
>
> This looks like it would be pretty straightforward to do with a recursive template though.
>
> - Jonathan M Davis

I think I've almost got it:

string generateGetInfo( /*alias*/ Info,alias func,args...)()
{
    string code;
    foreach(field; AliasSeq!(Info.tupleof))
    {
        code ~= "@property " ~ typeof(field).stringof ~ " " ~ field.stringof ~ "()" ~
                "{ " ~
                "    typeof(return) ret;" ~
                func.stringof ~ "(" ~ args.stringof ~ "," ~ __traits(getAttributes, field).stringof ~ ",ret.sizeof,&ret,null);" ~
                "return ret; " ~
                "}";
        }
    }
    return code;
}
struct MyType
{
    void* raw;
    static struct Info
    {
        @(42) int foo;
    }
    mixin(generateGetInfo!(Info,MyTypeGetInfo,raw));//errors out
    pragma(msg,generateGetInfo!(Info,MyTypeGetInfo,raw)); // also errors out
}
but this gives a bunch of

    Error: need 'this' for 'foo' of type 'int'.

even with the pragma msg.
I dont see why though:
    a) the Info struct is static
    b) I'm only using .stringof on the _type's_ fields
    c) its got the name in the errors message! it just wont let me use it.

> The latter doesn't really work though when the arguments are an AliasSeq of fields like you get from tupleof.

Why is that and is that what is causing the problem here?

Thanks
Nic
September 30, 2017
On Sunday, October 01, 2017 03:11:15 Nicholas Wilson via Digitalmars-d-learn wrote:
> On Sunday, 1 October 2017 at 02:29:57 UTC, Jonathan M Davis wrote:
> > I would have thought that it would be pretty straightforward to just write a recursive, eponymous template to solve the problem and have it recursively build a single string to mix in for everything.
> >
> > In general though, without static foreach, you're either going to be using templates to operate in a functional manner rather than a procedural one, or you're going to be writing procedural code that generates a string to mix in. The latter doesn't really work though when the arguments are an AliasSeq of fields like you get from tupleof.
> >
> > This looks like it would be pretty straightforward to do with a recursive template though.
> >
> > - Jonathan M Davis
>
> I think I've almost got it:
>
> string generateGetInfo( /*alias*/ Info,alias func,args...)()
> {
>      string code;
>      foreach(field; AliasSeq!(Info.tupleof))
>      {
>          code ~= "@property " ~ typeof(field).stringof ~ " " ~
> field.stringof ~ "()" ~
>                  "{ " ~
>                  "    typeof(return) ret;" ~
>                  func.stringof ~ "(" ~ args.stringof ~ "," ~
> __traits(getAttributes, field).stringof ~
> ",ret.sizeof,&ret,null);" ~
>                  "return ret; " ~
>                  "}";
>          }
>      }
>      return code;
> }
> struct MyType
> {
>      void* raw;
>      static struct Info
>      {
>          @(42) int foo;
>      }
>      mixin(generateGetInfo!(Info,MyTypeGetInfo,raw));//errors out
>      pragma(msg,generateGetInfo!(Info,MyTypeGetInfo,raw)); // also
> errors out
> }
> but this gives a bunch of
>
>      Error: need 'this' for 'foo' of type 'int'.
>
> even with the pragma msg.
> I dont see why though:
>      a) the Info struct is static
>      b) I'm only using .stringof on the _type's_ fields
>      c) its got the name in the errors message! it just wont let
> me use it.
>
> > The latter doesn't really work though when the arguments are an AliasSeq of fields like you get from tupleof.
>
> Why is that and is that what is causing the problem here?

Well, what I meant was that you can't pass fields as function arguments, which is generally what you do with a function that's generating a string mixin, and if you pass the fields as template arguments, then you tend to be restricted in the ways that you're normally restricted with compile-time stuff, which means no procedural code. However, you can use foreach with an AliasSeq in the fashion that you're doing, which should work and give you some level of procedural code in spite of the fact that you're dealing with compile-time stuff. The explicit AliasSeq should not be necessary, since the tupleof already is one, but it shouldn't matter either way, since AliasSeqs automatically expand (one of the reasons that calling them tuples is a poor idea).

I would note that the static on Info should have zero effect. It would matter if Info were declared inside of a function, and IIRC, it would matter when declaring a class inside a class, but I don't believe that it has any effect when declaring a struct inside a struct. Such structs aren't associated with the struct that they're in aside from namespacing. It's required in the case of nested classes, because otherwise, they get access to the class that they're in and are associated with an instance of it, and it's required for structs and classes in functions in order to indicate that they don't have access to the function's variables. But nothing like that is happening with a struct inside a struct.

I don't see any reason why the compiler would be complaining about 'this' being required. I would think that that would imply that the compiler thinks that you're accessing the member rather than introspecting on it. The fact that you're passing raw to generateGetInfo does seem off though, since it wouldn't be accessible at compile time. I would have expected that to result in a compilation errero, but if it's complaining about 'this' when accessing foo rather than complaining about raw, then that implies that raw isn't the problem - or at least not that problem.

- Jonathan M Davis

October 01, 2017
On Sunday, 1 October 2017 at 04:44:16 UTC, Jonathan M Davis wrote:
> I don't see any reason why the compiler would be complaining about 'this' being required.

Neither do I.

> I would think that that would imply that the compiler thinks that you're accessing the member rather than introspecting on it.

Perhaps, but I would expect cannot access variable at compile time of something like that , not "need 'this'".

> The fact that you're passing raw to generateGetInfo does seem off though, since it wouldn't be accessible at compile time. I would have expected that to result in a compilation error, but if it's complaining about 'this' when accessing foo rather than complaining about raw, then that implies that raw isn't the problem - or at least not that problem.

'raw' is an ellipse arg and I think that defaults to by alias, which is what I want. some of the structs have multiple arguments that i need to pass.

I tried en eponymous template:

string generateGetInfo(Info,alias func,args...)()
{
    template helper(Fields...)
    {
        static if (Fields.length == 0)
            enum helper = "";
        else
        {
            enum helper = "@property " ~ typeof(Field[0]).stringof ~ " " ~ Field[0].stringof ~ "()" ~
                "{ " ~
                "    typeof(return) ret;" ~
                "%1$s(%2$s,"~ __traits(getAttributes, Field[0]).stringof ~ ",ret.sizeof,&ret,null);" ~
                "return ret; " ~
                "}\n" ~ helper!(Fields[1 .. $]);
        }
    }

    return helper!(Info.tupleof).format(func.stringof,args.stringof); // line 72
}

And am getting
util.d(72,12): Error: template instance helper!(foo) cannot use local 'foo' as parameter to non-global template helper(Fields...)

October 01, 2017
On Sunday, 1 October 2017 at 06:27:21 UTC, Nicholas Wilson wrote:
> And am getting
> util.d(72,12): Error: template instance helper!(foo) cannot use local 'foo' as parameter to non-global template helper(Fields...)

Fixed by making helper a global template.

Thanks Jonathan!
October 01, 2017
On Sunday, October 01, 2017 06:32:53 Nicholas Wilson via Digitalmars-d-learn wrote:
> On Sunday, 1 October 2017 at 06:27:21 UTC, Nicholas Wilson wrote:
> > And am getting
> > util.d(72,12): Error: template instance helper!(foo) cannot use
> > local 'foo' as parameter to non-global template
> > helper(Fields...)
>
> Fixed by making helper a global template.

It might also work to keep it local but mark it as static.

- Jonathan M Davis