3

Is it possible to iterate through a struct?

For example

struct team{
   int player1;
   int player2;
   int player3;
   int player4;
   ...
   int player99; 
   int size = 99;
}

then run a for loop to set or access foo 1-4?

i guess pseudocode would look something like

for(int i = 0; i < size; i++){
    player i = (i+1); 
 }

A more simplified explanation if that doesnt make sense is I Just want to be able to go through each variable without having to hard code player1 = 1; player2 =2.

sean
  • 510
  • 7
  • 12
  • 10
    Any reason why you're not using an array/vector instead of a struct? – Shawn Chin Nov 01 '12 at 16:59
  • You can cheat, to a certain extent. In this case, with a struct composed entirely of items of the same size, you can get a pointer to an instance of the struct and treat it like an array. If the struct is more heterogenous, that won't work. If it is homogenous, like this one, why not just use a vector or array? – Rook Nov 01 '12 at 17:03
  • Well I have a lot of variables that are not related to each other that will be hard coded into a struct. And i just want to iterate through them all to set another set of data to the variables that exist in my struct – sean Nov 01 '12 at 17:04
  • If you have such set of other data, why not just make it use same struct, so you can just assign the whole struct with one =, like in plain C? – hyde Nov 01 '12 at 17:09
  • I'm creating an interface to a program. A function for example setAge(foo bar) will take my struct foo. Lets say there's 4 people in my struct bob joe jane chris. On the program setMensAge will only have bob joe chris. So I want to go through my struct and say is there a bob if so grab his age and setMensAge to what ever bob is set to in my struct. Does that make sense? – sean Nov 01 '12 at 17:13
  • 1
    So you actually need a map of key-value pairs...? – hyde Nov 01 '12 at 17:20
  • std::map seems like an appropriate data structure if you want both enumeration and access by name. – Matt Montag Jun 02 '20 at 09:21

4 Answers4

5

One way is to put the players/elements into an array:

struct Team {
    static int const size = 99;
    int players[size];
};

And then:

for(int i = 0; i < size; ++i)
    int player = players[i];
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
2

To answer your question as you've asked it, I believe that you can use the pre-compiler macro Pack (the exact phrase depends on your compiler) to guarantee the structure of the memory used to create an instance of your struct. And then you technically could increment a pointer to move through it... if you're mad. That would be a very poor way to do and not at all guaranteed to work on different compilers or even different days of the week. No what you want is a data structure to do the job for you; they come with a 100% cash-back guarantee!

The most basic structure to do this with is a fixed size array, e.g:

struct team
{
    int players[99]; //an array
    int manager;
    int coach;
    string teamName;
    //etc etc
}

Then to access your players

team myTeam;
for(int i(0); i < 99; ++i)
{
    myTeam.players[i]; //do whatever
}

The limitation of an array is that you cannot change its size once it's created. So if you try

myTeam.players[99]; //accessing invalid memory - the array values are 0 - 98

More advanced

If you need a data structure that can change size after it's created, e.g you might want to add a few more players to your team at some point in the future. Then you can use a dynamic data structure such as the std::vector or the std::deque or std::list

Ian
  • 450
  • 4
  • 18
  • 1
    `//exception thrown - the array values are 0 - 98`. What. This is not Java. No exception is raised when you access an array out of its bounds. It's UB. – user703016 Nov 01 '12 at 17:15
  • @Cicada *shame faced* I was bi-lingual curious, DON'T JUDGE ME! :( (fixed) – Ian Nov 01 '12 at 17:19
1

I would propose to use container instead of many variables, for example you could use std::array or std::vector. This way it will be trivial to iterate, much easier to make a copy. But also it's better from design point of view: in case you decide to change the number of players it will be much easier to change the container rather than add/remove many fields

nogard
  • 9,432
  • 6
  • 33
  • 53
1

You can define pointer to member, like pointer to member function:

  typedef int team::*member_t;

You can have array of pointers to all your members:

  static member_t member[size];

With this approach defining member function to iterate over all members is easy:

  template <class F>
  void for_each(F f)
  {
     for (int i = 0; i < size; ++i)
       f(this->*member[i]);
  }

And with using of preprocessor macro - you can have in one place definition of all members, in other definition of pointer to members - so you will not make any mistake with changing their order. See full code:

struct team {
#define TEAM_MEMBERS(prefix,suffix) \
  prefix player1 suffix, \
  prefix player2 suffix, \
  prefix player3 suffix

  int TEAM_MEMBERS(,);
  static const int size = 3;
  typedef int team::*member_t;
  static member_t member[size];
  template <class F>
  void for_each(F f)
  {
     for (int i = 0; i < size; ++i)
       f(this->*member[i]);
  }
};
team::member_t team::member[team::size] = {
  TEAM_MEMBERS(&team::,)
};

And some test:

#include <iostream>

int main() {
  team t = { 0 };
  t.for_each([](int m) { std::cout << m << "\n"; }); // prints 0,0,0
  int n = 0;
  t.for_each([&n](int& m) { m = n++; });
  t.for_each([](int m) { std::cout << m << "\n"; }); // prints 0,1,2
  t.player2 = 7;
  t.for_each([](int m) { std::cout << m << "\n"; }); // prints 0,7,2
}
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112