Access to elements of a homogeneous structure

Hi! How can I better access elements of a homogeneous struct?
Here are the options to consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Test {
    string s1 = "test1";
    string s2 = "tst2";
    string s3 = "t3";
} test;

// 1st
for (int i = 0; i < 3; ++i)
    cout << *(&test.s1 + i) << "\n";

// 2nd
string *strArr[] = {&test.s1, &test.s2, &test.s3};
for (int i = 0; i < 3; ++i)
    cout << *strArr[i] << "\n";


Which option would be better and safer? I would like to use the first, but will this work correctly for most compilers?
Last edited on
If it were just output, then the Test should provide interface (that encapsulates implementation):
1
2
3
4
5
6
7
8
9
10
std::ostream& operator<< ( std::ostream&, const Test& );
cout << test; // access


std::ostream& operator<< ( std::ostream& out, const Test& rhs ) {
  out << rhs.s1 << '\n';
  out << rhs.s2 << '\n';
  out << rhs.s3 << '\n';
  return out;
}


However, if you can redesign the struct ...
1
2
3
4
5
6
7
8
struct Test {
  std::string s[3] {"test1", "tst2", "t3"};
};

Test test;
for ( auto w : test.s ) {
  std::cout << w << '\n';
}
No, I can't redesign. And it isn't just for output. I must make a choice between two that in the question above. Code above just for example
> Which option would be better and safer?

The array. If we want to access the elements as an array, make it an array.


> will this work correctly for most compilers?

In practice, it should work as expected, if the second and third static assertions hold.
(On most compilers, they would.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <string>
#include <type_traits>
#include <cstddef>

struct test {

    std::string a = "test1" ;
    std::string b = "tst2" ;
    std::string c = "t3" ;
};

static_assert( std::is_standard_layout<test>::value, "this is not a standard layout type" ) ; // 1
static_assert( offsetof(test,b) == offsetof(test,a) + sizeof(std::string), "padding detected" ) ; // 2
static_assert( offsetof(test,c) == offsetof(test,b) + sizeof(std::string), "padding detected" ) ; // 3 
Thanks!
Pointers aren't just for dynamic memory.

1
2
3
4
5
6
7
8
9
10
11
struct Test {
    string s1 = "test1";
    string s2 = "tst2";
    string s3 = "t3";
} test;

string *members[3] = {&test.s1, &test.s2, &test.s3};

for (unsigned i=0; i<3; ++i) {
   doStuff(*members[i]);
}

Last edited on
if you can't redesign it but you can modify it, overload the [] or * or () or something operator to do the uglies for you. This would be similar to the above wrapper. You could also inherit it into a new object that provided such a wrapper/operator/etc.
Last edited on
There are also library-based solutions:
Antony Polukhin's magic_get library provides this sort of reflection by leveraging SFINAE + structured bindings.
https://github.com/apolukhin/magic_get
Seems like a good option here.

Boost.Fusion offers a macro that generates the traits classes required for reflection:
https://www.boost.org/doc/libs/1_72_0/libs/fusion/doc/html/fusion/adapted/adapt_struct.html
Last edited on
Topic archived. No new replies allowed.