質問
実行時にクラスにフィールド(以前は存在しなかったフィールド)を追加する方法はありますか?このスニペットのようなもの:
Myobject *ob; // create an object
ob->addField("newField",44); // we add the field to the class and we assign an initial value to it
printf("%d",ob->newField); // now we can access that field
どのように行われるかはあまり気にしません。itいハックかどうかは気にしません。できるかどうか、そして可能であれば小さな例を知りたいです。
別の例:このクラスを説明するXMLファイルがあるとします:
<class name="MyClass">
<member name="field1" />
<member name="field2" />
</class>
「追加」したいフィールド&quot; field1&quot;および「field2」クラスへ(クラスが既に存在すると仮定)。これがクラスのコードだとしましょう:
class MyClass {
};
実行時にクラスを作成するのではなく、既存のメンバー/フィールドに追加するだけです。
ありがとう!
解決
マップとバリアントを使用します。
たとえば、boost :: variantを使用します。 http://www.boost.org/doc/libs/をご覧ください。 1_36_0 / doc / html / variant.html
(もちろん、XML属性のタイプに合わせて、独自に作成することもできます。)
#include <map>
#include <boost/variant.hpp>
typedef boost::variant< int, std::string > MyValue ;
typedef std::map<std::string, MyValue> MyValueMap ;
クラスのメンバーとしてMyValueMapを追加することにより、名前に従ってプロパティを追加できます。コードを意味します:
oMyValueMap.insert(std::make_pair("newField", 44)) ;
oMyValueMap.insert(std::make_pair("newField2", "Hello World")) ;
std::cout << oMyValueMap["newField"] ;
std::cout << oMyValueMap["newField2"] ;
これをMyObjectクラスにカプセル化し、このMyObjectクラスに適切なオーバーロードアクセサーを追加すると、上記のコードが多少明確になります。
oMyObject.addField("newField", 44) ;
oMyObject.addField("newField2", "Hello World") ;
std::cout << oMyObject["newField"] ;
std::cout << oMyObject["newField2"] ;
しかし、そうするとC ++の型安全性がいくらか失われます。しかし、XMLの場合、これは避けられないでしょう。
他のヒント
コンパイラはコンパイル時に参照を解決する必要があるため、これまで説明した方法では実行できません。エラーが生成されます。
ただし、ユニバーサルデザインパターンを参照してください。
(コンパイル時の静的チェックのため)その構文を機能させることはできませんが、構文を変更する場合は、同じ効果を簡単に実現できます。文字列-&gt; blobマッピングを持つディクショナリメンバーを作成し、次のようなメンバー関数を作成するのは非常に簡単です。
template< typename T > T get_member( string name );
template< typename T > void set_member( string name, T value );
必要に応じて、構文をよりコンパクト/トリッキーにすることができます(例: '-&gt;'演算子オーバーライドを使用)。また、活用できるコンパイラ固有のトリックもいくつかあります(たとえば、MSVCは__declspec(property)をサポートしています。これにより、メンバー変数への参照を特定の形式のメソッドにマップできます)。ただし、結局のところ、コンパイラーが言語で受け入れない何かを実行してコンパイルすることはできません。
ショートバージョン:できません。これに対するネイティブサポートはありません。c++は静的に型指定されており、コンパイラは操作される各オブジェクトの構造を知っている必要があります。
推奨事項:組み込みインタープリターを使用します。また、独自に作成するのではなく(以下を参照)、既に動作してデバッグされているものを取得してください。
できること:必要に応じて十分な数のインターパーターを実装します。
次のようなデータメンバーを使用してクラスを設定するのは十分に簡単です
std::vector<void*> extra_data;
実行時に任意のデータを添付できる。これのコストは、次のような方法でそのデータを手動で管理する必要があることです:
size_t add_data_link(void *p); // points to existing data, returns index
size_t add_data_copy(void *p, size_t s) // copies data (dispose at
// destruction time!), returns
// index
void* get_data(size_t i); //...
ただし、これは制限ではありません。もう少し注意を払って、任意のデータを名前に関連付けることができます。また、希望する限りこのスキームを詳しく説明できます(型情報の追加など)。これは、実行時の柔軟性に対処するためにインターパーターを実装することです。
いいえ-C ++は、このような型システムの操作をサポートしません。ある程度のランタイムリフレクションを備えた言語(.NETなど)でさえ、このパラダイムを正確にサポートしません。それを行うには、はるかに動的な言語が必要になります。
私はこれを見ていましたが、少し検索を行いました。このコードスニペットは、 Michael Hammerのブログ これを行うには、 boost ::
最初に、キー(変数名)と値を含むstd :: mapを定義する構造を定義します。関数は、ペアを追加し、値を取得する関数とともに設定するように定義されています。あなたが私に尋ねるとかなり簡単ですが、より複雑なことをする前に始めるのに良い方法のようです。
struct AnyMap {
void addAnyPair( const std::string& key , boost::any& value );
template<typename T>
T& get( const std::string key ) {
return( boost::any_cast<T&>(map_[key]) );
}
std::map<const std::string, boost::any> map_;
};
void AnyMap::addAnyPair( const std::string& key , boost::any& value ) {
map_.insert( std::make_pair( key, value ) );
}
一番下の行、これはハックです。C++は厳密な型チェック言語であり、したがって、ルールを曲げる人のためにモンスターが存在するからです。