We have an anonymous type, typedefed to void *, which is the handle for an API (all code in C11). It is deliberately void * as what it is pointing to changes depending on the platform we are compiled for and we also don't want the application to try dereferencing it. Internally we know what it should be pointing to and we cast it appropriately. This is fine, the code is public, we've been using it for years, it cannot be changed.
The problem is that we now need to introduce another one of these, and we don't want the user to get the two confused, we want the compiler to throw an error if the wrong handle is passed to one of our functions. However, all of the versions of all of the C compilers I have tried so far (GCC, Clang, MSVC) don't care; they know that the underlying type is void * and so anything goes (this is with -Wall and -Werror). Putting it another way, our typedef has not achieved anything, we might as well have just used void *. I have also tried Lint and CodeChecker, who also don't seem to care (though you could probably question my configurations for these). Note that I am not able to use -Wpedantic as we include third party code where that wouldn't fly.
I have tried making the new thing a specific typedefed pointer rather than a void * but that doesn't entirely fix things as the compiler is still happy for the caller to pass that new specific typedefed pointer into the existing functions that are expecting the existing handle typedef.
Is there (a) a way to construct a new anonymous handle such that the compiler will not allow it to be passed to the existing functions or (b) a checker that we can apply to pick the problem up, at least in our own use of these APIs?
Here is some code to illustrate the problem:
#include <stdlib.h>
typedef struct {
int contents;
} existingThing_t;
typedef void *anonExistingHandle_t;
typedef struct {
char contents[10];
} newThing_t;
typedef void *anonNewHandle_t;
typedef newThing_t *newHandle_t;
static void functionExisting(anonExistingHandle_t handle)
{
existingThing_t *pThing = (existingThing_t *) handle;
// Perform the function
(void) pThing;
}
static void functionNew(anonNewHandle_t handle)
{
newThing_t *pThing = (newThing_t *) handle;
// Perform a new function
(void) pThing;
}
int main() {
anonExistingHandle_t existingHandle = NULL;
anonNewHandle_t newHandleA = NULL;
newHandle_t newHandleB = NULL;
functionExisting(existingHandle);
functionNew(newHandleA);
// These should result in a compilation error
functionExisting(newHandleA);
functionNew(existingHandle);
functionExisting(newHandleB);
return 0;
}