简体   繁体   中英

strange compiler warnings of C++11 Type checking code

I'm writing a generic adder with carry/overflow check and I make heavy use of c++11 type check features.

this is my code:

#include <iostream>
using namespace std;
#define MIN_OF(TYPE) ( (std::is_signed<decltype(res)>::value) ? \
                                (1 << ( ( sizeof(decltype(res)) * 8 ) - 1)) : \
                                0 )

#define MAX_OF(TYPE) (~MIN_OF(TYPE))


#define ABS(x)  (x < 0 ? -x : x)

class Flags
{
public:
    void setSign(bool x)
    {
        cout << boolalpha;
        cout << "setSign: " << x << endl;
    }
    void setOverflow(bool x)
    {
        cout << boolalpha;
        cout << "setOverflow: " << x << endl;
    }
    void setCarry(bool x)
    {
        cout << boolalpha;
        cout << "setCarry: " << x << endl;
    }
    void setZero(bool x)
    {
        cout << boolalpha;
        cout << "setZero: " << x << endl;
    }
};

template <typename TYPE, TYPE def>
class Value
{
public:
static inline TYPE get()
{
    return def;
}
static inline void set(TYPE x)
{
    cout << "value: " << hex << x << endl;
}
};


template <class A, class B, class RES>
struct ADD
{
static void Do(Flags* _flags)
{
    if (std::is_convertible<decltype(A::get()),decltype(RES::get())>::value)
    {
        decltype(A::get()) _a = A::get();
        decltype(B::get()) _b = B::get();

        decltype(RES::get()) res = _a;

        if (_b != 0)
        {
            res = res + _b;

            if (std::is_signed<decltype(res)>::value)
            {
                unsigned char highestbit_a = static_cast<unsigned char>(0x1 & (_a >> (( sizeof(decltype(_a)) * 8 ) - 1)));
                unsigned char highestbit_b = static_cast<unsigned char>(0x1 & (_b >> (( sizeof(decltype(_b)) * 8 ) - 1)));
                unsigned char highestbit_res = static_cast<unsigned char>(0x1 & (res >> (( sizeof(decltype(res)) * 8 ) - 1)));

                _flags->setSign( (res < 0) );
                _flags->setOverflow( ((highestbit_a & highestbit_b) != highestbit_res) );
            }
            else
            {
                _flags->setSign( false );
                _flags->setOverflow( false );
            }

            bool setCarryFlag = false;

            if (std::is_signed<decltype(_b)>::value)
            {
                if(_b < 0)
                {
                    /* as _b is negative, we add _b to lowest_res, if the result
                     *  is greater as _a, _a + _b (with _b as negative number) would
                     * result in an carry out
                     */
                    setCarryFlag = (static_cast<decltype(_a)>(ABS((MIN_OF(decltype(res)) - _b))) > _a);
                }
                else
                {
                    setCarryFlag = (static_cast<decltype(_a)>((MAX_OF(decltype(res)) - _b)) < _a);
                }
            }
            else
            {
                //get difference of one summand to results highest until carry
                /* MARKED LINE: this branch gets wrongly checked */
                setCarryFlag = ((MAX_OF(decltype(res)) - _b) < _a);
            }

            _flags->setCarry( setCarryFlag );
        }
        else
        {
            if (std::is_signed<decltype(res)>::value)
            {
                _flags->setSign( (res < 0) );
            }
        }

        _flags->setZero( (res == 0) );

        //store result
        RES::set(res);
    }
}
};



int main()
{
Flags* f = new Flags();
ADD<Value<unsigned int, 1>, Value<signed int, 6>, Value<unsigned int, 1>>::Do(f);

return 0;
}

The problem occurs at the "MARKED LINE: ". Normally, I would understand that the compiler wont use this branch, as _b is type of signed int and so is_signed should be true, so the compiler should only use whats in the if-branch and throw away the else branch. But it doesnt seem to do this, as I get the warning:

 warning: comparison between signed and unsigned integer expressions [-Wsign-compare]|

Pointed to this line. But this is not what i want. Any Ideas how to tell the compiler to do the right thing?

comiler is: gcc 4.7.2 on x86-64, debian

Thanks!

It is a warning, not an error. Warnings are there to warn you about things that you likely do not want to do, but are llegal. In this case you want to do it, so ignore it. You could use a pragma to ignore the warning, however I would recomend documenting how you know it is safe for future devs.

The GCC specific pragmas to disable this warning are:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
setCarryFlag = ((MAX_OF(decltype(res)) - _b) < _a);
#pragma GCC diagnostic pop

The problem here is the compiler is compiling all the code, even that which will become dead code, because it doesn't know any better.

The classic solution is, I believe, to provide another template parameter which has a default value of std::is_signed<B::get()>::value , and then specialize on that parameter *. But since you're testing signed-ness for two of your parameters, that's going to get complicated.

Another option may simply be to create a signed variable inside the if condition and use that. That will simply bypass the warning.

if (std::is_signed<decltype(res)>::value) {
    typename std::make_signed<decltype(res)>::type sres = res;
    // now use sres
}

*Xeo reminds me that you can't partially specialize function templates, so the classic solution is actually tag dispatching.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM