I try to include a c code library from Github in my C++ application but encountered some compile error.
Errors for the original code:
'_u16' cannot be defined in a type specifier
non-constant-expression cannot be narrowed from type 'int' to 'uint8_t' (aka 'unsigned char') in initializer list [-Wc++11-narrowing]
The application is compiled by Clang 10.0.1 using CMake on macOS with following in CMakeLists.txt:
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
Following is the original code:
inline static void UInt16SetBE(void *b2, uint16_t u)
{
*(union _u16 { uint8_t u8[16/8]; } *)b2 = (union _u16) { (u >> 8) & 0xff, u & 0xff };
}
Following is my code after adding casting to try to resolve the error:
inline static void UInt16SetBE(void *b2, uint16_t u)
{
*(union _u16 { uint8_t u8[16/8]; } *)b2 = (union _u16) { (uint8_t)((u >> 8) & 0xff), (uint8_t)(u & 0xff) };
}
Errors after my change:
'_u16' cannot be defined in a type specifier
Does anyone know what the syntax of the declaration means?
*(union _u16 { uint8_t u8[16/8]; } *)b2
They are declaring a union type, _u16, which contains an array of two bytes, and then they pretend the pointer b2 is a pointer to a union of that type. Then, they take u and take the upper and lower bytes of it, and have them assigned to the two positions of that array respectively. It is a rather messy way of setting a big-endian 16-bit value.
Your attempt to fix the compilation error was in the right direction, but for it to work with Clang I think you need to declare the union type first so the compiler recognizes it in the right-hand side of the assignment.
inline static void UInt16SetBE(void *b2, uint16_t u)
{
union _u16 { uint8_t u8[16/8]; };
*(union _u16 *)b2 = (union _u16) { (uint8_t)((u >> 8) & 0xff), (uint8_t)(u & 0xff) };
}
That said, that code is quite confusing, and there are simpler ways of doing the same, such as simply:
inline static void UInt16SetBE(void *b2, uint16_t u)
{
((uint8_t *) b2)[0] = (uint8_t) (u >> 8) & 0xff;
((uint8_t *) b2)[1] = (uint8_t) u & 0xff;
}
In C, the expression
*(union _u16 { uint8_t u8[16/8]; } *)b2
defines a union with tag _u16, casts b2 to a pointer to that newly-defined union type, then dereferences the pointer. However, in C++, attempting to define a class or union type inside a cast in this manner is invalid syntax. We can fix this by moving the definition ahead:
union _u16 { uint8_t u8[16/8]; };
*(union _u16*)b2 = (union _u16) { (u >> 8) & 0xff, u & 0xff };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With