A template for method forwarding?

Bill Baxter wbaxter at gmail.com
Fri Dec 12 13:13:41 PST 2008


Cool.  I don't see anything D2 specific there, so I think it should
work in D1 ok.
std.traits.ParameterTypeTuple and std.traits.ReturnType both exist in
D1, if that's what you were worried about.

I think there may be a problem with 0-arg functions in the code?
You handle the void return type (which I'm not sure is necessary
actually -- I think D lets you say "return foo()" for a void function
specifically to handle this kind of template situation), but I think
maybe you don't handle a void argument?    I'm getting an error with
that for some reason... will dig more.

--bb

On Sat, Dec 13, 2008 at 5:54 AM, dsimcha <dsimcha at yahoo.com> wrote:
> == Quote from Bill Baxter (wbaxter at gmail.com)'s article
>> Let's say you want to use object composition instead of inheritance.
>> Now you want to forward half-a-dozen method from the new to class to
>> the composed class, like so:
>> class NewClass
>> {
>>      ImplT implementor;
>>      ...
>>      // Do some method forwarding
>>      void func1(int a, float b) { implementor.func1(a,b); }
>>      string func2(string s) { return implementor.func2(s); }
>>      T aTemplate(T)(T val, T[] arr) { return implementor!(T)(val,arr); }
>>      ...
>> }
>> It becomes pretty tedious to type all these things out, and if the
>> base class changes a method signature, you have to remember to do it
>> in the parent class too.
>> So the challenge is to write some kind of template that does the
>> necessary argument deduction to implement a forwarder just by
>> mentioning the name of the method and the object to forward to.
>> Something like this perhaps for the usage syntax:
>>     mixin call_forward!(implementor, "func1");
>>     mixin call_forward!(implementor, "func2");
>>     mixin call_forward!(implementor, "aTemplate");
>> Is it possible?  Somebody must have done something like this already.
>> --bb
>
> That was fun.  Disclaimer:  This probably is impossible in D1.  This is probably
> strictly a D2 hack.  The one bug I see is that this template will not handle
> default parameters correctly (or at all).
>
> import std.traits;
>
> template Forward(string clName, Methods...) {
>    static if(Methods.length == 1) {
>        mixin ForwardImpl!(clName, Methods[0]);
>    } else {
>        mixin ForwardImpl!(clName, Methods[0]);
>        mixin Forward!(clName, Methods[1..$]);
>    }
> }
>
> template ForwardImpl(string clName, string method) {
>    private mixin("alias ParameterTypeTuple!(" ~ clName ~ "."
>                  ~ method ~ ") params;");
>    private mixin("alias ReturnType!(" ~ clName ~ "."
>                  ~ method ~ ") retType;");
>
>    static if(is(retType == void)) {
>        mixin("void " ~ method ~ "(Tuple!" ~ params.stringof ~ " args){" ~
>              clName ~ "." ~ method ~ "(args); }");
>    } else {
>        mixin(retType.stringof ~ " " ~ method ~
>              "(Tuple!" ~ params.stringof ~ " args){ return " ~ clName ~ "."
>              ~ method ~ "(args); }");
>    }
> }
>
> template Tuple(T...) {
>    alias T Tuple;
> }
>
> // Test code.
>
> class Mul {  // The class you are delegating to.
>
>    this() {}
>
>    uint multiply(uint l, uint r) {
>        return l * r;
>    }
>
>    uint divide(uint l, uint r) {
>        return l / r;
>    }
>
>    void testVoid() {
>        writeln("Success:  testVoid");
>    }
> }
>
> class Arithmetic {
>    Mul mul;
>
>    this() {
>        mul = new Mul;
>    }
>
>
>    uint add(uint l, uint r) {
>        return l + r;
>    }
>
>    mixin Forward!("mul", "multiply", "divide", "testVoid");
> }
>
>
>
> import std.stdio;
>
> void main() {
>    auto arith = new Arithmetic;
>    writeln(arith.multiply(2, 3));
>    writeln(arith.divide(4, 2));
>    arith.testVoid();
> }
>



More information about the Digitalmars-d mailing list