I'm looking to implem开发者_运维知识库ent a dictionary data structure in C which I want to be as generic as possible. That is to say it can accept a pair of values that can be of any type.
How can I init a variable that can accept any types?
And how can I convert that type back to a type I want? (Typecast)
Thanks.
The way to define a variable that can hold values of more than one type is to use unions:
union uu {
int i;
float f;
char c;
char *s;
} x;
x.i is an integer, x.s a char pointer etc. and the size of x is the maximum among the sizes of the members type.
Unfortunately the only way to remember the type of the variable is to store it somewhere else. A common solution is to have a structure like this:
struct ss {
int type;
union {
int i;
float f;
char c;
char *s;
} val;
} x;
And do something like:
#define FLOAT 1
#define INT 2
....
x.val.i = 12;
x.type = INT;
....
if (x.type = INT) printf("%d\n",x.val.i);
....
really ugly.
There are other possibility playing with the macro processor to make it a little bit more pleasent to the eye but the essence is that you have to know in advance the type of the value stored in the union and access the proper field.
This isn't trivial to do, but here's the simplest way to do it:
You need to know all the types you'll support, and what they are when you insert them.
First, you'll actually have a data structure of some struct like
struct { void * data; enum Type tag }
and define an enum Type { int, char*, ... etc }
The void *
is a pointer to data without a type, so you can use it to store to a chunk of memory that contains the data you want to store.
The Type tag stores what the data is so that your code using it can know what is being returned from your data structure.
If you don't need to store the type, and you can cast it back to the correct type when you pull it out of your data structure, then you can omit the Type tag and just store void *
In C there is no easy way to do this. You can use void* and pass around pointers to the types, but there is no concept of templates or generics or variants that exists.
To use void* you would need to treat everything as a pointer so would have to allocate it on the heap. You would then need to cast those pointers to void* when sending to this data structure and then cast back on the other side. This can be tricky because you have to remember what the types were to start with.
If you happen to be programming on Windows you can use Variants to do this, but there is some overhead associated with it.
You probably want void *
.
You've got two options:
void* foo;
which can point to data of any type, or:
union my_types {
int* i;
char* s;
double* d;
bool* b;
void* other;
}
which gives you "automatic" pointer casting (in that you can reference a variable of type "my_types" as if it were any of the above types). See this link for more on unions.
Neither is a great option -- consider C++ for what you're trying to do.
Use a void *
?
You can typecast to whatever you want provided of course you must perform the checking yourself.
Your solution will have to use pointers to void. Pointers to void can hold the address of any object type (not functions) and can be converted back without loss of information.
Best is to use a "parent structure" so that you know the type of the object pointer to:
enum MyType { INTEGER, DOUBLE_STAR };
struct anytype {
enum MyType mytype;
size_t mysize;
void *myaddress;
};
and then
struct anytype any_1, any_2;
int num_chars;
double array[100];
any_1.mytype = INTEGER;
any_1.myaddress = &num_chars;
any_1.mysize = sizeof num_chars;
any_2.mytype = DOUBLE_STAR;
any_2.myaddress = array;
any_2.size = sizeof array;
and, to work with that
foo(any_1);
foo(any_2);
where foo
is defined as
void foo(struct anytype thing) {
if (thing.mytype == INTEGER) {
int *x = thing.myaddress;
printf("the integer is %d\n", *x);
}
if (thing.mytype == DOUBLE_STAR) {
size_t k;
double *x = thing.myaddress;
double sum = 0;
for (k = 0; k < thing.mysize; k++) {
sum += thing.myaddress[k];
}
printf("sum of array: %f\n", sum);
}
}
CODE NOT TESTED
You can use a void *
pointer to accept a pointer to pretty much anything. Casting back to the type you want is the tricky part: you have to store the type information about the pointer somewhere.
One thing you could do is use a struct
to store both the void *
pointer and the type information, and add that to your data structure. The major problem there is the type information itself; C doesn't include any sort of type reflection, so you'll probably have to create an enumeration of types or store a string describing the type. You would then have to force the user of the struct
to cast back from the void *
to the original type by querying the type information.
Not an ideal situation. A much better solution would probably be to just move to C++ or even C# or Java.
enum _myType { CHAR, INT, FLOAT, ... }myType;
struct MyNewType
{
void * val;
myType type;
}
Then you could pass elements of type MyNewType to your functions. Check the type and perform the proper casting.
C offers unions which may suit your purposes, you need to define all the types before compilation but the same variable can point to all the types you include in the union.
精彩评论