In my code I'm trying to use dummy objects to perform modularity in C.
At the moment I specify important function useful for every objects via function pointers, like destructors, toString
, equals
as follows:
typedef void (*destructor)(const void* obj);
typedef void (*to_string)(void* obj, int bufferSize, const char* buffer);
typedef bool (*equals)(void* obj, const void* context);
In my code base then I use function pointer compatible to the given typedef
to abstractly handle objects, for example:
struct Foo {
int a;
} Foo;
void destroyFoo1(const Foo* p) {
free((void*)p);
}
int main() {
//...
Foo* object_to_remove_from_heap = //instance of foo
destructor d = destroyFoo1;
//somewhere else
d(object_to_remove_from_heap, context);
}
The code compiles and normally it would generate only a warning (destructor first parameter should be a const void*
but instead it is a const Foo*
).
However,
since I've enabled -Werror
, the "invalid pointer cast" is treated as an error.
To solve this issue, I need to cast the function pointer, as follows:
destructor d = (destructor)destroyFoo1;
I know per standard const void*
and const Foo*
may have different memory size, but I assume the platform where the code is deployed const void*
and const Foo*
are allocated in the same memory space and have the same size. In general I assume the cast of function pointer where at least one pointer argument is changed into some other pointer is a safe casting.
This is all good but the approach shows its weakness when, for example, I need to change the signature of destructor
type, for example by adding a new const void* context
parameter. Now the interesing warning is silenced and the number of parameters in the function pointer call mismatch:
//now destructor is
typedef void (*destructor)(const void* obj, const void* context);
void destroyFoo1(const Foo* p) {
free((void*)p);
}
destructor d = (destructor)destroyFoo1; //SILCENCED ERROR!!destroyFoo1 has invalid parameters number!!!!
//somewhere else
d(object_to_remove_from_heap, context); //may mess the stack
My question is: is there a way to check if a function pointer can indeed be safely casted into another (and generating a compile error if not)?, something like:
destructor d = CHECK_IF_FUNCTION_RETURNS_VOID_AND_REQUIRE_2_VOID_POINTERS(destroyFoo1);
Something that if we pass destroyFoo1
everything is fine but if we pass destroyFoo2
the compiler complains.
Below a code that summarizes the problem
typedef void (*destructor)(const void* obj, const void* context);
typedef struct Foo {
int a;
} Foo;
void destroyFoo1(const Foo* p, const void* context) {
free((void*)p);
if (*((int*)context) == 0) {
printf("hello world\n");
}
}
void destroyFoo2(const Foo* p) {
free((void*)p);
}
int main() {
//this is(in my case) safe
destructor destructor = (destructor) destroyFoo1;
//this is really a severe error!
//destructor destructor = (destructor) destroyFoo2;
Foo* a = (Foo*) malloc(sizeof(Foo));
a->a = 3;
int context = 5;
if (a != NULL) {
//call a destructor: if destructor is destroyFoo2 this is a SEVERE ERROR!
//calling a function accepting a single parameter with 2 parameters!
destructor(a, &context);
}
}
Thanks for any kind of reply