Reputation: 12920
I have found code of this form in a local project:
#include <iostream>
const unsigned LOWER_LIMIT = 0;
int main(int argc, char* argv[])
{
if (argc > 1) {
unsigned a = atoi(argv[1]);
if (a < LOWER_LIMIT) {
// if (a - LOWER_LIMIT > a) {
std::cerr << "lower\n";
a = LOWER_LIMIT;
}
}
}
The atoi()
part is only for runability of the demo and is supplied from a different part of the software in the real case. Naturally this code produces a warning with a recent g++ -Wall -Wextra
:
comparison of unsigned expression < 0 is always false
The maintainer does not want to change the comparison because the constant LOWER_LIMIT might be changed in the future and he wants it to still work without changes. Additionally he wants the type to be unsigned also. My first idea to suppress the warning is
if (a - LOWER_LIMIT > a) {
which uses an unsigned underflow. But this solution is somewhat hard to understand for some people. I found a different solution:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
if (a < LOWER_LIMIT) {
#pragma GCC diagnostic pop
This works, but I assume from the GCC
part that this is not a portable solution.
Is there a portable, generic and easy to understand way to avoid this warning and still maintain the ability for some developer to simply change the constant without having to know about this part of the code base where the comparison is happening?
EDIT: I have a template solution for this, but it feels like overkill:
template<typename NumericTypeA, typename NumericTypeB>
typename std::enable_if<std::is_unsigned<NumericTypeA>::value &&
std::is_unsigned<NumericTypeB>::value, bool >::type
is_smaller_than(NumericTypeA a, NumericTypeB b)
{
return (a - b > a);
}
template<typename NumericTypeA, typename NumericTypeB>
typename std::enable_if<std::is_signed<NumericTypeA>::value &&
std::is_signed<NumericTypeB>::value, bool >::type
is_smaller_than(NumericTypeA a, NumericTypeB b)
{
return (a < b);
}
EDIT 2: If the compiler detects that the comparison is always false, wouldn't it be necessary for it to check the following statement and detect that this is ok (also suggested in a response)?
if ((LOWER_LIMIT > 0) && (a < LOWER_LIMIT)) {
It kind of feels that the compiler should accept this without a warning, but unfortunately it does warn in that case.
Upvotes: 1
Views: 744
Reputation: 29013
If you wrap your constant in a do nothing function that just returns the constant's value the warning should go away.
#include <iostream>
const unsigned LOWER_LIMIT = 0;
constexpr auto get_lower_limit() { return LOWER_LIMIT; }
int main(int argc, char* argv[])
{
if (argc > 1) {
unsigned a = atoi(argv[1]);
if (a < get_lower_limit()) {
std::cerr << "lower\n";
a = LOWER_LIMIT;
}
}
}
Upvotes: 2
Reputation: 217065
There are probably ways to work around the check of compiler with extra layer:
if (std::less<>()(a, LOWER_LIMIT)) // extra function
Probably overkill with template:
template <std::size_t Limit>
struct DoesIfLess
{
template <typename F>
void operator ()(std::size_t a, F&&f) {
if (a < Limit) {
std::forward<F>(f)();
}
}
};
template <>
struct DoesIfLess<0>
{
template <typename F>
void operator ()(std::size_t, F&&) {}
};
And then
DoesIfLess<LOWER_LIMIT>(a, [&](){std::cerr << "lower\n"; a = LOWER_LIMIT; });
or with C++17
if constexpr (LOWER_LIMIT != 0) {
if (a < LOWER_LIMIT) {
std::cerr << "lower\n";
a = LOWER_LIMIT;
}
}
Upvotes: 2
Reputation: 238281
Is there a portable #pragma?
No. #pragma
is by definition implementation defined.
Warning options in particular vary wildly across compilers.
The maintainer does not want ...
Since there is a maintainer involved, I presume that the code in question is in an included header provided by a third party.
You can configure your compiler to not report warnings from third party headers. In the case of GCC, you can use -isystem
option in place of -I
. This way you don't need to worry about their warnings.
If you want to support multiple platforms, you probably need to use a cross platform build system generator such as cmake. In cmake, you can designate system include dirs with the SYSTEM option - although not all systems necessarily support this concept.
Upvotes: 2