Difference between revisions of "Compile-time Command Pattern"

From D Wiki
Jump to: navigation, search
m
 
Line 44: Line 44:
  
 
A lambda function that multiplies two numbers together is used as an example.  This is actually a template literal because the types of <code>a</code> and <code>b</code> are not specified.
 
A lambda function that multiplies two numbers together is used as an example.  This is actually a template literal because the types of <code>a</code> and <code>b</code> are not specified.
 +
 +
[[Category:DesignPattern]]

Latest revision as of 10:19, 14 November 2014

From David Simcha's D-Specific Design Patterns talk at DConf 2013.

Problem: Need to store function and arguments to be invoked later

Want:

  • Variadic argument list
  • Inlineability
  • Return type polymorphism
  • Ability to store uninstantiated function templates
  • Minimal boilerplate

Solution: Tempate alias + variadic templates

struct Command(alias fun, Args...)
{
    Args args_;

    auto invoke() 
    { 
        return fun(args_); 
    }
}

// Instantiator Function
auto command(alias fun, Args...)(Args args)
{
    return Command!(fun, Args)(args);
}

void main()
{
    auto cmd = command!((a, b) => a * b)(3, 2);
    assert(cmd.invoke() == 6);
}

Command struct takes an alias for the function and a set of variadic arguments. An alias can be any compile-time symbol including an uninstantiated template.

The invoke function is not virtual because you can't have virtual functions in structs in D, so it's inlinable, and it simply forwards to the function that's passed in at compile-time as an alias with the arguments.

Since everything is bound at compile-time, invoke is inlinable and the function it forwards to in inlineable, there should be no overhead.

An instantiator function is used to avoid explicitly specifying all the arguments when instantiating cmd.

A lambda function that multiplies two numbers together is used as an example. This is actually a template literal because the types of a and b are not specified.