タプルパラメタとtemplate function literal

D言語の最新である2.029から入ったtemplate function literalを使うと,templateのパラメタで使う場合においては,Rubyとかのように型を書かなくても引数に使うことが出来るのでいいなぁと思った(きっとコンパイル時の型推論のおかげ).ということで,簡単なコールバックはリテラルで書いて,複雑なのは関数やネスト関数でやればいいやと思ったのだけど,普通にやるとコンパイルできなかったのでメモ.

まとめ

重要なのはこれだけ.だけど,これに気付いてかつ使えるようにするのに,無駄に時間が掛かってしまった.これでもリテラルの返り値がnullの場合はコンパイルできない(nullが多義だからかな?).std.date.benchmarkはiで呼べてたので,void f() {}的なものだとコンパイル出来るらしい(バグ?).

PythonのScannerっぽいやつ

サンプルで書いた.解決策としてはtemplateを使って,取得したパラメタの分だけswitchのcaseを作るという方法で,あまり綺麗ではなかったり^^;

template actionSwitch(uint index, T...)
{
    static if (T.length) {
        enum actionSwitch = "case " ~ ToString!(index) ~ ": auto r = action[" ~ ToString!(index) ~ "](m.hit); if (r.length) result ~= r; break;\n" ~ actionSwitch!(index + 1, T[1..$]);
    } else {
        enum actionSwitch = "default:\n";
    }
}

class Scanner(T, action...) if (isSomeString!(T))
{
  private:
    .Regex!(.Unqual!(.ElementType!(T)))[] regexs;

  public:
    this(T[] patterns, string[uint] attributes = null)
    {
        if (action.length != patterns.length)
            throw new Exception("Function and Pattern pair invalid");

        foreach (i, pattern; patterns)
            regexs ~= .regex(pattern, i in attributes ? attributes[i] : null);
    }

    T[] run(T src)
    out(result)
    {
        assert(result !is null);
    }
    body
    {
        uint index;
        T[] result;

        while (index < src.length) {
            for (int i; i < action.length; i++) {
                auto m = .match(src[index..$], regexs[i]);
                if (m.pmatch[0].startIdx == 0) {
                    mixin("switch(i) {\n" ~ actionSwitch!(0, action) ~ "}");

                    index += m.hit.length;

                    goto Success;
                }
            }
            break;

          Success: ;
        }

        return result ~= src[index..$];
    }
}

Scanner!(string, func) scanner(func...)(string[] patterns, string[uint] attributes = null)
{
    return new typeof(return)(patterns, attributes);
}

使い方はこんな感じ.

auto s = scanner!((s){return "[" ~ s ~ "]";}, foo, bar)(["a", "b", "c"]);
auto result = s.run("abcdcba");

fooやbarは関数とか.タプルパラメタは最後尾という制約があるので,patternsの型を推論出来ないのが悲しい.構造体のパラメタにしたかったけど,前質問したバグがあったの思い出して諦めた(変わりに奇数パターン,偶数コールバックでもいいけど).
多分バグだと思うので,いずれ修正されるかもしれないし,上手い回避方法があるかもしれない(bugzillaを探すのが面倒だったw).というかそもそもこれの必要性が(ry

追記

patternsからの型推論はtemplate functionの省略系を使わなければ普通に出来た.map方式だと上手くインスタンス化出来なかったので,reduce方式だけど.

template scanner(action...)
{
    alias scannerImpl!(action).scanner scanner;
}

private template scannerImpl(action...)
{
    Scanner!(ValueType!(T), action) scanner(T)(T patterns, string[uint] attributes = null)
    {
        return new typeof(return)(patterns, attributes);
    }

    private template ValueType(T : T[]) { alias T ValueType; }
}

最近省略系ばかり使ってたから,すっかり忘れていたww