How to create mixin template and pass name of a variable?

monarch_dodra monarchdodra at gmail.com
Fri Aug 2 05:47:49 PDT 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:
>> 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:
>>>> 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).
>>
>> 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 in D enums are like constants in C#, right? I see 
>> 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?

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.

> 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.


More information about the Digitalmars-d-learn mailing list