Warning narrowing conversion (uint32 to uint8) - c++

i have this warning. Can u help me ?
warning: narrowing conversion of ‘action’ from ‘uint32 {aka unsigned int}’ to ‘uint8 {aka unsigned char}’
How to convert uint32 to uint8 securely ?
Thanks

The warning is correct, such a conversion is potentially dangerous (it loses 24 higher bits).
You can tell the compiler that you really want this [unsafe] conversion by using explicit cast:
uint8_t my_byte_var = static_cast<uint8_t>(my_int32_var);
This should suppress the warning.

The narrowing warning/error should only arise if you use list-initialization, e.g.:
uint32_t action = 12345;
uint8_t val{action};
You could instead just use normal assignment:
uint8_t val = action;
The narrowing is still going to happen though, as there is no way for 12345 to fit in a uint8_t, so you'll be left with 9. If you're OK with that, then there's your solution. If not, then it's a good thing there's a warning because you're using the wrong type!

Related

How to disable implicit conversion changes signedness warning in CMAKE

Does anybody know if there is a way to disable this kind of warning given by clang in CMAKE please ?
std::vector<float> v{1.f, 2.f, 1.f};
int i = 1;
float foo = v[i]; // Here we get the warning, variable i should be a size_t
...
Implicit conversion changes signedness: 'int' to 'std::__1::vector<float>::size_type' (aka 'unsigned long')
Clang compiler gives me a warning on v[i] as it wants me to cast it as a size_t which is quite verbose if I do explicit casting everywhere.
Also, I don't want to use size_t. For me this type it is error prone as it breaks the standard substraction when getting negative results. (my vectors will never exceed 2^31 btw)

Clang-Tidy Narrowing Conversion from uint8_t to float

I'm getting a clang-tidy warning that reads narrowing conversion from 'int' to 'float' when I convert from a uint8_t to a float, which to my understanding is not a narrowing conversion since float can represent every integer that a uint8_t can.
Code example:
uint8_t testVar = 8;
float test2 = 2.0f * testVar;
clang-tidy flags the second line of that with the warning cppcoreguidelines-narrowing-conversions: narrowing conversion from 'int' to 'float'. In my IDE, the squiggle shows up under the testVar.
According to the reference, this warning should be flagged if we convert from an integer to a narrower floating-point (e.g. uint64_t to float), but to the best of my knowledge, float is not narrower than uint8_t.
Am I fundamentally misunderstanding these data types, or is something else going on here?
I'm on LLVM version 11.0.0 if that matters.

Recommended way to perform a cast to a smaller type on micro controller in C++

I am programming for the Arduino Due, which is based on a 32-bits micro controller. I want to read an ADC result register (it has width 32 bits if I am right, but it has a true maximum width of 12 bits, which is the resolution of the ADC), and write it at a given location in an array of integers with width 16 bits.
This works:
volatile uint16_t Buf[nchannels];
[... some code...]
void ADC_Handler() {
for (int i = 0; i < nchannels; i++)
{
Buf[i] = (volatile uint16_t) * (ADC->ADC_CDR + channels[i]); // my modification
}
FlagConversion = true;
}
But using instead a more "explicit" cast does not work:
Buf[i] = dynamic_cast<volatile uint16_t *>(ADC->ADC_CDR + channels[i]);
Produces:
"error: cannot dynamic_cast '(((RoReg*)1074528336) + ((sizetype)(((unsigned int)channels[i]) * 4)))' (of type 'RoReg* {aka volatile long unsigned int*}') to type 'volatile uint16_t* {aka volatile short unsigned int*}' (target is not pointer or reference to class)"
and similar unclear errors with static and reinterpret casts:
"error: cannot dynamic_cast '(((RoReg*)1074528336) + ((sizetype)(((unsigned int)channels[i]) * 4)))' (of type 'RoReg* {aka volatile long unsigned int*}') to type 'volatile uint16_t* {aka volatile short unsigned int*}' (target is not pointer or reference to class)"
and
"error: invalid conversion from 'volatile uint16_t* {aka volatile short unsigned int*}' to 'uint16_t {aka short unsigned int}"
Any idea why these more explicit casts fail?
What would be the best practice here?
A 'safe' way to do the cast is to first dereference the pointer to the 32-bit data, then mask out the upper 16 bits of that data item, then static_cast the result to your 16-bit destination data:
Buf[i] = static_cast<volatile uint16_t>( *(ADC->ADC_CDR + channels[i]) & 0x0FFFF );
It is always best to use the 'simplest' form of cast, and to avoid both C-style casts and reinterpret_cast whenever possible. In this case, it makes little sense to cast the pointer, and it is much safer and simpler to convert/cast the data itself.
Actually, the masking with 0x0FFFF is, strictly speaking, entirely unnecessary and superfluous. However, it makes it clear to any future reader of your code that you do, indeed, know what you're doing!

Clang gives me a warning of signedness change, however the code still produces correct output

I was analyzing some warnings in a codebase and got puzzled by this one generated by Clang
Consider the following C++ code:
#include <iostream>
int main(int , char *[])
{
uint32_t val1 = 10;
int32_t val2 = -20;
int32_t result = val1 + val2;
std::cout << "Result is " << result << "\n";
return 0;
}
Clang gives me the following warning when compiling this code with -Wconversion
<source>:9:25: warning: implicit conversion changes signedness:
'unsigned int' to 'int32_t' (aka 'int') [-Wsign-conversion]
int32_t result = val1 + val2;
~~~~~~ ~~~~~^~~~~~
<source>:9:27: warning: implicit conversion changes signedness:
'int32_t' (aka 'int') to 'unsigned int' [-Wsign-conversion]
int32_t result = val1 + val2;
~ ^~~~
2 warnings generated.
GCC also gives me this warning, however I need to provide -Wsign-conversion to trigger it.
The warning says that val2 will be cast to an unsigned int and therefore will loose it's sign. So far so good. However I was expecting that the code above would produce incorrect output, to my surprise it works perfectly fine.
Result is -10
See the program running on both compilers on godbolt.
The cast does not happen in the compiled code and val2 keep its original value. The result of the computation is correct. What is the actual danger that this warning is warning me against? How can I trigger this behaviour? Is the warning bogus?
The second conversion is where things become implementation dependent.
The first (evaluation of expression val1+val2) triggers conversion of val2 to unsigned from signed, which is standard-compliant and documented. The result of the expression is therefore unsigned.
The second (conversion of the resulting unsigned back to signed) is where potential problems ensue. If the unsigned value is not within the defined domain of the target signed type (and in this case, it isn't), implementation behavior ensues, which you cannot assume is portable across the known universe.
What is the actual danger that this warning is warning me against?
The potential danger is that you may have been unaware of the implicit sign conversion, and have made it by accident. If it is intentional and behaves as desired, then there is no danger).
How can I trigger this behaviour?
You already have triggered an implicit sign conversion.
That said, if you want to see some output which may be surprising to you, try this instead:
std::cout << val1 + val2;
Is the warning bogus?
Depends on your definition of bogus.
There definitely is an implicit sign conversion in the program, and therefore if you ask the compiler to warn about implicit sign conversions, then it is entirely correct for the compiler to warn about the implicit sign conversion that is in the program.
There is a reason why this warning option is not enabled by default, nor when enabling "all" warnings using -Wall, nor even when enabling "extra" warnings with -Wextra. These sign conversion warnings warn about a program with well defined behaviour, but which may be surprising to someone who is not paying close attention. A program can be meaningful and correct despite this warning.

Is bitmask necessary when assigning a value from a big type to a small one?

Lets say I want to store the low 16 bits of a uint32_t in a uint16_t on windows, I could do it either
uint32_t value = 123456789;
uint16_t low1 = value; //like this
uint16_t low2 = value & 0xFFFF; //or this
There appears to be no difference in the results but I couldn't find any documentation explicitly stating that this is defined behavior. Could it be different under circumstances X or Y? Or is this just how it works?
The C++ standard guarantees that assignment to and initialization of unsigned types gives you the value modulo 2n, where n is the number of bits in the value representation of the unsigned type.
In Windows all bits participate in the value representation.
Hence using a bitmask serves no purpose other than to put a little stumbling block in the way for the future, when one might change the types.
If you absolutely want to use a mask, e.g. to avoid a compilation warning from an over-zealous compiler, then you can do it in a type-independent way like this, assuming that the type is unsigned:
uint16_t low2 = value & uint16_t(-1);
Which relies on the aforementioned modulo-2n guarantee.
The compiler should give you a warning, that value will be truncated if you use -Wconversion.
warning: conversion to 'uint16_t {aka short unsigned int}' from 'uint32_t {aka unsigned int}' may alter its value [-Wconversion]
uint16_t low1 = value;
^
With bitmask g++ do not produce a warning...