【D言語】なぜ is( typeof( { ... } ) ) は動くのか

今回は、D言語でイディオム的に使われている is( typeof( { ... } ) ) の仕組みについて解説していきます。 まずは、 is( typeof( { ... } ) ) のより一般的な場合である is( typeof( Expression ) ) を見ていきます。

is( typeof( Expression ) ) とは?

コンパイル時に、特定の式が意味的に正しいかどうか確かめることが出来るイディオムです。 意味的に間違っているとは、

  • 存在しない識別子を指定している
  • 関数の引数の型、数が間違っている
  • 互換性のない型への明示的、暗黙的キャストを行なっている

などです。 is( typeof( Expression ) ) は Expression に書かれた式が意味的に正しいならtrueを返し、間違っているならfalseを返します。

なぜ動くのか

is式

まず、外側のis式について見ていきます。 is( Type ) は、Type に書かれた型が意味的に正しい時trueを返し、間違っているときfalseを返します。


import std.stdio;
void main(){
    is(A).writeln(); // Aという型が存在しないので、false
    is(typeof(main)[]).writeln(); // 関数の型の配列は作れないので、false
    is(int).writeln(); // true
}

さらに、is式は内部でなんらかの意味的なエラーが起こると、そのエラーを握りつぶしてfalseを返します。 この性質によって、 is( Type ) でTypeの中に意味的なエラーがあるかどうか確かめるとこができます。


import std.stdio;
template t(A){
    static assert(false); // エラーを起こす
    alias A t;
}
void main(){
    is(t!int).writeln(); // false
}

typeof式

次に、is式のすぐ内側のtypeofを見ていきます。 is式は、引数として型を取ります。 なので、typeof式を挟まずに is( Expression ) と書くと、構文的なエラーが起きます。 この構文的なエラーを起こさないために、任意の式の型を取得できるtypeofで囲みます。

式だけだと不便

is( typeof( Expression ) ) だと、式の意味的な正しさしか確認できません。 しかし、いろいろ工夫することによって、文や宣言の意味的な正しさを確かめることができます。

1. デリゲートリテラルを使う

D言語では、リテラルも式の一つです。 さらに、リテラルの中には、デリゲートリテラルというものもあります。 デリゲートリテラルを使い、 is( typeof( { ... } ) ) と書くと、...に書かれた任意の文の意味的な正しさを確かめることができます。 この用法はとても良く出てきます。この記事のタイトルにも出てきています。


import std.stdio;
struct S{}
void main(){
    is(typeof({ int i = "1"; })).writeln(); // false
    is(typeof({ if(S.init){} })).writeln(); // false
}

2. 無名クラスnew式を使う

D言語の式の中には、無名クラスnew式というものがあります。 これは、無名クラスを作り、その場でnewするという式です。 この式を使い、 is( typeof( new class{ ... } ) ) と書くと、...に書かれた任意の宣言の意味的な正しさを確かめることができます。


import std.stdio;
void main(){
    is(typeof(new class{ void f(){ return 0; } })).writeln(); // false
    is(typeof(new class{ static assert(false); })).writeln(); // false
}

この用法はあまり見かけませんが、とても有用だと思われます。

まとめ

is( typeof( {...} ) ) がなぜ動くのか解説してきました。 実は、 traits(compiles, Expression) でも is( typeof( Expression ) ) と同じことができます。 個人的には、イディオム臭い is( typeof( Expression ) ) よりも、わかりやすい traits(compiles, Expression) のほうが好きです。 traits(compiles, Expression) も同様に、Expressionにデリゲートリテラルや無名クラスnew式を書くことによって、式から文や宣言に広げることができます。 みなさんも、ぜひテンプレートメタプログラミングのお供に traits(compiles, Expression) を使ってみて下さい。