Search code examples
templatesdmixinsvariadic-templates

Are variadic mixin templates possible in D?


Suppose I need to do something similar to this:

mixin(some_template!("x", "foo"));

mixin(some_template!("x", "bar"));

mixin(some_template!("x", "baz"));

mixin(some_template!("y", "foo"));

mixin(some_template!("y", "bar"));

mixin(some_template!("z", "baz"));

Is it somehow possible to create another_template which is a variadic version of some_template, where you can give anywhere from 2 to however-many arguments? What I mean is that I would like to be able to say:

mixin(another_template!("x", "foo", "bar", "baz"));

mixin(another_template!("y", "foo", "bar"));

mixin(another_template!("z", "baz"));

and have it expand to equivalent code to what the first example would expand to.


Solution

  • Yes, using the regular variadic template syntax (Something... collects the rest of the template arguments given and they can be of mixed types. see: http://dlang.org/template#TemplateTupleParameter):

    string some_template(strings...)() {
            string a;
            foreach(f; strings)
                    a ~= f;
            return a;
    }
    
    void main() {
            mixin(some_template!("int ", "giggle", ";"));
    }
    

    You can restrict the type with a constraint like this:

    // this condition will only compile if it is passed a string
    import std.typetuple;
    enum check_if_string(string s) = true;
    
    // then we check that they all compile with the check_if_string check
    string some_template(strings...)() if(allSatisfy!(check_if_string, strings)) {
            string a;
            foreach(f; strings)
                    a ~= f;
            return a;
    }
    

    The error message will be ugly though, and non-strings won't compile even without this check so it isn't really important. But you can also use the if check in there for like if(strings.length >= 2) as well.

    This is called a template constraint: http://dlang.org/template#Constraint