汎用ファンクタ


できないと言っていた汎用ファンクタができてしまった。


ArgType の定義で、インスタンス化した TypeArray.typeAt に typeof をかけているのがミソ。
(TypeArray の定義は 5-23 のと同じ)

module doki.functor;

import doki.typearray;

class Functor(R, Args) {
    invariant {
        static assert(is (typeof(Args.length)));
        static assert(
            Args.length >= 10 && is (typeof(Args.typeAt!(9))) ||
            Args.length >=  9 && is (typeof(Args.typeAt!(8))) ||
            Args.length >=  8 && is (typeof(Args.typeAt!(7))) ||
            Args.length >=  7 && is (typeof(Args.typeAt!(6))) ||
            Args.length >=  6 && is (typeof(Args.typeAt!(5))) ||
            Args.length >=  5 && is (typeof(Args.typeAt!(4))) ||
            Args.length >=  4 && is (typeof(Args.typeAt!(3))) ||
            Args.length >=  3 && is (typeof(Args.typeAt!(2))) ||
            Args.length >=  2 && is (typeof(Args.typeAt!(1))) ||
            Args.length >=  1 && is (typeof(Args.typeAt!(0)))
        );
    }

    alias R ReturnType;
    alias Args ArgsType;

    template ArgType(int n) { alias typeof(Args.typeAt!(n)) ArgType; }

    alias ArgType!(0) Arg0Type;
    alias ArgType!(1) Arg1Type;
    alias ArgType!(2) Arg2Type;
    alias ArgType!(3) Arg3Type;
    alias ArgType!(4) Arg4Type;
    alias ArgType!(5) Arg5Type;
    alias ArgType!(6) Arg6Type;
    alias ArgType!(7) Arg7Type;
    alias ArgType!(8) Arg8Type;
    alias ArgType!(9) Arg9Type;

    template callOperator(int n :  1) { abstract ReturnType opCall(ArgType!(0)); }
    template callOperator(int n :  2) { abstract ReturnType opCall(ArgType!(0), ArgType!(1)); }
    ...
    mixin callOperator!(Args.length);

}

で、実行。

import std.stdio;

void main() {
    alias Functor!(bool, TypeArray!(int, char, double)) Arg3Functor;
    writefln(typeid(Arg3Functor.ArgsType));
    writefln(typeid(typeof(Arg3Functor.ArgsType.typeAt!(0))));
    writefln(typeid(typeof(Arg3Functor.ArgsType.typeAt!(1))));
    writefln(typeid(typeof(Arg3Functor.ArgsType.typeAt!(2))));
    writefln(typeid(Arg3Functor.ArgType!(0)));
    writefln(typeid(Arg3Functor.ArgType!(1)));
    writefln(typeid(Arg3Functor.ArgType!(2)));
}
...
TypeArray
int
char
double
int
char
double

ンマー、よかですな。
戻り値の型 bool、引数の型が int, char, double の opCall を持つ、関数オブジェクトの定義は以下のようになる。

    class MyFunctor : Functor!(bool, TypeArray!(int, char, double)) {
        override ReturnType opCall(
            ArgType!(0) arg0,
            ArgType!(1) arg1,
            ArgType!(2) arg2
        ) {
            ReturnType result = false;
            return result;
        }
    }

Functor を継承してナニいいことあんのよと言われそうだが、これを継承することによって、ReturnType と ArgType!(0〜n) の型 alias を持つことが保証され、汎用プログラミングがやりやすくなる。STL の関数オブジェクトが unary_function や binary_function を継承しているように。

    writefln(typeid(MyFunctor));
    writefln(typeid(MyFunctor.ReturnType));
    writefln(typeid(MyFunctor.ArgsType));
    writefln(MyFunctor.ArgsType.length);
    writefln(typeid(MyFunctor.ArgType!(0)));
    writefln(typeid(MyFunctor.ArgType!(1)));
    writefln(typeid(MyFunctor.ArgType!(2)));
    writefln(typeid(typeof(MyFunctor.opCall)));
...
MyFunctor
bool
TypeArray
3
int
char
double
bool()

といった具合。


TypeArray の実装が、羅列型のナサケナバージョンから変わる可能性はあるけど、どっちみち length と、typaAt テンプレートはつけるので、変わってもこちらに影響なし。