How to dynamically access structure fields?

Pages: 12
Hi everyone,
Could anyone explain me how to access fields / variables in a structure without directly calling them?
For example if I have a structure like so:
1
2
3
4
5
6
7
struct User
{
  int id;
  std::string username;
  std::string password;
  bool active;
};

And instead of assigning all the variables manually like so:
1
2
3
4
5
6
7
8
9
10
11
12
User *u;
vector<User> users;
while (ext->exists()) {
  u = new User;
  u->id = (int)ext->fields[0];
  u->username = ext->fields[1];
  u->password = ext->fields[2];
  u->active = (bool)ext->fields[3];
  users.push_back(u);
  delete u;
  ext->next();
}

I'd like to be able to do something like that:
1
2
3
4
5
6
7
8
9
10
11
User *u;
vector<User> users;
while (ext->exists()) {
  u = new User;
  for (int i = 0; i < ext->field_count(); i++) {
    u->something = dynamic_cast<ext->field_type()*>ext->fields[i];
  }
  users.push_back(u);
  delete u;
  ext->next();
}

I know the previous code is a complete nonsense, but I just don't know how it should actually look.
That's not possible, at least not in this form. C++ has no reflection.
What do you want that for? Are you sure you don't just want a constructor?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct User
{
    int id;
    string username;
    string password;
    bool active;

    User(int id_, string username_,
        string password_, bool active_):
        id(id_),username(username_),
        password(password_),active(active_){}

};

int main()
{
    vector<User> users;

    users.push_back(User(1,"Bob","asdf",true));
    users.push_back(User(2,"Tom","1234",false));

    return 0;
}
It can be done, but it's extremely ugly. If you tell us a little about what you're trying to do, perhaps we can suggest an alternative.
Thank you for all your responses and I'm pretty sure I need it and not the constructor.
And the reason I want this is because of a database class that should get data for more then just users and every table has different field count and different field names, so I want to be able to fill the vector without the need to actually intrude the code into database class that shouldn't be there, or get database related code outside the db class.
I want to be able to get all the results from a database somehow like that:
1
2
3
4
5
6
7
8
9
10
vector<User> users;
vector<Assignment> assignments;

ext->fetchData("users");
ext->getData(&users);
ext->freeResults();

ext->fetchData("assignments");
ext->getData(&assignments);
ext->freeResults();

At first I was considering something like that:
1
2
3
4
5
6
7
8
9
10
void Externals::getData(...)
{
  int count;
  va_list vl;
  va_start(vl, count);
  for (int i = 0; i < count; i++) {
    va_arg(vl, this->field_type()) = this->fields[i];
  }
  va_end(vl);
}

but then I realized, that I can't set values in variable argument list, and only accessing them is just not enough.
If you need any more background info, just ask and I hope someone eventually will be able to help me out.
Last edited on
I think what you need is a generic data type that can be converted to and from anything. This is generally done with a string. Then you can add a vector to the structure that holds a pointer to each of those strings.
Would it be correct to think of such generic data type as something like this:
1
2
3
4
5
struct ExtData
{
	std::string title;
	vector< <vector <std::string> > records;
};

?
Or is there a better way to achieve a similar result? Because when I think of it, your suggestion really does make sense, but in order to achieve a db like matrix with vectors in mint I can't think of anything that would look less insane then this.
Or should I use dynamic arrays to achieve what I want? Or maybe even something else that I'm not aware of?
Last edited on
I was thinking something more like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct User{
    std::string id,
        username,
        password,
        active;
    std::vector<std::string *> records;
    User(){
        this->records.reserve(4);
        this->records.push_back(&this->id);
        this->records.push_back(&this->username);
        this->records.push_back(&this->password);
        this->records.push_back(&this->active);
    }
};

bool read_bool(const std::string &);
int read_int(const std::string &);
The thing with databases is that their records often can be dynamically modified. Like... users can add/remove fields, change the name of the field, etc.

If your database is doing any of that, then you can't use a a structure like User or Assignment structure, since that would require the fields all be rigid and fully defined at compile time.

So that's the question I have to ask... is this database dynamically constructed? Or do you have fixed types?

If it's the former, the you need a completely different approach from what you're doing now, so I'm going to assume the latter.


Since this is the latter, I don't really see why the more obvious solutions can't apply here. My first instinct would be to just overload getData:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void YourClass::getData( vector<User>& users )
{
  // fill users here
}

void YourClas::getData( vector<Assignment>& assignments )
{
  // fill assignments here
}


void someothercode()
{
  vector<User> users;
  vector<Assignment> assignments;

  db->getData( users );  // gets the users
  db->getData( assignments ); // gets the assignments
}


Furthermore, since you're filling different types (struct User vs. struct Assignment), the type can determine which data you're getting. The 'fetchData' function seems redundant. Does that function do something I'm not understanding? What would you want to have happen if they do something like this:

1
2
3
4
  vector<User> users;

  db->fetchData( "assignments" );
  db->getData( users );  // wtf ?explode? 
Thanks for all your replies.

I don't know why didn't I think of overloading functions in the first place, maybe I just thought I could do it some more hardcore and more dynamic way. Not sure what was in my head at that time.

And about database being dynamic constructed. Well, I could say yes and no at the same time as I have all the plan set as UML and i know all the data types, that will be used in the database, but who knows what may happen after some update in the future.

And about fetch and get part.
fetchData function actually picks records from the database passed as an argument, i.e.:
 
ext->fetchData("assignments");

would get all the records from assignments table in the database and place them in a buffer, while getData should get all the data from the buffer and place it into a corresponding vector of objects.

I just thought that there might be some way of doing this dynamically, without any need to specify a separate getData method for each different object type.
Even though, it seems more and more like it's just not possible or is just not worth the effort.

If there's some way to do that, I'd be more then delighted to find it out, so all further replies will be much appreciated. And if not then I'll just go for overloading functions.
P.S.: thank you Disch.
Last edited on
Hmmm... My guess is that the overloading approach is the best you can do as long as the number of tables is small and the database isn't dynamically created. However, unless both of these conditions are met, I think a more generic approach would be better. That's what I have in mind:

(Disch, please don't throw up again... :/ If you want, you can have helios or someone else you trust take a look at this and then tell you if it's ok to look or not... Thank you...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class FieldInfo
{
public:

    enum FieldType
    {
        FT_INT,FT_DOUBLE,
        FT_STRING,FT_CHAR,FT_BOOL
    };

private:

    string name;
    FieldType type;
};

class Field
{
    union
    {
        int i;
        double d;
        string s;
        char c;
        bool b;
    } data;
};

class Record
{
    vector<Field> fields;
};

class Table
{
    vector<FieldInfo> field_info;
    deque<Record> records;
};

class Database
{
    vector<string> table_names;
    vector<Table> tables;
};

Then, instead of having a User struct you would have a Table named "User" whose field_info vector would "look" like this:

{{"id",FT_INT},{"username",FT_STRING},{"password",FT_STRING},{"active",FT_BOOL}}

One benefit from doing this, is that if you have many tables you don't have to write overloads for all of them. You just have to write functions to get/set the basic types (int,string, etc...)

The other benefit is of course that the generic way is the only way to go if you have a dynamically created database.
I wouldn't put complex types (like string) in a union.

Is that even legal/defined behavior?
It isn't. Only basic types and I think PODs are allowed as part of a union.
Last edited on
Indeed, it is not allowed... What other options are there aside from a void pointer?

EDIT: I mean, aside from something like this:

1
2
3
4
class Field
{
    void * data;
};

EDIT 2: I suppose something like this would work, but it's a waste of memory...

1
2
3
4
5
6
7
8
9
10
11
class Field
{
    union
    {
        int i;
        double d;
        char s[50];
        char c;
        bool b;
    } data;
};

Last edited on
How about an std::string * inside the union?
Well, according to the current project, there is 17 tables of which only 12 will be actually used in the program. Is that many or not, I'm not sure, but definitely not as many as it could be. Either way, I'm not sure if overloading is okay for such amount. Any suggestions?
Last edited on
helios wrote:
How about an std::string * inside the union?

Yes, I'm experimenting with something like this now... I tried a string * but I ended up with a runtime error :/ Then I used a void * and it worked ok:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//PROBLEM
#include <iostream>
#include <string>
using namespace std;

union Type
{
    int i;
    double d;
    string * s;
};

int main()
{
    Type var;

    var.i=3;
    cout << var.i << endl;

    *(string*)var.s="asdf";
    cout << *(string*)var.s << endl;

    cout << "\nhit enter to quit";
    cin.get();
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//OK
#include <iostream>
#include <string>
using namespace std;

union Type
{
    int i;
    double d;
    void * s;
};

int main()
{
    Type var;

    var.i=3;
    cout << var.i << endl;

    string * pstr = new string;
    var.s=pstr;

    *(string*)var.s="asdf";
    cout << *(string*)var.s << endl;

    delete pstr;

    cout << "\nhit enter to quit";
    cin.get();
    return 0;
}
Last edited on
the void pointer has nothing to do with it.

The reason it didn't work with a string* is because you never allocated any string with new.

This would have worked just as well, but doesn't have all the fugly casts:

1
2
3
4
5
6
    var.s = new string;

    *var.s="asdf";
    cout << *var.s << endl;

    delete var.s;


void pointers are evil. Also, casting to/from a void pointer kind of defeats the whole point of using a union, doesn't it?
...
Um... Why aren't you initializing the std::string pointer? It's not going to magically get set. Well, I mean, sure, setting the int union member will technically set the pointer, too, but not in any way that's useful or desirable.
Hahahaha yeah, that was just me being stupid :D I just noticed too and was about to correct it but you were faster.
Last edited on
Pages: 12