web-dev-qa-db-ja.com

構造体とクラスメンバーを反復処理する

C++では、StructまたはClassを反復処理してそのすべてのメンバーを見つけることはできますか?たとえば、構造体aとクラスbがある場合:

struct a
{
  int a;
  int b;
  int c;
}

class b
{
  public:
    int a;
    int b;
  private:
    int c;
}

それらをループして、「構造aにはa、b、cという名前のintがある」または「クラスbにはa、b、cという名前のintがある」という印刷ステートメントを取得するようにループさせることは可能でしょうか

27
Theopile

これを行うにはいくつかの方法がありますが、構造体を定義または適応させるためにいくつかのマクロを使用する必要があります。

this answer で指定されたREFLECTABLEマクロを使用して、次のような構造体を定義できます。

struct A
{
    REFLECTABLE
    (
        (int) a,
        (int) b,
        (int) c
    )
};

そして、フィールドを反復処理して、各値を次のように出力できます。

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());
}

A x;
print_fields(x);

別の方法は、構造体を融合シーケンスとして適応させることです( ドキュメント を参照)。以下に例を示します。

struct A
{
    int a;
    int b;
    int c;
};

BOOST_FUSION_ADAPT_STRUCT
(
    A,
    (int, a)
    (int, b)
    (int, c)
)

次に、これを使用してフィールドも印刷できます。

struct print_visitor
{
    template<class Index, class C>
    void operator()(Index, C & c)
    {

        std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call() 
                  << "=" 
                  << boost:::fusion::at<Index>(c) 
                  << std::endl;
    }
};


template<class C>
void print_fields(C & c)
{
    typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
    boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), boost::ref(c), _1));
}
27
Paul Fultz II

いいえ、できません。C++にはリフレクションがないためです。

15
villekulla

(最初の特定の例のように)同じ型のメンバーがあり、(a)名前を持ち、(b)反復可能にする場合は、配列を列挙型と組み合わせることができます。

enum names { alice, bob, carl };
struct myStruct;
{
  std::array<int, 3> members;
}

その後、あなたは両方することができます

myStruct instance;
// iterate through them...
for (auto elem : instance.members)
{
    // work with each element in sequence
} 
// and call them by name, taking away the need to remember which element is the first, etc.
instance[bob] = 100;

明らかに一般的な解決策ではありませんが、これは自分の仕事で役立つことがわかりました。

2
aquirdturtle

メンバー変数の型が同じであれば、GLMライブラリから盗んだこのようなことができます。

class Point
{
    Point();// you must re-implement the default constructor if you need one

    union
    {
        struct
        {
            double x;
            double y;
            double z;
        }
        std::array<double, 3> components;
    }
}

確かに、これは保守性の観点から最もエレガントなソリューションではありません。手作業で変数の数を数え続けることは、トラブルを求めています。ただし、追加のライブラリやマクロがなくても機能し、この動作が必要なほとんどの状況に適用できます。

ユニオンは自動的に生成されたデフォルトコンストラクターをサポートしていないため、オブジェクトにユニオンの初期化方法を指示するコンストラクターを記述する必要があります。

for (double component : point.components)
{
    // do something
}
0
QCTDev