haxeのマクロ:コンパイル時にコードを直接実行します(これは正常です)

前の記事で 、単純で便利な汎用言語であるhaxeについて少し説明しました。 ただし、単純さと明瞭さに加えて、マクロの概念など、コンパイル中に実行されるコードにも深い部分があります。 haxeに従来のCのようなマクロが存在しない理由と、haxe-macrosがどのような可能性を開いているのか、そして記事について説明します。





haxeにCのようなマクロがないのはなぜですか?



Cでは、マクロは非常に広く使用されています。これらは定数であり、条件付きコンパイルの定義であり、インライン関数です。 これらは、言語の柔軟性を大幅に向上させます。 ただし、ミキシングの結果として、1つの概念にはまったく異なる機能があることに同意する必要があります。Cを含むマクロは、ソーステキストを理解するのが困難です。 これが、haxe Nicolas Kanasieの作成者がこの概念をコンポーネントに分割することにした理由です。 したがって、言語は個別に表示されました。定数(静的インライン変数)、条件付きコンパイルの定義(定義)、およびインラインメソッドです。



一方、haxeのマクロ...



haxeのマクロは際立っています-他の言語でこれを見たことはありません。 それらについて簡単に説明すると、次のように言えます。



マクロで正確に何ができますか? 次の点が思い浮かびます:





実際のマクロ



著者は、実際のタスクでマクロを繰り返し使用しています。 マクロはコードに複雑さを追加することを覚えておく価値があります。つまり、マクロは本当に必要な場合にのみ使用する必要があります。 標準haxeライブラリでは、 SPODデータベースと対話するためのシステムを構築しました。 このマクロを使用すると、自動補完機能を備えたハード構文(C#のLINQなど)を使用してデータベースクエリを作成できます。



例:調整可能な戻り値の型を持つclone()メソッド



典型的な状況では、clone()メソッドは基本クラスで定義され、その後子孫でオーバーライドされます。 このメソッドの問題は、正式な戻り値の型です。 一方では、基本クラスで定義されたメソッドは「base」型のオブジェクトを返し、他方では子孫で再定義され、理想的には「descendant」型のオブジェクトを返す必要があります。 したがって、メソッドの署名に違反し、私が知っているすべての型付き言語(haxeを含む)は、異なる戻り値の型でメソッドを再定義する試みを停止します。 しかし、そのようなコードを書くことができればいいでしょう。

class Base { public function new() { } public function clone() : Base { return new Base(); } } class Child extends Base { public function new() { super(); } override function clone() : Base { return new Child(); } public function childMethod() return "childMethod"; } class Main { static function main() { //  ,       trace(new Child().clone().childMethod()); } }
      
      





正しい型を返すマクロメソッドを作成しましょう。 すぐに別のファイルに保存することをお勧めします(これにより、通常のマクロコードとコンパイラの分離が簡単になり、多くの問題を回避できます。特に、通常は別の場所でクローンが必要になるため、クローンクラスにバインドせずにマクロメソッドを作成すると便利です)。 マクロを持つクラスは次のようになります。

 //  Clonable.hx import haxe.macro.Expr; import haxe.macro.Context; import haxe.macro.Type; class Clonable { //         //  ,       macro public function cloneTyped(ethis:Expr) { var tpath = Context.toComplexType(Context.typeof(ethis)); //  ,         -; //   ,   -   - $      return macro (cast $ethis.clone():$tpath); } }
      
      





ClonableからBaseを継承するだけで十分で、cloneTyped()メソッドを使用できます。

 //  Main.hx class Base extends Clonable { public function new() { } public function clone() return new Base(); } class Child extends Base { public function new() super(); override function clone() return new Child(); public function childMethod() return "childMethod"; } class Main { static function main() { // ,   ,  Child.clone()    Base, // , ,    childMethod(),      clone()   Child trace(new Child().cloneTyped().childMethod()); } }
      
      





いくつかのメモ:

  1. コードでは、メソッドボディとメソッドの戻り値のデータ型を囲むオプションの(単一の式の場合)中括弧を削除しました(コンパイラーが出力するため)。
  2. 残念ながら、「cloneTyped」の代わりに「clone」と書くことができませんでした(おそらくこの点は修正できるでしょう)。
  3. cloneTyped()メソッドは、結果のコードとその呼び出しに含まれません(cloneTyped()呼び出しの代わりに、プログラム速度を落とさずに目的の型に変換するclone()呼び出しがあります)。
  4. マクロに興味のある人は、私の最初の知り合いの後に、具体化(「具体化」)について読むことをお勧めします。


結論



haxe言語のマクロは、従来の方法が機能しない場合に使用する必要がある、かなりユニークで強力なものです。 自分でマクロを作成/使用するのは比較的簡単ですが、既製のライブラリを忘れないでください( http://lib.haxe.org/t/macroを参照)。 この資料があなたにとって興味深いものだったと思います。



All Articles