Defining the fields of a structure at run-time

  • Thread starter nambissan.nisha
  • Start date
N

nambissan.nisha

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.
 
V

Vladimir S. Oka

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.
 
M

Michael Mair

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
 
F

Flash Gordon

Vladimir said:
Are you, perchance, writing a compiler? ;-)


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).
 
S

stathis gotsis

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

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));

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

   /*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];

   /*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));

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

   getchar();
   return 0;
}
 
F

Flash Gordon

stathis said:
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));[/QUOTE]

If the compiler does not complain here it is broken. If it does you 
[QUOTE="should have fixed it. The correct fix is in #include"]
/*For this example*/
member_size[0]=2;
member_size[1]=1;[/QUOTE]

Bad example. On many systems these days int is 4 bytes. You should be 
using sizeof the appropriate variable.
[QUOTE]
/*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];[/QUOTE]

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.
[QUOTE]
/*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));[/QUOTE]

Since, on all my systems, sizeof(int) is 4 not 2 this will do very bad 
things.
[QUOTE]
/*Print for test purposes*/
printf("Integer is: %d\n",*(a+member_offset[0]));
printf("Character is: %c\n",*(a+member_offset[1]));[/QUOTE]

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.
[QUOTE]
getchar();
return 0;
}[/QUOTE]

<snip problem description which should have been above your response>
 
V

Vladimir S. Oka

Flash said:
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):

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
 
S

stathis gotsis

Flash Gordon said:
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));[/QUOTE]

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>[/QUOTE]

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

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

True as well.
[QUOTE][QUOTE]
/*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];[/QUOTE]

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.[/QUOTE]

I did not fully understand this. I suppose this derives from the fact that i
supposed sizeif(int) is 2 in any system?
[QUOTE][QUOTE]
/*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));[/QUOTE]

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

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

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.[/QUOTE]

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

Flash Gordon

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

Thank you.

/*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];

/*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);
}
 
S

stathis gotsis

Flash Gordon said:
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?
 
F

Flash Gordon

stathis said:

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top