typesafe printf in D

http://www.kmonos.net/wlog/68.html#_2331061204

最後やりたかったのは本当はこんな感じですか!

import std.stdio;
import std.typetuple;

template FormatCheckerType(char[] orig, char[] fmt, T...) {
    static if (fmt.length == 0) {
        static assert(false, orig ~ ": % comes without type specifier");
    }
    else {
        const c = fmt[0];
        const l = c | 32; // lower case

        template msg(char[] orig, char[] type, char c, T) {
            static assert(false,
                          orig ~ ": " ~ type ~ " argument is expected"
                          " for %" ~ c ~
                          " but comes " ~ T);
        }

        static if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || l == 'x') {
            static if (!is(T[0] : int)) alias msg!(orig, "int", c, T[0]) m;
        }
        else static if (l == 'e' || l == 'f' || l == 'g' || l == 'a') {
            static assert(is(T[0] : double),
                          orig ~ ": double argument is expected for %" ~ c);
        }
        else static if (c == 'c') {
            static assert(is(T[0]==char) || is(T[0]==ubyte) || is(T[0]==byte),
                          orig ~ ": char argument is expected for %c");
        }
        else static if (c == 's') {
            static if (!is(T[0] : char[]) && !is(T[0] : char*) &&
                       !is(typeof(T[0].toString()) == char[]))
                alias msg!(orig, "string", c, T[0]) m;
        }
        else static if (c == 'p') {
            static assert(is(typeof(*T[0])),
                          orig ~ ": pointer argument is expected for %p");
        }
        else {
            alias FormatCheckerType!(orig, fmt[1..$], T) fc;
        }
        static if (!is(fc)) {
            alias FormatChecker!(orig, fmt[1..$], T[1..$]) fc;
        }
    }
}

template FormatChecker(char[] orig, char[] fmt, T...) {
    static if (fmt.length == 0) {
        static if (T.length != 0) {
            static assert(false, orig ~ ": too many arguments for format");
        }
        alias void FormatChecker;
    }
    else static if (fmt[0] == '%') {
        static if (T.length == 0) {
            static assert(false, orig ~ ": too few arguments for format");
        }
        alias FormatCheckerType!(orig, fmt[1..$], T) FormatChecker;
    }
    else {
        alias FormatChecker!(orig, fmt[1..$], T) FormatChecker;
    }
}

template mywritefln(char[] fmt) {
    class mywritefln {
        template opCall(T...) {
            static void opCall(T t) {
                alias FormatChecker!(fmt, fmt, T) fc;
                writefln(fmt, t);
            }
        }
    private:
        this(); // cannot be constructed
    }
}

void main() {
    mywritefln!("Hello world!")();
    mywritefln!("Hello %s world!")("D");
    mywritefln!("Hello %s %d.%d world!")("D", 1, 0);
    version (TEST_ERRORS) {
        mywritefln!("Hello %s %d.%d world!")('D', 1, 0);
        mywritefln!("Hello %s %d.%d world!")("D", 1, 0, 1);
        mywritefln!("Hello %s %d.%d world!")("D", 1);
    }
}

なんか too few arguments のチェックがうまくいってませんし、なんか msg ってやつを使ったり使わなかったりしてるんですがそれは読者への宿題ですよっていうかこんなことやってる暇無い気がしますよ!あと mywritefln!("Hello 100%% world!")(); が通らないしすぐに直せないのも似たような理由なので宿題です。誰もやらなければ私がする宿題です。生徒の宿題をしてくる負け組み大先生です。よろしくおねがいします。

ちなみに mywritefln!(format_string) までがこう、 template なのでコンパイル時に評価されるので適当に中身と型の検証をしてやって、あと適当に型チェック自前でやってるというかそんな感じ。本当は mywritefln!(format_string) が適切な型を引数とする関数になってる、とできるといいんでしょうが。

最近あろはさんが C++ とかをいじめてる><ので「正規表現SQL のライブラリは,その文字列として表現された部分の中身の検証やコンパイルまではしてくれない」 C 言語と違って D 言語はその検証ができるし、そこまで template まみれでもなくフツーに if 連打で書けるんだお!と主張してみる青年の主張 2006 。

http://alohakun.blog7.fc2.com/blog-entry-571.html

そしてその D 言語に、どういった機能が言語にあるべきか、という様々な教訓を与えたのは C++ という言語だと思います。あろはさんが何を期待しているかはある程度わかるつもりですし、ある種プログラム書き共通の夢の一つかなぁと思うのですが、今現在はまぁ、その模索の最中なのかなぁと。そしてその模索の道具として、 C++ というのは極めて素晴らしいサンプルだと思うのですよ。

http://d.hatena.ne.jp/Cryolite/20061130#c1164907149

そうそう、ここに極めて同意なのでして(というには最近の C++ 事情に全然ついてけてないのが悲しいのですが)。「C++ 的には組み込むなんてとんでもない」にもとても同意なのですが、ていうと お前言ってること全然違うくねえか という感じですが、それはつまり D 的には組み込んでも問題無い程度の汎用部品なんじゃ、と思ったわけでした。で、 D 言語的には組み込んでみて欲しかったのですが Boost.bind そのまんまの std.bind できちまったよチクショウ、という。

あと関係無いですが段階的なプログラム変換というと EPP みたいなのを想像するんですが、あろはさん的にはこういうのどうなんだろうとふと思いました。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h