www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - [Template] Mixins and foreach

reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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.
Sep 30 2017
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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.
Sep 30 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Sep 30 2017
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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
Sep 30 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Sep 30 2017
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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...)
Sep 30 2017
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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!
Sep 30 2017
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Oct 01 2017