tags:

views:

378

answers:

3

Hello!

How can I do something like that (just an example):

any_struct *my_struct = create_struct();
add_struct_member(my_struct, "a", int_member);
add_struct_member(my_struct, "b", float_member);

So that I could load and use a struct instance "from the outside" (at the address addressOfMyStruct) with the given structure here?

any_struct_instance *instance = instance(my_struct, addressOfMyStruct);
int a = instance_get_member(instance, "a");
float b = instance_get_member(instance, "b");

I would also like to be able to create struct instances dynamically this way.

I hope it's clear what I want to do. I know that C/Invoke is able to do it, but is there a separate library to do that?

+3  A: 

Actually demonstrating the code to make this work in C is a bit too involved for an SO post. But explaining the basic concept is doable.

What you're really creating here is a templated property bag system. The one thing you'll need a lot of to keep this going is some assiociative structure like a hash table. I'd say go with std::map but you mentioned this was a C only solution. For the sake of discussion I'm just going to assume you have some sort of hashtable available.

The "create_struct" call will need to return a structure which contains a pointer to a hashtable which makes const char* to essentially a size_t. This map defines what you need in order to create a new instance of the struct.

The "insance" method will essentially create a new hashtable with equal number of members as the template hashtable. Lets throw lazy evualation out the window for a second and assume you create all members up front. The method will need to loop over the template hashtable adding a member for every entry and malloc'ing a memory chunk of the specified size.

The implementation of instance_get_member will simply do a lookup in the map by name. The signature though and usage pattern will need to change though. C does not support templates and must chose a common return type that can represent all data. In this case you'll need to chose void* since that's how the memory will need to be stored.

void* instance_get_member(any_struct_instance* inst, const char* name);

You can make this a bit better by adding an envil macro to simulate templates

#define instance_get_member2(inst, name, type) \
  *((type*)instance_get_member((inst),(name)))
...
int i = instance_get_member2(pInst,"a", int);
JaredPar
Thank you, that sounds not so hard. However, is it possible that I stumble upon issues with padding or something like that?
@frw, you shouldn't because you're using malloc to allocate space for the struct data. Since it's stored in a dictionary there should be one alloc per member. Padding issues only really come about if you're padding them within a contiguous block of memory. You could take that approach although I would avoid it, specifically because of packing reasons ;)
JaredPar
Fine, I'll read a bit about packing and see what I'm going to do. However, using C/Invoke would be the simplest solution. Thanks for now!
+1  A: 

You've gone so far defining the problem that all that's left is a bit of (slightly tricky in some parts) implementation. You just need to keep track of the information:

typedef struct {
    fieldType type;
    char      name[NAMEMAX];
    /* anything else */
} meta_struct_field;
typedef struct {
    unsigned          num_fields;
    meta_struct_field *fields;
    /* anything else */
} meta_struct;

Then create_struct() allocates memory for meta_struct and initialized it to 0, and add_struct_member() does an alloc()/realloc() on my_struct.fields and increments my_struct.num_fields. The rest follows in the same vein.

You'll also want a union in meta_struct_field to hold actual values in instances.

dwc
A: 

I did some of this a long time ago.

The way I did it was to generate code containing the struct definition, plus all routines for accessing it and then compile and link it into a DLL "on the fly", then load that DLL dynamically.

Mike Dunlavey
Unfortunately that's no option for me. Thanks anyway :)