C ++クラスの名前、内容(つまり、メンバーとそのタイプ)などをイントロスペクトできるようにしたいと思います。ここでは、リフレクションのあるマネージC ++ではなく、ネイティブC ++について話しています。C ++がRTTIを使用していくつかの限られた情報を提供することを理解しています。この情報を提供できる追加のライブラリ(または他の手法)はどれですか?
C ++クラスの名前、内容(つまり、メンバーとそのタイプ)などをイントロスペクトできるようにしたいと思います。ここでは、リフレクションのあるマネージC ++ではなく、ネイティブC ++について話しています。C ++がRTTIを使用していくつかの限られた情報を提供することを理解しています。この情報を提供できる追加のライブラリ(または他の手法)はどれですか?
回答:
プリプロセッサにフィールドに関するリフレクションデータを生成させる必要があります。このデータは、ネストされたクラスとして保存できます。
まず、プリプロセッサで簡単かつ簡潔に記述できるように、型付き式を使用します。型付き式とは、型を括弧で囲んだ式のことです。したがって、書く代わりにint x
あなたは書くでしょう(int) x
。次に、型付き式に役立つ便利なマクロをいくつか示します。
#define REM(...) __VA_ARGS__
#define EAT(...)
// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x
次に、REFLECTABLE
各フィールド(およびフィールド自体)に関するデータを生成するマクロを定義します。このマクロは次のように呼び出されます:
REFLECTABLE
(
(const char *) name,
(int) age
)
Boost.PPを使用して、各引数を反復処理し、次のようなデータを生成します。
// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
typedef T type;
};
template<class M, class T>
struct make_const<const M, T>
{
typedef typename boost::add_const<T>::type type;
};
#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
Self & self; \
field_data(Self & self) : self(self) {} \
\
typename make_const<Self, TYPEOF(x)>::type & get() \
{ \
return self.STRIP(x); \
}\
typename boost::add_const<TYPEOF(x)>::type & get() const \
{ \
return self.STRIP(x); \
}\
const char * name() const \
{\
return BOOST_PP_STRINGIZE(STRIP(x)); \
} \
}; \
これが行うfields_n
ことは、クラス内の反射可能なフィールドの数である定数を生成することです。次にfield_data
、各フィールドに特化します。また、reflector
クラスを友だちにします。これにより、フィールドがプライベートの場合でもフィールドにアクセスできます。
struct reflector
{
//Get field_data at index N
template<int N, class T>
static typename T::template field_data<N, T> get_field_data(T& x)
{
return typename T::template field_data<N, T>(x);
}
// Get the number of fields
template<class T>
struct fields
{
static const int n = T::fields_n;
};
};
次に、フィールドを反復処理するために、訪問者パターンを使用します。0からフィールド数までのMPL範囲を作成し、そのインデックスでフィールドデータにアクセスします。次に、フィールドデータをユーザー指定の訪問者に渡します。
struct field_visitor
{
template<class C, class Visitor, class I>
void operator()(C& c, Visitor v, I)
{
v(reflector::get_field_data<I::value>(c));
}
};
template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}
真実の瞬間のために、私たちはそれをすべてまとめました。これが反映可能なPerson
クラスを定義する方法です:
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
次にprint_fields
、リフレクションデータを使用してフィールドを反復処理する一般化された関数を示します。
struct print_visitor
{
template<class FieldData>
void operator()(FieldData f)
{
std::cout << f.name() << "=" << f.get() << std::endl;
}
};
template<class T>
void print_fields(T & x)
{
visit_each(x, print_visitor());
}
print_fields
リフレクタブルPerson
クラスでを使用する例:
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
どの出力:
name=Tom
age=82
ちなみに、C ++で100行未満のコードでリフレクションを実装したところです。
#define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tuple
と #define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__))
:とにTYPEOF(X)の定義を変更#define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,)
二種類のreflection
泳ぎがあります。
template-tricks
です。boost::type_traits
多くのことに使用します(型が整数であるかどうかのチェックなど)。メンバー関数の存在を確認するには、関数の存在を確認するテンプレートを作成することはできますか?。特定のネストされたタイプが存在するかどうかを確認するには、プレーンなSFINAEを使用します。 1)クラスのメソッド数やクラスIDの文字列表現の取得など、達成する方法を探している場合、標準のC ++でこれを行う方法はありません。あなたはどちらかを使わなければなりません
C ++は速度を考慮して作成されています。C#やJavaのような高レベルの検査が必要な場合は、少し手間をかけないと方法がないと言わざるを得ません。
members<T>
Tのすべてのメンバーのリストを返すコンパイル時関数。実行時リフレクション(つまり、RTTIとリフレクションを組み合わせたもの)が必要な場合でも、コンパイラーはすべての反映された基本型を知っています。members<T>(T&)
T = std :: stringではインスタンス化されない可能性が高いため、std :: stringまたはその派生クラスのRTTIを含める必要はありません。
そして、私はポニーが大好きですが、ポニーは自由ではありません。:-p
http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTIが取得するものです。あなたが考えているような反射-実行時に利用できる完全に記述的なメタデータ-は、デフォルトではC ++には存在しません。
情報は存在しますが、必要な形式ではなく、クラスをエクスポートする場合のみです。これはWindowsで動作しますが、他のプラットフォームについては知りません。ストレージクラス指定子を次のように使用します。
class __declspec(export) MyClass
{
public:
void Foo(float x);
}
これにより、コンパイラーはクラス定義データをDLL / Exeにビルドします。ただし、リフレクションにすぐに使用できる形式ではありません。
私の会社では、このメタデータを解釈し、追加のマクロなどをクラス自体に挿入せずにクラスを反映できるライブラリを構築しました。次のように関数を呼び出すことができます。
MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);
これは効果的に行います:
instance_ptr->Foo(1.331);
Invoke(this_pointer、...)関数には可変引数があります。明らかに、この方法で関数を呼び出すことにより、const-safetyなどのようなものを回避しているため、これらの側面はランタイムチェックとして実装されます。
構文を改善できると確信しています。これは、これまでのところWin32とWin64でのみ機能します。クラスへの自動GUIインターフェイス、C ++でのプロパティの作成、XMLとのストリーミングなどに非常に役立ち、特定の基本クラスから派生する必要はありません。十分な需要があれば、リリースのためにそれを形にすることができるでしょう。
__declspec(dllexport)
と、ビルド時に、このようなの作成を有効にした場合、あなたは.MAPファイルから情報を取得することができます。
リフレクションは、そのままではC ++でサポートされていません。防御テストが面倒になるので、これは悲しいことです。
リフレクションを行うには、いくつかの方法があります。
最初のリンクは最も有望です(modからclangを使用します)。2番目のリンクはいくつかの手法について説明し、3番目はgccを使用した別のアプローチです。
現在、C ++リフレクションの作業グループがあります。C ++ 14 @ CERNのニュースを参照してください。
2017年8月13日の編集:
最初の投稿以来、反省に関して多くの潜在的な進歩がありました。以下は、さまざまな手法とステータスに関する詳細と説明です。
ただし、コミュニティからC ++でのリフレクションのサポートに多くの関心がない限り、近い将来、C ++での標準化されたリフレクションアプローチで有望とは思われません。
以下は、前回のC ++標準会議からのフィードバックに基づく現在のステータスの詳細です。
2017年12月13日編集
ReflectionはC ++ 20以上、おそらくTSRに向かっているようです。しかし動きは遅いです。
2018年9月15日を編集
TS草案が投票のために国の機関に送られました。
テキストはここにあります:https : //github.com/cplusplus/reflection-ts
編集11/07/2019
リフレクションTSは機能が完全であり、夏の間(2019)にコメントと投票が行われます。
メタテンプレートプログラミングアプローチは、単純なコンパイルタイムコードアプローチ(TSには反映されません)に置き換えられます。
2020年10月2日の編集
Visual StudioでリフレクションTSをサポートする要求がここにあります:
著者David SankelによるTSについての話:
編集2020年3月17日
反省の進展がなされている。「2020-02プラハISO C ++委員会旅行レポート」のレポートは、次の場所にあります。
C ++ 23で検討されていることの詳細については、こちら(Reflectionに関する短いセクションを含む)を参照してください。
2020年6月4日を編集
ランタイムリフレクションのメカニズムを含む「プライウッド」と呼ばれる新しいフレームワークがJeff Preshingによってリリースされました。詳細はここにあります:
ツールとアプローチは、これまでで最も洗練され、最も使いやすいように見えます。
RTTIが要件を満たしているかどうかを確認する必要があります。非常に具体的な目的のために、独自の疑似反射を実装しました。たとえば、シミュレーションが出力する内容を柔軟に構成できるようにしたかったことがあります。出力されるクラスに定型コードを追加する必要がありました。
namespace {
static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
}
bool MyObj::BuildMap()
{
Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
return true;
}
最初の呼び出しは、このオブジェクトをフィルタリングシステムに追加します。フィルタリングシステムは、BuildMap()
メソッドを呼び出して、使用可能なメソッドを把握します。
次に、設定ファイルで次のようなことができます:
FILTER-OUTPUT-OBJECT MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1 person == 1773
FILTER-CLAUSE-2 time > 2000
を含むいくつかのテンプレートマジックによりboost
、これは実行時(構成ファイルが読み取られるとき)に一連のメソッド呼び出しに変換されるため、かなり効率的です。本当に必要な場合を除いて、これを行うことはお勧めしませんが、そうすることで、いくつかの本当にクールなことができます。
私はあなたが一度やりたいことのようなことをしました、そしてある程度の反映を得てより高いレベルの機能にアクセスすることは可能ですが、メンテナンスの頭痛はそれに値しないかもしれません。私のシステムは、メッセージの受け渡しと転送というObjective-Cの概念に類似した委任を通じて、UIクラスをビジネスロジックから完全に分離するために使用されました。これを行う方法は、シンボルをマッピングできるいくつかの基本クラスを作成することです(私は文字列プールを使用しましたが、完全な柔軟性よりも速度とコンパイル時のエラー処理を優先したい場合は、列挙型を使用して実行できます)。純粋な関数ポインタですが、BoostがBoost.Functionで持っているものに似ています(当時はアクセスできませんでした)。任意の値を表すことができるいくつかの共通の基本クラスがある限り、メンバー変数に対して同じことを行うことができます。システム全体は、Key-Valueコーディングと委任のあからさまな模造品であり、システムを使用するすべてのクラスがそのすべてのメソッドとメンバーを合法的な呼び出しに一致させるために必要なまったくの時間に見合うだけの副作用がいくつかありました:1)コンパイラー用にインターフェースを事前定義できるように、ヘッダーを含めたり、偽の基本クラスを作成したりすることなく、どのクラスでも他のクラスのメソッドを呼び出すことができます。2)メンバー変数のゲッターとセッターは、すべてのオブジェクトの基本クラスの2つのメソッドを通じて常に値の変更またはアクセスが行われるため、スレッドセーフにするのは簡単です。システム全体は、Key-Valueコーディングと委任のあからさまな模造品であり、システムを使用するすべてのクラスがそのすべてのメソッドとメンバーを合法的な呼び出しに一致させるために必要なまったくの時間に見合うだけの副作用がいくつかありました:1)コンパイラー用にインターフェースを事前定義できるように、ヘッダーを含めたり、偽の基本クラスを作成したりすることなく、どのクラスでも他のクラスのメソッドを呼び出すことができます。2)メンバー変数のゲッターとセッターは、すべてのオブジェクトの基本クラスの2つのメソッドを通じて常に値の変更またはアクセスが行われるため、スレッドセーフにするのは簡単です。システム全体は、Key-Valueコーディングと委任のあからさまな模造品であり、システムを使用するすべてのクラスがそのすべてのメソッドとメンバーを合法的な呼び出しに一致させるために必要なまったくの時間に見合うだけの副作用がいくつかありました:1)コンパイラー用にインターフェースを事前定義できるように、ヘッダーを含めたり、偽の基本クラスを作成したりすることなく、どのクラスでも他のクラスのメソッドを呼び出すことができます。2)メンバー変数のゲッターとセッターは、すべてのオブジェクトの基本クラスの2つのメソッドを通じて常に値の変更またはアクセスが行われるため、スレッドセーフにするのは簡単です。1)コンパイラー用にインターフェースを事前定義できるように、ヘッダーを含めたり、偽の基本クラスを作成したりすることなく、どのクラスでも他のクラスのメソッドを呼び出すことができます。2)メンバー変数のゲッターとセッターは、すべてのオブジェクトの基本クラスの2つのメソッドを通じて常に値の変更またはアクセスが行われるため、スレッドセーフにするのは簡単です。1)コンパイラー用にインターフェースを事前定義できるように、ヘッダーを含めたり、偽の基本クラスを作成したりすることなく、どのクラスでも他のクラスのメソッドを呼び出すことができます。2)メンバー変数のゲッターとセッターは、すべてのオブジェクトの基本クラスの2つのメソッドを通じて常に値の変更またはアクセスが行われるため、スレッドセーフにするのは簡単です。
また、C ++では簡単ではない、非常に奇妙なことを行う可能性も生まれました。たとえば、自分自身を含む任意のタイプの任意の項目を含むArrayオブジェクトを作成し、すべての配列項目にメッセージを渡して戻り値を収集することで動的に新しい配列を作成できます(Lispのマップと同様)。もう1つは、キー値監視の実装でした。これにより、常にデータをポーリングしたり、不必要に表示を再描画したりするのではなく、バックエンドクラスのメンバーの変更にすぐに応答するようにUIを設定できました。
おそらくあなたにとってもっと興味深いのは、クラスに定義されたすべてのメソッドとメンバーを、文字列形式でもダンプできることです。
煩わしさを和らげるシステムの欠点:すべてのメッセージとKey-Valueを追加するのは非常に面倒です。反射がない場合よりも遅くなります。あなたは見て嫌いに育つだろうboost::static_pointer_cast
とboost::dynamic_pointer_cast
暴力的な情熱を持って、あなたのコードベースの上にすべて。強く型付けされたシステムの制限はまだありますが、実際には少しだけ非表示にしているので、それほど明白ではありません。文字列のタイプミスも、面白くないし、驚きを発見するのも簡単ではありません。
このようなものを実装する方法については、いくつかの共通のベース(私は非常に想像力豊かに "オブジェクト"と呼ばれていました)への共有ポインタと弱いポインタを使用し、使用するすべての型から派生させます。私は、Boost.Functionを、私が行った方法ではなくインストールすることをお勧めします。これは、カスタムのがらくたと、関数ポインターの呼び出しをラップするための醜いマクロのトンでした。すべてがマッピングされているので、オブジェクトの検査は、すべてのキーを反復処理するだけです。私のクラスは本質的にC ++のみを使用して、Cocoaの直接の詐欺にできるだけ近いため、そのようなものが必要な場合は、Cocoaのドキュメントを青写真として使用することをお勧めします。
私のC ++時代から知っている2つの反射のようなソリューションは次のとおりです。
1)すべてのクラスを「オブジェクト」基本クラスから派生させることができる場合、リフレクションのような動作を構築するためのブートストラップを提供するRTTIを使用します。そのクラスは、GetMethod、GetBaseClassなどのメソッドを提供できます。これらのメソッドがどのように機能するかについては、型を装飾するためにいくつかのマクロを手動で追加する必要があります。これにより、背後でGetMethodsなどへの回答を提供するメタデータが型に作成されます。
2)コンパイラー・オブジェクトにアクセスできる場合の別のオプションは、DIA SDKを使用することです。私の記憶が正しければ、C ++型のメタデータを含むpdbを開くことができます。それはあなたが必要とすることをするのに十分かもしれません。このページでは、たとえばクラスのすべての基本型を取得する方法を示します。
これらのソリューションはどちらも少し醜いです!C#の贅沢さを理解してもらうためのC ++のようなものはありません。
幸運を。
編集:2017年2月7日現在、リンク切れを更新しました。
私は誰もこれに言及しなかったと思います:
CERNでは、C ++の完全なリフレクションシステムを使用しています。
CERN Reflex。それは非常にうまく機能するようです。
この質問は少し古いです(今日、なぜ私が古い質問を続けているのかわかりません)が、コンパイル時のリフレクションを導入するBOOST_FUSION_ADAPT_STRUCTについて考えていました。
もちろん、これを実行時のリフレクションにマップするのはあなた次第であり、それほど簡単ではありませんが、逆方向ではありませんが、この方向で可能です。
マクロをカプセル化BOOST_FUSION_ADAPT_STRUCT
して、ランタイムの動作を取得するために必要なメソッドを生成できると本当に思います。
Dominic Filionによる記事「Using Templates for Reflection in C ++」をおもしろいと思うかもしれません。これは、Game Programming Gems 5のセクション1.4にあります。残念ながら私は私のコピーを持っていませんが、あなたが何を求めているのかを説明していると思いますので探してください。
Ponderは、この質問に答えるC ++リフレクションライブラリです。私はすべてのボックスにチェックを入れるオプションが見つからなかったので、オプションを検討して自分で作成することにしました。
この質問には素晴らしい答えがありますが、私は大量のマクロを使用したり、Boostに依存したりしたくありません。Boostは優れたライブラリーですが、よりシンプルでコンパイル時間が短い、特注の小さなC ++ 0xプロジェクトがたくさんあります。C ++ 11をサポートしていない(まだ?)C ++ライブラリをラップするなど、クラスを外部で装飾できることには利点もあります。C ++ 11を使用するCAMPのフォークであり、Boostは必要ありません。
リフレクションとは、基本的には、ランタイムコードがクエリできるコードのフットプリントとしてコンパイラが残すことを決定したものです。C ++は、使用しないものにお金を払わないことで有名です。ほとんどの人はリフレクションを使用しない/望まないため、C ++コンパイラは何も記録しないことでコストを回避します。
したがって、C ++はリフレクションを提供せず、他の回答が指摘しているように、一般的なルールとして自分で「シミュレート」することは容易ではありません。
「その他の手法」では、リフレクションのある言語がない場合は、コンパイル時に必要な情報を抽出できるツールを入手してください。
当社のDMSソフトウェアリエンジニアリングツールキットは、明示的な言語定義によってパラメーター化された汎用コンパイラテクノロジーです。C、C ++、Java、COBOL、PHPなどの言語定義があります。
C、C ++、Java、およびCOBOLバージョンの場合、解析ツリーとシンボルテーブル情報への完全なアクセスを提供します。そのシンボルテーブル情報には、「リフレクション」から取得する可能性が高いデータの種類が含まれています。目的がフィールドまたはメソッドのセットを列挙し、それらを使って何かを行うことである場合、DMSを使用して、シンボルテーブルにあるものに従ってコードを任意の方法で変換できます。
ここで別のライブラリを見つけることができます:http : //www.garret.ru/cppreflection/docs/reflect.html これは2つの方法をサポートします:デバッグ情報から型情報を取得し、プログラマーにこの情報を提供させます。
私は自分のプロジェクトのリフレクションにも興味があり、このライブラリを見つけました。まだ試していませんが、この人の他のツールを試しましたが、それらがどのように機能するかが好きです:-)
Classdesc http://classdesc.sf.netをチェックしてください。これは、クラス「記述子」の形式でリフレクションを提供し、標準のC ++コンパイラーで動作します(はい、Visual StudioとGCCで動作することがわかっています)。 )。10年以上にわたって開発されており、多くの産業規模のプロジェクトで使用されています。
リフレクションはそのままではc ++でサポートされていませんが、実装するのはそれほど難しくありません。私はこの素晴らしい記事に遭遇しました:http : //replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html
この記事では、非常にシンプルで基本的なリフレクションシステムを実装する方法について詳しく説明しています。これは最も健全な解決策ではなく、整理する必要のあるラフなエッジが残っていますが、私のニーズにはそれで十分でした。
肝心なこと-リフレクションが正しく行われれば報われることがあり、C ++では完全に実現可能です。
比較的単純なC ++リフレクションを探している場合-私はさまざまなソースからマクロ/定義を収集し、それらがどのように機能するかコメントアウトしました。ここからヘッダーファイルをダウンロードできます。
https://github.com/tapika/TestCppReflect/blob/master/MacroHelpers.h
一連の定義とそれに加えた機能:
https://github.com/tapika/TestCppReflect/blob/master/CppReflect.h https://github.com/tapika/TestCppReflect/blob/master/CppReflect.cpp https://github.com/tapika/TestCppReflect/ blob / master / TypeTraits.h
サンプルアプリケーションはgitリポジトリにもあります。https: //github.com/tapika/TestCppReflect/
説明とともにここに部分的にコピーします。
#include "CppReflect.h"
using namespace std;
class Person
{
public:
// Repack your code into REFLECTABLE macro, in (<C++ Type>) <Field name>
// form , like this:
REFLECTABLE( Person,
(CString) name,
(int) age,
...
)
};
void main(void)
{
Person p;
p.name = L"Roger";
p.age = 37;
...
// And here you can convert your class contents into xml form:
CStringW xml = ToXML( &p );
CStringW errors;
People ppl2;
// And here you convert from xml back to class:
FromXml( &ppl2, xml, errors );
CStringA xml2 = ToXML( &ppl2 );
printf( xml2 );
}
REFLECTABLE
defineは、クラス名+フィールド名とoffsetof
-を使用して、メモリ内の特定のフィールドが配置されている場所を識別します。私はできる限り.NETの用語を取り上げようとしましたが、C ++とC#は異なるため、1対1ではありません。C++全体のリフレクションモデルはTypeInfo
とFieldInfo
クラスに存在します。
私はpugi xmlパーサーを使用してデモコードをxmlにフェッチし、xmlから復元しました。
したがって、デモコードによって生成される出力は次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
<people>
<Person name="Roger" age="37" />
<Person name="Alice" age="27" />
<Person name="Cindy" age="17" />
</people>
</People>
TypeTraitsクラスを介したサードパーティのクラス/構造のサポート、および部分的なテンプレート仕様を有効にすることも可能です-CStringまたはintと同様の方法で独自のTypeTraitsTクラスを定義します-サンプルコードを参照してください
https://github.com/tapika/TestCppReflect/blob/master/TypeTraits.h#L195
このソリューションは、Windows / Visual Studioに適用できます。他のOS /コンパイラに移植することは可能ですが、まだ行っていません。(あなたが本当にソリューションが好きなら私に尋ねてください、私はあなたを助けることができるかもしれません)
このソリューションは、複数のサブクラスを持つ1つのクラスのワンショットシリアル化に適用できます。
ただし、クラスパーツをシリアル化するメカニズム、またはリフレクションコールが生成する機能を制御するメカニズムを探している場合は、次の解決策を検討してください。
https://github.com/tapika/cppscriptcore/tree/master/SolutionProjectModel
詳細については、youtubeのビデオをご覧ください。
C ++ランタイム型リフレクション https://youtu.be/TN8tJijkeFE
私はc ++リフレクションがどのように機能するかについて少し深く説明しようとしています。
サンプルコードは、たとえば次のようになります。
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/testCppApp.cpp
c.General.IntDir = LR"(obj\$(ProjectName)_$(Configuration)_$(Platform)\)";
c.General.OutDir = LR"(bin\$(Configuration)_$(Platform)\)";
c.General.UseDebugLibraries = true;
c.General.LinkIncremental = true;
c.CCpp.Optimization = optimization_Disabled;
c.Linker.System.SubSystem = subsystem_Console;
c.Linker.Debugging.GenerateDebugInformation = debuginfo_true;
ただし、ここでの各ステップでは、実際に、C ++プロパティを使用した関数呼び出しが発生します__declspec(property(get =, put ... )
。
これは、C ++データ型、C ++プロパティ名、クラスインスタンスポインターに関する完全な情報をパスの形式で受け取り、その情報に基づいてxml、jsonを生成したり、インターネット経由でシリアル化することもできます。
そのような仮想コールバック関数の例はここにあります:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/VCConfiguration.cpp
関数ReflectCopy
と仮想関数を参照してください::OnAfterSetProperty
。
しかし、トピックは本当に進んでいるので、まずビデオを確認することをお勧めします。
改善のアイデアがありましたら、お気軽にご連絡ください。
C ++でのリフレクションは、各メンバーに対していくつかのメソッドを実行する必要がある場合に非常に便利です(たとえば、シリアル化、ハッシュ、比較)。私は非常に単純な構文で、一般的なソリューションを用意しました:
struct S1
{
ENUMERATE_MEMBERS(str,i);
std::string str;
int i;
};
struct S2
{
ENUMERATE_MEMBERS(s1,i2);
S1 s1;
int i2;
};
ENUMERATE_MEMBERSは、後で説明するマクロです(UPDATE)。
次のように、intおよびstd :: stringのシリアル化関数を定義したと仮定します。
void EnumerateWith(BinaryWriter & writer, int val)
{
//store integer
writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
//store string
writer.WriteBuffer(val.c_str(), val.size());
}
そして、「秘密のマクロ」の近くに一般的な機能があります;)
template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}
今あなたは書くことができます
S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");
EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)
したがって、構造体定義にENUMERATE_MEMBERSマクロがあると、元の型を変更することなく、シリアル化、比較、ハッシュなどを構築できます。唯一の要件は、列挙型ごとに列挙型ごとに「EnumerateWith」メソッドを実装することです。列挙型ごとに(BinaryWriterのように) 。通常、プロジェクトで任意のタイプをサポートするには、10〜20の「単純な」タイプを実装する必要があります。
このマクロには、実行時に構造体の作成/破棄を行うためのオーバーヘッドがなく、T.EnumerateWith()のコードはオンデマンドで生成する必要があります。これは、テンプレートインライン関数にすることで実現できるため、すべての話は、各構造体にENUMERATE_MEMBERS(m1、m2、m3 ...)を追加することですが、メンバータイプごとに特定のメソッドを実装することはどのソリューションでも必須であるため、オーバーヘッドとは想定していません。
更新:ENUMERATE_MEMBERSマクロの非常に単純な実装があります(ただし、列挙可能な構造体からの継承をサポートするために少し拡張することができます)
#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }
// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v)
{
int x[] = { (EnumerateWith(enumerator, v), 1)... };
}
// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
val.EnumerateWith(enumerator);
}
また、これらの15行のコードにサードパーティのライブラリは必要ありません;)
Boost :: HanaライブラリのBOOST_HANA_DEFINE_STRUCTを使用して、構造体のクールな静的反射機能を実現できます。
Hanaは、用途が広いだけでなく、多くのテンプレートメタプログラミングにも使用できます。
ランダムアクセスリフレクションライブラリはかなり簡単で直感的な反射になります-すべてのフィールド/タイプ情報が配列に利用可能であるか、または配列アクセスのように感じるためにどちらかに設計されています。C ++ 17用に書かれており、Visual Studio、g ++、およびClangで動作します。ライブラリはヘッダーのみです。つまり、「Reflect.h」をプロジェクトにコピーするだけで使用できます。
リフレクトされた構造体またはクラスには、リフレクトマクロが必要です。ここで、リフレクトするクラスの名前とフィールドの名前を指定します。
class FuelTank {
public:
float capacity;
float currentLevel;
float tickMarks[2];
REFLECT(() FuelTank, () capacity, () currentLevel, () tickMarks)
};
これですべてです。リフレクションをセットアップするために追加のコードは必要ありません。オプションで、スーパークラス(最初の引数の括弧内)とフィールド注釈(注釈を付けるフィールドの前の括弧内)を指定して、スーパークラスを走査したり、フィールドに追加のコンパイル時情報(Jsonなど)を追加したりできます。 :無視)。
フィールドのループは次のように簡単です...
for ( size_t i=0; i<FuelTank::Class::TotalFields; i++ )
std::cout << FuelTank::Class::Fields[i].name << std::endl;
オブジェクトインスタンスをループして、フィールド値(読み取りまたは変更可能)とフィールドタイプ情報にアクセスできます...
FuelTank::Class::ForEachField(fuelTank, [&](auto & field, auto & value) {
using Type = typename std::remove_reference<decltype(value)>::type;
std::cout << TypeToStr<Type>() << " " << field.name << ": " << value << std::endl;
});
A JSONライブラリ RandomAccessReflectionの上に構築された読み出しまたは書き込み、及び再帰反射任意のフィールド、ならびに配列とSTLコンテナを横断することができるため、自動識別適切なJSON出力表現を。
struct MyOtherObject { int myOtherInt; REFLECT(() MyOtherObject, () myOtherInt) };
struct MyObject
{
int myInt;
std::string myString;
MyOtherObject myOtherObject;
std::vector<int> myIntCollection;
REFLECT(() MyObject, () myInt, () myString, (Reflected) myOtherObject, () myIntCollection)
};
int main()
{
MyObject myObject = {};
std::cout << "Enter MyObject:" << std::endl;
std::cin >> Json::in(myObject);
std::cout << std::endl << std::endl << "You entered:" << std::endl;
std::cout << Json::pretty(myObject);
}
上記はそのように実行できます...
Enter MyObject:
{
"myInt": 1337, "myString": "stringy", "myIntCollection": [2,4,6],
"myOtherObject": {
"myOtherInt": 9001
}
}
You entered:
{
"myInt": 1337,
"myString": "stringy",
"myOtherObject": {
"myOtherInt": 9001
},
"myIntCollection": [ 2, 4, 6 ]
}
も参照してください...
次のような関数へのポインタを宣言すると、
int (*func)(int a, int b);
あなたはこのようにその機能にメモリ内の場所を割り当てることができます(必要とlibdl
してdlopen
)
#include <dlfcn.h>
int main(void)
{
void *handle;
char *func_name = "bla_bla_bla";
handle = dlopen("foo.so", RTLD_LAZY);
*(void **)(&func) = dlsym(handle, func_name);
return func(1,2);
}
インダイレクションを使用してローカルシンボルをロードするdlopen
には、呼び出し元のバイナリ(argv[0]
)で使用できます。
これに対する唯一の要件(、、および以外dlopen()
)libdl
dlfcn.h
引数と機能の種類を知っています)。