I want to implement a function that takes either int or a char pointer as the input and stores then as a void pointer but I also want to store the type of the pointer that has been passed so is there a way that I could find the type of the pointer
CodePudding user response:
I want to implement a function that takes either int or a char pointer as the input
You can't. You can implement a function that accepts a void pointer, or a function that accepts a union containing int pointer and char pointer members, or a function that accepts an int pointer and a char pointer as separate arguments, or two functions, one accepting an int pointer and the other a char pointer, but you cannot implement one that accepts a choice of int pointer or char pointer via the same argument. These two pointer types are not compatible.
If you opt for the void pointer argument then you benefit from implicit conversion of any object pointer type (not just int pointer and char pointer) to void pointer, but this just moves the locus of your actual issue:
and stores then as a void pointer but I also want to store the type of the pointer that has been passed so is there a way that I could find the type of the pointer
If you convert a pointer of one type to a different pointer type then the result does not contain any information about the original type. If you want such information then you need to convey and store it separately. With the void pointer option, that would mean another argument to your function that conveys the additional information. Every available option requires some form of that.
CodePudding user response:
If you are using a C11 compiler then you could use _Generic
to dispatch an type identifier between int*
and char*
.
typedef enum {
VT_INT,
VT_CHAR,
} void_type_id;
typedef struct {
void_type_id id;
void *ptr;
} void_typed;
void_typed make_void_typed_impl(void_type_id id, void* ptr) {
return (void_typed) { .id = id, ptr = ptr };
}
// returns type id from the pointer type
#define vt_id(ptr) _Generic((ptr), char*: VT_CHAR, int*: VT_INT)
#define make_void_typed(ptr) \
make_void_typed_impl(vt_id(ptr), ptr)
Exemplary usage:
#include <stdio.h>
int main() {
char *c_ptr = 0;
int *i_ptr = 0;
void_typed vc = make_void_typed(c_ptr);
void_typed vi = make_void_typed(i_ptr);
printf("vc=(%d, %p)\n", (int)vc.id, vc.ptr);
printf("vi=(%d, %p)\n", (int)vi.id, vc.ptr);
}
Prints:
vc=(1, (nil))
vi=(0, (nil))
This solution can be easily extended to support other types.
CodePudding user response:
You can perform sort-of-overloading on types using _Generic macro if you are using a C11 compliant compiler, but the type has to be stored separately, be it a void*
or an union with some sort of tag.
The closest to what you want is sth like this:
#include <stdio.h>
#include <assert.h>
struct TaggedUnion
{
void* contents;
/* union {int* i;char* c;} contents; */
enum Type {CHAR_TYPE, INT_TYPE} type;
};
struct TaggedUnion storeI(int* x)
{
struct TaggedUnion ret = {.contents =x, .type=INT_TYPE};
return ret;
}
struct TaggedUnion storeC(char* x)
{
struct TaggedUnion ret = {.contents =x, .type=CHAR_TYPE};
return ret;
}
#define storeTagged(x) _Generic((x),\
int*: storeI,\
char*: storeC)(x)
int* getInt(const struct TaggedUnion x)
{
return x.type == INT_TYPE ? x.contents : NULL;
}
char* getChar(const struct TaggedUnion x)
{
return x.type == CHAR_TYPE ? x.contents : NULL;
}
void fi(int* arg)
{
printf("i\n");
}
void fc(char* arg)
{
printf("c\n");
}
#define ff(x) _Generic((x),\
int*: fi,\
char*: fc)(x)
int main(int argc, const char* argv[])
{
printf("entry\n");
int* i;
char* c;
ff(i);
ff(c);
struct TaggedUnion ti = storeTagged(i);
struct TaggedUnion tc = storeTagged(c);
assert(ti.type == INT_TYPE);
assert(tc.type == CHAR_TYPE);
return 0;
}