[Solved] What does this code mean?

I've been going through Henning Karlsens UTFT library code and there is a part that I don't quite understand:

unsigned int dx = (x2 > x1 ? x2 - x1 : x1 - x2);
short xstep = x2 > x1 ? 1 : -1;
unsigned int dy = (y2 > y1 ? y2 - y1 : y1 - y2);
short ystep = y2 > y1 ? 1 : -1;
int col = x1, row = y1;

Does not look like the usual arduino code - "?" and ":" seem to be operators of some kind, but they are not covered on the reference page. Also ">" usually mean "greater than" but a comparison don't really make sense with an assignment operator...

Please shade some light :slight_smile:

unsigned int dx = (x2 > x1 ? x2 - x1 : x1 - x2);

means the same as
if(x2>x1) {
x2-x1;
}
else {
x1-x2;
}
I cant remember the name but the expression before the ? is evaluated and if true executes the expression to the left of the : and if false executes the expression to the right of the :.

I cant remember the name

Ternary if

Edit.... gf, you mean:

if(x2>x1) {
dx = x2-x1;
}
else {
dx = dx1-x2;
}

.... I reckon

I find a lot of references to the ternary operation being "that obscure statement that no one understands" but I think it's fantastic. It's a very concise way of picking between two (or more) options, and seems plenty clear to me. (Well, except with PHP, where they changed which side of the colon is associative, compared to every other language that uses it, but that's PHP for ya.)

groundfungus is right on with the explanation, but it might help to see it another way:

variable = (condition) ? optionA : optionB;

This means the value of "variable" is "optionA" if (condition) evaluates to true, or "optionB" if (condition) evaluates to false;

For example, if you wanted to emulate a digital input from an analog reading, you could set a pin HIGH for values of 512 and higher, or LOW for values of 511 and lower like this:

int  pin_state = ( analogRead(A_PIN) >= 512 )  ?  HIGH : LOW;
digitalWrite(D_PIN, pin_state);

Thanks for fixing that, JimboZA. SirNickity, you say that ternary operation "is a very concise way of picking between two (or more) options". Could you elaborate on the "or more" part. I seem to remember something like that from Fortran (maybe). Just curious.

SirNickity:
I find a lot of references to the ternary operation being "that obscure statement that no one understands"

Which says a lot about how little whoever says that knows! Same person probably thinks that using bits is arcane and can't parse text without using String objects.

Which says a lot about how little whoever says that knows!

I disagree. I've written a lot of C and C++ code over the years, and I've never needed to use the ternary operator. On the occasions I've reused code that does use it. I always have to look up the syntax to see which part represents true and which part represents false.

It is indeed an unnecessary addition to the language. It offers nothing that a combination of if and else doesn't.

There are a lot of "no one" who don't need to look it up even if they don't use it all the time.
It's not exactly a new part of C either. It expresses in one line what otherwise takes at least two.

Why bother with switch-case when nested if statements will do?
Why bother with half or more of the C language?
Maybe we should all just switch to BASIC.

But please don't tell me that no one uses code I have seen used for decades!

IMO, using ternary operators is OK for simple statements, but if they are themselves nested, it becomes quickly very tricky (should I say "unreadable" ? :wink: ) .

The ternary operator is particularly useful if you want to use its value in an expression. Doing that with crude if/else statements would require storing the result in a temporary variable. It would also mean that you would not be able to use the expression order of evaluation and short-circuiting logic to control whether the expression actually got evaluated - which might matter in some situations. I don't use the ternary operator frequently, but sometimes it can be very useful.

alnath:
IMO, using ternary operators is OK for simple statements, but if they are themselves nested, it becomes quickly very tricky (should I say "unreadable" ? :wink: ) .

When taken to extremes, the trade word is "obfuscated". There are even contests.
Given the nature of algorithms, really ingenious code often walks back and forth over the line even with comments.

What can I say? Some Shakespeare isn't immediately obvious either. That doesn't make it poor use of English.

groundfungus:
SirNickity, you say that ternary operation "is a very concise way of picking between two (or more) options". Could you elaborate on the "or more" part. I seem to remember something like that from Fortran (maybe). Just curious.

The op itself can only choose between two options, but you can extend that by compounding statements. Here's an example that determines whether "it's still today" based on whether I've mentally changed days, regardless of wall-clock time:

uint8_t is_still_today = (have_slept)
                       ? ((just_a_nap) ? true : false)      // Still today if it was just a nap
                       : (up_over_24_hours) ? false : true; // No longer today if it's been an all-nighter

Here's an example that falls through conditions to return a status code based on a signed int. 0 means nothing happened, > 0 means data received, < 0 means there was a specific error, which is passed to the caller:

int getData () {
    int retval = some_function();
    return (retval == 0) ? ERROR_NOTHING_TO_DO
                         : (retval > 0) ? ERROR_DATA_RECEIVED : retval;
}

In general, compounding statements can get messy and it might be better to expand this to if/else or switch instead. However, I'll almost always choose the ternary over if/else for simple assignments. In dynamically-typed langauges like perl and PHP, it can be really handy to do things like this:

function get_preferences ($uid = NULL) {
    // If the caller passed a UID, look up that user's preferences
    // Otherwise, use system defaults (uid 0 in the database)
    $uid = ($uid === NULL) ? 0 : intval($uid);
    
    $q = Util::getDB()->prepare( "SELECT * from user_prefs WHERE uid = ?" );
    $q->bind_param('i', $uid);
    $q->execute();
    
    return $q->fetch();
}

Even if you, as a non-ternary fan, come across this code, it's pretty evident what it does. Good comments will help make it obvious, and abuse of syntax and order of operations will make it a tangled, obfuscated mess. That's the case with just about any language construct. There's often more than one way to do it, and I'll not hesitate to use different tools (and as you can see in my examples, also different indentation and line-break styles) to help illustrate the purpose and intent, while being as efficient as possible.

A very interesting discussion, with curious implications and thank you for detailed explanation.

SirNickity:
Which says a lot about how little whoever says that knows!

At the present I'm making a "cruise control" for my horse. Part of it is to get a 3.2' LCD to work with ATmel1284graphic, which is not supported by Henning's library... On other words I'm an advanced beginner. After using Arduino hardware, IDE and ATmel238 uC in different projects for past year and a half have always remained in a sheltered pool of arduino's version of C... More than that, the very idea to explore "open waters" is somewhat intimidating.

GoForSmoke:
Why bother with half or more of the C language?

Reference page creates a toolbox, which is sufficient for a great number of projects. Personally, until I came across this piece of code and asked this question here I was not really aware, that there is something else out there! More then that, wondering what percentage of arduino users out there are actually know "computer programming? Or does this platform and this forum form a self-sufficient swamp, breeding ground for incompetent codewriters (myself included)?
_

Well, there's always the risk that building an idiot-proof ecosystem succeeds in breeding better idiots, but I don't really see it that way myself. No one on this tiny little planet was born an expert. We all get there through trial and error. The Arduino project simplifies a lot of things to the bare essentials, which is often seen as a short-circuit to making progress without having a clue how any of it works. There's truth in that. On the other hand, I don't think you can enforce curiosity by preserving complexity. Either a given user will want to expand their knowledge, or they will be satisfied with their limited success and that's that.

Accessibility lowers the barrier to entry for the pragmatists who just want to get something done, but the best thing about Arduinoland is, while it's a walled garden, that garden has an unlocked gate. You're welcome to come and go any time. You can stick to flashing LEDs, or you can build automated drones. You can stick to digitalWrite() and delay(), or you can learn PORTB = ~(1 << PORTB2) | (PINB & (1 << PINB4)) ? (1 << PORTB2) : 0;. It's as simple or complex as you want it to be -- and if you put in the time and effort to learn it, that knowledge is transferable. XD

SirNickity:

groundfungus:
SirNickity, you say that ternary operation "is a very concise way of picking between two (or more) options". Could you elaborate on the "or more" part. I seem to remember something like that from Fortran (maybe). Just curious.

The op itself can only choose between two options, but you can extend that by compounding statements. Here's an example that determines whether "it's still today" based on whether I've mentally changed days, regardless of wall-clock time:

uint8_t is_still_today = (have_slept)

? ((just_a_nap) ? true : false)      // Still today if it was just a nap
                       : (up_over_24_hours) ? false : true; // No longer today if it's been an all-nighter




Here's an example that falls through conditions to return a status code based on a signed int. 0 means nothing happened, > 0 means data received, < 0 means there was a specific error, which is passed to the caller:



int getData () {
    int retval = some_function();
    return (retval == 0) ? ERROR_NOTHING_TO_DO
                         : (retval > 0) ? ERROR_DATA_RECEIVED : retval;
}




In general, compounding statements can get messy and it might be better to expand this to if/else or switch instead. However, I'll almost always choose the ternary over if/else for simple assignments. In dynamically-typed langauges like perl and PHP, it can be really handy to do things like this:



function get_preferences ($uid = NULL) {
    // If the caller passed a UID, look up that user's preferences
    // Otherwise, use system defaults (uid 0 in the database)
    $uid = ($uid === NULL) ? 0 : intval($uid);
   
    $q = Util::getDB()->prepare( "SELECT * from user_prefs WHERE uid = ?" );
    $q->bind_param('i', $uid);
    $q->execute();
   
    return $q->fetch();
}




Even if you, as a non-ternary fan, come across this code, it's pretty evident what it does. Good comments will help make it obvious, and abuse of syntax and order of operations will make it a tangled, obfuscated mess. That's the case with just about any language construct. There's often more than one way to do it, and I'll not hesitate to use different tools (and as you can see in my examples, also different indentation and line-break styles) to help illustrate the purpose and intent, while being as efficient as possible.

You can always hide the scary code in an object definition in a library. Who looks in them at all is probably well enough versed to look it up if they don't already know.

AlfaOmega:
I'm making a "cruise control" for my horse

XD

SirNickity:
Well, there's always the risk that building an idiot-proof ecosystem succeeds in breeding better idiots, but I don't really see it that way myself. No one on this tiny little planet was born an expert. We all get there through trial and error. The Arduino project simplifies a lot of things to the bare essentials, which is often seen as a short-circuit to making progress without having a clue how any of it works. There's truth in that. On the other hand, I don't think you can enforce curiosity by preserving complexity. Either a given user will want to expand their knowledge, or they will be satisfied with their limited success and that's that.

Accessibility lowers the barrier to entry for the pragmatists who just want to get something done, but the best thing about Arduinoland is, while it's a walled garden, that garden has an unlocked gate. You're welcome to come and go any time. You can stick to flashing LEDs, or you can build automated drones. You can stick to digitalWrite() and delay(), or you can learn PORTB = ~(1 << PORTB2) | (PINB & (1 << PINB4)) ? (1 << PORTB2) : 0;. It's as simple or complex as you want it to be -- and if you put in the time and effort to learn it, that knowledge is transferable. XD

All good and fine but it doesn't mean throw away everything that would puzzle beginners.
How many beginners do you know that use libraries that they never look inside? That's where to put advanced code and speed-code algorithms with the tricky parts.

Perhaps some defines would help.

// Variable ON/OFF BlinkWithoutDelay 

#define IFTRUE ?
#define NOTTRUE :
#define FLIPBIT0 ^= 1

unsigned long tNow, tStart, tOn, tOff; // BlinkWithoutDelay timer
unsigned long tOffOn[ 2 ] = { 1500, 500 }; // [0] is time OFF, [1] os time ON

byte ledPin = 13;
short tf = 1; // tf is my test true flag for IFTRUE - NOTTRUE

void setup( void )
{
  pinMode( ledPin, OUTPUT ); // default LOW

  Serial.begin( 9600 );
  Serial.print( "\n testing tf\n tf = " );
  Serial.println( tf );
  Serial.print( " tf IFTRUE 100 NOTTRUE -1 returns " );
  Serial.println( tf IFTRUE 100 NOTTRUE -1 );

  tOn = 500; 
  tOff = 1500;
}

void loop( void )
{
  static byte  reportLines = 0;

  tNow = millis();
  if ( tNow - tStart >= tOffOn[ tf ] ) // is blink OFF or ON time over? 
  {
    if ( reportLines < 20 )
    {
      reportLines++;
      Serial.print( tNow );
      Serial.print( " -- " );
      Serial.println( tf IFTRUE "ON" NOTTRUE "OFF" );
    }

    digitalWrite( ledPin, tf FLIPBIT0 ); // flips bit 0 and writes the pin 
    tStart = tNow; // start the next second where you got the time the last ended
  }
}