Force a parameter to be lower than something? (how to cause assertion)

This might be a little too fancy but I was just wondering if it's possible to do in C++.

Suppose a function:
1
2
unsigned short int rand(const unsigned short int& lower_limit, const short unsigned int& upper_limit)
{ return (rand % (upper_limit - lower_limit + 1)) + lower_limit; }

(by the way it's correct right?)

How do I enforce that upper_limit is always higher than lower_limit or vice versa.

How can I cause an assertion? An error to the compiler?

Anyways do modifier overloads work with return types?
eg. long long int func(){} vs int func(){}?

Can we use template for return type? Will template also consider modifiers?

Last edited on
Your code looks like perfectly legal C++ to me. Have you tried compiling and running it, to see if it does what you want?

How do I enforce that upper_limit is always higher than lower_limit or vice versa.

A simple if statement before the return statement can check for that.

How can I cause an assertion?

https://en.cppreference.com/w/cpp/error/assert

An error to the compiler?

The compiler can only give errors for things that are known to be wrong at compile-time, when the compiler processes that line. One way to make that possible is to make it a template function, and instead of passing the limits in as arguments, specify them as template parameters. Then, use static assertions to test the values.

That's only possible if, when you call the function, you're using values for the limits that are known at compile-time.
Okay thanks yea it seems better to write if statement before return. But I get warning saying that not all paths give a return statement. So what can I do to remove this warning (in program)?

Can I assert and have a message displayed so that the user knows where the assertion came from?

The compiler can only give errors for things that are known to be wrong at compile-time, when the compiler processes that line. One way to make that possible is to make it a template function, and instead of passing the limits in as arguments, specify them as template parameters. Then, use static assertions to test the values.

That's only possible if, when you call the function, you're using values for the limits that are known at compile-time.


Did not understand. Sorry I'm noob.
Last edited on
Okay thanks yea it seems better to write if statement before return. But I get warning saying that not all paths give a return statement. So what can I do to remove this warning?

Make sure every path includes a return statement, that returns a value of the expected type.

Typically, you want to decide on single value that is returned when an error occurs, e.g. -1.

Can I assert and have a message displayed so that the user knows where the assertion came from?

Yes, as shown by the example section in the link I posted.

Did not understand. Sorry I'm noob.

Don't worry, this is advanced stuff. If you're posting in the Beginners section here, it's almost certainly something you want to leave for later.

All you need to understand is that the compiler can only throw errors for something it can see is wrong at compile-time.
It only has condition in the link.. Can I cause message in the assertion dialogue itself?

I thought template is only for making compile-time overloads for the function. But in this case I'm passing arguments to the function which is run-time and not compile-time right? If it were constant rvalue then I would be able to do that is it (then that is intelligent)?

Sorry I should have been more specific with my confusion. And also I don't know much about template programming just know the basic concept of template, haven't ever used it.

I still don't understand why are we using template? Why template?
Last edited on
You could include <cassert> and use the assert macro.

1
2
3
4
5
6
7
#include <cassert>

unsigned short int rand(const unsigned short int& lower_limit, const short unsigned int& upper_limit)
{
	assert(lower_limit <= upper_limit);
	return (rand() % (upper_limit - lower_limit + 1)) + lower_limit;
}


This will terminate the program with an error message if the assertion fails.

The assertions can be turned off by defining NDEBUG. This is useful if you only want to check assertions in debug builds but don't want the extra overhead in the final release build.
Last edited on
How about telling the user what caused the assertion and where it was??
It only has condition in the link.

Look again. It also shows the name of the file, and the line number. That's what you wanted, right - to know where the error occurred?

I thought template is only for making compile-time overloads for the function.

As well as templating something on a type, you can template something on a value. This is useful, because the arguments that you're templating something on are known at compile-time. This means that you can do compile-time checks on the values that you're using as template parameters.

If the values of the limits were specified as template values, then the compiler would know what those values are at every point where the function is called, and could perform the check that you want.

But in this case I'm passing arguments to the function which is run-time and not compile-time right?

Correct.

If it were constant rvalue then I would be able to do that is it (then that is intelligent)?

No. When compiling the function, the compiler cannot know what arguments were passed into the function by the calling code. Which means the compiler cannot do any checks based on those values.

I still don't understand why are we using template? Why template?

Because template parameters are known at compile-time.
> How about telling the user what caused the assertion and where it was??

1
2
3
4
5
int rand( int minv, int maxv )
{
    assert( maxv>minv && "in call of function rand: error - maxv is not greater than minv!" ) ;
    return std::rand() % (maxv-minv+1) + minv ;
}
Oh so because you use template (otherwise this doesn't happen I take it), the compiler will actually read the values of the function call and you can then use that in the function? Thanks a lot!

Same as assertion though right?

Also can you change the body depending on the output of the template by the way? Is that something that is possible?

How about telling the user what caused the assertion and where it was??

Most implementations will print the file name, the line number, function name and the condition when an assertion fails.

test: test.cpp:5: short unsigned int rand(const short unsigned int&, const short unsigned int&): Assertion `lower_limit <= upper_limit' failed.
Aborted



templates...

Templates could be used if the upper and lower limits are known at compile time. The advantage is that you would be able to get a compilation error, instead of having to run the program to trigger the assertion, but this only works if the upper and lower limits are known at compile time.

1
2
3
4
5
6
7
8
9
10
11
template <unsigned short lower_limit, unsigned short upper_limit>
unsigned short rand()
{
	static_assert(lower_limit <= upper_limit);
	return (rand() % (upper_limit - lower_limit + 1)) + lower_limit;
}

int main()
{
	rand<5, 2>(); // error: static assertion failed
}
Last edited on
Oh that makes sense. Thanks a lot Mikey and Peter
Another option is to swap the upper and lower limits if needed. This will still return a value in the range, but it's likely that it really just masks a bug in the calling code.

A third option is to code it away by passing a lower limit and a size:
unsigned short rand (unsigned short &lower_limit, unsigned short rangeSize);
But this leaves you open to overflow if lower_limit+rangeSize is larger than what unsigned short can hold. And if the caller usually has the upper and lower limits, they will just call it with rand(lowerLimit, upperLimit-lowerLimit) which will will overflow if lowerLimit > upperLimit

For the type of code that I write (long running daemon processes), it's really useful to in any debugging code to print the arguments to the function that failed. Simply knowing that rand() failed might not be helpful if you think you're always calling it correctly. On the other hand, if you see that one argument or the other is way off, it can really help debugging.

Above all, document your code. A simple comment in the header file saying that the caller is responsible for assuring that lowerLimit <= upperLimit goes a long way.
You're welcome - hope it helped!
Topic archived. No new replies allowed.