tags:

views:

299

answers:

3

I have some code that looks like:

template<unsigned int A, unsigned int B>
int foo() {
  int v = 1;
  const int x = A - B;
  if (x > 0) {
    v = v << x;
  }
  bar(v);
}

gcc will complain about x being negative for certain instantiations of A, B; however, I do perform a check to make sure it is non-negative. What's the best way around this? I know I can cast x to be unsigned int but that will cause warnings about x being larger than the width of v (since it is casting a negative number to be positive). I know there is a work-around that involves creating a new templatized shift function, but I'd like to avoid that if possible.

A: 

Would this work?

const short unsigned int x = A - B;

It's cutting off a lot more bits than need to be cut off, but if your values of A - B are small enough...

Claudiu
To be more concrete: I have `A == 25` and `B == 31`. That gives me `x == -6` which is looks like `111...11010` and truncating it would still give me a rather large number
Edward Luong
+2  A: 

why not make x an unsigned char type and cast it? surely you don't need to shift more than 255 bits?

const unsigned char x = static_cast<unsigned char>(A - B);

or perhaps use masking to ensure that the shift is in bounds like this:

const unsigned int x = static_cast<unsigned int>(A - B) & 0x1f; // limit A-B to have a range of (0 - 31)

EDIT:

in response to the comment here's an idea:

template<unsigned int A, unsigned int B>
int foo() {
  int v = 1;
  const int x = A - B;
  if (x > 0) {
    v = v << (static_cast<unsigned int>(x) & 0x1f);
  }
  bar(v);
}

NOTE: you can replace 0x1f with something like: (CHAR_BIT * sizeof(T) - 1)

EDIT: in response to the latest comment, this code does not issue any warning compiling with: g++ -W -Wall -ansi -pedantic test.cc -o test

#include <iostream>

template<unsigned int A, unsigned int B>
int foo() {
  int v = 1;
  const int x = A - B;
  if (x > 0) {
    v = v << (static_cast<unsigned int>(x) & 0x1f);
  }
  return v;
}

int main() {
    std::cout << foo<1, 3>() << std::endl;
    std::cout << foo<3, 1>() << std::endl;
    std::cout << foo<300, 1>() << std::endl;
    std::cout << foo<25, 31>() << std::endl;
}
Evan Teran
I believe if the right hand side were negative, it would cast it to an `unsigned int` which would make it positive. I don't want to do run the code if the result of `A-B` is negative.
Edward Luong
Since it knows the values at compile time, anything number greater than 32 will flag another warning about shifting by a number greater than the type width. So while this does indeed solve the negative problem, it will raise that second warning.
Edward Luong
I am limiting the value to be 0-32, so there should be no warning
Evan Teran
ahh, sorry, I miss-read that the mask. I thought it was masking the negative sign.
Edward Luong
+2  A: 

Since A and B are known at compile time, not only can you get rid of your warning, but you can also get rid of a runtime if, without any casts, like this:

#include <iostream>
using namespace std;

template< unsigned int A, unsigned int B >
struct my
{
    template< bool P >
    static void shift_if( int & );

    template<>
    static void shift_if< false >( int & ) {}

    template<>
    static void shift_if< true >( int & v ) { v <<= A - B; }

    static void op( int & v ) { shift_if< (A > B) >( v ); }
};

template< unsigned int A, unsigned int B >
int foo()
{
    int v = 1;
    my< A, B >::op( v );
    return v;
}

int main() {
    cout << foo< 1, 3 >() << endl;
    cout << foo< 3, 1 >() << endl;
    cout << foo< 300, 1 >() << endl;
    cout << foo< 25, 31 >() << endl;
    return 0;
}
jwfearn
A very good solution. though it would need tweaking to address the (A - B >= 32) issue. BTW, the if would likely be factored out anyway since the compiler can detect that it will never happen.
Evan Teran
@Evan Teran, Thanks! I noticed the shift-wrap issue, but OP's original code let it wrap so I assumed that's what he wanted. I suspect you're right about compiler optimization but in my version the expression A - B is unsigned thus avoiding a warning.
jwfearn
Ideally, I'd want the code to warn me if I was shifting by too much if that number was positive and not warn me at all if it were negative (since it wouldn't get executed). I guess there is no way of getting around it but this way. Good to know.
Edward Luong
You can make the excessive positive shift an error by declaring, but not defining extra specializations.
MSalters