Thread overview
How to pass alias as template parameter?
Aug 27, 2018
Andrey
Aug 27, 2018
Paul Backus
Aug 27, 2018
Everlast
August 27, 2018
Hello,
This code doesn't compile:

-----------------------------------------------------------------------
import std.meta;
import std.stdio;

enum Option : string
{
    First = "-first" ,
    Second = "-second",
    Qwerty = "-qwerty"
}

void handler(Option option)(string[] args, ref ushort index)
{
    writeln("Case: ", args[index]);
}

void handler(string arg, ref ushort index)
{
    writeln("Default: ", arg, " at index ", index);
}

alias Pair(alias key, alias value) = AliasSeq!(key, value);
alias Pairs = AliasSeq!(Pair!(Option.First, handler!(Option.First)), Pair!(Option.Second, handler!(Option.Second)), handler);

void parseArgs(alias sequence)(ushort index, string[] args)
{
    alias last = sequence[$ - 1];
    alias pairs = sequence[0 .. $ - 2];

    for(ushort i = index; i < args.length; ++i)
    {
        string arg = args[i];
        switch(arg)
	    {
	        static foreach(pair; pairs)
	        {
	            case pair.key:
	                pair.value(args, i);
	                break;
	        }
	
	        default:
                last(arg, i);
                break;
	    }
    }
}

void main()
{
    ushort index = 1;
    string[] args = ["-second", "123", "-qaz", "true", "-first", "77", "value"];
    parseArgs!Pairs(index, args);
}
-----------------------------------------------------------------------

Output:
>onlineapp.d(52): Error: template instance `parseArgs!("-first", handler, "-second", handler, handler)` does not match template declaration parseArgs(alias sequence)(ushort index, string[] args)

I don't understand how to pass my "Pairs" alias into template function "parseArgs".

P.S. Yes, I know that exists "getopt".
August 27, 2018
On Monday, 27 August 2018 at 09:50:01 UTC, Andrey wrote:
> alias Pair(alias key, alias value) = AliasSeq!(key, value);
> alias Pairs = AliasSeq!(Pair!(Option.First, handler!(Option.First)), Pair!(Option.Second, handler!(Option.Second)), handler);

You can't nest AliasSeqs. If you examine Pairs with pragma(msg, ...), you'll see that the inner AliasSeqs have been expanded, and their members inserted into the outer sequence. As a result, when you attempt to iterate over the pairs later, you're actually iterating over the individual Options and handlers, one at a time:

> 	        static foreach(pair; pairs)
> 	        {
> 	            case pair.key:
> 	                pair.value(args, i);
> 	                break;
> 	        }

Also, you can't use the dot operator to access parameters of a template like that.

>>onlineapp.d(52): Error: template instance `parseArgs!("-first", handler, "-second", handler, handler)` does not match template declaration parseArgs(alias sequence)(ushort index, string[] args)

Here again, your AliasSeq "Pairs" is being expanded into multiple template arguments, rather than being passed as a single one. If you want to pass an AliasSeq to a template, you have to use a variadic template.

Further reading:
https://dlang.org/spec/template.html#variadic-templates
https://dlang.org/articles/ctarguments.html
August 27, 2018
On Monday, 27 August 2018 at 09:50:01 UTC, Andrey wrote:
> Hello,
> This code doesn't compile:
>
> -----------------------------------------------------------------------
> import std.meta;
> import std.stdio;
>
> enum Option : string
> {
>     First = "-first" ,
>     Second = "-second",
>     Qwerty = "-qwerty"
> }
>
> void handler(Option option)(string[] args, ref ushort index)
> {
>     writeln("Case: ", args[index]);
> }
>
> void handler(string arg, ref ushort index)
> {
>     writeln("Default: ", arg, " at index ", index);
> }
>
// Multiple issues here.
   for example, you call handler but it means nothing at compile time(the writeln should give that away)
// You seem to think AliasSeq is a list/array when it is slightly different, nesting AliasSeq's do not produce a nested array(unfortunately, although it shouldn't be difficult to implement such a thing.

> alias Pair(alias key, alias value) = AliasSeq!(key, value);
> alias Pairs = AliasSeq!(Pair!(Option.First, handler!(Option.First)), Pair!(Option.Second, handler!(Option.Second)), handler);
>
> void parseArgs(alias sequence)(ushort index, string[] args)
> {
>     alias last = sequence[$ - 1];
>     alias pairs = sequence[0 .. $ - 2];
>
>     for(ushort i = index; i < args.length; ++i)
>     {
>         string arg = args[i];
>         switch(arg)
> 	    {
> 	        static foreach(pair; pairs)
> 	        {
> 	            case pair.key:
> 	                pair.value(args, i);
> 	                break;
> 	        }
> 	
> 	        default:
>                 last(arg, i);
>                 break;
> 	    }
>     }
> }
>
> void main()
> {
      // These are run time variables, that is, the compiler does not know about them at compile time, hence you cannot use them for compile time parameters(Template parameters)

>     ushort index = 1;
>     string[] args = ["-second", "123", "-qaz", "true", "-first", "77", "value"];
      // This makes no sense, the only sense it could make is that you mean
      parseArgs!(Pairs(index, args));

      but index and args are runtime. If you convert them to compile time using enum then they can be passed, but Pairs is still malformed as is parseArgs.

>     parseArgs!Pairs(index, args);
> }
> -----------------------------------------------------------------------
>
> Output:
>>onlineapp.d(52): Error: template instance `parseArgs!("-first", handler, "-second", handler, handler)` does not match template declaration parseArgs(alias sequence)(ushort index, string[] args)
>
> I don't understand how to pass my "Pairs" alias into template function "parseArgs".
>
> P.S. Yes, I know that exists "getopt".


None of that code really makes any sense. Do you have any idea what you are actually doing?


Better for you to use getopt until you learn what you are trying to do.


Basically the code is so malformed in the use of meta programming that it really does not make a lot of sense.

This is what you should do:

1. Write all your code without templates, don't think of meta programming!

2. Your code then will work fine with CTFE and will be optimized.

e.g., suppose you have

ushort index = 1;
string[] args = ["-second", "123", "-qaz", "true", "-first", "77", value"];
parseArgs(index, args);

Then by changing those types to enum the compiler will automatically evaluate the code at compile time for you! no meta programming involved(except the use of enum).

This is actually what you need to do because using arguments is a runtime thing... if you wrote your code to take compile time arguments you can only use them at compile time, defeating the whole purpose.

Your mind has not learned to divide the compile time meta programming with the runtime programming and so it looks like you are mixing the two, and they can never be mixed up!  Except that runtime can be executed at compile time using a "trick"(CTFE) which can make life very easy.


In fact, you always want to try to do everything as if it were runtime because you can almost always use it at compile time using CTFE. So you cover much more area than think you do.

Then, naturally, you will want to make the compile time version behave a little different and you will use a little meta programming, but naturally instead of trying to force it.

Then you will develop a better understanding of the meta side of programming, which is just really about making really efficient code by doing things at compile time that can be done there(such as precomputing values(which CTFE generally covers well), reducing code bloat by working in more generic type space(e.g., overloading is a form of meta programming), etc..)

One of the bad things about D is that it's error messages in meta code can be total gibberish, so it is not a good idea to over complicate things. You also cannot debug the meta code nor debug string mixins and so you'll end up wasting a lot of time and not get very far.

Basically you shouldn't be trying to force anything to be meta in D, It will generally naturally fall out of a problem after you realize you can generalize something and then you generalize the specific runtime code you already have.