Why is the ternary operator used to define 1 and 0 in a macro?

C++CBooleanTernary Operator

C++ 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionViktor SView Question on Stackoverflow
Solution 1 - C++BathshebaView Answer on Stackoverflow
Solution 2 - C++unwindView Answer on Stackoverflow
Solution 3 - C++zwolView Answer on Stackoverflow
Solution 4 - C++KonchogView Answer on Stackoverflow
Solution 5 - C++PSkocikView Answer on Stackoverflow
Solution 6 - C++Hans OlssonView Answer on Stackoverflow
Solution 7 - C++J.GuarinView Answer on Stackoverflow