hochl
hochl

Reputation: 12920

Suppress warning that comparison is always false due to constant being zero

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

Answers (3)

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

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

Jarod42
Jarod42

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

eerorika
eerorika

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

Related Questions