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