C++: Iterating through all of an object's members? C++遍历成员变量

转自:http://stackoverflow.com/questions/7142532/c-iterating-through-all-of-an-objects-members

Suppose I have an object with many members:

class Example {
    AnotherClass member1;
    AnotherClass member2;
    YetAnotherClass member3;
    ...
};

Is there a short/concise way to do something like:

foreach(member m: myExample)
    m.sharedMethod();

Instead of accessing each one individually?

I think I could put them in a vector and use a shared_ptr for the same effect, I was just wondering if say, Boost or some other popular library doesn't have something to do this automatically.

share improve this question
 
2 
I really don't think that this is a good idea. If you need to iterate through something, then declare it as an iteratable container, or why would you not do this? –  leftaroundabout  Aug 22 '11 at 2:18
 
check out Boost serialization –  Cheers and hth. - Alf  Aug 22 '11 at 2:19
1 
C++ doesn't have reflection. That means it's not possible. –  Ryan Amos  Aug 22 '11 at 2:22
6 
Out of curiosity, how would this even be useful? Often when something is so fundamentally impossible it's because it wouldn't make sense in the first place. –  Kerrek SB  Aug 22 '11 at 2:30

6 Answers

up vote 10 down vote accepted

C++ does not support class introspection, so you cannot iterate over all of the members in a class like that - not without having a (manually written) function that iterates over all members for you anyway.

You could, in principle, add a template member like so:

template<typename Functor>
void doAllMembers(Functor &f) {
  f(member1);
  f(member2);
  f(member3);
}

That said, I would regard this as a broken design; you've gone and exposed all of your internal members publicly. What happens if you add one later? Or change the semantics of one? Make one a cached value that's sometimes out of date? etc. Moreover, what happens if you have members which don't all inherit from the same types?

Step back and reconsider your design.

share improve this answer
 

There are several solutions to this issue, contrary to what the naysayers blabber, but no built-in way.

C++ support a limited kind of introspection, at compile-time: you can check the template parameters.

Using either Boost.Tuple or Boost.Fusion (for its map), you can indeed achieve what you wish. In Boost.Fusion you even have BOOST_FUSION_ADAPT_STRUCT to transform a basic structure into a Fusion Sequence (and thus iterate it).

It requires quite a bit of template meta-programming though.

share improve this answer
 
 
Liked this solution. Posted the detailed code here: C++ iterate into nested struct field with boost fusion adapt_struct –  minghua  Sep 6 '12 at 7:04

C++ can do something like this, if you play by its rules and use template metaprogramming.

Instead of storing your stuff in a struct or class, store it in a tuple:

typedef boost::tuple<AnotherClass, AnotherClass, YetAnotherClass> Example;

Then you can use template metaprogramming algorithms and so forth (see Boost.Fusion) to access members and poke at stuff. You can iterate, template-style, over the elements of the tuple.

share improve this answer
 

It is possible that what you're looking for here is the Visitor pattern. If you have an object as you describe, with a number of non-trivial member fields, and you find that you have a number of different functions that all traverse this data structure in the same way, the visitor pattern can be very helpful in reducing the amount of code duplication. It is not automatic, through, you have to write the functions that traverse all the member fields, but you only have to do that once, and you can use it many times over with different visitor classes that do different things.

The visitor pattern does involve writing quite a bit of code, you need an abstract base class for the visitors:

class VisitorBase 
{
    virtual void enter(Example& e)=0;
    virtual void leave(Example& e)=0;
    virtual void enter(AnotherClass& e)=0;
    virtual void leave(AnotherClass& e)=0;
    etc ...
};

Then you need accept functions in all the classes that are going to be visited:

void Example::accept( VisitorBase& visitor ) 
{
    visitor.enter(*this);
    member1.accept(visitor);
    member2.accept(visitor);
    member3.accept(visitor);
    visitor.leave(*this);
}

And finally you need to implement concrete visitor classes that do the actual work you're interested in, which generally amounts to gather information from the data structure, making changes to the data structure, or combinations of both. Google Visitor pattern and you'll find lots of help on this.

share improve this answer
 

This is my way to do what you want to achieve in C++11. It uses tuple and templates. It's not short and concise but if you wrap somecode in a header file is acceptable. There is my full compilable example:

#include <iostream>
#include <string>
using namespace std;

#include <tuple>

//Our iteratation code

//first a iteration helper structure
template<int N = 0>
struct IterateP {
  template<class T>
  typename std::enable_if<(N < std::tuple_size<T>::value), void>::type
  iterate(T& t) {
    std::get<N>(t).sharedMethod(); //there is the method name
    IterateP<N+1>().iterate<T>(t);
  }

  template<class T>
  typename std::enable_if<!(N < std::tuple_size<T>::value), void>::type
    iterate(T&) {}
};

//wrapper of the helper structure for a more comfortable usage
template <typename T>
void iterate(T& t) { IterateP<>().iterate(t.members); } //look at the .members, is the name of the class tuple

//Helper notation macro. 
#define MEMBER(name, i) std::tuple_element<i,decltype(members)>::type &name = std::get<i>(members)

//YOUR CLASSES

struct AnotherClass {
  int value;
  void sharedMethod() { cout << value << endl; }
};

struct YetAnotherClass {
  string value;
  void sharedMethod() { cout << value << endl; }
};


//The class with iterable members
struct Example {
  std::tuple<AnotherClass, AnotherClass, YetAnotherClass> members; //members must be in a tuple

  //These are helper member definition to access the tuple elements as normal members (instance.member1)
  //If you don't you this you need to access the members with tuple classes
  MEMBER(member1, 0); //first param is the member name and the second is it's position in the tuple.
  MEMBER(member2, 1);
  MEMBER(member3, 2);
};

//USAGE
int main() {

  Example example;

  //setting members just as a normal class 
  example.member1.value = 1;
  example.member2.value = 2;
  example.member3.value = "hola";

  //magic
  iterate(example);
}
share improve this answer
 

This is not possible with C++, if you really Really REALLY need this then instead use C#. But I seriously doubt you do.

Having said that, about the only option you really have is to use .NET and use managed C++ which microsoft calls Managed C++/CLI. But the caveat, is your class must be a managed 'ref class', meaning a managed class. Eventually it all gets compiled down to MSIL, and intermediate language that is language agnostic. That's the only way you could then use .NET reflection on it at runtime to discover it's member functions and values. However even using .NET it's not as easy as you described how you would like to use it above. Another downside to reflection is that it's slow, so if you are using it heavily you have a bad design. Reflection is nice in that .NET uses it to help in serializing data types, which makes XML serialization very easy to use.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值