If I understand correctly, the core of the original question is how to iterate over a struct. In short, as Jerry Coffin pointed out in a comment, this cannot be done. I will try to explain why, and then I will try to explain how to do the next best thing.
A struct is stored in memory as a monolithic piece of data without any metadata describing its structure. For example, the following structure:
struct Foo {
char a;
char b;
char c;
int i;
}
Foo f = {'x', 'y', 'z', 122};
may be represented in memory using hexadecimal notation as follows
78 79 7A FF 7A 00 00 00
where the first 3 bytes contain the char fields, the fourth is a random value used for padding, and the next four bytes are the little-endian representation of the integer 122. This layout will vary from compiler to compiler and system to system. In short, the binary representation doesn't tell you what the data is or where individual fields are stored.
So how does the compiler access fields in structures? The code
char c = f.c;
is translated into an instruction like
COPY BYTE FROM ([address of f] + 2) TO [address of c]
in other words, the compiler encodes the literal offsets of the fields into the code. Again, this doesn't help us.
Therefore, we have to annotate the structure ourselves. This can either be done by adding information inside the structure to turn it into a sort of key-value store or by adding a second structure. You don't want to change the original structure, so a second structure is the way to go.
I am assuming that your struct holds only basic types: int, char, etc. If you are complex other classes in the struct, then I would suggest adding a ToString() method to their base class and calling that method - that's how C# and Java do it.
Foo tmp;
#define FIELD_OFFSET(f) ((char*)&(tmp.f) - (char*)&tmp)
enum FieldType { INT_FIELD, CHAR_FIELD, OBJECT_FIELD };
struct StructMeta {
FieldType type;
size_t offset;
};
StructMeta[] metadata = {
{CHAR_FIELD, FIELD_OFFSET(a)},
{CHAR_FIELD, FIELD_OFFSET(b)},
{CHAR_FIELD, FIELD_OFFSET(c)},
{INT_FIELD, FIELD_OFFSET(i)},
{OBJECT_FIELD, FIELD_OFFSET(o)},
}
void RenderStruct(Foo* f)
{
for (int i = 0; i < sizeof(metadata)/sizeof(StructMeta); i++)
{
switch (metadata[i].type)
{
case CHAR_FIELD:
char c = *((char*)f + metadata[i].offset);
// render c
break;
case INT_FIELD:
int i = *(int*)((char*)f + metadata[i].offset);
// render i
break;
case OBJECT_FIELD:
Object* o = (object*)((char*)f + metadata[i].offset);
const char* s = o->ToString();
// render s
break;
}
}
}
Note: all pointer arithmetic should be done on (char*) pointers to make sure the offsets are interpreted as bytes.