By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,149 Members | 1,051 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,149 IT Pros & Developers. It's quick & easy.

Defining the fields of a structure at run-time

P: n/a
I am facing this problem....
I have to define a structure at runtime as the user specifies...

The user will tell the number of fields,the actual fields...(maybe
basic or array types or multiple arrays,etc)

I do not understand how to define the structure at run time.i.e.what
fields it will contain.
Creating the field variables at run time is fine...but defining the
structure so as to contain these fields is where the problem lies...

I thought of unions within the structure representing each possible
field variable...
but again only one of these unions can exist at time...array of unions
again similar problem.

Any possible solution occuring to any one...please do let me know as
early as possible.
Thanks a lot...

Nisha.

Feb 8 '06 #1
Share this Question
Share on Google+
10 Replies


P: n/a
na*************@gmail.com wrote:
I have to define a structure at runtime as the user specifies...
Are you, perchance, writing a compiler? ;-)
The user will tell the number of fields,the actual fields...(maybe
basic or array types or multiple arrays,etc)

I do not understand how to define the structure at run time.i.e.what
fields it will contain.
Creating the field variables at run time is fine...but defining the
structure so as to contain these fields is where the problem lies...


I don't think you can do it the way you expect (if I got you right).

You could, however, allocate as much memory as the user data requires in
total. By carefully moving a char pointer around, and judiciously
casting, you can achieve your goal. I must warn you though, that this
is extremely bug-prone, and the bugs you create will be a nightmare to
fix. You'll also need an inordinate amount of book-keeping.
--
BR, Vladimir

Mickey Mouse wears a Spiro Agnew watch.

Feb 8 '06 #2

P: n/a
na*************@gmail.com wrote:
I am facing this problem....
I have to define a structure at runtime as the user specifies...

The user will tell the number of fields,the actual fields...(maybe
basic or array types or multiple arrays,etc)

I do not understand how to define the structure at run time.i.e.what
fields it will contain.
Creating the field variables at run time is fine...but defining the
structure so as to contain these fields is where the problem lies...

I thought of unions within the structure representing each possible
field variable...
but again only one of these unions can exist at time...array of unions
again similar problem.

Any possible solution occuring to any one...please do let me know as
early as possible.
Thanks a lot...


You cannot construct a run-time "struct" type.
What you can do, is keeping a dynamic "array" the elements of
which contain "struct member" like information, e.g.

struct memberdata {
datatype_t type;
#ifdef USES_FLAT_TYPES
size_t number;
int isScalar;
#endif
const char * membername;
void * memberdata;
};

where we have

#ifdef USES_FLAT_TYPES
typedef enum EMemberType {
EMT_invalid,
EMT_signedchar,
EMT_unsignedchar,
EMT_char,
EMT_signedshort,
EMT_unsignedshort,
EMT_signedint,
....
EMT_voidpointer,
EMT_signedcharpointer,
....
} datatype_t;
#endif

The above enables us to have a couple of "cheap" basic
types and derived types and arrays thereof. The isScalar is
intended to give the difference between "array 1 of Type"
and "Type".
membername always points to an array of char of size
strlen(membername)+1.

Now, you have to make sure that no membername is used twice,
that you access every piece of data according to type, and so
on.

This gives enough of the nightmarish bookkeeping Vladimir S. Oka
mentioned.

If you want to be able to construct arbitrary derived types,
datatype_t has to be much more complex. You can make some things
easier in some respect for you by always using
long double/long/unsigned long (or long double/intmax_t/uintmax_t
in C99) to store the values. On the other hand this requires
range checking in other places to correctly simulate overflow or
complain in time about invalid values.
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Feb 8 '06 #3

P: n/a
Vladimir S. Oka wrote:
na*************@gmail.com wrote:
I have to define a structure at runtime as the user specifies...
Are you, perchance, writing a compiler? ;-)
The user will tell the number of fields,the actual fields...(maybe
basic or array types or multiple arrays,etc)

I do not understand how to define the structure at run time.i.e.what
fields it will contain.
Creating the field variables at run time is fine...but defining the
structure so as to contain these fields is where the problem lies...


I don't think you can do it the way you expect (if I got you right).

You could, however, allocate as much memory as the user data requires in
total. By carefully moving a char pointer around, and judiciously
casting, you can achieve your goal.


You also have to watch out for alignment issues.
I must warn you though, that this
is extremely bug-prone, and the bugs you create will be a nightmare to
fix. You'll also need an inordinate amount of book-keeping.


This is why assuming there are a finite number of types the user can
specify and given that memory efficiency is not an issue I would do
something like this:
enum field_types { SHORT, INT, LONG, TEXT };
union field_union {
short s;
int i;
long l;
char *t;
};
struct field {
enum field_types type;
union field_union val;
};

Then you can use malloc & friends to manage dynamic arrays of the field
struc.

You have a bit more work to do for TEXT, since you would obviously have
to allocate the space for the string as well. However, it avoids the
need to mess about with char pointers and casting (casting is generally
a sign you are entering dangerous territory, but it is sometimes required).
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
Feb 8 '06 #4

P: n/a
Well, here is something i wrote some time ago, when i had a similar problem:

Expand|Select|Wrap|Line Numbers
  1. #include <stdio.h>
  2.  
  3. int main(void)
  4. {
  5. int count;
  6.  
  7. /*Pointer to allocated space for struct data*/
  8. char *a;
  9. /*Number of struct members*/
  10. unsigned int struct_members;
  11. /*Array containing members' size*/
  12. unsigned int *member_size;
  13. /*Array containing members' offset in allocated space*/
  14. unsigned int *member_offset;
  15. /*Allocated space size*/
  16. int total_size=0;
  17.  
  18. /*Example*/
  19. int  i=10;
  20. char k='a';
  21. struct_members=2;
  22.  
  23. /*Allocate space for member_size array*/
  24. member_size = malloc(struct_members*sizeof(*member_size));
  25.  
  26. /*For this example*/
  27. member_size[0]=2;
  28. member_size[1]=1;
  29.  
  30. /*Fill in member_size array*/
  31. for (count=0;count<struct_members;count++)
  32. total_size+=member_size[count];
  33.  
  34. /*Allocate and fill in member_offset array*/
  35. member_offset = malloc(struct_members*sizeof(*member_offset));
  36. member_offset[0]=0;
  37. for (count=1;count<struct_members;count++)
  38. member_offset[count]=member_offset[count-1]+member_size[count];
  39.  
  40. /*Allocate struct space*/
  41. a = malloc(total_size);
  42.  
  43. /*Copy example values in the proper place*/
  44. memcpy(a+member_offset[0],&i,sizeof(i));
  45. memcpy(a+member_offset[1],&k,sizeof(k));
  46.  
  47. /*Print for test purposes*/
  48. printf("Integer is: %d\n",*(a+member_offset[0]));
  49. printf("Character is: %c\n",*(a+member_offset[1]));
  50.  
  51. getchar();
  52. return 0;
  53. }
  54.  
<na*************@gmail.com> wrote in message
news:11**********************@g47g2000cwa.googlegr oups.com...
I am facing this problem....
I have to define a structure at runtime as the user specifies...

The user will tell the number of fields,the actual fields...(maybe
basic or array types or multiple arrays,etc)

I do not understand how to define the structure at run time.i.e.what
fields it will contain.
Creating the field variables at run time is fine...but defining the
structure so as to contain these fields is where the problem lies...

I thought of unions within the structure representing each possible
field variable...
but again only one of these unions can exist at time...array of unions
again similar problem.

Any possible solution occuring to any one...please do let me know as
early as possible.
Thanks a lot...

Nisha.

Feb 8 '06 #5

P: n/a
stathis gotsis wrote:
Well, here is something i wrote some time ago, when i had a similar problem:
Please don't top post. Your reply belongs under the text you are
replying to (after appropriate snippage) not above.
[code]
#include <stdio.h>

int main(void)
{
int count;

/*Pointer to allocated space for struct data*/
char *a;
/*Number of struct members*/
unsigned int struct_members;
/*Array containing members' size*/
unsigned int *member_size;
/*Array containing members' offset in allocated space*/
unsigned int *member_offset;
/*Allocated space size*/
int total_size=0;

/*Example*/
int i=10;
char k='a';
struct_members=2;

/*Allocate space for member_size array*/
member_size = malloc(struct_members*sizeof(*member_size));
If the compiler does not complain here it is broken. If it does you
should have fixed it. The correct fix is in #include <stdlib.h>
/*For this example*/
member_size[0]=2;
member_size[1]=1;
Bad example. On many systems these days int is 4 bytes. You should be
using sizeof the appropriate variable.
/*Fill in member_size array*/
for (count=0;count<struct_members;count++)
total_size+=member_size[count];

/*Allocate and fill in member_offset array*/
member_offset = malloc(struct_members*sizeof(*member_offset));
member_offset[0]=0;
for (count=1;count<struct_members;count++)
member_offset[count]=member_offset[count-1]+member_size[count];
So when count is 1 you get:
member_offset[1]=member_offset[0]+member_size[1]
which is
member_offset[1]=0+1
which is
member_offset[1]=1
When, since the fist element (according to you, but not if I built it on
any system I currently have access to) should be 2 to place it after the
first element instead of overlapping with it.
/*Allocate struct space*/
a = malloc(total_size);

/*Copy example values in the proper place*/
memcpy(a+member_offset[0],&i,sizeof(i));
memcpy(a+member_offset[1],&k,sizeof(k));
Since, on all my systems, sizeof(int) is 4 not 2 this will do very bad
things.
/*Print for test purposes*/
printf("Integer is: %d\n",*(a+member_offset[0]));
printf("Character is: %c\n",*(a+member_offset[1]));
This, in the general case, will not work, since you are completely
ignoring alignment requirements. If using this type of approach you have
to use memcpy to copy the data out in to a suitable variable data
(except for character types) before using it.
getchar();
return 0;
}


<snip problem description which should have been above your response>
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
Feb 9 '06 #6

P: n/a
Flash Gordon wrote:
Vladimir S. Oka wrote:
This is why assuming there are a finite number of types the user can
specify and given that memory efficiency is not an issue I would do
something like this:
enum field_types { SHORT, INT, LONG, TEXT };
union field_union {
short s;
int i;
long l;
char *t;
};
struct field {
enum field_types type;
union field_union val;
};

Then you can use malloc & friends to manage dynamic arrays of the
field struc.

You have a bit more work to do for TEXT, since you would obviously
have to allocate the space for the string as well. However, it avoids
the need to mess about with char pointers and casting (casting is
generally a sign you are entering dangerous territory, but it is
sometimes required).


A much better solution yes. I also happened to think of something along
the same lines, but OP had as well (I wrongly snipped that bit):
na*************@gmail.com wrote:
I thought of unions within the structure representing each possible
field variable...


He didn't sound happy about it so I offered an alternative -- ugly as it
was. ;-)
--
BR, Vladimir

In America, any boy may become president and I suppose that's just one
of the risks he takes.
-- Adlai Stevenson

Feb 9 '06 #7

P: n/a
"Flash Gordon" <sp**@flash-gordon.me.uk> wrote in message
news:5h************@news.flash-gordon.me.uk...
stathis gotsis wrote:
Well, here is something i wrote some time ago, when i had a similar problem:

Please don't top post. Your reply belongs under the text you are
replying to (after appropriate snippage) not above.


Thank you for taking the time to do these corrections and i will take
that into account in the future.
[code]
#include <stdio.h>

int main(void)
{
int count;

/*Pointer to allocated space for struct data*/
char *a;
/*Number of struct members*/
unsigned int struct_members;
/*Array containing members' size*/
unsigned int *member_size;
/*Array containing members' offset in allocated space*/
unsigned int *member_offset;
/*Allocated space size*/
int total_size=0;

/*Example*/
int i=10;
char k='a';
struct_members=2;

/*Allocate space for member_size array*/
member_size = malloc(struct_members*sizeof(*member_size));


If the compiler does not complain here it is broken. If it does you
should have fixed it. The correct fix is in #include <stdlib.h>


My mistake, i copy-pasted from line 3:
"#include <stdlib.h>
#include <string.h>" should have preceded.
/*For this example*/
member_size[0]=2;
member_size[1]=1;


Bad example. On many systems these days int is 4 bytes. You should be
using sizeof the appropriate variable.


True as well.
/*Fill in member_size array*/
for (count=0;count<struct_members;count++)
total_size+=member_size[count];

/*Allocate and fill in member_offset array*/
member_offset = malloc(struct_members*sizeof(*member_offset));
member_offset[0]=0;
for (count=1;count<struct_members;count++)
member_offset[count]=member_offset[count-1]+member_size[count];


So when count is 1 you get:
member_offset[1]=member_offset[0]+member_size[1]
which is
member_offset[1]=0+1
which is
member_offset[1]=1
When, since the fist element (according to you, but not if I built it on
any system I currently have access to) should be 2 to place it after the
first element instead of overlapping with it.


I did not fully understand this. I suppose this derives from the fact that i
supposed sizeif(int) is 2 in any system?
/*Allocate struct space*/
a = malloc(total_size);

/*Copy example values in the proper place*/
memcpy(a+member_offset[0],&i,sizeof(i));
memcpy(a+member_offset[1],&k,sizeof(k));


Since, on all my systems, sizeof(int) is 4 not 2 this will do very bad
things.


That same mistake propagates.
/*Print for test purposes*/
printf("Integer is: %d\n",*(a+member_offset[0]));
printf("Character is: %c\n",*(a+member_offset[1]));


This, in the general case, will not work, since you are completely
ignoring alignment requirements. If using this type of approach you have
to use memcpy to copy the data out in to a suitable variable data
(except for character types) before using it.


I see, will that generally work instead:
"printf("Integer is: %d\n", *((int *)(a+member_offset[0])));" ? Please shed
some more light on this.
getchar();
return 0;
}


<snip problem description which should have been above your response>
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.

Feb 9 '06 #8

P: n/a
stathis gotsis wrote:
"Flash Gordon" <sp**@flash-gordon.me.uk> wrote in message
news:5h************@news.flash-gordon.me.uk...
stathis gotsis wrote:
Well, here is something i wrote some time ago, when i had a similar problem:
Please don't top post. Your reply belongs under the text you are
replying to (after appropriate snippage) not above.


Thank you for taking the time to do these corrections and i will take
that into account in the future.


Thank you.

<snip>
/*For this example*/
member_size[0]=2;
member_size[1]=1;

Bad example. On many systems these days int is 4 bytes. You should be
using sizeof the appropriate variable.


True as well.
/*Fill in member_size array*/
for (count=0;count<struct_members;count++)
total_size+=member_size[count];

/*Allocate and fill in member_offset array*/
member_offset = malloc(struct_members*sizeof(*member_offset));
member_offset[0]=0;
for (count=1;count<struct_members;count++)
member_offset[count]=member_offset[count-1]+member_size[count];

So when count is 1 you get:
member_offset[1]=member_offset[0]+member_size[1]
which is
member_offset[1]=0+1
which is
member_offset[1]=1
When, since the fist element (according to you, but not if I built it on
any system I currently have access to) should be 2 to place it after the
first element instead of overlapping with it.


I did not fully understand this. I suppose this derives from the fact that i
supposed sizeif(int) is 2 in any system?


The fundamental problem in this part is nothing to do with the size of
an int.

In your code you store the size of the first item in member_size[0] and
its offset (which is 0) in member_offset[0]. Given that the first member
takes two bytes, that would be a[0] and a[1]. So to calculate the
position of the second item you need to take the offset of the first
item and add the size of the first item, where as you were adding the
size of the second item. I.e., you should have:

member_offset[count]=member_offset[count-1]+member_size[count-1];

<snip>
/*Print for test purposes*/
printf("Integer is: %d\n",*(a+member_offset[0]));
printf("Character is: %c\n",*(a+member_offset[1]));

This, in the general case, will not work, since you are completely
ignoring alignment requirements. If using this type of approach you have
to use memcpy to copy the data out in to a suitable variable data
(except for character types) before using it.


I see, will that generally work instead:
"printf("Integer is: %d\n", *((int *)(a+member_offset[0])));" ? Please shed
some more light on this.


Actually, I've just realised that I had miss-read what you initially
posted, so what I said it not actually quite true, although there was
still a problem.

Your original line:
printf("Integer is: %d\n",*(a+member_offset[0]));
will print one byte of the first member, since a is an array of char.
Obviously this is not what you want.

Your suggestion of
printf("Integer is: %d\n", *((int *)(a+member_offset[0])));
has potentially bigger problems.

Some real systems have alignment requirements for various types, so if
you try to read a 2 byte integer from an odd address they will generate
an error (often called a "bus error" or SIGBUS) and abort your program.
Since as far as the compiler is concerned a is an array of char (and
char does not have any alignment requirements) a could start on an odd
address. So casting it to an int* then dereferencing it could crash your
program (the C standard allows this).

So what you have to do is the reverse of when you put the data in. I.e.:
memcpy(i, a+member_offset[0], member_size[0]);
printf("Integer is: %d\n", i);
Ideally you should add at least basic error trapping, i.e.
if (member_size[0] != sizeof i)
fputs("ERROR: Size miss-match!\n");
else {
memcpy(i, a+member_offset[0], member_size[0]);
printf("Integer is: %d\n", i);
}
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
Feb 9 '06 #9

P: n/a

"Flash Gordon" <sp**@flash-gordon.me.uk> wrote in message
news:mq************@news.flash-gordon.me.uk...
member_offset[count]=member_offset[count-1]+member_size[count-1];
Yes of course, careless writing.
Some real systems have alignment requirements for various types, so if
you try to read a 2 byte integer from an odd address they will generate
an error (often called a "bus error" or SIGBUS) and abort your program.
Since as far as the compiler is concerned a is an array of char (and
char does not have any alignment requirements) a could start on an odd
address. So casting it to an int* then dereferencing it could crash your
program (the C standard allows this).
Impressive, i had no idea about this!
So what you have to do is the reverse of when you put the data in. I.e.:
memcpy(i, a+member_offset[0], member_size[0]);
printf("Integer is: %d\n", i);
Ideally you should add at least basic error trapping, i.e.
if (member_size[0] != sizeof i)
fputs("ERROR: Size miss-match!\n");
else {
memcpy(i, a+member_offset[0], member_size[0]);
printf("Integer is: %d\n", i);
}


All is perfectly clear now, but why the error checking since i should have
explicitly set member_size[0] to sizeof(i) earlier in the program anyway?
Feb 9 '06 #10

P: n/a
stathis gotsis wrote:
"Flash Gordon" <sp**@flash-gordon.me.uk> wrote in message
news:mq************@news.flash-gordon.me.uk...
<snip>
Impressive, i had no idea about this!


That is what we are here for, increasing peoples knowledge :-)
So what you have to do is the reverse of when you put the data in. I.e.:
memcpy(i, a+member_offset[0], member_size[0]);
printf("Integer is: %d\n", i);
Ideally you should add at least basic error trapping, i.e.
if (member_size[0] != sizeof i)
fputs("ERROR: Size miss-match!\n");
else {
memcpy(i, a+member_offset[0], member_size[0]);
printf("Integer is: %d\n", i);
}


All is perfectly clear now, but why the error checking since i should have
explicitly set member_size[0] to sizeof(i) earlier in the program anyway?


It's called defensive programming. Although you believe that the size is
always explicitly set, there is always the possibility of error. Such as
your assumption that an int is 2 bytes! It also helps to catch something
having gone wrong corrupting the contents of member_size.
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
Feb 10 '06 #11

This discussion thread is closed

Replies have been disabled for this discussion.