Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate through Struct and Class Members [duplicate]

Tags:

c++

class

struct

Is it possible in C++ to iterate through a Struct or Class to find all of its members? For example, if I have struct a, and class b:

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

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

Would it be possible to loop them to say get a print statement saying "Struct a has int named a, b, c" or "Class b has int named a, b, c"

like image 428
Theopile Avatar asked Sep 27 '13 20:09

Theopile


6 Answers

There are a couple of ways to do this, but you need to use some macros to either define or adapt the struct.

You can use the REFLECTABLE macro given in this answer to define the struct like this:

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

And then you can iterate over the fields and print each value like this:

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

Another way is to adapt the struct as a fusion sequence (see the documentation). Here's an example:

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

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

Then you can print the fields as well using this:

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));
}
like image 136
Paul Fultz II Avatar answered Oct 22 '22 10:10

Paul Fultz II


No, it's not possible, because there is no reflection in C++.

like image 27
villekulla Avatar answered Oct 22 '22 10:10

villekulla


If you have members of the same type (as you do in your first specific example) that you want to both (a) have names, and (b) be iterable, then you can combine an array with an enum:

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

Then you can both

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.members[bob] = 100;

Clearly not a general solution, but I've found this useful in my own work.

like image 24
aquirdturtle Avatar answered Oct 22 '22 09:10

aquirdturtle


Provided your member variables are of the same type, you can do something like this that i stole from the GLM library:

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

Admittedly this isn't the most elegant solution from a maintainability standpoint, manually keeping count of the number of variables you have is asking for trouble. However It will work without additional libraries or macros and is applicable in most situations that you'd want this behaviour.

Unions don't support automatically generated default constructors so you'll need to write one that tells the object how to initialise the union.

for (double component : point.components)
{
    // do something
}
like image 23
QCTDev Avatar answered Oct 22 '22 10:10

QCTDev


Assuming that all class members are of the same type, you may employ a C++17 feature called structured binding. Assuming that all members are public this would work:

struct SI
{
    int x;
    int y;
    int z;
};

struct SD
{
    double x;
    double y;
    double z;
};

template<typename T>
void print(const T &val)
{
    const auto& [a, b, c] = val;
    for (auto elem : {a, b, c})
    {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

This would work with any struct that has exactly 3 public elements of the same (copyable) type. In case of non-public members the function must be a friend or a member. This approach however cannot be easily extented to arbitrary number of elements.

like image 1
Dmitry Kuzminov Avatar answered Oct 22 '22 11:10

Dmitry Kuzminov


This is an improved version of QCTDevs answer:

class MyClass
{
    union
    {
        struct Memberstruct
        {
            double test0;
            double test1;
            double test2;
        } m;
        array<double, sizeof( Memberstruct ) / sizeof( double )> memberarray;
    };
    bool test() { &(m.test1) == &(memberarray[1]); }
};

The requirement is still to have all the same datatypes, and you also need to implement the default Constructor, if needed.

It is improved in that you don't need to manually maintain the size of the array.

A drawback is an altered syntax in comparison to the class without this workaround.

like image 1
Daniel Bauer Avatar answered Oct 22 '22 11:10

Daniel Bauer