1

We have a handle type declared like:

typedef void *config_h;

We have a function declared like:

void func(config_h hConfig);

I called it like this:

config_h hConfig;
func(&hConfig);

Not even a warning. Things I cannot change about this project: It is C++11, and compiled with -fpermissive. The config_h typedef is done in a file that is also compiled by C compilers, btw..

I looked here: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

Looked like -Wstrict-aliasing might be the ticket, but it was not. I get loads of warnings about various pointer conversions, but not this one.

The question is "What is the narrowest warning (or preferably error) I can enable to prevent this problem?"

Bonus question: If stuff like this drives me nuts, is switching to clang likely to pay dividends?

zzxyz
  • 2,953
  • 1
  • 16
  • 31
  • 3
    `&hConfig` is a `void**`. Any data pointer, including `void**`, can be converted to `void*`. How should the compiler know that this particular conversion isn't what you wanted? – Joseph Sible-Reinstate Monica Sep 19 '18 at 01:48
  • 1
    `void *` is a generic pointer. Anything can be converted to a `void *`. You're basically getting what you're asking for. This is a typical situation when calling C code is involved. This is why you always write a wrapper for C code that takes fully-typed parameters, and passes them to C code as a generic void pointer, so that the C++ code is sill type-safe. Do whatever you need to do to have a type-safe facade for C code, to avoid this problem. – Sam Varshavchik Sep 19 '18 at 01:50
  • @SamVarshavchik - This is 200,000 lines of C code written in the 80s, ported to C++ in the early 2000s. `Wrapping` the Cisms would be....a fruitless endeavor. That said...`Uintptr_t` or `char*` would be possible....for at least these handles. And to clarify, I am not calling C code. The header file is just compiled by multiple compilers. – zzxyz Sep 19 '18 at 02:03
  • 2
    One way to do handles is `typedef struct nonexisting_handle_struct_1 *handle1; typedef struct nonexisting_handle_struct_2 *handle2;` etc – user253751 Sep 19 '18 at 02:18
  • @immibis - thank you...that's a good point...I was just thinking about forward-declaring the backing objects...I might prefer your method since this is in-theory in a "public" header. I honestly just had no idea how little type-safety `void *` had. I knew it was basically none, but I thought at least differing levels of indirection could cause an explicit cast to be required.... – zzxyz Sep 19 '18 at 02:22
  • I would suggest changing the definition to `typedef char *config_h;` – M.M Sep 19 '18 at 04:51
  • 1
    The comment of @immibis reminds me to a technique called "opaque structs" (which we especially use for C API bindings of C++ libraries). Googling "C struct opaque", I found e.g. [SO: Opaque C structs: how should they be declared?](https://stackoverflow.com/q/3965279/7478597). – Scheff's Cat Sep 19 '18 at 05:55
  • Looking at results of `Find all "typedef.*void\s*\*.*_h", Regular expressions` and sobbing. – zzxyz Sep 19 '18 at 17:16
  • Oh good...one of the handles is returned interchangeably as two different types through various public functions. Except the public APIs can't treat them interchangeably. So much rage.... – zzxyz Sep 19 '18 at 18:34

1 Answers1

1

As was pointed out to me by several folks in the comments (thanks all), even differing levels of indirection aren't considered when it comes to void*. I verified this in other compilers. So the answer to my actual question, best I can tell, is that there is no warning or error that can be enabled, regardless of compiler.

That said, if the void* actually points to a concrete type, it is relatively painless, even in very large solutions to clean this up. I went with forward-declaring the types:

struct config_o;
typedef config_o* config_h;
//removed typedef void* config_h;

I was able to clean up about 40 handle types in 200,000 lines of code in significantly less than 8 hours. Found (and fixed) several serious bugs while I was at it, which consumed the majority of the time.

zzxyz
  • 2,953
  • 1
  • 16
  • 31