Here is the context of my question. I have this code :
typedef enum DType { float32 } DType;
typedef struct Array {
void *data;
DType dtype;
...
} Array;
The whole idea is to cast the pointer data
to the adequate type depending on the value of the field dtype
. Although it is possible to just switch
on the dtype
everytime it is needed, I would like to know if it's possible to have a function / macro encapsulating that logic and usable anywhere needed.
To further illustrate, here is a non-solution I came up with :
void *cast_data_pointer(Array *arr) {
switch (arr->dtype) {
case float32:
return (float *) arr->data;
default:
exit(1);
}
}
Although the type casting logic is correct, it cannot be used because it requires to know in advance which pointer type will be returned :
Array arr = ...; # with dtype float32
void *data_ptr;
data_ptr = cast_data_pointer(&arr); # wrong because data_ptr is still a void pointer
Array arr = ...; # with dtype float32
float *data_ptr;
data_ptr = cast_data_pointer(&arr); # wrong because it required to know in advance that the function will return a float pointer
Having such a function / macro would avoid repetition as well as would be more flexible when I'll add more dtypes to the DType
enum in the future.
The whole idea is to cast the pointer
data
to the adequate type depending on the value of the fielddtype
. Although it is possible to justswitch
on thedtype
everytime it is needed, I would like to know if it's possible to have a function / macro encapsulating that logic and usable anywhere needed.
No, not as you seem to mean.
It is not possible to do what you want with a macro, because macros work at compile time, consuming and producing source code. They know nothing about the values of variables, and that's the basis on which you say you want to make decisions.
You also cannot do it with a function, because you run into the issue described in the question: you need to know the chosen data type in order to interpret the function result, or else work with a function result such as a tagged union that leaves you in the same place you started.
More generally, in C, every expression has a data type that is known at compile time, and which therefore cannot vary according to the runtime value of any variable. Choosing behavior based on variables' values requires using conditional statements and / or conditional expressions.
I could imagine various kinds of macro tooling with which you could ease generating the kind of conditional code you will need, but details are situation specific.
On the other hand, you might want to consider approaching the problem in a different way. There are several alternatives to consider, such as:
create type-specific array-wrapper data types and corresponding functions. You could avoid tedious repetition by preparing one or several macros that generate the needed declarations and definitions. This is more or less a C version of C++ templates.
implement generic behavior by associating appropriate type-specific functions with your data, that know without referencing a variable how properly to interpret the generic data structure as having elements of the appropriate type. The callback function used by qsort()
is the canonical example of such an approach. I imagine you may need several such functions, in which case this would amount to a vtable.
rely on the client to do conversions. Make the array-management functions type-agnostic by having them work in terms of untyped items of specified size (for example, using memcpy()
instead of =
). This, too, is reminiscent of qsort()
.