【D言語】任意のメンバー関数の存在を確かめる方法

前回の記事では”型Tがメンバ関数fooを持っているかどうかを確かめるテンプレート”を紹介しました。 しかし、fooで決め打ちというのは、使い勝手があまり良くありません。 そんなわけで、今回の記事では”型Tが任意のメンバ関数を持っているかどうかを確かめるテンプレート”を紹介します。

前回のテンプレートの引数に、”調べる関数の名前”を追加します。 今回の場合、こっちのほうが簡単という理由で__traits(hasMember, …)を使っていきます。


import std.stdio, std.traits;

template hasFunction(T, string name){
    static if(__traits(hasMember, T, name)){
        enum hasFunction = mixin("isSomeFunction!(T." ~ name ~ ")");
    }else{
        enum hasFunction = false;
    }
}

struct Hoge{
    void hoge(){}
    string piyo(int i){ return ""; }
    double fuga;
}

void main(){
    hasFunction!(Hoge, "hoge").writeln(); // true
    hasFunction!(Hoge, "piyo").writeln(); // true
    hasFunction!(Hoge, "fuga").writeln(); // false
    hasFunction!(Hoge, "hogehoge").writeln(); // false
}

上のコードでは、hasFunctionは型Tがnameという名前のメンバを持っているかを調べた後、そのメンバが関数かどうかも調べています。 関数かどうか調べるときに、std.traits.isSomeFunctionを使っていますが、このテンプレートの引数は文字列ではなく、調べたいもの自体です。 つまり、isSomeFunctionは


isSomeFunction!(T.hoge)


isSomeFunction!(T.piyo)

という風にしか使えないという事です。 当然、hasFunctionはメンバを文字列で受け取っているため、上のように書けません。 そこで、伝家の宝刀であるmixinを使っています。


mixin("isSomeFunction!(T." ~ name ~ ")")

mixinは、コンパイル時に評価できる文字列(つまりテンプレート引数の文字列や、それの連結など)を、D言語ソースコードとして埋め込むことが出来るD言語の機能です。 上のコードでは、mixinを使い、isSomeFunction!(T.hoge)やisSomeFunction!(T.piyo)という風にisSomeFunctionを使うことを可能にしています。

まとめ

見ての通り、D言語は、コンパイル時にテンプレートやmixinを使って色々と出来る言語です。 テンプレートで実現が難しいことでも、D言語だとmixinを使えば大抵実現できます。 コンパイル時処理に興味が出てきた方は、D言語を使ってみると楽しいかもしれません。