ANSI C で Objective-C の @encode() コンパイラ ディレクティブに似たものを実装するにはどうすればよいでしょうか?
-
20-09-2019 - |
質問
@encode ディレクティブは、渡されたデータ型のさまざまな要素のコード化された型記述子である const char * を返します。例は次のとおりです。
struct test
{ int ti ;
char tc ;
} ;
printf( "%s", @encode(struct test) ) ;
// returns "{test=ic}"
sizeof() を使用してプリミティブ型を決定することができました。また、それが完全なオブジェクトであれば、クラス メソッドを使用してイントロスペクションを行うことができました。
しかし、不透明な構造体の各要素はどのように決定されるのでしょうか?
解決
@Lotharsの答えは「冷笑的」かもしれませんが、残念ながらかなり的を得ています。次のようなものを実装するには @encode()
, 、型情報を抽出するには本格的なパーサーが必要です。まあ、少なくとも「些細なこと」以外のことに関しては @encode()
ステートメント (すなわち、 @encode(char *)
)。最新のコンパイラには通常、2 つまたは 3 つの主要コンポーネントがあります。
- フロントエンド。
- 中間端 (一部のコンパイラの場合)。
- バックエンド。
フロントエンドはすべてのソース コードを解析する必要があり、基本的にソース コード テキストを内部の「マシンで使用可能な」形式に変換します。
バックエンドは、内部の「マシンで使用可能な」形式を実行可能コードに変換します。
「中間エンド」を持つコンパイラーは、通常、次のような必要があるためにそれを行います。それらは、おそらく完全に異なる言語で構成される複数の「フロントエンド」をサポートします。もう 1 つの理由は、最適化を簡素化するためです。すべての最適化パスは同じ中間表現に対して機能します。の gcc
コンパイラ スイートは、「3 ステージ」コンパイラの例です。 llvm
「中間およびバックエンド」段階のコンパイラと考えることができます。「低レベル仮想マシン」は中間表現であり、すべての最適化はこの形式で行われます。 llvm
また、最後の 1 秒までこの中間表現に保持することもできます。これにより、「リンク時間の最適化」が可能になります。の clang
コンパイラは実際には、(事実上) 出力を行う「フロントエンド」です。 llvm
中間表現。
それで、追加したい場合は、 @encode()
「既存の」コンパイラに機能を追加するには、おそらく「ソース間」の「コンパイラ/プリプロセッサ」として実行する必要があります。これが、元の Objective-C および C++ コンパイラの作成方法でした。入力ソース テキストを解析して、それを「プレーン C」に変換し、標準 C コンパイラに送り込みました。これを行うにはいくつかの方法があります。
自分で巻いてください
- 使用
yacc
そしてlex
ANSI-C パーサーを組み立てます。文法が必要になります- ANSI C 文法 (Yacc) 良いスタートです。実は、はっきり言っておきますと、yacc
, 、本当に言いたいのは バイソン そしてflex
. 。また、大まかに言うと、その他のさまざまなyacc
そしてlex
C ベースのツールと同様: レモン, dパーサー, 、など... - 使用
perl
と ヤップ または EYapp, 、これは擬似的なものです。yacc
クローンのperl
. 。おそらく、C ベースと比較して、アイデアのプロトタイプを迅速に作成するのに適しています。yacc
そしてlex
- そのperl
結局:正規表現、連想配列、メモリ管理なしなど。 - パーサーを構築する アントラー. 。私はこのツールチェーンを使用した経験がありませんが、これも Java 開発者向けに作られた (ように見える) 別の「コンパイラ コンパイラ」ツールです。無料で利用できる C および Objective-C 文法があるようです。
別のツールをハックする
注記: 私はこれらのツールを使用して追加などのことを行った個人的な経験はありません。 @encode()
, 、しかし、それらは大きな助けになると思います。
- CIL - このツールの個人的な使用経験はありませんが、C ソース コードを解析し、それを使って「何かを行う」ために設計されています。ドキュメントから収集した情報によると、このツールを使用すると、必要な型情報を抽出できるはずです。
- まばらな - 一見の価値はありますが、わかりません。
- カラン - この目的で使用したことはありませんが、伝えられるところによると、目的の 1 つは、この種の目的で「簡単にハッキングできる」ようにすることでした。特に (繰り返しになりますが、個人的な経験はありませんが) すべての解析の「重労働」を行うことで、「興味深い」部分に集中できます。この場合、コンテキストと構文に依存する型情報を抽出し、それを変換することになります。プレーンな C 文字列に変換します。
- gcc プラグイン - プラグインは gcc 4.5 (コンパイラの現在のアルファ/ベータ バージョン) の機能であり、コンパイラに簡単に接続して必要な型情報を抽出できるようにする「可能性があります」。プラグインのアーキテクチャがこの種のことを可能にするかどうかはわかりません。
その他
- コクシネレ - 「後で見る」ために最近これをブックマークしました。これは、あなたが望むことを実現できる「かもしれない」し、それほど努力せずにそれを実現できる「かもしれない」。
- メタC - 最近これもブックマークしました。これがどれほど役立つかわかりません。
- mygcc - あなたが望むことを「できるかもしれません」。興味深いアイデアですが、必要なものに直接適用できるわけではありません。Web ページから:「Mygcc を使用すると、プログラマは構文、制御フロー、データ フロー情報を考慮した独自のチェックを追加できます。」
リンク。
- CocoaDev Objective-C の解析 - 一見の価値があります。レクサーと文法へのリンクがいくつかあります。
編集 #1、ボーナスリンク。
@Lothar はコメントで良い点を指摘しています。本当は入れるつもりだったんだけど lcc
, しかし途中で道に迷ってしまったようです。
- LCC -
lcc
Cコンパイラ。これは、少なくともソース コード サイズの点で特に小さい C コンパイラです。また 本を持っています, 、強くお勧めします。 - tcc -
tcc
Cコンパイラ。それほど教育的ではありませんlcc
, 、しかし間違いなくまだ見る価値があります。 - ポク -
poc
Objective-C コンパイラ。これは「ソースからソースへ」の Objective-C コンパイラです。Objective-C ソース コードを解析し、C ソース コードを出力し、それを次の関数に渡します。gcc
(まあ、普通はgcc
)。Objective-C では利用できない多数の Objective-C 拡張機能/機能が備わっています。gcc
. 。間違いなく一見の価値があります。
他のヒント
あなたは最初のANSI Cコンパイラを実装することで、これを実装し、それにいくつかの実装固有のプラグマや機能を追加することになります。
はい、私は、これは皮肉な答えは知っていると私はdownvotesを受け入れます。
これを行う方法の1つは、リテラル対応する文字列で...型定義のためのソースコードを読み取り、また@encode
を置き換えプリプロセッサを書くことになります。
-g
でコンパイルされている場合、別のアプローチは、実行時にプログラムのデバッグ情報から型定義を読み込む関数を記述、またはあなたのためにそれを読むためにgdb
または別のプログラムを使用し、それを再フォーマットすることです望んだ通りに。 gdb
のptype
コマンドは、特定のタイプの定義を印刷するために使用される(またはそれが不十分な場合にも、あなたはおそらくしたいことよりも、はるかに多くの情報を印刷することを確認されmaint print type
、そこにある)ことができます。
、それがかもしれませんまた、このためのコンパイラプラグインを書くことが可能であること。あなたのプラグインは、コンパイラが既に解析された型情報を利用することができます。明らかに、このアプローチは非常にコンパイラ固有のだろう。