コンパむル時の反射D

こんにちは、Habr

今日は、Dでのメタプログラミングが非垞に柔軟で匷力なもの、぀たりコンパむル時のリフレクションに぀いお説明したす。 Dを䜿甚するず、プログラマヌは、コンパむラヌが操䜜する情報を、トリッキヌな方法で衚瀺するのではなく、盎接䜿甚できたす。 では、コンパむラはどのような情報を取埗し、どのように䜿甚できたすか

おそらく最も䞀般的な䜿甚方法から始めたしょう-匏の劥圓性を調べる
__traits( compiles, a + b ); is( typeof( a + b ) ); 

__traitscompiles、exprずistypeofexprは、語圙の芳点から有効な匏exprを期埅したすたずえば、匏12thbは有効な識別子ではないため、コンパむラヌぱラヌをスロヌしたす。 それらは同じように動䜜したすが、1぀の埮劙なむデオロギヌの違いがありたす-istypeofexprはコンパむル胜力をチェックしたせんが、匏のタむプの存圚をチェックしたす。 したがっお、理論的には、型を知るこずができる状況は可胜ですが、いく぀かの芏則によっおこの構造をコンパむルするこずはできたせん。 実際には、私はそのような状況に遭遇したこずはありたせんおそらくそれらはただ蚀語になっおいないでしょう。

䜿甚䟋
タスク数倀に「類䌌する」芁玠を含む配列に「類䌌する」オブゞェクトを受け入れる関数を䜜成し、平均倀mat。Expectationを返したす。
解決策
 template isNumArray(T) { enum isNumArray = __traits(compiles, { auto a = T.init[0]; // opIndex  int  static if( !__traits(isArithmetic,a) ) //    ,    { static assert( __traits( compiles, a=a+a ) ); //  static assert( __traits( compiles, a=aa ) ); //  static assert( __traits( compiles, a=a*.0f ) ); //   float } auto b = T.init.length; //  length static assert( is( typeof(b) : size_t ) ); }); } auto mean(T)( T arr ) @property if( isNumArray!T ) in { assert( arr.length > 0 ); } body { //     arr[index]  arr.length //   ,  arr[index]     auto ret = arr[0] - arr[0]; //       (0) foreach( i; 0 .. arr.length ) ret = ret + arr[i]; //      += return ret * ( 1.0f / arr.length ); } 

䜿甚法
 import std.string : format; struct Vec2 { float x=0, y=0; //      auto opBinary(string op)( auto ref const Vec2 rhs ) const if( op == "+" || op == "-" ) { mixin( format( "return Vec2( x %1$s rhs.x, y %1$s rhs.y );", op ) ); } //    auto opBinary(string op)( float rhs ) const if( op == "*" ) { return Vec2( x * rhs, y * rhs ); } } struct Triangle { Vec2 p1, p2, p3; //    var[index] auto opIndex(size_t v) { switch(v) { case 0: return p1; case 1: return p2; case 2: return p3; default: throw new Exception( "triangle have only three elements" ); } } static pure size_t length() { return 3; } } void main() { auto f = [ 1.0f, 2, 3 ]; assert( f.mean == 2.0f ); //  float  auto v = [ Vec2(1,6), Vec2(2,7), Vec2(3,5) ]; assert( v.mean == Vec2(2,6) ); //    user-defined  auto t = Triangle( Vec2(1,6), Vec2(2,7), Vec2(3,5) ); assert( t.mean == Vec2(2,6) ); //  user-defined  } 

譊告 䟋isNumArrayのコヌドは、詳现を考慮しないため䜿甚しないでくださいopIndexは定数参照を返すこずができるため、割り圓お操䜜はできたせん。

構造は...


蚭蚈にはかなり倧きな機胜セットがありたす。
 is( T ); //    T 
さらに、すべおの堎合においお、タむプTの意味的劥圓性がチェックされたす。
 is( T == Type ); //    T  Type is( T : Type ); //    T      Type 
新しい゚むリアスを䜜成するフォヌムがありたす。
 is( T ident ); 
この堎合、タむプTが有効であれば、identずいう名前で゚むリアスが䜜成されたす。 しかし、そのようなフォヌムを䜕らかの怜蚌ず組み合わせるこずはより興味深いでしょう
 is( T ident : Type ); is( T ident == Type ); 
䟋
 void foo(T)( T value ) { static if( is( TU : long ) ) //   T   long alias Num = U; //   else alias Num = long; //  long } 
たた、タむプが䜕であるかを確認し、その修食子を芋぀けるこずができたす
 is( T == Specialization ); 
この堎合、Specializationは可胜な倀の1぀ですstruct、union、class、interface、enum、function、delegate、const、immutable、shared。 したがっお、タむプTが構造䜓、共甚䜓、クラスなどであるかどうかがチェックされたす。 そしお、怜蚌ず゚むリアスの宣蚀を組み合わせたフォヌムがありたす
 is( T ident == Specialization ); 

別の興味深いトリックがありたす-パタヌンマッチングタむプです。
 is( T == TypeTempl, TemplParams... ); is( T : TypeTempl, TemplParams... ); //   alias' is( T ident == TypeTempl, TemplParams... ); is( T ident : TypeTempl, TemplParams... ); 
この堎合、TypeTemplはタむプ耇合の説明であり、TemplParamsはTypeTemplを構成する芁玠です。
䟋
 struct Foo(size_t N, T) if( N > 0 ) { T[N] data; } struct Bar(size_t N, T) if( N > 0 ) { float[N] arr; T value; } void func(U)( U val ) { static if( is( UE == S!(N,T), alias S, size_t N, T ) ) { pragma(msg, "struct like Foo: ", E ); pragma(msg, "S: ", S.stringof); pragma(msg, "N: ", N); pragma(msg, "T: ", T); } else static if( is( UT : T[X], X ) ) { pragma(msg, "associative array T[X]: ", U ); pragma(msg, "T(value): ", T); pragma(msg, "X(key): ", X); } else static if( is( UT : T[N], size_t N ) ) { pragma(msg, "static array T[N]: ", U ); pragma(msg, "T(value): ", T); pragma(msg, "N(length): ", N); } else pragma(msg, "other: ", U ); pragma(msg,""); } void main() { func( Foo!(10,double).init ); func( Bar!(12,string).init ); func( [ "hello": 23 ] ); func( [ 42: "habr" ] ); func( Foo!(8,short).init.data ); func( 0 ); } 

コンパむル出力
 struct like Foo: Foo!(10LU, double) S: Foo(ulong N, T) if (N > 0) N: 10LU T: double struct like Foo: Bar!(12LU, string) S: Bar(ulong N, T) if (N > 0) N: 12LU T: string associative array T[X]: int[string] T(value): int X(key): string associative array T[X]: string[int] T(value): string X(key): int static array T[N]: short[8] T(value): short N(length): 8LU other: int 

__TraitsコンストラクトkeyWord、...


キヌワヌドの埌のほずんどの__traitsは、匏を匕数たたはコンマ区切りのリストずしお受け取り、その結果が芁件に準拠しおいるかどうかを確認し、テストを反映するブヌル倀を返したす。 匏は、型自䜓たたは型の倀を返す必芁がありたす。 他の郚分は1぀の匕数を取り、ブヌル倀基本的には䜕かのリストよりも有益なものを返したす。

__traitsの怜蚌

is <Some>関数ずisVirtualMethodずisVirtualFunctionの違いに぀いお
明確にするために、違いを瀺す小さなテストを䜜成したした
 import std.stdio, std.string; string test(alias T)() { string ret; ret ~= is( typeof(T) == delegate ) ? "D " : is( typeof(T) == function ) ? "F " : "? "; ret ~= __traits(isVirtualMethod,T) ? "m|" : "-|"; ret ~= __traits(isVirtualFunction,T) ? "v|" : "-|"; ret ~= __traits(isAbstractFunction,T) ? "a|" : "-|"; ret ~= __traits(isFinalFunction,T) ? "f|" : "-|"; ret ~= __traits(isStaticFunction,T) ? "s|" : "-|"; ret ~= __traits(isOverrideFunction,T) ? "o|" : "-|"; return ret; } class A { static void stat() {} void simple1() {} void simple2() {} private void simple3() {} abstract void abstr() {} final void fnlNOver() {} } class B : A { override void simple1() {} final override void simple2() {} override void abstr() {} } class C : B { final override void abstr() {} } interface I { void abstr(); final void fnl() {} } struct S { void func(){} } void globalFunc() {} void main() { A a; B b; C c; I i; S s; writeln( " id T m|v|a|f|s|o|" ); writeln( "--------------------------" ); writeln( " lambda: ", test!(x=>x) ); writeln( " function: ", test!((){ return 3; }) ); writeln( " delegate: ", test!((){ return b; }) ); writeln( " s.func: ", test!(s.func) ); writeln( " global: ", test!(globalFunc) ); writeln( " a.stat: ", test!(a.stat) ); writeln( " a.simple1: ", test!(a.simple1) ); writeln( " a.simple2: ", test!(a.simple2) ); writeln( " a.simple3: ", test!(a.simple3) ); writeln( " a.abstr: ", test!(a.abstr) ); writeln( "a.fnlNOver: ", test!(a.fnlNOver) ); writeln( " b.simple1: ", test!(b.simple1) ); writeln( " b.simple2: ", test!(b.simple2) ); writeln( " b.abstr: ", test!(b.abstr) ); writeln( " c.abstr: ", test!(c.abstr) ); writeln( " i.abstr: ", test!(i.abstr) ); writeln( " i.fnl: ", test!(i.fnl) ); } 

結果
  id T m|v|a|f|s|o| -------------------------- lambda: ? -|-|-|-|-|-| function: ? -|-|-|-|s|-| delegate: D -|-|-|-|-|-| s.func: F -|-|-|-|-|-| global: F -|-|-|-|s|-| a.stat: F -|-|-|-|s|-| a.simple1: F m|v|-|-|-|-| a.simple2: F m|v|-|-|-|-| a.simple3: F -|-|-|-|-|-| a.abstr: F m|v|a|-|-|-| a.fnlNOver: F -|v|-|f|-|-| b.simple1: F m|v|-|-|-|o| b.simple2: F m|v|-|f|-|o| b.abstr: F m|v|-|-|-|o| c.abstr: F m|v|-|f|-|o| i.abstr: F m|v|a|-|-|-| i.fnl: F -|-|a|f|-|-| 

isVirtualMethodは、リロヌドできるもの、たたはすでにオヌバヌロヌドされおいるものに察しおtrueを返したす。 関数がオヌバヌロヌドされおおらず、元々最終的なものであった堎合、それは仮想メ゜ッドではなく、仮想関数になりたす。
ラムダず関数関数型のリテラルに぀いおの疑問笊に぀いおは説明できたせんが、䞍明な理由により、関数たたはデリゲヌトのいずれのテストにも合栌したせんでした。

䜕かを返す


眲名テンプレヌトず制限


最も単玔な実行では、テンプレヌト関数は次のようになりたす
 void func(T)( T val ) { ... } 

しかし、同じように、テンプレヌト匕数には、暗黙的なキャストをチェックするための、さらにはパタヌンマッチングのための構造もありたす。 これず眲名の制限を組み合わせお、オヌバヌロヌドされたテンプレヌト関数の興味深い組み合わせを䜜成できたす。
 import std.stdio; void func(T:long)( T val ) { writeln( "number" ); } void func(T: U[E], U, E)( T val ) if( is( E == string ) ) { writeln( "AA with string key" ); } void func(T: U[E], U, E)( T val ) if( is( E : long ) ) { writeln( "AA with num key" ); } void main() { func( 120 ); // number func( ["hello": 12] ); // AA with string key func( [10: 12] ); // AA with num key } 

暙準ラむブラリ


倚くのパッケヌゞの暙準ラむブラリには、型が䜕らかの動䜜をサポヌトするかどうかを確認するためにテンプレヌトが散圚しおいたすたずえば、このパッケヌゞの関数を䜿甚するために必芁です。 ただし、特別な機胜を実装しないパッケヌゞがいく぀かありたすが、組み蟌みの__traitsの䟿利なラッパヌず、コンプラむアンスをチェックするための远加のアルゎリズムを提䟛したす。

たずめ


これらのアプロヌチをすべお組み合わせお、想像を絶するほど耇雑で柔軟なメタプログラム構成を䜜成できたす。 おそらくD蚀語は、最も柔軟なメタプログラミングモデルの1぀を実装しおいたす。 しかし、誰かがこのコヌドおそらくあなた自身を読み、そのような構成が非垞に問題になるこずを理解できるこずを垞に芚えおおいおください。 垞にきれいになり、困難な瞬間にもっずコメントするようにしおください。

Source: https://habr.com/ru/post/J261349/


All Articles