Why is the ternary operator used to define 1 and 0 in a macro?
C++CBooleanTernary OperatorC++ Problem Overview
I'm using an SDK for an embedded project. In this source code I found some code which at least I found peculiar. In many places in the SDK there is source code in this format:
#define ATCI_IS_LOWER( alpha_char ) ( ( (alpha_char >= ATCI_char_a) && (alpha_char <= ATCI_char_z) ) ? 1 : 0 )
#define ATCI_IS_UPPER( alpha_char ) ( ( (alpha_char >= ATCI_CHAR_A) && (alpha_char <= ATCI_CHAR_Z) ) ? 1 : 0 )
Does the use of the ternary operator here make any difference?
Isn't
#define FOO (1 > 0)
the same as
#define BAR ( (1 > 0) ? 1 : 0)
?
I tried evaluating it by using
printf("%d", FOO == BAR);
and get the result 1, so it seems that they are equal. Is there a reason to write the code like they did?
C++ Solutions
Solution 1 - C++
You are correct, in C it is tautologous. Both your particular ternary conditional and (1 > 0)
are of type int
.
But it would matter in C++ though, in some curious corner cases (e.g. as parameters to overloaded functions), since your ternary conditional expression is of type int
, whereas (1 > 0)
is of type bool
.
My guess is that the author has put some thought into this, with an eye to preserving C++ compatibility.
Solution 2 - C++
There are linting tools that are of the opinion that the result of a comparison is boolean, and can't be used directly in arithmetic.
Not to name names or point any fingers, but PC-lint is such a linting tool.
I'm not saying they're right, but it's a possible explanation to why the code was written like that.
Solution 3 - C++
You'll sometimes see this in very old code, from before there was a C standard to spell out that (x > y)
evaluates to numeric 1 or 0; some CPUs would rather make that evaluate to −1 or 0 instead, and some very old compilers may have just followed along, so some programmers felt they needed the extra defensiveness.
You'll sometimes also see this because similar expressions don't necessarily evaluate to numeric 1 or 0. For instance, in
#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0)
the inner &
-expression evaluates to either 0 or the numeric value of F_DO_GRENFELZ
, which is probably not 1, so the ? 1 : 0
serves to canonicalize it. I personally think it's clearer to write that as
#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0)
but reasonable people can disagree. If you had a whole bunch of these in a row, testing different kinds of expressions, someone might've decided that it was more maintainable to put ? 1 : 0
on the end of all of them than to worry about which ones actually needed it.
Solution 4 - C++
There's a bug in the SDK code, and the ternary was probably a kludge to fix it.
Being a macro the arguments (alpha_char) can be any expression and should be parenthesized because expressions such as 'A' && 'c' will fail the test.
#define IS_LOWER( x ) ( ( (x >= 'a') && (x <= 'z') ) ? 1 : 0 )
std::cout << IS_LOWER('A' && 'c');
**1**
std::cout << IS_LOWER('c' && 'A');
**0**
This is why one should always parenthesize macro arguments in the expansion.
So in your example (but with parameters), these are both bugged.
#define FOO(x) (x > 0)
#define BAR(x) ((x > 0) ? 1 : 0)
They would most correctly be replaced by
#define BIM(x) ((x) > 0)
@CiaPan Makes a great point in following comment which is that using a parameter more than once leads to undefinable results. For instance
#define IS_LOWER( x ) (((x) >= 'a') && ((x) <= 'z'))
char ch = 'y';
std::cout << IS_LOWER(ch++);
**1**
**BUT ch is now '{'**
Solution 5 - C++
In C it doesn't matter.
Boolean expressions in C have type int
and a value that's either 0
or 1
, so
ConditionalExpr ? 1 : 0
has no effect.
In C++, it's effectively a cast to int
, because conditional expressions in C++ have type bool
.
#include <stdio.h>
#include <stdbool.h>
#ifndef __cplusplus
#define print_type(X) _Generic(X, int: puts("int"), bool: puts("bool") );
#else
template<class T>
int print_type(T const& x);
template<> int print_type<>(int const& x) { return puts("int"); }
template<> int print_type<>(bool const& x) { return puts("bool"); }
#endif
int main()
{
print_type(1);
print_type(1 > 0);
print_type(1 > 0 ? 1 : 0);
/*c++ output:
int
int
int
cc output:
int
bool
int
*/
}
It's also possible no effect was intended, and the author simply thought it made the code clearer.
Solution 6 - C++
One simple explanation is that some people either don't understand that a condition would return the same value in C, or they think that it is cleaner to write ((a>b)?1:0)
.
That explains why some also use similar constructs in languages with proper booleans, which in C-syntax would be (a>b)?true:false)
.
This also explains why you shouldn't needlessly change this macro.
Solution 7 - C++
Maybe, being an embedded software, would give some clues. Maybe there are many macros written using this style, to easy hint that ACTI lines use direct logic rather than Inverted logic.