回答:
C ++でのリフレクションにはいくつかの問題があります。
追加するのは大変な作業であり、C ++委員会はかなり保守的であり、成果が出ると確信しない限り、根本的な新機能に時間を費やさないでください。(.NETアセンブリに似たモジュールシステムを追加する提案があり、それがいいと思うという一般的な合意はあると思いますが、現時点では最優先事項ではないため、かなり後になるまで押し戻されました。 C ++ 0x。この機能の動機は#include
システムを取り除くことですが、少なくとも一部のメタデータも有効にします)。
使用しないものについては支払いません。これは、C ++の基礎となる必須の基本設計哲学の1つです。メタデータが必要ない場合でも、コードでメタデータを保持する必要があるのはなぜですか?さらに、メタデータを追加すると、コンパイラーが最適化できなくなる可能性があります。メタデータがまったく必要ない可能性があるのに、コードにそのコストを支払う必要があるのはなぜですか?
これはもう一つの大きなポイントに私たちをリード:C ++は作る非常にコンパイルされたコードについてのいくつかの保証を。コンパイラーは、結果として得られる機能が期待どおりのものである限り、好きなことをほとんど実行できます。たとえば、クラスが実際に存在する必要はありません
。単純なテンプレートコードでもかなりの数のテンプレートのインスタンス化を作成する傾向があるため、コンパイラーはそれらを最適化して削除し、実行するすべてのことをインライン化できます。C ++標準ライブラリは、この積極的な最適化に依存しています。ファンクタは、オブジェクトのインスタンス化と破棄のオーバーヘッドを最適化できる場合にのみ機能します。
operator[]
演算子全体をインライン化して、コンパイルされたコードから完全に削除できるため、ベクトルでのパフォーマンスは、生の配列のインデックス作成に匹敵します。C#とJavaは、コンパイラーの出力に関して多くの保証をします。C#でクラスを定義すると、そのクラスは結果のアセンブリに存在します。私がそれを使ったことがないとしても。そのメンバー関数へのすべての呼び出しがインライン化されたとしても。クラスはそこにある必要があるので、リフレクションがそれを見つけることができます。これの一部は、バイトコードにコンパイルするC#によって緩和されます。つまり、JITコンパイラは初期のC#コンパイラができなくても、必要に応じてクラス定義とインライン関数を削除します。C ++では、コンパイラは1つだけで、効率的なコードを出力する必要があります。C ++実行可能ファイルのメタデータを検査することが許可されている場合、定義されたすべてのクラスが表示されるはずです。つまり、コンパイラーは、必要でない場合でも、定義されたすべてのクラスを保持する必要があります。
そして、テンプレートがあります。C ++のテンプレートは、他の言語のジェネリックのようなものではありません。テンプレートのインスタンス化ごとに新しいタイプが作成されます
。std::vector<int>
はとは完全に別のクラスです
std::vector<float>
。これは、プログラム全体でさまざまなタイプの合計になります。私たちの反射は何を見るべきですか?テンプレート std::vector
?しかし、それは実行時に何の意味もないソースコード構造なので、どうすればよいのでしょうか。個別のクラスstd::vector<int>
とを確認する必要が
あり
std::vector<float>
ます。そして
std::vector<int>::iterator
、
std::vector<float>::iterator
と同じconst_iterator
等々。また、テンプレートのメタプログラミングに入ると、何百ものテンプレートがインスタンス化され、そのすべてがコンパイラーによってインライン化および削除されます。コンパイル時のメタプログラムの一部として以外は、意味がありません。これらの何百ものクラスすべてがリフレクションから見えるはずですか?そうでなければ、私が定義したクラスが実際にそこにあることさえ保証されなければ、リフレクションは役に立たないためです。そして、副次的な問題は、インスタンス化されるまでテンプレートクラスが存在しないことです。を使用するプログラムを想像してみてくださいstd::vector<int>
。私たちの反射システムは見ることができますstd::vector<int>::iterator
か?一方で、あなたは確かにそう期待するでしょう。これは重要なクラスであり、で定義されstd::vector<int>
ています。メタデータに存在します。一方、プログラムがこのイテレータクラステンプレートを実際に使用しない場合、その型はインスタンス化されないため、コンパイラは最初からクラスを生成していません。また、ソースコードにアクセスする必要があるため、実行時に作成するには遅すぎます。
boost::type_traits
簡単な例です。タイプについて知りたい
T
ですか?を確認してくださいtype_traits
。C#では、リフレクションを使用してタイプの後に釣り回る必要があります。リフレクションは依然としていくつかの点で有用です(私が見ることができる主な用途はメタプログラミングでは簡単に置き換えることができず、自動生成されたシリアル化コード用です)が、C ++にはかなりのコストがかかり、それほど頻繁には必要ありません。他の言語です。編集: コメントへの応答:
cdleary:はい、デバッグシンボルは実行可能ファイルで使用される型に関するメタデータを格納するという点で、同様のことを行います。しかし、彼らは私が説明した問題にも苦しんでいます。これまでにリリースビルドのデバッグを試みたことがあれば、私が何を意味しているのかわかるでしょう。ソースコードでクラスを作成したところ、最終的なコードでインライン化された大きな論理的なギャップがあります。役立つものにリフレクションを使用する場合は、より信頼性が高く一貫性のあるものにする必要があります。現状では、コンパイルするたびに型は消滅し、消えてしまいます。小さな細部を変更すると、コンパイラーは、応答として、インライン化されるタイプとインライン化されないタイプを変更することを決定します。あなたがしているときに、そこからどのように有用なものを抽出しますか 最も関連性の高いタイプがメタデータで表されることが保証されていませんか?あなたが探していたタイプは最後のビルドにあったかもしれませんが、今ではなくなっています。そして明日、誰かが小さな無害な関数への小さな無害な変更をチェックインします。これにより、型は十分に大きくなり、完全にインライン化されないため、再び元に戻ります。これはまだデバッグシンボルに役立ちますが、それ以上のものではありません。これらの条件の下でクラスのシリアル化コードを生成しようとするのは嫌です。それ以上ではありません。これらの条件の下でクラスのシリアル化コードを生成しようとするのは嫌です。それ以上ではありません。これらの条件の下でクラスのシリアル化コードを生成しようとするのは嫌です。
エヴァン・テラン:もちろん、これらの問題は解決される可能性があります。しかし、それは私のポイント1に戻ります。それは多くの作業を必要とし、C ++委員会は彼らがより重要だと感じている多くのことを持っています。他の機能を犠牲にしてそれに集中することを正当化するのに十分な大きさのC ++のリフレクション(およびリミテッド)を得るメリットは本当に大きいのでしょうか?QTのようなライブラリやプリプロセッサを介して既に(ほとんど)実行できるコア言語を機能に追加することには、本当に大きなメリットがありますか?おそらく、そのようなライブラリが存在しなかった場合よりも、必要性はそれほど緊急ではありません。ただし、具体的な提案については、テンプレートで禁止すると完全に役に立たなくなると思います。たとえば、標準ライブラリではリフレクションを使用できません。どのような反射がありませんかstd::vector
?テンプレートはC ++の重要な部分です。テンプレートでは機能しない機能は基本的に役に立ちません。
しかし、そうです、何らかの形のリフレクションを実装できます。しかし、それは言語の大きな変化になるでしょう。現在のように、型はコンパイル時の構成要素です。それらはコンパイラーの利益のために存在し、それ以外にはありません。コードがコンパイルされると、クラスはなくなります。自分を伸ばすと、関数はまだ存在していると主張することができますが、実際には、たくさんのジャンプアセンブラ命令と多くのスタックプッシュ/ポップがあります。そのようなメタデータを追加する場合、何もする必要はありません。
しかし、私が言ったように、コンパイルモデルの変更、自己完結型モジュールの追加、選択タイプのメタデータの保存、#include
s をいじる必要なく他のモジュールがそれらを参照できるようにする提案があります。これは良い出発点であり、正直に言うと、標準委員会があまりにも大きな変更であるという提案を捨てなかったのには驚いています。それでおそらく5-10年で?:)
export
とvector<bool>
。
typeinfo
のname()
機能が未定義のプログラマーではなく、何かによってで入力した名前を返さなければなりません。また、列挙子の文字列指定子も指定してください。これは、工場などを作る際に支援し、シリアライズ/デシリアライゼーションのため、実際に非常に重要である
リフレクションでは、タイプに関するいくつかのメタデータをクエリ可能な場所に保存する必要があります。C ++はネイティブマシンコードにコンパイルされ、最適化のために大幅な変更が加えられるため、アプリケーションの高レベルのビューはコンパイルのプロセスでかなり失われ、その結果、実行時にそれらをクエリすることはできません。Javaおよび.NETは、仮想マシンのバイナリコードで非常に高レベルの表現を使用して、このレベルのリフレクションを可能にします。ただし、一部のC ++実装では、実行時タイプ情報(RTTI)と呼ばれるものがあります。これは、リフレクションのストリップバージョンと見なすことができます。
すべての言語が他のすべての言語のすべての機能を組み込むことを試みるべきではありません。
C ++は基本的に、非常に高度なマクロアセンブラです。これは(伝統的な意味で)C#、Java、Objective-C、Smalltalkなどの高級言語ではありません。
仕事ごとに異なるツールを用意するとよいでしょう。ハンマーしかない場合は、すべてが釘などのように見えます。スクリプト言語を使用することは、一部のジョブでは役立ち、反射型OO言語(Java、Obj-C、C#)は、別のクラスのジョブで役立ちます。マシンに近い効率的なベアボーンは、さらに別のクラスのジョブ(C ++、C、アセンブラー)に役立ちます。
C ++は、アセンブラーテクノロジーを信じられないほどのレベルの複雑さ管理に拡張するという驚くべき仕事と、人間にとってプログラミングをより大きく、より複雑なタスクをはるかに可能にする抽象化を行います。しかし、厳密に高レベルの観点から問題に取り組んでいる人(Lisp、Smalltalk、Java、C#)に最適な言語であるとは限りません。問題の解決策を最適に実装するためにこれらの機能を備えた言語が必要な場合は、そのような言語を作成してくれたすべての人に感謝します。
しかし、C ++は、何らかの理由で、コードと基盤となるマシンの操作との間に強い相関関係が必要な人向けです。その効率、デバイスドライバのプログラミング、低レベルのOSサービスとのやり取りなど、C ++はこれらのタスクにより適しています。
C#、Java、Objective-Cはすべて、その実行をサポートするために、はるかに大規模で豊富なランタイムシステムを必要とします。そのランタイムは、問題のシステムに配信する必要があります-ソフトウェアの操作をサポートするためにプレインストールされています。そして、その層は、そのプラットフォームで動作するように他の言語によってカスタマイズされた、さまざまなターゲットシステム用に維持する必要があります。そして、その中間層-ホストOSとコードの間のアダプティブレイヤー-ランタイムは、ほとんどの場合、効率が#1であるCまたはC ++のような言語で記述され、ソフトウェアとハードウェア間の正確な相互作用を予測可能に理解することができます。理解し、最大のゲインに操作されました。
私はSmalltalk、Objective-C、およびリフレクション、メタデータ、ガベージコレクションなどを備えた豊富なランタイムシステムが大好きです。これらの機能を利用するためのすばらしいコードを書くことができます。しかし、それは単にスタックの上位層であり、下位層に置かれる必要のある層であり、それ自体が最終的にOSとハードウェア上に置かれる必要があります。そして、その層の構築に最適な言語が常に必要です。C++ / C / Assembler。
補遺:C ++ 11/14は、C ++の機能を拡張して、より高いレベルの抽象化とシステムをサポートしています。スレッド化、同期、正確なメモリモデル、より正確な抽象的なマシン定義により、C ++開発者は、これらの高レベルのみの言語の一部が排他的なドメインを持つために使用していた多くの高レベルの抽象化を実現しながら、金属のパフォーマンスと優れた予測可能性(つまり、最小限のランタイムサブシステム)。おそらく、C ++の将来のリビジョンでは、リフレクション機能が選択的に有効になる可能性があります。あるいは、ライブラリがそのようなランタイムサービスを提供する可能性があります(たぶん、現在あるか、最初の1つが後押しですか?)。
C ++に関する設計上の決定を本当に理解したい場合は、EllisとStroustrupによるThe Annotated C ++ Reference Manualのコピーを見つけてください。それは最新の標準では最新ではありませんが、元の標準を通過し、物事がどのように機能するか、頻繁に、どのようにしてそのようになったかを説明します。
それを持っている言語のリフレクションは、リフレクションを有効にするためにコンパイラーがオブジェクトコードに残しておくソースコードの量と、そのリフレクトされた情報を解釈するために利用できる分析機構についてです。コンパイラがすべてのソースコードを保持しない限り、リフレクションはソースコードに関する利用可能な事実を分析する能力に制限されます。
C ++コンパイラーは(RTTIを無視して)何も保持しないため、言語に反映されません。(JavaおよびC#コンパイラは、クラス、メソッド名、および戻り値の型を保持するだけなので、リフレクションデータを少し取得しますが、式やプログラム構造を検査することはできません。つまり、これらの「リフレクション対応」言語でも、あなたが得ることができる情報はかなりまばらであり、その結果、あなたは本当に多くの分析を行うことができません。
しかし、言語の外に出て、完全なリフレクション機能を利用できます。Cでのリフレクションに関する別のスタックオーバーフローの議論への答えは、これについて議論します。
言語によってデフォルトで設定されるべきではない重いコスト(メモリと速度)があるため、ネイティブのC ++機能ではありません。言語は「デフォルトで最大のパフォーマンス」を指向しています。
不要なものにお金を払うべきではなく、あなたが言うには他のアプリケーションよりもエディターで必要とされるため、すべてのコードに「強制」するのではなく、必要な場所にのみ実装する必要があります(エディターや他の同様のアプリケーションで使用するすべてのデータを反映する必要はありません)。
C ++がリフレクションを持たない理由は、クラス型が持つメンバー、メンバーに関する情報、関数に関するすべてなど、シンボル情報をオブジェクトファイルに追加する必要があるためです。これにより、宣言によって送信された情報がそれらのオブジェクトファイル(モジュール)から読み取られるため、インクルードファイルは本質的に役に立たなくなります。C ++では、型定義は、それぞれのヘッダーを含めることでプログラム内で複数回発生する可能性があります(これらの定義がすべて同じである場合)。そのため、その型に関する情報をどこに置くかを決める必要があります。ここで合併症。数十のクラステンプレートのインスタンス化を最適化できるC ++コンパイラーによる積極的な最適化も、もう1つの強みです。可能ですが、C ++はCと互換性があるため、
Alistair Cockburn氏によると、反射型の環境ではサブタイピングは保証されません。
リフレクションは潜在的なタイピングシステムにより関連します。C ++では、自分が持っている型と、それを使って何ができるかがわかります。
リフレクションは、プリプロセッサディレクティブのようにオプションにすることができます。何かのようなもの
#pragma enable reflection
そうすれば、このプラグマライブラリがリフレクションなしで作成され(オーバーヘッドは前述のようになりません)、両方の利点を活用できるため、スピードや使いやすさを求める個々の開発者の責任になります。
C ++が持つことができる場合:
const
修飾子のクラスメンバーデータconst
修飾子のクラスメンバーデータ今日のWebおよびデータベースアプリケーション(すべてのorms、メッセージングメカニズム、xml / jsonパーサー、データのシリアル化など)で普及しているタイプレスデータ処理の核心で非常に使いやすいライブラリを作成するには、これで十分です。
たとえば、Q_PROPERTY
マクロ(Qtフレームワークの一部)で
サポートされている基本情報http://qt.nokia.com/doc/4.5/properties.htmlは、クラスメソッドとe)をカバーするように拡張されています-C ++とソフトウェアコミュニティ全般。
確かに、私が言及しているリフレクションは、意味の意味や、より複雑な問題(コメントのソースコードの行番号、データフロー分析など)をカバーしていませんが、言語標準の一部である必要はないと思います。
C ++での反映、C ++をデータベースアクセス、Webセッション処理/ http、およびGUI開発の言語として使用する場合、私は非常に重要であると考えています。リフレクションの欠如により、ORM(HibernateやLINQなど)、クラスをインスタンス化するXMLパーサー、JSONパーサー、データのシリアル化、およびその他の多くの問題(最初は型のないデータを使用してクラスのインスタンスを作成する必要があります)が妨げられます。
ビルドプロセス中にソフトウェア開発者が利用できるコンパイル時スイッチを使用して、この「使用した分に対して支払う」という懸念を取り除くことができます。
私は、ファームウェア開発者がシリアルポートからデータを読み取るためにリフレクションを必要としません-その場合、スイッチを使用しないでください。しかし、C ++を使い続けたいデータベース開発者として、私はデータメンバーとデータベース構造の間でデータをマップするコードを維持するのが非常に困難で困難な状態に常に移行しています。
Boostシリアライゼーションも他のメカニズムも反射を実際に解決していません-それはコンパイラーによって行われる必要があります-そしてそれが完了すると、C ++は再び学校で学習され、データ処理を扱うソフトウェアで使用されます
私にはこの問題#1(そしてネイティブスレッドプリミティブは問題#2)です。
基本的には「追加オプション」だからです。多くの人々は、JavaやC#などの言語よりもC ++を選択して、コンパイラの出力をより細かく制御できるようにしています。
リフレクションを追加することを選択した場合、さまざまなソリューションが利用可能です。