Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Union cannot be defined in a type specifier

Tags:

c++

c++11

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

like image 624
Ashton Avatar asked Nov 15 '25 14:11

Ashton


2 Answers

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;
}
like image 183
jdehesa Avatar answered Nov 17 '25 09:11

jdehesa


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 };
like image 35
Brian Bi Avatar answered Nov 17 '25 09:11

Brian Bi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!