6

I have a function declared as:

void foo(unsigned int x)

How can I check that foo() is not receiving negative numbers? I'd like that if I call foo(-1) an exception is thrown, but of course since x is converted automatically to unsigned, I can't check for its positivity like:

if not(x>=0){ do_something();};

or similar checks.

jogojapan
  • 68,383
  • 11
  • 101
  • 131
lucacerone
  • 9,859
  • 13
  • 52
  • 80
  • Why not just make it take an `int`? – nneonneo Sep 16 '12 at 01:13
  • If `foo` is getting passed an implicitly-converted `int`, compare to make sure `x <= std::numeric_limits::max()` – obataku Sep 16 '12 at 01:17
  • but what if what I pass is exactly the max number? – lucacerone Sep 16 '12 at 01:20
  • There's no way to do this check. However, a good compiler will issue a warning when it sees code that requires conversion between signed and unsigned. – Barmar Sep 16 '12 at 01:23
  • @oldrinb What if it's called with an unsigned int larger that that? Basically, there's no way to tell the difference between a large number that was used explicitly, or one that results from conversion of a negative. – Barmar Sep 16 '12 at 01:26
  • @Barmar ... hence "if `foo` is getting passed an implicitly-converted `int`". – obataku Sep 16 '12 at 01:28
  • @oldrinb good point! My idea was that using templates and some form of cast, it would have been possible to perform such a check, but now I am not so convinced.. – lucacerone Sep 16 '12 at 08:28

5 Answers5

2

There is not really any good way to do it, but you can do it with some tricks:

1) declare an undefined better match:

void foo(unsigned int x) {
  //do something
}

void foo(int x);

2) use typeid to determine the type.

wich
  • 16,709
  • 6
  • 47
  • 72
  • 2
    I like the gist of #2, and it becomes better with C++11 utilities. I'm sure [this](http://liveworkspace.org/code/5d72d4ed342ba48c82076217f51efffe) isn't perfect, but it does give the gist of a possibility. – chris Sep 16 '12 at 01:28
  • @chris I like that too. Plus, there is also `std::is_unsigned`, which can be used to keep the specific type of integer (short vs long etc) flexible. – jogojapan Sep 16 '12 at 05:13
  • wich Can you please expand point 2? @jogojapan so there are such functionalities in the standard library? – lucacerone Sep 16 '12 at 08:38
  • @LucaCerone If you look at the code chris prepared on LiveWorkSpace you will see how you can use templates and `static_assert` to avoid the automatic type conversion from signed to unsigned. The only thing I suggested is that instead of `std::is_same<>`, you may as well use `std::is_unsigned<>` directly, like this: http://liveworkspace.org/code/a7ec7f81b452ebf74acc73f990f79d9a# The advantage is that automated conversion from `unsigned short` etc. will still work, while `signed` data types will not be accepted. (Note that all this works at compile time, not by throwing run-time exceptions.) – jogojapan Sep 16 '12 at 09:12
2

You defined the function so it takes an unsigned int argument, so it can't possibly receive a negative value.

If you call it with an int argument, that value will be implicitly converted from int to unsigned int, and the result of that conversion is well defined. It's a feature of the language, and you can't turn it off unless your compiler happens to give you a way to do so -- and I don't know of a compiler that does.

As far as your function is concerned, these two calls:

foo(-1);       /* -1 is converted to unsigned int, to the value UINT_MAX */
foo(UINT_MAX);

are indistinguishable. And that's not the only case; any negative int value will be converted to a valid unsigned value.

If very large values are considered invalid for foo(), then you can check that the parameter value is within some "reasonable" range -- but defining what's "reasonable" isn't always easy.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • I don't think this addresses the question, the question was not whether foo can deal with numbers between `INT_MAX` and `UINT_MAX`, the question was how to get a compiler error when passing an argument of type int instead of unsigned int. – wich Sep 16 '12 at 01:48
1

As wich's answer suggests, you can add an overloaded version of foo that takes an int argument. And since you said you wanted foo(-1) to throw an exception, you can just have the overloaded foo() do that:

#include <iostream>

class Wrong_Type {
public:
    Wrong_Type(){}
};

void foo(unsigned n) {
    std::cout << "In foo, unsigned n = " << n << "\n";
}

void foo(int n) {
    std::cout << "In foo, int n = " << n << "\n";
    throw Wrong_Type();
}

int main() {
    try {
        foo(-1);
    }
    catch (Wrong_Type) {
        std::cout << "Caught Wrong_Type exception\n";
    }
}

When I run this, the output is:

In foo, int n = -1
Caught Wrong_Type exception

But this is not an ideal solution, since it throws the exception based on the type of the argument, not its value. Both 1 and -1 are of type int, so calling foo(1) will also throw an exception. And some otherwise valid calls, such as foo(1.0), become ambiguous.

If you can replace foo(1) by foo(1U), where 1U is a constant of type unsigned int, then this could work.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

I'm not a C++ expert, but ...

You only have a fixed number of bits (usually 32 or 64) to represent an integer. If you know that you will never have an integer less than zero, you can use all of those bits and store a much bigger number. If it's possible that your number is less than zero, then the compiler must reserve a bit for the sign.

It gets a bit more complicated than that - see the Wikipedia article on 2's complement.

Long story short, inside foo if you see that x is "really really big", you might have been passed a negative number.

Check your compiler docs for size of int vs uint.

You'll probably want to either change your param to a regular integer void foo(int x) or just relax knowing that with your current setup x will never be less than zero.

Dan Pichelman
  • 2,312
  • 2
  • 31
  • 42
0

This is a possible solution:

void foo(unsigned int x)
{
  unsigned int last_bit_one = 1 << 31; 
  if ( (x & last_bit_one) != 0)           // check if the last bit of x is 1 
    std::cout << "NEGATIVE NUMBER" << std::endl;
  else
    std::cout << "do something" << std::endl;
}

Of course, we need to assume that you enter an unsigned number smaller than 2147483648.

Cantaro
  • 77
  • 6