36

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"

3
  • 24
    No reflection in C++. Commented Sep 27, 2013 at 20:03
  • Not in general. If you've left debugging information in the program, a debugger can give you that information, and you might be able to use certain libraries/APIs to inspect your own binary, but that's going to be less fun than, well, pretty much anything... Commented Sep 27, 2013 at 20:28
  • 3
    Worth considering whether this could be possible as a compile-time activity using meta/template techniques. The information should be available to the compiler - though may need support in a future C++XX. Commented Sep 11, 2017 at 14:14

6 Answers 6

34

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));
}
Sign up to request clarification or add additional context in comments.

Comments

25

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

7 Comments

I'm not sure how this isn't an answer. He's telling the OP about a language limitation. This is about as answery as it gets.
@CaptainObvlious: Answers should answer the question. The answer to the question is "no". I mean, the answer could provide a whole mechanism to annotate a specific structure so that someone can iterate some data representing its fields, but that still doesn't iterate the fields of a struct, it iterates the fields of a specially-configured struct.
The answer to the question is not 'no'. You can easily iterate through the members of a struct and print them using standard C++: A a; printf(a.a); printf(a.b); printf(a.c);. There are plenty of ways to make the syntax more like the desired 'loop', using some kind of custom reflection mechanism.
@willj: your code is not iteration by any sensible interpretation of the question (and it doesn't even print what the questioner asked for ;-p). And unless you can count on using debug info, custom reflection mechanisms only work on structures that have been somehow annotated, not on the example structures in the question and not on structures that you didn't define yourself.
Years later, I found this answer significantly more useful than the selected answer. Truly, the answer is no. Yes, there are ways to work around and achieve a similar effect, but I don't feel any of the suggestions should be used beyond "extreme debugging". Doing all of this extra work on every single class/struct would be a mess in a production application, in my opinion.
|
3

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.

Comments

3

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.

Comments

3

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
}

3 Comments

There needs to be a semicolon after the struct and before std:array. The edit queue is currently full, so I can't edit the question.
And after the union needs to be a semicolon, as well.
Unfortunately, this solution is rather pointless as accessing an inactive union member will lead to undefined behavior.
1

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.

1 Comment

Another drawback is that it is rather pointless or will lead to undefined behavior, as you can only access the active member of the union.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.