メタプログラミングとは、コンピュータプログラムが他のプログラムをデータとして扱うことができるコンピュータプログラミング技術です。つまり、プログラムは実行中に他のプログラムを読み込み、生成し、分析し、変換し、さらには自身を修正するように設計できます。[ 1 ] [ 2 ]場合によっては、これによりプログラマは解決策を表現するコード行数を最小限に抑えることができ、結果として開発時間を短縮できます。[ 3 ]また、再コンパイルなしで新しい状況を効率的に処理できる柔軟性も得られます。
メタプログラミングは、計算をランタイムからコンパイル時へ移動したり、コンパイル時の計算を使用してコードを生成したり、自己修正コードを可能にしたりするために使用できます。プログラミング言語がそれ自体メタ言語である能力は、リフレクションプログラミングを可能にし、リフレクションと呼ばれます。[ 4 ]リフレクションは、メタプログラミングを容易にするための貴重な言語機能です。
メタプログラミングは、 Lispなどのリスト処理言語を用いて1970年代から1980年代にかけて人気を博した。Lispマシンハードウェアは1980年代に注目を集め、コードを処理できるアプリケーションを可能にした。これらはしばしば人工知能アプリケーションに利用された。
メタプログラミングによって、開発者は汎用プログラミングパラダイムに準拠したプログラムやコードを作成できます。プログラミング言語自体を第一級データ型として扱うこと(Lisp、Prolog、SNOBOL、Rebolなど)も非常に便利です。これはホモイコニシティと呼ばれます。汎用プログラミングでは、データ型を指定することを気にせずにコードを記述できるため、使用時にデータ型をパラメータとして指定できます。これにより、言語内にメタプログラミング機能が組み込まれます。
メタプログラミングは通常、次の3つの方法のいずれかで機能します。[ 5 ]
Lispは、その歴史的な先例と、メタプログラミングのシンプルさと強力さの両方から、メタプログラミング機能を備えた言語の典型と言えるでしょう。Lispのメタプログラミングでは、引用符解除演算子(通常はカンマ)によって、実行時ではなくプログラム定義時に評価されるコードが導入されます。そのため、メタプログラミング言語はホストプログラミング言語と同一であり、必要に応じて既存のLispルーチンをメタプログラミングに直接再利用できます。このアプローチは、プログラムのデータと直接やり取りするインタプリタをプログラムに組み込むことで、他の言語でも実装されています。RemObjectsの「Object Pascal用Pascalスクリプト」など、一般的な高水準言語にもこの種の実装が存在します。
メタプログラムの簡単な例として、生成プログラミングの一例であるこのPOSIXシェルスクリプトを挙げます。
#!/bin/sh # メタプログラムecho '#!/bin/sh' >プログラム for i in $( seq 992 ) do echo "echo $i " >>プログラム done chmod +xプログラム このスクリプト(またはプログラム)は、1から992までの数字を出力する993行の新しいプログラムを生成します。これは、コードを使ってさらにコードを書く方法を示す一例に過ぎず、数字のリストを出力する最も効率的な方法ではありません。しかしながら、プログラマーはこのメタプログラムを1分以内に記述して実行でき、その間に1000行を超えるコードを生成することができます。
クワインとは、自身のソースコードを出力として生成する特殊なメタプログラムである。クワインは一般的に、娯楽的あるいは理論的な興味の対象にとどまる。
すべてのメタプログラミングが生成プログラミングを伴うわけではありません。プログラムが実行時に変更可能であったり、インクリメンタルコンパイルが利用可能であったりする場合(C#、Forth、Frink、Groovy、JavaScript、Lisp、Elixir、Lua、Nim、Perl、PHP、Python、Rebol、Ruby、Rust、R、SAS、Smalltalk 、 Tclなど)、ソースコードを生成せずにメタプログラミングを実行する手法を用いることができます。
生成型アプローチの1つのスタイルは、ドメイン固有言語(DSL)を使用することです。DSLを使用する比較的よくある例として、生成型メタプログラミングがあります。lexとyaccは、字句解析器と構文解析器を生成するために使用される2つのツールで、ユーザーが正規表現と文脈自由文法を使用して言語を記述し、言語を効率的に解析するために必要な複雑なアルゴリズムを組み込むことができます。
メタプログラミングの用途の一つは、動的プログラム解析を行うためにプログラムに計測機能を組み込むことである。
メタプログラミング機能を完全に活用するには、急な学習曲線があると主張する人もいます。[ 8 ]メタプログラミングは実行時に柔軟性と構成可能性を高めるため、メタプログラミングを誤用したり、誤って使用したりすると、平均的な開発者にとってデバッグが非常に困難な、不当で予期しないエラーが発生する可能性があります。注意深く使用しないと、システムにリスクをもたらし、脆弱性を高める可能性があります。メタプログラミングの誤った使用によって発生する一般的な問題には、コンパイラが不足している構成パラメータを識別できないこと、無効または不正確なデータによって未知の例外や異なる結果が生じることなどがあります。[ 9 ]このため、メタプログラミングを言語やプラットフォームで実行する機能の開発は、高度なスキルを持つ開発者のみが行うべきであり、平均的な開発者は慣習の一部としてこれらの機能の使い方を学ぶ必要があると考える人もいます。[ 8 ]
IBM /360とその派生機種には強力なマクロアセンブラ機能が搭載されており、完全なアセンブリ言語プログラムやプログラムの一部(例えば、異なるオペレーティングシステム向け)を生成するためによく利用された。CICSトランザクション処理システムに付属するマクロには、前処理ステップとしてCOBOLステートメントを生成するアセンブラマクロが含まれていた。
MASMなどの他のアセンブラもマクロをサポートしています。
メタクラスは、以下のプログラミング言語で提供されています。
依存型の使用により、生成されたコードが無効にならないことを証明できます。[ 15 ]ただし、このアプローチは最先端であり、研究用プログラミング言語以外ではほとんど見られません。
注目すべきメタプログラミングシステムの一覧は、「プログラム変換システムの一覧」に掲載されています。