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

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

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

ベストアンサー1

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

まず、プリプロセッサでより簡単かつきれいに記述するために、型付き式を使用します。型付き式は、単に型を括弧内に置く式です。したがって、 と書く代わりに、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
)

そこでブースト.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_fieldsreflectable クラスを使用する例Person:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

出力は次のようになります:

name=Tom
age=82

すると、100 行未満のコードで C++ でリフレクションを実装できました。

おすすめ記事