haxeにCのようなマクロがないのはなぜですか?
Cでは、マクロは非常に広く使用されています。これらは定数であり、条件付きコンパイルの定義であり、インライン関数です。 これらは、言語の柔軟性を大幅に向上させます。 ただし、ミキシングの結果として、1つの概念にはまったく異なる機能があることに同意する必要があります。Cを含むマクロは、ソーステキストを理解するのが困難です。 これが、haxe Nicolas Kanasieの作成者がこの概念をコンポーネントに分割することにした理由です。 したがって、言語は個別に表示されました。定数(静的インライン変数)、条件付きコンパイルの定義(定義)、およびインラインメソッドです。
一方、haxeのマクロ...
haxeのマクロは際立っています-他の言語でこれを見たことはありません。 それらについて簡単に説明すると、次のように言えます。
- haxeで書かれています。
- 正式には、通常のクラスの静的および非静的メソッドです。
- これらのメソッドは入力パラメーターを持ち、任意の式を返すことができます。
- この式は、マクロ呼び出しの場所で置換されます。
- inside:nekoにコンパイルされ、プログラムコードの残りをコンパイルする前に実行されます。
- したがって、nekoができることはすべて(任意のファイルの読み取り/書き込み、データベースへのアクセスなど)。
マクロで正確に何ができますか? 次の点が思い浮かびます:
- クラスの内容の変更-新しい変数とメソッドの挿入、データ型の変更など。
- より複雑なものにコードをデプロイする
- メタ情報の変更-後で反映するためにクラス/メソッド/変数をマークします。
- 条件付きコンパイルの定義を変更します(つまり、たとえば、プログラムは外部条件に応じて異なる方法でアセンブルできます。たとえば、構成ファイルの値に応じて開発バージョンまたは製品バージョンを収集できます)。
- 新しいデータ型の追加、既存のデータ型の削除、すべての型の処理(たとえば、これによりドキュメントジェネレーターを作成できます)。
- 静的メソッドの形式のマクロは、コンパイラのコマンドラインオプションから呼び出すことができます(たとえば、特定のフォルダーをクラスにインポートするための既製のマクロがあり、コンパイルから特定のファイルを除外し、さらにいくつかの特定の関数があります)。
実際のマクロ
著者は、実際のタスクでマクロを繰り返し使用しています。 マクロはコードに複雑さを追加することを覚えておく価値があります。つまり、マクロは本当に必要な場合にのみ使用する必要があります。 標準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()); } }
いくつかのメモ:
- コードでは、メソッドボディとメソッドの戻り値のデータ型を囲むオプションの(単一の式の場合)中括弧を削除しました(コンパイラーが出力するため)。
- 残念ながら、「cloneTyped」の代わりに「clone」と書くことができませんでした(おそらくこの点は修正できるでしょう)。
- cloneTyped()メソッドは、結果のコードとその呼び出しに含まれません(cloneTyped()呼び出しの代わりに、プログラム速度を落とさずに目的の型に変換するclone()呼び出しがあります)。
- マクロに興味のある人は、私の最初の知り合いの後に、具体化(「具体化」)について読むことをお勧めします。
結論
haxe言語のマクロは、従来の方法が機能しない場合に使用する必要がある、かなりユニークで強力なものです。 自分でマクロを作成/使用するのは比較的簡単ですが、既製のライブラリを忘れないでください( http://lib.haxe.org/t/macroを参照)。 この資料があなたにとって興味深いものだったと思います。