Discussion:
c++11 and packed structures, bitwise operation with -Wconversion
Zygmunt Ptak
2014-07-08 17:33:17 UTC
Permalink
Hi all,

I have two problems (maybe I misunderstanding something, or maybe I
don't know something).

1) Is it possible to pack structure in c++11 instead of using attribute
packed (__attribute__((packed)))? There is alignas keyword for
structures and classes but using alignas(1) doesn't change structure.
alignas with '1' value is accepted by compiler but default align is used.

2) What is going on third line exactly (for x86_64/i686/avr machine):

/*0:*/ typedef uint8_t u8;
/*1:*/ static u8 MAIN_DDR;
/*2:*/ constexpr u8 BV(u8 v) { return static_cast<u8>(1 << v); }
/*3:*/ inline void setPinAsOut(u8 PIN) { MAIN_DDR |= BV(PIN); }

I want use -Wconversion option, but this option raises warning
for third line:

warning: conversion to ‘u8 {aka unsigned char}’ from ‘int’ may alter its
value [-Wconversion]
inline void setPinAsOut(u8 PIN) { MAIN_DDR |= BV(PIN); }

But types operates on 8 bits, so why compiler uses here bigger type? Or
maybe it's a bug?

This line can be resolved by:
MAIN_DDR = MAIN_DDR | BV(PIN);
or by adding static_cast, but IMO it's not good;-/
The same to & operator, but changing line to:
MAIN_DDR = MAIN_DDR & BV(PIN);
doesn't resolve problem.

I'm using compilers in versions: 4.7.2, 4.8.3, 4.9.

Regards
Zygmunt
Manuel López-Ibáñez
2014-07-08 22:54:59 UTC
Permalink
Post by Zygmunt Ptak
/*0:*/ typedef uint8_t u8;
/*1:*/ static u8 MAIN_DDR;
/*2:*/ constexpr u8 BV(u8 v) { return static_cast<u8>(1 << v); }
/*3:*/ inline void setPinAsOut(u8 PIN) { MAIN_DDR |= BV(PIN); }
I want use -Wconversion option, but this option raises warning
warning: conversion to 'u8 {aka unsigned char}' from 'int' may alter its
value [-Wconversion]
inline void setPinAsOut(u8 PIN) { MAIN_DDR |= BV(PIN); }
But types operates on 8 bits, so why compiler uses here bigger type? Or
maybe it's a bug?
The result of "|" is really converted to int because of promotions,
but -Wconversion has some heuristics to not warn when the conversion
back to a smaller type does not actually alter the value. It seems the
heuristics are not triggering here because of some artifact of how the
C++ FE handles "&=" and "|=". For the equivalent C testcase:

typedef unsigned char u8;
static u8 MAIN_DDR;
u8 BV(u8 v) { return (u8) (1 << v); }
void setPinAsOut(u8 PIN)
{
MAIN_DDR |= BV(PIN);
MAIN_DDR = MAIN_DDR | BV(PIN);
MAIN_DDR &= BV(PIN);
MAIN_DDR = MAIN_DDR & BV(PIN);
}

gcc -Wconversion does not give any warnings, whereas g++ -Wconversion says:

test.c:6:12: warning: conversion to 'u8 {aka unsigned char}' from
'int' may alter its value [-Wconversion]
MAIN_DDR |= BV(PIN);
^
test.c:8:12: warning: conversion to 'u8 {aka unsigned char}' from
'int' may alter its value [-Wconversion]
MAIN_DDR &= BV(PIN);
^
Post by Zygmunt Ptak
MAIN_DDR = MAIN_DDR | BV(PIN);
or by adding static_cast, but IMO it's not good;-/
MAIN_DDR = MAIN_DDR & BV(PIN);
doesn't resolve problem.
I does for me with current development version of GCC as shown above.

Anyway, this is either a bug in the C++ FE or a possible enhancement
of the Wconversion heuristics. Please open a bug report:
https://gcc.gnu.org/bugs/

Cheers,

Manuel.
Zygmunt Ptak
2014-07-09 20:12:57 UTC
Permalink
Post by Manuel López-Ibáñez
Post by Zygmunt Ptak
/*0:*/ typedef uint8_t u8;
/*1:*/ static u8 MAIN_DDR;
/*2:*/ constexpr u8 BV(u8 v) { return static_cast<u8>(1 << v); }
/*3:*/ inline void setPinAsOut(u8 PIN) { MAIN_DDR |= BV(PIN); }
I want use -Wconversion option, but this option raises warning
warning: conversion to 'u8 {aka unsigned char}' from 'int' may alter its
value [-Wconversion]
inline void setPinAsOut(u8 PIN) { MAIN_DDR |= BV(PIN); }
But types operates on 8 bits, so why compiler uses here bigger type? Or
maybe it's a bug?
The result of "|" is really converted to int because of promotions,
but -Wconversion has some heuristics to not warn when the conversion
back to a smaller type does not actually alter the value. It seems the
heuristics are not triggering here because of some artifact of how the
typedef unsigned char u8;
static u8 MAIN_DDR;
u8 BV(u8 v) { return (u8) (1 << v); }
void setPinAsOut(u8 PIN)
{
MAIN_DDR |= BV(PIN);
MAIN_DDR = MAIN_DDR | BV(PIN);
MAIN_DDR &= BV(PIN);
MAIN_DDR = MAIN_DDR & BV(PIN);
}
test.c:6:12: warning: conversion to 'u8 {aka unsigned char}' from
'int' may alter its value [-Wconversion]
MAIN_DDR |= BV(PIN);
^
test.c:8:12: warning: conversion to 'u8 {aka unsigned char}' from
'int' may alter its value [-Wconversion]
MAIN_DDR &= BV(PIN);
^
Post by Zygmunt Ptak
MAIN_DDR = MAIN_DDR | BV(PIN);
or by adding static_cast, but IMO it's not good;-/
MAIN_DDR = MAIN_DDR & BV(PIN);
doesn't resolve problem.
I does for me with current development version of GCC as shown above.
Anyway, this is either a bug in the C++ FE or a possible enhancement
https://gcc.gnu.org/bugs/
Cheers,
Manuel.
I found one report:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56908

But added one year ago;-/
Manuel López-Ibáñez
2014-07-09 22:55:01 UTC
Permalink
Post by Zygmunt Ptak
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56908
But added one year ago;-/
Unfortunately, the C/C++ FEs do not have enough contributors to even
triage all the bugs that we get (3561 unconfirmed and counting), much
less to fix them all (1447 open just for C++). Unless GCC users or
companies step up to fix the bugs that affect them, they are likely to
stay unfixed for a long time.

I wish all bugs were fixed by tomorrow., but this is how it works.

Thanks for taking the time to identify a duplicate. Every little thing helps.

Manuel.

Loading...