5

Is it possible to iterate over all elements in a struct or class?

For example if I have a struct of three elements of different type:

struct A {
    classA a;
    classB b;
    classC c;
};

then I need some iterator such that a method next() would give me the value of the next element. The problem is that as you see, the values have different types.

user3111311
  • 7,583
  • 7
  • 33
  • 48
  • 2
    No. Some languages support this via reflection. There's been talk of adding some sort of compile-time reflection capability to C++ (or the library), but it isn't there yet. – Jerry Coffin Dec 17 '13 at 16:41
  • 3
    Let's say you could iterate over them, then what would you do to each one? If you're iterating, you generally want to apply the same operation to each item, but if they are different types, how can you? – Joseph Mansfield Dec 17 '13 at 16:41
  • Not out of the box. C++ is not a reflected language by design. – Seva Alekseyev Dec 17 '13 at 16:41
  • 3
    This is not possible with struct or classes. You can however do it with `std::tuple` and some template meta-programming. [Boost.Fusion](http://www.boost.org/doc/libs/1_55_0/libs/fusion/doc/html/) is a library that offers types and algorithms that support such functionality very conveniently. – Björn Pollex Dec 17 '13 at 16:41
  • 1
    @SevaAlekseyev: Ngised yb egaugnal detcelfer a ton si ++C? – Kerrek SB Dec 17 '13 at 16:43
  • @Kerrek: no, it's not :) RTTI is hardly reflection. – Seva Alekseyev Dec 17 '13 at 16:49
  • possible duplicate of [How can I add reflection to a C++ application?](http://stackoverflow.com/questions/41453/how-can-i-add-reflection-to-a-c-application) – Matthieu M. Dec 17 '13 at 18:23
  • @MatthieuM. Ehhh, potentially, but the questions are asked differently. Needless to say, an important thing to point out, my answer is at least 71% shinier than the accepted one in the said question ^^. – ScarletAmaranth Dec 17 '13 at 19:06
  • @ScarletAmaranth: I've used http://stackoverflow.com/a/11748131/147192 with some adaptations, for *toys*. – Matthieu M. Dec 17 '13 at 19:07
  • Why do you want this? – Casey Dec 17 '13 at 19:11
  • There is no reflection in the C++ language now. You need an external tool, that generate reflection info, like QT moc, or my minimal tool: https://github.com/hun-nemethpeter/cpp-reflector-mini – Industrial-antidepressant Dec 18 '13 at 19:45

3 Answers3

4

Nope, not with the language as it is.

You could do it by deriving your classes from a common base, and then implementing your own iterator to return pointers to each item as the iterator is traversed.

Alternatively put the items in a std::vector and use that to provide the iteration.

Sean
  • 60,939
  • 11
  • 97
  • 136
4

No, there is no reflection in C++, (yet, there are murmurs about static reflection coming one day).

Anyway, there is a way to work around this, to an extent - first of all, you'll need a (temporary) tuple with references to your data members.

Then you will need a construct "iterating" over the tuple, such as:

void applyToAll() { }

template <typename Lambda, typename... Lambdas>
void applyToAll(Lambda&& closure, Lambdas&&... closures) {
    std::forward<Lambda>(closure)();
    applyToAll(std::forward<Lambdas>(closures)...);
}

// use your favourite sequence-making trick
template <unsigned... Is>
struct _Sequence {
    typedef _Sequence<Is...> type;
};

template <unsigned Max, unsigned... Is>
struct _MakeSequence : _MakeSequence<Max - 1, Max - 1, Is...> { };

template <unsigned... Is>
struct _MakeSequence<0, Is...> : _Sequence<Is...> { };

template <typename Tuple, typename Functor, unsigned... Is>
void _foreachElemInTuple(_Sequence<Is...>, Tuple&& t, Functor&& f) {
    applyToAll(
       [&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
    );
}

template <typename Tuple, typename Functor>
void foreachElemInTuple(Tuple&& t, Functor&& f) {
    _foreachElemInTuple(
         _MakeSequence<std::tuple_size<
              typename std::decay<Tuple>::type>::value>(),
         std::forward<Tuple>(t), std::forward<Functor>(f)
    );
}

Then you can call foreachElemInTuple(yourTuple, some_adapter()).

Your adapter will look like:

struct some_adapter {
    template <typename... Args>
    // A little bit of C++14, you can also just -> decltype the thing
    decltype(auto) operator()(Args&& ... args) const {
        return doStuff(std::forward<Args>(args)...);
    }
};
ScarletAmaranth
  • 5,065
  • 2
  • 23
  • 34
0

As everyone else says, you cannot directly iterate over data members of a class. However, it is not difficult to do it indirectly, provided of course that you can access each of the data members you want to iterate over. The idea in essense, as per ScarletAmaranth's solution, is to iterate over an std::tuple of references to those data members.

The following program shows how to obtain such a tuple, using std::forward_as_tuple, and another way to do the iterating by compiletime recursion, without auxiliary apparatus.

#include <tuple>

/*  You want to be able do something with the values of the members of an `A` 
    in turn.
*/
struct A
{
    char ch;
    int i;
    double d;
    // May also have members of class type. It doesn't matter 
};

/*  1) Provide yourself with the means of creating a sequence that contains
    references to the data members of a given `A`
*/
std::tuple<char const &, int const &, double const &> get_A_vals(A const & a)
{
    return std::forward_as_tuple(a.ch,a.i,a.d);
}


/*  2) Provide yourself with a means of applying some operation, `Func`, 
    to each element of an `std::tuple`
*/
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const &, Func) {}

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const & tpl, Func func) 
{
    func(std::get<I>(tpl));
    for_each_in_tuple<I + 1>(tpl,func);
}

/* 3) Combine 1) and 2) to apply `Func` over the members of an `A`
*/
template<typename Func>
void for_each_in_A(A const & a, Func func)
{
    for_each_in_tuple(get_A_vals(a),func);
}

// Testing...

#include <iostream>

// A specimen operation: just prints its argument
struct printer
{
    template<typename T>
    void operator () (T && t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    A a{'a',1,2.0};
    for_each_in_A(a,printer());
    return 0;
}

// EOF

The program outputs:

a
1
2

If you have control of the structs or classes over whose members you need to iterate, you may consider whether it is practical simply to dispense with them and use the corresponding std::tuples everywhere.

Code built with gcc 4.8.2 and clang 3.3, -std=c++11.

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182