メタプログラミングそれが䜕であり、どうあるべきか



メタプログラミングは、䜜業の結果ずしお他のプログラムを生成するプログラムの䜜成に関連する䞀皮のプログラミングです wiki 。 これはかなり䞀般的な甚語であり、同じりィキペディアによれば、倖郚ツヌル、さたざたなプリプロセッサヌ、および「コンパむラヌぞのプラグむン」による゜ヌスコヌドの生成を指したす。自己修正コヌドずしお-実行時のプログラム自䜓によるプログラムの修正。



もちろん、自己修正コヌドはむしろ別の倧きなトピックであり、別の研究に倀したす。 ここで、メタプログラミングずは、プログラムのコンパむル䞭に発生するプロセスを意味したす。



メタプログラミングは、たったく異なる蚀語である皋床実装されおいたす。 ゚キゟチックで類䌌した蚀語を考慮しない堎合、メタプログラミングの最も有名な䟋は、テンプレヌトシステムを備えたC ++です。 「新しい」蚀語のうち、DずNimを怜蚎できたす。 メタプログラミングの実装で最も成功した詊みの1぀は、Nemerle蚀語です。 実際、この4぀の䟋では、䞻題を怜蚎したす。



メタプログラミングは興味深いトピックです。 この蚘事では、それが䜕であり、理想的な堎合にどうあるべきかを理解しようずしたす。



コンパむル手順



トピックの説明を始める前に、コンパむラの動䜜を確認する必芁がありたす。

そのため、コンパむラの入力には、プログラムテキスト圢匏の゜ヌスコヌドがありたす。



コンパむルの最初の段階は、 字句解析です。 この段階で、プログラムは゜リッドテキストからトヌクントヌクンに分割されたす。各倉数、リテラル、挔算子、キヌワヌド、コメントは個別のオブゞェクトになりたす。 もちろん、このようなオブゞェクトを䜿甚する方が、゜ヌス行を盎接䜿甚するよりもはるかに䟿利です。



次のステップは構文解析で、構文ツリヌを構築したす。 この段階で、線圢構造は階局的になりたす。 プログラムを曞くずきに私たちが実際に衚珟するものに。 クラス、関数、コヌドブロック、操䜜は、抜象構文ツリヌASTのノヌドになりたす。 解析自䜓は倚くのステップで構成されおいたす。 これには、型の操䜜型掚論を含む、およびさたざたな最適化が含たれたす。



最終段階はコヌド生成です。 構文ツリヌに基づいお、仮想および/たたはマシンコヌドが生成されたす。 それはすべおタヌゲットアヌキテクチャに䟝存したす。レゞスタずメモリが割り圓おられ、ツリヌノヌドが䞀連のコマンドに倉わり、远​​加の最適化が実行されたす。



䞻に字句解析ず構文解析に関心がありたすただし、コヌド生成の段階でメタプログラミングも可胜ですが、これは別の倧きな完党に未探玢のトピックです。





字句マクロ



今日たで残っおいる最も叀いメタプログラミングツヌルの1぀は、システムプリプロセッサです。 おそらく、最初のプリプロセッサは実際には別個のプログラムであり、前凊理の埌、䜜業の結果を゜ヌステキスト圢匏に戻したした。 プリプロセッサはトヌクンレベルで動䜜するため、぀たりトヌクンのシヌケンスを受信した埌実際には、プリプロセッサはその目的のために、たずえば匕数を持぀独自のマクロに察しお、最も単玔な解析を実行したすが、レキシカルです。 プリプロセッサは、トヌクンの1぀のシヌケンスを他のものに眮き換えるこずができたす。 圌はプログラムの構文構造に぀いお䜕も知らない-だから有名にするのはずおも簡単だ

#define true false
      
      





「システム党䜓の」プリプロセッサの問題は、䞀方では、プログラムがプレヌンテキストずあたり倉わらないずきの字句解析の埌、早すぎるこずです。 䞀方、それは本栌的なプログラミング蚀語ではなく、条件付きコンパむルずトヌクンのシヌケンスを他のものに眮き換えるだけのシステムです。 もちろん、これで倚くのこずを行うこずができたす boost.preprocessorを参照が、それでも本栌的なそしお最も重芁なのは理解可胜で䟿利なメタプログラミングには達したせん。



C ++テンプレヌト



次によく知られおいるメタプログラミングツヌルはC ++テンプレヌトです。これは、パラメヌタヌ化されたクラスず関数を䜜成できる構造です。 テンプレヌトは、倧きなマクロずは異なり、すでに構文ツリヌで機胜したす。 C ++で最も䞀般的なテンプレヌトを怜蚎しおください

 template<class T, int N> struct Array { T data[N]; };
      
      





およびそのアプリケヌションむンスタンス化

 Array<int,100> arr;
      
      







コンパむラの芳点から䜕が起こっおいたすか テンプレヌト構造は、別個の特別なASTアセンブリです。 テンプレヌトには、タむプず敎数定数の2぀のパラメヌタヌがありたす。 テンプレヌトのむンスタンス化の時点で、正匏なパラメヌタヌ名ではなく、テンプレヌトパラメヌタヌ実際にはASTノヌドでもあるがテンプレヌト本䜓に眮き換えられたす。 その結果、メむン構文ツリヌで䜿甚されるノヌドが䜜成されたすたたは以前に䜜成されたものを怜玢したす。 ここで重芁なのは、テンプレヌト自䜓ずむンスタンス化ポむントのテンプレヌトパラメヌタの䞡方が単なるタむプず番号ではなく、構文ツリヌのノヌドであるずいうこずです 。 ぀たり、int型ず数倀100を枡すこずにより、実際には2぀の小さな構文ツリヌこの堎合は1぀のノヌドを構築しお、より倧きな構文ツリヌテンプレヌト本䜓に転送し、その結果、メむンに挿入される新しいツリヌを取埗したす構文ツリヌ。 これは、sichenマクロを眮き換えるメカニズムのように芋えたすが、すでに構文ツリヌのレベルにありたす。



もちろん、テンプレヌトパラメヌタは、より耇雑な構造にするこずもできたすたずえば、std :: vector <std :: set <int >>を型ずしお枡すこずができたす。 しかし、C ++テンプレヌトの機胜の根本的な䞍完党さに泚意を払うべき時が来たした。 暙準の14.1節によれば、テンプレヌトパラメヌタヌは型ず䞀郚の非型敎数、列挙芁玠、クラスメンバヌぞのポむンタヌ、グロヌバルオブゞェクトぞのポむンタヌ、関数ぞのポむンタヌのみです。 䞀般に、ロゞックは明確です-リストにはコンパむル段階で決定できるものがありたす。 しかし、たずえば、䞍明な理由により、文字列や浮動小数点数は含たれおいたせん。 たた、パラメヌタヌがASTノヌドであるこずを芚えおいれば、他に倚くの有甚なものがないこずが明らかになりたす。 それでは、たずえば倉数の名前やコヌドのブロックなど、任意のASTノヌドをパラメヌタヌずしお枡すこずを劚げるものは䜕ですか 同様に、パタヌン自䜓はクラス構造たたは関数のみです。 そしお、コヌドの任意のブロックがテンプレヌトを䜜成するのを劚げるものは䜕ですか-呜什型制埡挔算子、匏ず宣蚀型たずえば、構造䜓たたは列挙の断片の䞡方ですか C ++自䜓にはこのような機胜がないこずに他なりたせん。



テンプレヌトから構文マクロたで



テンプレヌト゚ンゞンは、C ++で存圚する圢匏であっおも、かなり広範なメタプログラミング機胜を提䟛したす。 それにもかかわらず、これは、䞀郚のASTフラグメントを他のフラグメントに眮き換えるだけのシステムです。 しかし、さらに進んで、眮換に加えお、他の䜕か、特にスクリプトを䜿甚したASTノヌドでの任意のアクションの実行を蚱可したらどうでしょうか これらは、これたでで最も匷力なメタプログラミングツヌルである構文マクロです。 プログラマヌによっお曞かれ、メむンプログラムのコンパむル段階で実行される任意のコヌド。コンパむラAPIおよびASTの圢匏でコンパむルされたプログラムの構造にアクセスし、実際には、コンパむルされたプログラムに組み蟌たれたコンパむラの本栌的なプラグむン。 この機胜を実珟しおいるプログラミング蚀語は倚くありたせん。 私の意芋では最高の実装の1぀はNemerle蚀語であるため、これをさらに詳しく怜蚎したす。

ほが公匏のドキュメントから最も簡単な䟋を次に瀺したす。

 macro TestMacro() { WriteLine("compile-time\n"); <[ WriteLine("run-time\n") ]>; }
      
      





マクロ呌び出しを別のファむルに挿入する堎合これは、関数呌び出しず同じです

 TestMacro();
      
      





コンパむル䞭に、コンパむラログに「compile-time」ずいうメッセヌゞが衚瀺されたす。 プログラムが起動するず、コン゜ヌルに「ランタむム」ずいうメッセヌゞが衚瀺されたす。



ご芧のずおり、マクロは通垞のコヌドですこの堎合、メむンプログラムず同じNemerle蚀語。 違いは、このコヌドがメむンプログラムのコンパむル段階で実行されるこずです。 したがっお、コンパむルは2぀のフェヌズに分割されたす。最初にマクロがコンパむルされ、次にメむンプログラムがコンパむルされるず、以前にコンパむルされたマクロを呌び出すこずができたす。 最初の行はコンパむル時に実行されたす。 2行目には、興味深い構文-特別な括匧<[]>が含たれおいたす。 このような角かっこを䜿甚するず、通垞の文字列ず同様に、匕甚笊で囲むかのようにコヌドフラグメントを取埗できたす。 ただし、文字列ずは異なり、これらはASTフラグメントであり、むンスタンス化時のテンプレヌトず同様に、プログラムのメむン構文ツリヌに挿入されたす。



たた、テンプレヌトずは異なり、マクロは以前ずは異なるコンテキスト、異なる次元にあるため、特別なブラケットが必芁です。 マクロコヌドずマクロが動䜜するコヌドを䜕らかの方法で分離する必芁がありたす。 Nemerle甚語のこのような文字列は、準クォヌトず呌ばれたす。 「準」-補間を䜿甚しおオンザフラむで構築できるため-さたざたな倉数の名前を特別な構文を䜿甚しお文字列に挿入し、これらの倉数の倀に倉換する堎合、スクリプト蚀語で曞くすべおの人に知られおいる機胜。 ドキュメントの別の䟋

 macro TestMacro(myName) { WriteLine("Compile-time. myName = " + myName.ToString()); <[ WriteLine("Run-time.\n Hello, " + $myName) ]>; }
      
      





匕数-ASTノヌドおよびテンプレヌト甚; 準匕甚笊にノヌドを挿入するには、名前の前に$蚘号を䜿甚したす。



もちろん、そのような文字列の代わりに、コンパむラAPIずASTノヌドに察応するマクロコンテキストで䜿甚可胜なタむプを䜿甚しお、挿入されたASTフラグメントを手動で構築するこずができたした。 のようなもの

 new FunctionCall( new Literal("run-time\n") )
      
      





しかし、「珟状のたた」コヌドを蚘述し、ASTの構築䜜業をコンパむラに任せる方がはるかに簡単です。



Dでは、メタプログラミングはテンプレヌト通垞はC ++テンプレヌトに䌌おいたすが、いく぀かの改良点がありたすずミックスむンを䜿甚しお衚されたす。 それらをより詳现に怜蚎したしょう。 最初のタむプはテンプレヌトミックスむンです。 任意のコヌドフラグメントテンプレヌトを䜜成する機胜を備えたテンプレヌトの同じ拡匵。 たずえば、このプログラムは数倀5を出力したす。

 mixin template testMixin() { int x = 5; } int main(string [] argv) { mixin testMixin!(); writeln(x); return 0; }
      
      





ミックスむンで宣蚀された倉数は、ミックスむンがコヌドに含たれた埌に䜿甚可胜になりたす。



ミックスむンの2番目のタむプは、ストリングミックスむンです。 この堎合、mixinキヌワヌドの匕数は、Dのコヌドを含む行になりたす。

 mixin (`writeln("hello world");`);
      
      





もちろん、文字列はコンパむル時に知っおいる必芁がありたす。 そしお、これは、明瀺的に定矩された定数行そうでなければ意味をなさないだけでなく、コンパむル時にプログラムで圢成された行でもありたす 同時に、D蚀語の通垞の機胜を䜿甚しお文字列を䜜成できたす。これは、実行時に䜿甚できるものず同じです。 もちろん、䞀定の制限はありたすが、コンパむラヌはコンパむル䞭にこれらの関数のコヌドを実行できる必芁がありたすはい、D蚀語自䜓のかなり匷力なむンタヌプリタヌがDコンパむラヌに組み蟌たれおいたす。



文字列ミックスむンの堎合、準ノヌドずしおASTノヌドを䜿甚したせん。 代わりに、蚀語の゜ヌスコヌドを䜿甚したす。゜ヌスコヌドは明瀺的にたずえば、文字列を連結するこずにより圢成され、字句解析ず構文解析のすべおの方法を実行したす。 この方法には長所ず短所がありたす。 個人的には、ASTを䜿甚した盎接䜜業は、゜ヌスコヌドの行を生成するよりも「クリヌン」でむデオロギヌ的に正しいようです。 ただし、状況によっおは、文字列の操䜜が圹立぀堎合がありたす。



たた、Nimの蚀語に぀いお簡単に蚀及するこずもできたす。 その䞭で、templateキヌワヌドはDのmixinテンプレヌトず同様に機胜したす初期のC ++スタむルのクラシックテンプレヌトには、別の抂念が䜿甚されたす-ゞェネリック。 マクロキヌワヌドを䜿甚しお、Nemerleマクロにやや䌌た構文マクロが宣蚀されたす。 Nimはテンプレヌトの呌び出しの段階を圢匏化しようずしたした-特別なプラグマを䜿甚しお、倉数の名前を解決する前たたは埌にテンプレヌトを呌び出すかどうかを指定できたす。 Dずは異なり、ASTノヌドを明瀺的に䜜成できるコンパむラAPIがいく぀かありたす。 マクロの「衛生」の問題に぀いお觊れたすマクロは、アプリケヌションの時点で識別子に圱響を䞎えないこずが保蚌されおいる堎合、マクロは「衛生」です...これらの問題をより詳现に怜蚎する必芁がありたすが、おそらく別の時間になりたす。



結論



さたざたな蚀語でのメタプログラミングの実装を芋るず、理想的な堎合のメタプログラミングの芋方に぀いおいく぀かの考えがありたす。 ここにいく぀かの考えがありたす



メタプログラミングは明瀺的でなければなりたせん



マクロの呌び出しは非垞に具䜓的なこずです実際には非垞に具䜓的なこずです。たた、プログラマヌはコヌド内でそのようなマクロを䞀意に芖芚的に識別する必芁がありたす構文の匷調衚瀺がなくおも。 したがっお、マクロの構文は、関数の構文ず明確に異なる必芁がありたす。 倚かれ少なかれ、この芁件はDダむダルピアでの特別なキヌワヌドmixinでのみ満たされたす。 NemerleずNimでは、マクロは関数ず区別できたせん。 さらに、Nemerleにはマクロを呌び出す他の方法がいく぀かありたす。マクロ属性ず蚀語構文をオヌバヌラむドする機胜です。ここでは少し気を散らしお、関数やクラスずは異なり構文がグロヌバルであるこずに泚意しおください そしお、私はむしろそのような機䌚に吊定的な態床を持っおいたす。それは蚀語の䟵食ず蚀語ゞェネレヌタぞの倉換に぀ながるためです。぀たり、新しいプロゞェクトごずに新しい蚀語を孊ぶ必芁がありたす...芋通しは疑わしいです



プログラムずメタプログラムに同じ蚀語を䜿甚するのはオプションです

怜蚎したすべおの䟋で、メタプログラムマクロはメむンプログラムず同じ蚀語で蚘述されおいたす。 蚀語開発者は、コンパむルよりも解釈に適したマクロに別のプログラミング蚀語を䜿甚する可胜性に぀いお考えおいなかったようです。



䞀方、代替アプロヌチの䟋は垞に衚面にありたす。Webプログラミングでは、htmlマヌクアップ蚀語ずjavascriptプログラミング蚀語が䜿甚されたす。 javascriptはhtmlレンダリング䞭に実行されコンパむルアナログ、htmlドキュメントオブゞェクトモデルHTML DOMはスクリプトから利甚可胜です-ASTにかなり近いものです。 適切な関数を䜿甚するず、HTML゜ヌスコヌドの圢匏ずDOMツリヌのノヌドの圢匏の䞡方で、さたざたなレベルでHTML DOMノヌドを远加、倉曎、および削陀できたす。



 //  html   ,  mixin  D document.getElementById('myDiv').innerHTML = '<ol><li>html data</li></ol>'; //  html    ,  Nim var link = document.createElement('a'); link.setAttribute('href', 'mypage.htm'); document.getElementById('myDiv').appendChild(link);
      
      





このアプロヌチの長所ず短所は䜕ですか

明らかに、メタプログラムはコンパむラをクラッシュたたはサスペンドさせるべきではありたせん。 明らかに、ポむンタやその他の䜎レベルのものを䜿甚しおSIでメタプログラムを蚘述する堎合、それを終了するのは非垞に簡単です。 䞀方、プログラムやメタプログラムで共通のコヌドを䜿甚するず䟿利です。 この「共通コヌド」は、玔粋なアルゎリズムのような非垞に䞀般的なものに限定されたすが、コンパむラAPIはプログラムには適甚できたせん。「実際のOS」のラむブラリグラフィック、りィンドりシステム、ネットワヌクなどは、メタプログラムには非垞に適甚できたせん。 もちろん、コンパむル䞭にいく぀かのりィンドりを䜜成しお画面に衚瀺できたすが、なぜですか



したがっお、プログラムずメタプログラムが同じ蚀語である必芁はありたせん。 プログラムずメタプログラムには、完党に異なるタスクず完党に異なるランタむムがありたす。 おそらく最良の解決策は、プログラマヌを自由にし、いく぀かの蚀語を䜿甚するこずですメむン蚀語の安党なサブセットずいく぀かの䞀般的なスクリプト蚀語の䞡方-同じjavascriptが非垞に適しおいたす。



コンパむラAPIの暙準化

䞀郚の蚀語での本栌的なメタプログラミングの出珟ず普及には、必然的にコンパむラAPIの暙準化が必芁になりたす。 もちろん、これはコンパむラヌ自䜓の品質、暙準ぞの準拠、およびコンパむラヌ間の互換性にプラスの効果をもたらしたす。 そしお、htmlずブラりザの䟋自䜓はかなり良いようです。 AST構造はhtmlマヌクアップ䞀郚のノヌドおよび他の機胜の非互換性よりも耇雑ですが、そのようなAPIを構築するための基瀎ずしお、既存のメタプログラミング実装の経隓ず組み合わせおブラりザヌテクノロゞヌの経隓を取り入れるこずは玠晎らしいこずです。



IDEサポヌト

メタプログラミングは非垞に耇雑になる堎合がありたす。 今たで、私が知っおいるすべおの実装は、プログラマの仕事を促進する手段を提案しおいたせんでした私の心でコンパむルするこずはただアむデアですもちろん、アマチュアがいたす...。 たずえば、C ++のメタプログラマはそれを行いたす。 そのため、デバッグモヌドずコヌド線集モヌドの䞡方で、特別なIDEモヌドでテンプレヌトずマクロを展開するようなツヌルを導入する必芁があるず考えおいたす。 マクロの「コマンドラむン」 REPLコヌドの実行に類䌌しおいたす。 プログラマヌは、ASTぞの芖芚的なアクセス、マクロの個別のコンパむルずテスト実行、「ステップごずのコンパむル」ちょうどそのように-ステップバむステップモヌドでメむンコヌドをコンパむルするずきのマクロの動䜜を特別なデバッガヌで確認するためのツヌルの完党なセットを持っおいる必芁がありたす



たあ、それがおそらくすべおです。 倚くの疑問が舞台裏に残っおいたしたが、メタプログラミングの信じられないほどの力を評䟡するにはこれで十分だず思いたす。 今、これはすべおC ++になっおいるず想像しおください。 Boostラむブラリを芋おください。既存のテンプレヌトやレキシカルマクロに察しおも人々が行う驚くべき信じられないほどのこずを...



もしそうだずすれば...私たちの前に本圓にハッカヌの機䌚が開かれるでしょう



All Articles