A template for method forwarding?

dsimcha dsimcha at yahoo.com
Fri Dec 12 12:54:28 PST 2008


== 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