digitalmars.D.learn - How to create mixin template and pass name of a variable?
- Bosak (13/13) Aug 02 2013 I want to create a mixin template such that:
- Andrej Mitrovic (22/35) Aug 02 2013 Note that template mixins cannot inject arbitrary code, they can
- Bosak (20/60) Aug 02 2013 I ended up with the following code:
- Bosak (12/25) Aug 02 2013 I tested my code above and it seems it doesn't work either. I
- Tobias Pankrath (3/33) Aug 02 2013 void argNull(alias var)() { if(var is null) throw ... } ?
- monarch_dodra (26/39) Aug 02 2013 A mixin template can't inject arbitrary code (AFAIK), but only
- Bosak (7/52) Aug 02 2013 Oh I like your solution. It is just what I needed, thanks! Also I
- Bosak (4/63) Aug 02 2013 Also is there a way to see my code after mixins, templates and
- monarch_dodra (56/121) Aug 02 2013 In D, you can type enums, and string is part of the types
- Bosak (3/125) Aug 02 2013 Thanks a lot for your explanation. Now enums and templates are a
- Nicolas Sicard (22/81) Aug 02 2013 String mixins are fun, but you could also use a more classic
- monarch_dodra (3/24) Aug 02 2013 Good point! Alias parameter for the win!
I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");
Aug 02 2013
On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");Note that template mixins cannot inject arbitrary code, they can only inject declarations. You can use '__traits(identifier, symbol)' on an alias parameter to retrieve the symbol name, for example: ----- mixin template ArgNull(alias arg) { void test() { if (arg is null) throw new Exception(__traits(identifier, arg) ~ " cannot be null."); } } string text = null; mixin ArgNull!text; void main() { test(); } -----
Aug 02 2013
On Friday, 2 August 2013 at 11:48:32 UTC, Andrej Mitrovic wrote:On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:I ended up with the following code: string ArgNull(string arg)() { return "if("~arg~" is null)throw new Exception(\""~arg~" cannot be null.\");"; } unittest { import std.exception; void foo(Object obj, string str) { mixin(ArgNull!"obj"); mixin(ArgNull!"str"); } assertThrown(foo(null, "")); assertThrown(foo(new Object, null)); assertNotThrown(foo(new Object, "")); } It does what I wanted it to. Is there a better way to do it? Or do you have any suggestions on my code? I am kind of new to D and all this template and mixin stuff.I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");Note that template mixins cannot inject arbitrary code, they can only inject declarations. You can use '__traits(identifier, symbol)' on an alias parameter to retrieve the symbol name, for example: ----- mixin template ArgNull(alias arg) { void test() { if (arg is null) throw new Exception(__traits(identifier, arg) ~ " cannot be null."); } } string text = null; mixin ArgNull!text; void main() { test(); } -----
Aug 02 2013
On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");I tested my code above and it seems it doesn't work either. I came with an idea to write: mixin template ArgNull(string arg) { if(mixin(arg) is null) throw new Exception(arg~" cannot be null."); } But it seems that I can't put an if in a template like that. Is there a way to implement this?
Aug 02 2013
On Friday, 2 August 2013 at 11:50:05 UTC, Bosak wrote:On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:void argNull(alias var)() { if(var is null) throw ... } ? and then just call this template function.I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");I tested my code above and it seems it doesn't work either. I came with an idea to write: mixin template ArgNull(string arg) { if(mixin(arg) is null) throw new Exception(arg~" cannot be null."); } But it seems that I can't put an if in a template like that. Is there a way to implement this?
Aug 02 2013
On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");A mixin template can't inject arbitrary code (AFAIK), but only members/functions/structs etc. A simple string mixin would solve what you need better anyways: import std.stdio, std.typetuple, std.string; //---- template ArgNull(alias name) { enum ArgNull = format(q{ if(%1$s is null) throw new Exception("%1$s cannot be null."); }, name.stringof); } void main() { string s1 = "hello"; string s2; mixin(ArgNull!s1); mixin(ArgNull!s2); } //---- FYI, "q{}" is called a "token string", and is formidably useful to write a string that contains code. Also, I'm exploiting the alias arg to extract the name out of the variable. This makes it impossible to accidentally call the mixin with an invalid string (the error will be *at* the mixin call, not *in* the mixin call).
Aug 02 2013
On Friday, 2 August 2013 at 11:52:32 UTC, monarch_dodra wrote:On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:Oh I like your solution. It is just what I needed, thanks! Also I didn't knew you could make enums that contain string values. So used a lot with templates. And why is the name of the enum the same with the name of the template? Does it allways have to be like that?I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");A mixin template can't inject arbitrary code (AFAIK), but only members/functions/structs etc. A simple string mixin would solve what you need better anyways: import std.stdio, std.typetuple, std.string; //---- template ArgNull(alias name) { enum ArgNull = format(q{ if(%1$s is null) throw new Exception("%1$s cannot be null."); }, name.stringof); } void main() { string s1 = "hello"; string s2; mixin(ArgNull!s1); mixin(ArgNull!s2); } //---- FYI, "q{}" is called a "token string", and is formidably useful to write a string that contains code. Also, I'm exploiting the alias arg to extract the name out of the variable. This makes it impossible to accidentally call the mixin with an invalid string (the error will be *at* the mixin call, not *in* the mixin call).
Aug 02 2013
On Friday, 2 August 2013 at 12:10:00 UTC, Bosak wrote:On Friday, 2 August 2013 at 11:52:32 UTC, monarch_dodra wrote:Also is there a way to see my code after mixins, templates and CTFE gets executed? Like some kind of compiler switch or something?On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:Oh I like your solution. It is just what I needed, thanks! Also I didn't knew you could make enums that contain string values. are used a lot with templates. And why is the name of the enum the same with the name of the template? Does it allways have to be like that?I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");A mixin template can't inject arbitrary code (AFAIK), but only members/functions/structs etc. A simple string mixin would solve what you need better anyways: import std.stdio, std.typetuple, std.string; //---- template ArgNull(alias name) { enum ArgNull = format(q{ if(%1$s is null) throw new Exception("%1$s cannot be null."); }, name.stringof); } void main() { string s1 = "hello"; string s2; mixin(ArgNull!s1); mixin(ArgNull!s2); } //---- FYI, "q{}" is called a "token string", and is formidably useful to write a string that contains code. Also, I'm exploiting the alias arg to extract the name out of the variable. This makes it impossible to accidentally call the mixin with an invalid string (the error will be *at* the mixin call, not *in* the mixin call).
Aug 02 2013
On Friday, 2 August 2013 at 12:17:12 UTC, Bosak wrote:On Friday, 2 August 2013 at 12:10:00 UTC, Bosak wrote:In D, you can type enums, and string is part of the types compatible. enum food : string { burger = "burger", pie = "pie", } That said, this is not what is going on here. What you are seeing is known as "manifest constant". They keyword "enum" means: "This "variable" is a compile-time know name". EG: enum N = 25; //N is a *keyword* known by the compiler int M = 32; //M is just some variable int[N] arr1; //OK int[M] arr2; //Error: variable M cannot be read at compile time This brings us to templates. A "template" isn't an object in itself, but rather, a namespace that can hold tons of useful stuff. Unlike a namespace though, a template can be parameterized. This means basically, you can have the namespace "foo!int" and the namespace "foo!string" for example. This scheme becomes fun once you mix CTFE into this. CTFE means "compile time function evaluation", which means your function is run *while* your program is compiling. This is cool because you can store the result, and the compiler *knows* the result. A very simple template could be: template Hello(alias name) { enum String = "Hello " ~ name; } First, you create the namespace Hello!"bob", and then you can extract String to obtain "Hello bob". Templates most often contain enums or Aliases, but sometimes, also functions, or new types. As you can see though, adding ".String" is a bit of a pain. That's where "eponymous" templates come in. By making an object/alias carry the same name as the template, then the template *becomes* that alias directly. If we go back to the above example, and write it as: template Hello(alias name) { enum Hello = "Hello " ~ name; } In this case, Hello!"bob" *is* the string "Hello bob". You now have a parameterized string(!) You can use it: string hello_bob = Hello!"bob"; But what is coolest is that the compiler knows this too, so you can mix it in too, or whatever. This is exactly what I did. My template name is ArgNull. Instead of doing a "simple" string operation, I used the function format, which can be evaluated CTFE. There's not much magic going on. I hope it wasn't too much? These techniques in meta-programming are a bit hard to write and master, but the idea is that *useage* should be straight forward.On Friday, 2 August 2013 at 11:52:32 UTC, monarch_dodra wrote:On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:Oh I like your solution. It is just what I needed, thanks! Also I didn't knew you could make enums that contain string that enums are used a lot with templates. And why is the name of the enum the same with the name of the template? Does it allways have to be like that?I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");A mixin template can't inject arbitrary code (AFAIK), but only members/functions/structs etc. A simple string mixin would solve what you need better anyways: import std.stdio, std.typetuple, std.string; //---- template ArgNull(alias name) { enum ArgNull = format(q{ if(%1$s is null) throw new Exception("%1$s cannot be null."); }, name.stringof); } void main() { string s1 = "hello"; string s2; mixin(ArgNull!s1); mixin(ArgNull!s2); } //---- FYI, "q{}" is called a "token string", and is formidably useful to write a string that contains code. Also, I'm exploiting the alias arg to extract the name out of the variable. This makes it impossible to accidentally call the mixin with an invalid string (the error will be *at* the mixin call, not *in* the mixin call).Also is there a way to see my code after mixins, templates and CTFE gets executed? Like some kind of compiler switch or something?This is currently being discussed and worked on, but as of today, no.
Aug 02 2013
On Friday, 2 August 2013 at 12:47:51 UTC, monarch_dodra wrote:On Friday, 2 August 2013 at 12:17:12 UTC, Bosak wrote:Thanks a lot for your explanation. Now enums and templates are a lot more clear to me!On Friday, 2 August 2013 at 12:10:00 UTC, Bosak wrote:In D, you can type enums, and string is part of the types compatible. enum food : string { burger = "burger", pie = "pie", } That said, this is not what is going on here. What you are seeing is known as "manifest constant". They keyword "enum" means: "This "variable" is a compile-time know name". EG: enum N = 25; //N is a *keyword* known by the compiler int M = 32; //M is just some variable int[N] arr1; //OK int[M] arr2; //Error: variable M cannot be read at compile time This brings us to templates. A "template" isn't an object in itself, but rather, a namespace that can hold tons of useful stuff. Unlike a namespace though, a template can be parameterized. This means basically, you can have the namespace "foo!int" and the namespace "foo!string" for example. This scheme becomes fun once you mix CTFE into this. CTFE means "compile time function evaluation", which means your function is run *while* your program is compiling. This is cool because you can store the result, and the compiler *knows* the result. A very simple template could be: template Hello(alias name) { enum String = "Hello " ~ name; } First, you create the namespace Hello!"bob", and then you can extract String to obtain "Hello bob". Templates most often contain enums or Aliases, but sometimes, also functions, or new types. As you can see though, adding ".String" is a bit of a pain. That's where "eponymous" templates come in. By making an object/alias carry the same name as the template, then the template *becomes* that alias directly. If we go back to the above example, and write it as: template Hello(alias name) { enum Hello = "Hello " ~ name; } In this case, Hello!"bob" *is* the string "Hello bob". You now have a parameterized string(!) You can use it: string hello_bob = Hello!"bob"; But what is coolest is that the compiler knows this too, so you can mix it in too, or whatever. This is exactly what I did. My template name is ArgNull. Instead of doing a "simple" string operation, I used the function format, which can be evaluated CTFE. There's not much magic going on. I hope it wasn't too much? These techniques in meta-programming are a bit hard to write and master, but the idea is that *useage* should be straight forward.On Friday, 2 August 2013 at 11:52:32 UTC, monarch_dodra wrote:On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:Oh I like your solution. It is just what I needed, thanks! Also I didn't knew you could make enums that contain string that enums are used a lot with templates. And why is the name of the enum the same with the name of the template? Does it allways have to be like that?I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");A mixin template can't inject arbitrary code (AFAIK), but only members/functions/structs etc. A simple string mixin would solve what you need better anyways: import std.stdio, std.typetuple, std.string; //---- template ArgNull(alias name) { enum ArgNull = format(q{ if(%1$s is null) throw new Exception("%1$s cannot be null."); }, name.stringof); } void main() { string s1 = "hello"; string s2; mixin(ArgNull!s1); mixin(ArgNull!s2); } //---- FYI, "q{}" is called a "token string", and is formidably useful to write a string that contains code. Also, I'm exploiting the alias arg to extract the name out of the variable. This makes it impossible to accidentally call the mixin with an invalid string (the error will be *at* the mixin call, not *in* the mixin call).Also is there a way to see my code after mixins, templates and CTFE gets executed? Like some kind of compiler switch or something?This is currently being discussed and worked on, but as of today, no.
Aug 02 2013
On Friday, 2 August 2013 at 12:10:00 UTC, Bosak wrote:On Friday, 2 August 2013 at 11:52:32 UTC, monarch_dodra wrote:String mixins are fun, but you could also use a more classic template: --- void checkNonNull(alias var)() { if (var is null) throw new Exception(var.stringof ~ " cannot be null."); } void main() { string s; checkNonNull!s; } --- Or with file and line: --- void checkNonNull(alias var)(string file = __FILE__, size_t line = __LINE__) { if (var is null) throw new Exception(var.stringof ~ " cannot be null.", file, line); } ---On Friday, 2 August 2013 at 11:37:27 UTC, Bosak wrote:Oh I like your solution. It is just what I needed, thanks! Also I didn't knew you could make enums that contain string values. are used a lot with templates. And why is the name of the enum the same with the name of the template? Does it allways have to be like that?I want to create a mixin template such that: mixin template ArgNull(alias arg, string name) { if(arg is null) throw new Exception(name~" cannot be null."); } But is there a way to do that with only one template argument. And then use it like: string text = null; mixin ArgNull!(text); And the above mixin to make: if(text is null) throw new Exception("text cannot be null.");A mixin template can't inject arbitrary code (AFAIK), but only members/functions/structs etc. A simple string mixin would solve what you need better anyways: import std.stdio, std.typetuple, std.string; //---- template ArgNull(alias name) { enum ArgNull = format(q{ if(%1$s is null) throw new Exception("%1$s cannot be null."); }, name.stringof); } void main() { string s1 = "hello"; string s2; mixin(ArgNull!s1); mixin(ArgNull!s2); } //---- FYI, "q{}" is called a "token string", and is formidably useful to write a string that contains code. Also, I'm exploiting the alias arg to extract the name out of the variable. This makes it impossible to accidentally call the mixin with an invalid string (the error will be *at* the mixin call, not *in* the mixin call).
Aug 02 2013
On Friday, 2 August 2013 at 13:47:06 UTC, Nicolas Sicard wrote:String mixins are fun, but you could also use a more classic template: --- void checkNonNull(alias var)() { if (var is null) throw new Exception(var.stringof ~ " cannot be null."); } void main() { string s; checkNonNull!s; } --- Or with file and line: --- void checkNonNull(alias var)(string file = __FILE__, size_t line = __LINE__) { if (var is null) throw new Exception(var.stringof ~ " cannot be null.", file, line); } ---Good point! Alias parameter for the win! With this, there is simply no reason to use a mixin.
Aug 02 2013