简体   繁体   中英

Traversing member variables sequentially in a loop

This is my implementation for traversing member variables in a class/struct sequentially in a loop and here's a typical example. My implementation looks ugly, looks like implemented with brute force. How can it be improved or generalized. I want to advance through the variable like in a link-list's node->next or as in an array? Advice solicited. Thanks gurus in advance.

typedef struct {
    string name;
    string address;
    string phone;

    void set(const int idx, string str) {
        idx == 0 ? name = str : idx == 1 ? address = str : phone = str;
    }
    void print() const {
        std::cout << name << "-" << address << "-" << phone << '\n';
    }
} Person ;

int main()
{
    Person p;
    string str;
    stringstream ss{ "John London 9735048383" };

    int idx = 0;
    while (std::getline(ss, str, ' ')) {
        p.set(idx++,str); // Traversing the member variables.  The member variables will take turn to set their values in order.
    }
    p.print();

}

Edited: all member variables are of same type.

Rather than using macros, you can also use pointer-to-members. Note that in C++ you don't have to typedef struct

struct Person {
    std::string name;
    std::string address;
    std::string phone;

    void print() const {
        std::cout << name << "-" << address << "-" << phone << '\n';
    }
};

int main()
{
    Person p;
    std::string str;
    std::stringstream ss{ "John London 9735048383" };
    std::vector<std::string(Person::*)> members = { &Person::name, &Person::address, &Person::phone };

    for (auto it = members.begin(); std::getline(ss, str, ' ') && it != members.end(); ++it) {
        std::invoke(*it, p) = str;
        // Traversing the member variables.  The member variables will take turn to set their values in order.
    }
    p.print();
}

You can make the invoke slightly more palatable, providing you don't care about stopping on stream errors.

for (auto member : members)
{
    std::getline(ss, invoke(member, p), ' ');
}

If you really want have something that can be generalized and you are willing to resort to macros, you can use higher order macros for this. In your case, you would first add a macro that declares all the fields of your struct and their indices:

#define FOREACH_FIELD(OP)                       \
  OP(0, name)                                   \
  OP(1, address)                                \
  OP(2, phone)

Then, you would use this macro to implement your Person struct:

struct Person {
#define DECLARE_VAR(index, name) std::string name;
  FOREACH_FIELD(DECLARE_VAR)
#undef DECLARE_VAR

  void setField(int i, const std::string& value) {
    switch (i) {
#define SET_FIELD(index, name) case index: name = value; break;
    FOREACH_FIELD(SET_FIELD)
#undef SET_FIELD
      };
  }

  void print() {
#define PRINT_FIELD(index, name) \
    std::cout << #name << ": '" << name << "'" << std::endl;
    FOREACH_FIELD(PRINT_FIELD)
#undef PRINT_FIELD
  }
};

It looks ugly, and I would advice you to really think it through before adopting the above technique. Macros, in general, are avoided in C++. It makes your code harder to read, but if you have lots fields, well, you could use it. It could make your code more maintainable. After all, to add a new field, you only have to update the FOREACH_MACRO .

Use this technique carefully.

std::array seemed to work for me as advised by user463035818 and this seems to be the simplest solution so far. std::map will have additional advantage to go alongside an alias name I guess.

typedef struct {
    std::array<string, 3> data;

    void print() const {
        std::cout << data[0] << "-" << data[1] << "-" << data[2] << '\n';
    }
} Person ;

int main()
{
    Person p;
    string str;
    stringstream ss{ "John London 9735048383" };

    int idx = 0;
    while (std::getline(ss, str, ' ')) {
        p.data[idx++] = str; 
    }
    p.print();

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM