web-dev-qa-db-fra.com

Itérer à travers les membres Struct et Class

Est-il possible en C++ de parcourir une Struct ou une Classe pour trouver tous ses membres? Par exemple, si j'ai struct a et classe b:

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

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

Serait-il possible de les boucler pour dire obtenir une instruction print disant "Struct a has int named a, b, c" or "Class b has int named a, b, c"

27
Theopile

Il existe plusieurs façons de le faire, mais vous devez utiliser certaines macros pour définir ou adapter la structure.

Vous pouvez utiliser la macro REFLECTABLE donnée dans cette réponse pour définir la structure comme ceci:

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

Et puis vous pouvez parcourir les champs et imprimer chaque valeur comme ceci:

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

Une autre façon est d'adapter la structure comme une séquence de fusion (voir la documentation ). Voici un exemple:

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

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

Ensuite, vous pouvez également imprimer les champs en utilisant ceci:

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

Non, ce n'est pas possible, car il n'y a pas de réflexion en C++.

15
villekulla

Si vous avez des membres du même type (comme vous le faites dans votre premier exemple spécifique) que vous souhaitez à la fois (a) avoir des noms, et (b) être itérable, alors vous pouvez combiner un tableau avec une énumération:

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

Ensuite, vous pouvez à la fois

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;

Ce n'est clairement pas une solution générale, mais j'ai trouvé cela utile dans mon propre travail.

2
aquirdturtle

À condition que vos variables membres soient du même type, vous pouvez faire quelque chose comme ça que j'ai volé dans la bibliothèque 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;
    }
}

Certes, ce n'est pas la solution la plus élégante du point de vue de la maintenabilité, tenir compte manuellement du nombre de variables que vous avez pose problème. Cependant, cela fonctionnera sans bibliothèques ou macros supplémentaires et est applicable dans la plupart des situations où vous souhaitez ce comportement.

Les unions ne prennent pas en charge les constructeurs par défaut générés automatiquement, vous devrez donc en écrire une qui indique à l'objet comment initialiser l'union.

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