またまた文字列

いいかげん、D言語ネタは他にないのかいという感じであるが、またぞろ文字列化関数テンプレートの改良版。

template ToString(T) {
    char ToString(T value)
        in {
            static assert (!(is (T == union)));
            static assert (!(is (T == function)));
            static assert (!(is (T == delegate)));
        }
        body {
            static      if (is (T : char))
                return value;

            else static if (is (typeof(T.toString)))
                return value.toString();

            else static if (is (typeof(T.init.keys)))
                return MapToString!(
                    typeof(T.init.keys[0]),
                    typeof(T.init.values[0])
                )(value);

            else static if (is (typeof(T.init)[(T).length])) 
                return StaticArrayToString!(typeof(T.init), (T).length)(value);

            else static if (is (typeof(T.init.ptr)))
                return DynamicArrayToString!(typeof(*T.init.ptr))(value);

            else
                return std.string.toString(value);
        }
}

template ArrayToString(T) {
    char ArrayToString(T array) {
        static if (is (typeof(T.init)[(T).length])) 
            return StaticArrayToString!(typeof(T.init), (T).length)(array);
        else static if (is (typeof(T.init)))
            return DynamicArrayToString!(typeof(*T.ptr))(array);
        else
            return "";
    }
}

template DynamicArrayToString(T) {
    char DynamicArrayToString(T[ ] array) {
        char buffer = "[";
        foreach(T value; array) buffer ~= ToString!(T)(value) ~ ",";
        buffer = chop(buffer);
        buffer ~= "]";
        return buffer;
    }
}

template StaticArrayToString(T, uint N) {
    char StaticArrayToString(T[N] array) {
        char buffer = "[";
        foreach(T value; array) buffer ~= ToString!(T)(value) ~ ",";
        buffer = chop(buffer);
        buffer ~= "]";
        return buffer;
    }
}

template MapToString(Key, T) {
    char MapToString (T[Key] map) {
        char[] buffer = "[";
        foreach(Key key, T value; map) {
            buffer ~= "(";
            buffer ~= ToString!(Key)(key);
            buffer ~= ":";
            buffer ~= ToString!(T  )(value);
            buffer ~= "),";
        }
        buffer = chop(buffer);
        buffer ~= "]";
        return buffer;
    }
}

今回の改良点としては、struct や interface も toString メソッドを持っていれば出力可能になった。(interface に toString メソッドを定義することがどんだけ意味あるか知らんが)ただし、toString の型については言及していない。つまり、toString というデータメンバがあるとアウト。


あと、固定長配列にも対応。
int[ ] のインスタンスはもちろん length プロパティを持つが、int[ ]という型自体は length プロパティを持たないのがミソ。また、(T).length のカッコは必要である。


さぁこれでどんな型でも文字列化可能、と言いたいが、int[3][3]を渡すとインスタンス化時にコンパイルエラー発生。
ただしこれは、std.format も同じ問題を抱えている。"%s".doFormat() で int[ ][ ], int[ ][3] は文字列化が可能だが、int[3][], int[3][3] を渡すと、実行時エラーが発生する。
(要するにできないのは私だけではないのだ)


ArrayToString を除けばテンプレート引数がみな違うので、テンプレート識別子と関数名を ToString で統一して、個々の識別はディスパッチまかせにしようかとも思ったが、まぁ意味的にはっきりさせる意味で、名前は分けておいた。
まだ改良の余地ありそう。