How to cast from bool to void*?
-
03-07-2019 - |
Question
I'm trying to build cairomm for gtkmm on windows using mingw. Compilation breaks at a function call which has a parameter which does a reinterpret_cast of a bool to a void*.
cairo_font_face_set_user_data(cobj(), &USER_DATA_KEY_DEFAULT_TEXT_TO_GLYPHS, reinterpret_cast<void*>(true), NULL);
This is where the code breaks, and reason is "invalid reinterpret_cast from bool to void*". Why is this happening, and how can I modify this line to get it to compile? Need help
Solution
I see this is user data and you have control over what is done with the value, cast the bool to an int first: reinterpret_cast<void *> (static_cast<int> (true))
. Doing this makes sense in that the void* parameter takes the place of template functions in this ANSI-C library. All you need is a true/false value. So, there should be no danger in temporarily encoding this as a pointer as long as it is well documented as such. Really, you would be better off with this: reinterpret_cast<void *> (1)
or reinterpret_cast<void *> (+true)
.
OTHER TIPS
It looks like it should work, according to the standard. Section 3.9.1-7 says bool is an integral type, and 5.2.10-5 says a value of integral type can be explicitly converted to a pointer using reinterpret_cast. It appears that your compiler is not fully standard.
Could you get away with changing the "true" to a 1? Converting between integers and pointer types is an old and dishonorable tradition in C and hence C++, and it would be surprising to find a compiler that wouldn't do it.
Or, if you really really have to do this, try (void *)true. Then wash your hands.
reinterpret_cast is a bad idea. Tell us more about the problem you're trying to solve, and perhaps we'll find a solution without resorting to reinterpret. Why do you want to convert bool to void*?
The only compiler I have that complains about this is GCC (MinGW with GCC 3.4.5) - and I'm not sure why. The standard seems to clearly indicate this is permitted:
3.9.1 Fundamental types
...
Types bool, char, wchar_t, and the signed and unsigned integer types are collectively called integral types.
5.2.10 Reinterpret cast:
...
A value of integral type or enumeration type can be explicitly converted to a pointer.
That said, monjardin's workaround of using reinterpret_cast<void *> (static_cast<int> (true))
or reinterpret_cast<void *> (1)
are reasonable workarounds.
It fails because the cast makes no sense - you are taking a boolean true/false value, and asking the compilre to interpret this as a pointer, which in blunt terms is a memory location. The two arent even remotely related.
Try a newer version of your compiler. I just tested and this cast works on at least gcc 4.1 and above. I don't know exactly how gcc versions map to mingw versions though.
In some situations, it is highly desirable to have the compiler warn or error on code like reinterpret_cast<void*>(true)
, even though this code is apparently legal C++. For example, it aids in porting to 64-bit platforms.
Casting a 64-bit pointer into an integral type that is smaller than a pointer (such as int
or bool
) is often a bug: you're truncating the pointer's value. Furthermore, the C++ specification doesn't seem to guarantee that you can directly cast a pointer into a smaller integral type (emphasis added):
5.2.10.4. A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined.
Likewise, casting a smaller integral type into a 64-bit pointer (as with reinterpret_cast<void*>(true)
) is often a bug as well: the compiler has to fill in the pointer's upper bits with something; does it zero-fill or sign-extend? Unless you're writing low-level platform-specific code for memory mapped I/O access or DMA, you usually don't want to be doing this at all, unless you're doing something hacky (like stuffing a Boolean into a pointer). But the C++ specification doesn't seem to say much about this case other than that it is implementation-defined (footnote omitted):
5.2.10.5. A value of integral type or enumeration type can be explicitly converted to a pointer.*
A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined.
@monjardin suggested reinterpret_cast<void*>(static_cast<int>(true))
. If the origin of the error was the mismatch between the integral type's size and the pointer size, then this will work on most 32-bit platforms (where both int
and void*
are 32 bits) but fail on most 64-bit platforms (where int
is 32 bits and void*
is 64 bits). In that case, replacing int
in this expression with a pointer-sized integer type such as uintptr_t
or DWORD_PTR
(on Windows) should work, since conversions between bool
and pointer-sized integers are allowed, and so are conversions between pointer-sized integers and pointers.
Later versions of GCC have the following warning suppression options, but not for C++:
-Wno-int-to-pointer-cast (C and Objective-C only)
Suppress warnings from casts to pointer type of an integer of a different size.-Wno-pointer-to-int-cast (C and Objective-C only)
Suppress warnings from casts from a pointer to an integer type of a different size.