C ++アプリケーションにリフレクションを追加するにはどうすればよいですか?


263

C ++クラスの名前、内容(つまり、メンバーとそのタイプ)などをイントロスペクトできるようにしたいと思います。ここでは、リフレクションのあるマネージC ++ではなく、ネイティブC ++について話しています。C ++がRTTIを使用していくつかの限られた情報を提供することを理解しています。この情報を提供できる追加のライブラリ(または他の手法)はどれですか?


18
幸運にも、マクロや他の前処理なしではそれを行うことはできません。必要なメタデータ、マクロ前処理の魔法で手動で作成ない限り存在しないためです。
2008年

6
ただし、RTTIから取得できる情報は、実際に反映したいほとんどのことを行うには不十分です。たとえば、クラスのメンバー関数を反復処理することはできません。
ジョセフガービン、

回答:


259

プリプロセッサにフィールドに関するリフレクションデータを生成させる必要があります。このデータは、ネストされたクラスとして保存できます。

まず、プリプロセッサで簡単かつ簡潔に記述できるように、型付き式を使用します。型付き式とは、型を括弧で囲んだ式のことです。したがって、書く代わりに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行未満のコードでリフレクションを実装したところです。


106
リフレクションを実装できないと言うのではなく、実装方法を示したことに対する称賛。SOをすばらしいリソースにするのは、このような答えです。
fearless_fool

4
Visual Studioでこれをコンパイルしようとすると、VSが可変個のマクロ展開を適切に処理しないため、エラーが発生することに注意してください。:VSのために、追加してみてください #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,)
Phengleiカイ

「BOOST_PP_IIF_0」というエラーがタイプに指定されていません。手伝ってくれませんか。
Ankit Zalani

3
私自身の回答を参照してください-stackoverflow.com/a/28399807/2338477私はすべての定義を抽出して再パックしました。ブーストライブラリは必要ありません。デモコードとして、xmlへのシリアル化とxmlからの復元を提供しています。
TarmoPikaro 2017

106

二種類のreflection泳ぎがあります。

  1. 型のメンバーを繰り返し処理し、そのメソッドを列挙することなどによる検査。

    これはC ++では不可能です。
  2. クラス型(クラス、構造体、共用体)にメソッドまたはネストされた型があるかどうかを検査する検査は、別の特定の型から派生しています。

    この種のことは、C ++でを使用して可能template-tricksです。boost::type_traits多くのことに使用します(型が整数であるかどうかのチェックなど)。メンバー関数の存在を確認するには、関数の存在を確認するテンプレートを作成することはできますか?。特定のネストされたタイプが存在するかどうかを確認するには、プレーンなSFINAEを使用します。

1)クラスのメソッド数やクラスIDの文字列表現の取得など、達成する方法を探している場合、標準のC ++でこれを行う方法はありません。あなたはどちらかを使わなければなりません

  • コードを変換して追加のメタ情報を追加するQtメタオブジェクトコンパイラのようなメタコンパイラ。
  • 必要なメタ情報を追加できるマクロで構成されるフレームワーク。フレームワークにすべてのメソッド、クラス名、ベースクラス、および必要なすべてを伝える必要があります。

C ++は速度を考慮して作成されています。C#やJavaのような高レベルの検査が必要な場合は、少し手間をかけないと方法がないと言わざるを得ません。


122
C ++はスピードを念頭に置いて作られていますが、その哲学は「できるだけ速く」ではなく、「使用しない場合は料金を払わない」ということです。言語がその哲学に適合する方法でイントロスペクションを実装することは可能であると私は信じています。C++にはそれが欠けているだけです。
ジョセフガービン、

8
@ジョセフ:それはどのように行われるべきですか?すべてのメタデータを保存する必要があります。つまり、たとえ使用しなくても、その代金を支払う必要があります。(あなたは、「サポート反射」として個々のタイプをマークすることができますが、我々は同様に既存のマクロ策略を使用する可能性がありますどこ我々はほとんどダウンしている場合を除き。
jalf

25
@jalf:必要になる可能性のあるメタデータのみ。コンパイル時のリフレクションのみを考慮する場合、これは取るに足らないことです。たとえば、members<T>Tのすべてのメンバーのリストを返すコンパイル時関数。実行時リフレクション(つまり、RTTIとリフレクションを組み合わせたもの)が必要な場合でも、コンパイラーはすべての反映された基本型を知っています。members<T>(T&)T = std :: stringではインスタンス化されない可能性が高いため、std :: stringまたはその派生クラスのRTTIを含める必要はありません。
MSalters 2009

9
reflexライブラリ(下記)は、既存のコードを遅くすることなくC ++にリフレクションを追加します。root.cern.ch
Joseph Lisee

6
@Joe:リフレクションが既存のコードを遅くすることはありません。配信されるものを大きくするだけです(型情報データベースを配信する必要があるため...)。
mmmmmmmm 2012

56

そして、私はポニーが大好きですが、ポニーは自由ではありません。:-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTIが取得するものです。あなたが考えているような反射-実行時に利用できる完全に記述的なメタデータ-は、デフォルトではC ++には存在しません。


1
私は2番目のブラッド。C ++テンプレートはかなり強力な場合があり、さまざまな「リフレクション」タイプの動作(ブースト「任意の」ライブラリー、タイプの特性、C ++ RTTIなど)には、リフレクションが解決する問題の多くを解決できる豊富な経験があります。ではニック、あなたの目標は何ですか?
アーロン

7
ポニーの発言に賛成投票!あなたの答えもそれに値するので、私は2回賛成票を投じますが、悲しいことに私は1つしか得ないので、ポニーが勝ちます。:-)
フランシペノフ2008

6
なぜこれが賢い反応なのか、本当にわかりません。これを実装するために、ライブラリなどへの参照が必要であることはすでに述べました。反射/イントロスペクションは、シリアル化などのスクリプトのアクセスを許可するために様々なシステムのためのものである
ニック

3
@ニック:彼はすでにそれに答えました。それができず、データが存在しないので、それを実装できるライブラリはありません。
jalf

@jalfまだ奇妙なことに、プログラミングの世界の人々が「不可能だ」とか「方法がわからない」などとは考えていないと言っています。確かにメタデータは存在しませんが、マクロで挿入できます
Freddx L.

38

情報は存在しますが、必要な形式ではなく、クラスをエクスポートする場合のみです。これは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とのストリーミングなどに非常に役立ち、特定の基本クラスから派生する必要はありません。十分な需要があれば、リリースのためにそれを形にすることができるでしょう。


1
私はあなたが意味を考える__declspec(dllexport)と、ビルド時に、このようなの作成を有効にした場合、あなたは.MAPファイルから情報を取得することができます。
Orwellophile 2017年

18

リフレクションは、そのままではC ++でサポートされていません。防御テストが面倒になるので、これは悲しいことです。

リフレクションを行うには、いくつかの方法があります。

  1. デバッグ情報を使用します(非ポータブル)。
  2. マクロ/テンプレートまたはその他のソースのアプローチ(見苦しいように見える)を使用してコードを振りかける
  3. データベースを作成するためにclang / gccなどのコンパイラーを変更します。
  4. Qt mocアプローチを使用する
  5. ブーストリフレクト
  6. 正確でフラットな反射

最初のリンクは最も有望です(modからclangを使用します)。2番目のリンクはいくつかの手法について説明し、3番目はgccを使用した別のアプローチです。

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

現在、C ++リフレクションの作業グループがあります。C ++ 14 @ CERNのニュースを参照してください。

2017年8月13日の編集:

最初の投稿以来、反省に関して多くの潜在的な進歩がありました。以下は、さまざまな手法とステータスに関する詳細と説明です。

  1. 一言で言えば静的反射
  2. 静的反射
  3. 静的反射の設計

ただし、コミュニティから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によってリリースされました。詳細はここにあります:

ツールとアプローチは、これまでで最も洗練され、最も使いやすいように見えます。


1
Cernリンクが壊れています。
モストウスキー崩壊2015

Cernリンクは今修正する必要があります。彼らはかなり頻繁に壊れる傾向があり、これは苦痛です。
ダミアンディクソン

この回答はコンパイル時のリフレクションのみを考慮していますか?
einpoklum 2018年

@einpoklumリフレクションの現在の唯一のソリューションはコンパイル時間であり、通常はメタテンプレートコードまたはマクロを使用します。最新のドラフトTSはランタイムで機能するように見えますが、必要なメタデータを格納するには、正しいコンパイラーですべてのライブラリーをビルドする必要があります。
Damian Dixon

@DamianDixon:そうではありません。いくつかの実行時リフレクションライブラリがあります。当然のことながら、それらはかなり不格好であり、オプトインするか、コンパイラの通知を必要としますが、まだ存在しています。あなたのコメントを理解しているように、コンパイル時のリフレクションのみを参照した場合は、回答をより明確にするために編集してください。
einpoklum 2018年

15

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、これは実行時(構成ファイルが読み取られるとき)に一連のメソッド呼び出しに変換されるため、かなり効率的です。本当に必要な場合を除いて、これを行うことはお勧めしませんが、そうすることで、いくつかの本当にクールなことができます。


常にtrueを返すこれらの関数が大好きです
ポール2015年

14

Qtの使用をお勧めします。

オープンソースライセンスと商用ライセンスがあります。


1
私はこれを見ましたが、それはマクロを使用し、ソースコードはメタデータコードを生成するために解析が必要です。この余分なステップを回避したいと思います。C ++ライブラリまたは単純なマクロを使用したいと思います。アイデアをありがとう。
ニック

10
QT、または同様のアプローチを実装している別のライブラリがあなたが手に入れるのに最適です
jalf

5
コンパイル時に支払うか、実行時に支払う-どちらの方法でも支払う!
Martin Beckett

13

リフレクションで何をしようとしていますか?
Boost タイプの特性typeofライブラリを、コンパイル時のリフレクションの制限された形式として使用できます。つまり、テンプレートに渡されたタイプの基本プロパティを検査および変更できます。


13

編集キャンプはもはや維持されていません。2つのフォークを使用できます。

  • 1つはCAMPとも呼ばれ、同じAPIに基づいています。
  • Ponderは部分的な書き換えであり、Boostを必要としないため、推奨されます。C ++ 11を使用しています。

CAMPはMITライセンスライブラリ(以前のLGPL)で、C ++言語にリフレクションを追加します。コンパイル時に特定の前処理手順は必要ありませんが、バインドは手動で行う必要があります。

現在のTegesoftライブラリはBoostを使用していますが C ++ 11を使用して、Boostを必要とないフォークあります。


11

私はあなたが一度やりたいことのようなことをしました、そしてある程度の反映を得てより高いレベルの機能にアクセスすることは可能ですが、メンテナンスの頭痛はそれに値しないかもしれません。私のシステムは、メッセージの受け渡しと転送という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_castboost::dynamic_pointer_cast暴力的な情熱を持って、あなたのコードベースの上にすべて。強く型付けされたシステムの制限はまだありますが、実際には少しだけ非表示にしているので、それほど明白ではありません。文字列のタイプミスも、面白くないし、驚きを発見するのも簡単ではありません。

このようなものを実装する方法については、いくつかの共通のベース(私は非常に想像力豊かに "オブジェクト"と呼ばれていました)への共有ポインタと弱いポインタを使用し、使用するすべての型から派生させます。私は、Boost.Functionを、私が行った方法ではなくインストールすることをお勧めします。これは、カスタムのがらくたと、関数ポインターの呼び出しをラップするための醜いマクロのトンでした。すべてがマッピングされているので、オブジェクトの検査は、すべてのキーを反復処理するだけです。私のクラスは本質的にC ++のみを使用して、Cocoaの直接の詐欺にできるだけ近いため、そのようなものが必要な場合は、Cocoaのドキュメントを青写真として使用することをお勧めします。


@Michael; このためのソースコードはまだありますか、それともそれを取り除きましたか?よろしければご覧ください。
RandomDSdevel 2014年

おっと、あなたの名前のスペルを間違えた!返事がなかったのも不思議ではありません…
RandomDSdevel

10

C ++には、RTTR(ランタイムタイプリフレクション、githubも参照)と呼ばれるリフレクション用の新しいライブラリがあります。

インターフェイスはC#のリフレクションに似ており、RTTIなしで機能します。


8

私のC ++時代から知っている2つの反射のようなソリューションは次のとおりです。

1)すべてのクラスを「オブジェクト」基本クラスから派生させることができる場合、リフレクションのような動作を構築するためのブートストラップを提供するRTTIを使用します。そのクラスは、GetMethod、GetBaseClassなどのメソッドを提供できます。これらのメソッドがどのように機能するかについては、型を装飾するためにいくつかのマクロを手動で追加する必要があります。これにより、背後でGetMethodsなどへの回答を提供するメタデータが型に作成されます。

2)コンパイラー・オブジェクトにアクセスできる場合の別のオプションは、DIA SDKを使用することです。私の記憶が正しければ、C ++型のメタデータを含むpdbを開くことができます。それはあなたが必要とすることをするのに十分かもしれません。このページでは、たとえばクラスのすべての基本型を取得する方法を示します。

これらのソリューションはどちらも少し醜いです!C#の贅沢さを理解してもらうためのC ++のようなものはありません。

幸運を。


それは巧妙で巨大なハックであり、DIA SDKで提案されたものがあります。
Sqeaky

7

編集:2017年2月7日現在、リンク切れを更新しました。

私は誰もこれに言及しなかったと思います:

CERNでは、C ++の完全なリフレクションシステムを使用しています。

CERN Reflex。それは非常にうまく機能するようです。


彼らがマイルストーンに到達したと思われるので、@ j4nbur53リンクが壊れている:root.cern.ch
ヘルマン・Diago

このリンクroot.cern.ch/root/doc/ROOTUsersGuideHTML/ch07.html Chapter Reflex を意味しているのでしょうか?
モストウスキー崩壊2015

このroot.cern.ch/how/how-use-reflexを試してください。Reflexは、ヘッダーファイルを解析してc ++イントロスペクションコード/ライブラリーを生成するジェネレーターとして機能します。これにより、単純なAPIに対してリンクして使用できます。
Adam Ryczkowski、2017

6

この質問は少し古いです(今日、なぜ私が古い質問を続けているのかわかりません)が、コンパイル時のリフレクションを導入するBOOST_FUSION_ADAPT_STRUCTについて考えていました。

もちろん、これを実行時のリフレクションにマップするのはあなた次第であり、それほど簡単ではありませんが、逆方向ではありませんが、この方向で可能です。

マクロをカプセル化BOOST_FUSION_ADAPT_STRUCTして、ランタイムの動作を取得するために必要なメソッドを生成できると本当に思います。


2
minghua(投稿を最初に編集した人):私はこのBOOST_FUSION_ADAPT_STRUCTソリューションを掘り下げ、最終的に例を思いつきました。この新しいSOの質問を参照してください。C++は、ブーストフュージョンのadapte_structを使用してネストされた構造体フィールドに反復します。
Matthieu M.

素晴らしい、マシュー!この1年の間に、あちこちにヒントが表示されていることに気づきました。彼らが今までに関連していることに気づかなかった。それらは非常に刺激的でした。
minghua 2012

6

Dominic Filionによる記事「Using Templates for Reflection in C ++」をおもしろいと思うかもしれません。これは、Game Programming Gems 5のセクション1.4にあります。残念ながら私は私のコピーを持っていませんが、あなたが何を求めているのかを説明していると思いますので探してください。


4

Ponderは、この質問に答えるC ++リフレクションライブラリです。私はすべてのボックスにチェックを入れるオプションが見つからなかったので、オプションを検討して自分で作成することにしました。

この質問には素晴らしい答えがありますが、私は大量のマクロを使用したり、Boostに依存したりしたくありません。Boostは優れたライブラリーですが、よりシンプルでコンパイル時間が短い、特注の小さなC ++ 0xプロジェクトがたくさんあります。C ++ 11をサポートしていない(まだ?)C ++ライブラリをラップするなど、クラスを外部で装飾できることには利点もあります。C ++ 11を使用するCAMPのフォークであり、Boostは必要ありません


4

リフレクションとは、基本的には、ランタイムコードがクエリできるコードのフットプリントとしてコンパイラが残すことを決定したものです。C ++は、使用しないものにお金を払わないことで有名です。ほとんどの人はリフレクションを使用しない/望まないため、C ++コンパイラは何も記録しないことでコストを回避します。

したがって、C ++はリフレクションを提供せず、他の回答が指摘しているように、一般的なルールとして自分で「シミュレート」することは容易ではありません。

「その他の手法」では、リフレクションのある言語がない場合は、コンパイル時に必要な情報を抽出できるツールを入手してください。

当社のDMSソフトウェアリエンジニアリングツールキットは、明示的な言語定義によってパラメーター化された汎用コンパイラテクノロジーです。C、C ++、Java、COBOL、PHPなどの言語定義があります。

C、C ++、Java、およびCOBOLバージョンの場合、解析ツリーとシンボルテーブル情報への完全なアクセスを提供します。そのシンボルテーブル情報には、「リフレクション」から取得する可能性が高いデータの種類が含まれています。目的がフィールドまたはメソッドのセットを列挙し、それらを使って何かを行うことである場合、DMSを使用して、シンボルテーブルにあるものに従ってコードを任意の方法で変換できます。


3

ここで別のライブラリを見つけることができます:http : //www.garret.ru/cppreflection/docs/reflect.html これは2つの方法をサポートします:デバッグ情報から型情報を取得し、プログラマーにこの情報を提供させます。

私は自分のプロジェクトのリフレクションにも興味があり、このライブラリを見つけました。まだ試していませんが、この人の他のツールを試しましたが、それらがどのように機能するかが好きです:-)


3

Classdesc http://classdesc.sf.netをチェックしてください。これは、クラス「記述子」の形式でリフレクションを提供し、標準のC ++コンパイラーで動作します(はい、Visual StudioとGCCで動作することがわかっています)。 )。10年以上にわたって開発されており、多くの産業規模のプロジェクトで使用されています。


1
Stack Overflowへようこそ。この回答はトピックに関するものですが、公平な推奨ではないことを明確にするために、あなたがこのソフトウェアの作成者であることを指摘することが重要です:-)
Matthew Strawbridge 2013

2

C ++でリフレクションが必要なときに、この記事を読みそこで見たものを改善しました。すみません、できません。私は結果を所有していません...しかし、あなたは確かに私が持っていたものを手に入れ、そこから行くことができます。

私は現在、そのように感じたときに、継承可能な型の定義をはるかに簡単にするためにinherit_linearlyを使用するメソッドを研究しています。私は実際にはかなり遠くまで行きましたが、私はまだ行く方法があります。C ++ 0xの変更は、この領域で多くの助けとなる可能性が非常に高いです。


2

C ++にはまだこの機能がないようです。そしてC ++ 11もリフレクションを延期しました(((

マクロを検索するか、独自に作成します。Qtはリフレクションにも役立ちます(使用できる場合)。


2

リフレクションはそのままではc ++でサポートされていませんが、実装するのはそれほど難しくありません。私はこの素晴らしい記事に遭遇しました:http : //replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

この記事では、非常にシンプルで基本的なリフレクションシステムを実装する方法について詳しく説明しています。これは最も健全な解決策ではなく、整理する必要のあるラフなエッジが残っていますが、私のニーズにはそれで十分でした。

肝心なこと-リフレクションが正しく行われれば報われることがあり、C ++では完全に実現可能です。


2

自動イントロスペクション/リフレクションツールキット「IDK」の存在を宣伝したいと思います。Qtのようなメタコンパイラーを使用し、メタ情報をオブジェクトファイルに直接追加します。それは使いやすいと主張されています。外部依存関係はありません。std :: stringを自動的に反映し、スクリプトで使用することもできます。IDKをご覧ください


2

比較的単純な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 );

}

REFLECTABLEdefineは、クラス名+フィールド名とoffsetof-を使用して、メモリ内の特定のフィールドが配置されている場所を識別します。私はできる限り.NETの用語を取り上げようとしましたが、C ++とC#は異なるため、1対1ではありません。C++全体のリフレクションモデルはTypeInfoFieldInfoクラスに存在します。

私は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

しかし、トピックは本当に進んでいるので、まずビデオを確認することをお勧めします。

改善のアイデアがありましたら、お気軽にご連絡ください。


1

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行のコードにサードパーティのライブラリは必要ありません;)


1

Boost :: HanaライブラリのBOOST_HANA_DEFINE_STRUCTを使用して、構造体のクールな静的反射機能を実現できます。
Hanaは、用途が広いだけでなく、多くのテンプレートメタプログラミングにも使用できます。


1

ランダムアクセスリフレクションライブラリはかなり簡単で直感的な反射になります-すべてのフィールド/タイプ情報が配列に利用可能であるか、または配列アクセスのように感じるためにどちらかに設計されています。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 ]
}

も参照してください...


0

次のような関数へのポインタを宣言すると、

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()libdldlfcn.h引数と機能の種類を知っています)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.