Constructing a variadic template parameter with source in two files

Jon Degenhardt via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Dec 21 19:59:42 PST 2016


I'd like to find a way to define programming constructs in one 
file and reference them in a getopt call defined in another file. 
getopt uses variadic template argument, so the argument list must 
be known at compile time. The std.getopt.getopt signature:

      GetoptResult getopt(T...)(ref string[] args, T opts)

So, what I'm trying to do is construct the 'opts' parameter from 
definitions stored in two or more files. The reason for doing 
this is to create a customization mechanism where-by there are a 
number of default capabilities built-in to the main code base, 
but someone can customize their copy of the code, putting 
definitions in a separate file, and have it added in at compile 
time, including modifying command line arguments.

I found a way to do this with a mixin template, shown below. 
However, it doesn't strike me as a particularly modular design. 
My question - Is there a better approach?

The solution I identified is below. The '--say-hello' option is 
built-in (defined in app.d), the '--say-hello-world' command is 
defined in custom_commands.d. Running:

     $ ./app --say-hello --say-hello-world

will print:

      Hello
      Hello World

Which is the goal. But, is there a better way? Help appreciated.

--Jon

=== command_base.d ===
/* API for defining "commands". */
interface Command
{
     string exec();
}

class BaseCommand : Command
{
     private string _result;
     this (string result) { _result = result; }
     final string exec() { return _result; }
}

=== custom_commands.d ===
/* Defines custom commands and a mixin for generating the getopt 
argument.
  * Note that 'commandArgHandler' is defined in app.d, not visible 
in this file.
  */
import command_base;

class HelloWorldCommand : BaseCommand
{
     this() { super("Hello World"); }
}

mixin template CustomCommandDeclarations()
{
     import std.meta;

     auto pHelloWorldHandler = 
&commandArgHandler!HelloWorldCommand;

     alias CustomCommandOptions = AliasSeq!(
         "say-hello-world",  "Print 'hello world'.", 
pHelloWorldHandler,
         );
}

=== app.d ===
/* This puts it all together. It creates built-in commands and 
uses the mixin from
  * custom_commands.d to declare commands and construct the getopt 
argument.
  */
import std.stdio;
import command_base;

class HelloCommand : BaseCommand
{
     this() { super("Hello"); }
}

struct CmdOptions
{
     import std.meta;
     Command[] commands;

     void commandArgHandler(DerivedCommand : BaseCommand)()
     {
         commands ~= new DerivedCommand();
     }

     bool processArgs (ref string[] cmdArgs)
     {
         import std.getopt;
         import custom_commands;

         auto pHelloHandler = &commandArgHandler!HelloCommand;

         alias BuiltinCommandOptions = AliasSeq!(
             "say-hello",  "Print 'hello'.", pHelloHandler,
             );

         mixin CustomCommandDeclarations;
         auto CommandOptions = AliasSeq!(BuiltinCommandOptions, 
CustomCommandOptions);
         auto r = getopt(cmdArgs, CommandOptions);
         if (r.helpWanted) defaultGetoptPrinter("Options:", 
r.options);
         return !r.helpWanted;  // Return true if execution should 
continue
     }
}

void main(string[] cmdArgs)
{
     CmdOptions cmdopt;

     if (cmdopt.processArgs(cmdArgs))
         foreach (cmd; cmdopt.commands)
             writeln(cmd.exec());
}



More information about the Digitalmars-d-learn mailing list