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