45

In the Android open-source qemu code I ran across this line of code:

machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */

Is this just a confusing way of saying:

if (machine->max_cpus) {
   ; //do nothing
} else {
 machine->max_cpus = 1;
}

If so, wouldn't it be clearer as:

if (machine->max_cpus == 0) machine->max_cpus = 1;

Interestingly, this compiles and works fine with gcc, but doesn't compile on http://www.comeaucomputing.com/tryitout/ .

VividD
  • 10,456
  • 6
  • 64
  • 111
RickNotFred
  • 3,381
  • 2
  • 24
  • 26
  • 6
    Wait, what?! … that looks like a bug to me. – Konrad Rudolph May 10 '10 at 20:36
  • @Konrad - probably not. The comment suggests the line sets a default - if "max_cpus is not set, set the default value". – Mac May 10 '10 at 20:40
  • If it is legal, I would avoid it because it looks too much like it might be a bug. Here we having a big discussion about it, proving that it would have been clearer to use the if statement. – Jim Tshr May 10 '10 at 21:05
  • @Mac: In that case I would expect a commend à la “GNU extension syntax: …”. But you’re probably right. – Konrad Rudolph May 10 '10 at 21:14
  • 2
    @Konrad: Nah. In a large compiler-specific code base, you're hardly going to comment every single use of an extension with "this is an extension". Granted, this is a particularly tricky one for the reader to find by Google search, compared with, say, `__builtin_clz`, or (in C++) `long long`... – Steve Jessop May 10 '10 at 22:42
  • 1
    "wouldn't it be clearer as". Yes. But maybe not so much if it had been `machine->max_cpus = arguments->max_cpus ?: 1;`. Could be the result of over-using an idiom that works OK in some cases, but not others. – Steve Jessop May 10 '10 at 22:48

7 Answers7

55

This is permitted in GNU as an obscure extension to C

5.7 Conditionals with Omitted Operands

The middle operand in a conditional expression may be omitted. Then if the first operand is nonzero, its value is the value of the conditional expression.

Therefore, the expression

 x ? : y

has the value of x if that is nonzero; otherwise, the value of y.

This example is perfectly equivalent to

 x ? x : y

In this simple case, the ability to omit the middle operand is not especially useful. When it becomes useful is when the first operand does, or may (if it is a macro argument), contain a side effect. Then repeating the operand in the middle would perform the side effect twice. Omitting the middle operand uses the value already computed without the undesirable effects of recomputing it.

As you can probably guess, avoiding this is recommended for readability and portability reasons. I'm honestly surprised to see such a grammar-incompatible extension to C.

Uri
  • 88,451
  • 51
  • 221
  • 321
  • 7
    Interesting. This seems both incredibly useful as a syntactic feature, and incredibly obscure to understand. – Ben Zotto May 10 '10 at 20:44
  • I agree. But there are many things one could add to C to make it more elegant, I'm surprise a compiler vendor would explicitly make such a permissive change to the C grammar. If this was C++, that's another story since it used to be a jungle of incompatibility. – Uri May 10 '10 at 20:46
  • 3
    interresting. that became ?? in C# "x if x is not null, otherwise y" => c ?? y – Stéphane May 10 '10 at 21:01
  • 2
    @Potatoswatter: not really, since presumably `max_cpus` is not boolean valued. `||` has no way of evaluating to `3` if the prior value was `3`. – Steve Jessop May 10 '10 at 22:44
  • 4
    Since this idiom behaves differently from a `ternary conditional` and more like a `logical or` in most languages, I propopse it's henceforth called the `ternarator` – aelgoa Feb 11 '13 at 08:28
  • I wish more compilers supported this incredibly useful extension. I don't think that it is obscure at all if you know what it means, it actually is more expressive. – Kevin Cox Mar 21 '14 at 14:10
  • 1
    I'd like to add that clang also supports this (as with many GNU extensions) unless run with -pedantic. – Tor Klingberg Aug 21 '14 at 15:45
  • It's so obscure I didn't have any idea how I can search for this at first. – Gwangmu Lee Nov 17 '20 at 03:19
11

This is a GCC extension that means "if the condition is true, use it, else use this other value", so

machine->max_cpus = machine->max_cpus ?: 1;

is shorthand for

machine->max_cpus = machine->max_cpus ? machine->max_cpus : 1;

although if the conditional has side-effects, it will only be run once

Michael Mrozek
  • 169,610
  • 28
  • 168
  • 175
7

Using gcc's -pedantic flag, it does say

foo.c:5: warning: ISO C forbids omitting the middle term of a ?: expression

Larry Engholm
  • 370
  • 1
  • 6
4

It's a GCC extension, and it gets more interesting and useful when the condition has side effects.

In this case, yes, I for one would agree it's obscure more than anything else.

John Marshall
  • 6,815
  • 1
  • 28
  • 38
1

The K&R BNF shows an expression is required between "?" and ":". I don't think gcc should be compiling that without a diagnostic.

Larry Engholm
  • 370
  • 1
  • 6
0

There's another useful case for this -- the elimination of intermediate variables when calling a function or method that might return nil, that we wish to avoid calling twice. For example (Objective-C), suppose we want to unpack a file into an array if it exists, otherwise return an empty array.

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        backlog = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData] ?: backlog;
    }
    return backlog;
}

The alternatives are less concise.

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            backlog = tempArray;
        }
    }
    return backlog;
}

Or uglier with multiple returns etc.

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            return tempArray;
        }
    }
    return @[];
}

So it's useful syntactic sugar that I find fairly readable. The downsides are

  • Implicit conversion of a pointer to a bool. This is a long-standing C convention, but most modern languages disallow it, complicating any porting efforts.

  • As others have said it's also a non-standard extension, so it should be avoided if portability is a consideration at all.

David Gish
  • 750
  • 6
  • 14
0

I feel the other answers do not answer the question in the title and also taking the tag c into account. Therefore I add another answer.

I use this syntax to prevent my code from getting ugly through if-statements.

foo(1) == TRUE ?: error_quit("foo(1) failed");
foo(2) == TRUE ?: error_quit("foo(2) failed");
foo(3) == TRUE ?: error_quit("foo(3) failed");
foo(4) == TRUE ?: error_quit("foo(4) failed");

You can see the actual function call right in the beginning of the line. Compared it to the versions below, where the leading if obstructs the direct view of the function call.

if (foo(1) == FALSE) error_quit("foo(1)" failed");
if (foo(2) == FALSE) error_quit("foo(2)" failed");
if (foo(3) == FALSE) error_quit("foo(3)" failed");    
if (foo(4) == FALSE) error_quit("foo(4)" failed");

or even harder to read:

if (foo(1) == FALSE){
  error_quit("foo(1)" failed");
}

if (foo(2) == FALSE){
  error_quit("foo(2)" failed");
}

if (foo(3) == FALSE){
  error_quit("foo(3)" failed");
}

if (foo(4) == FALSE){
  error_quit("foo(4)" failed");
}
DarkTrick
  • 2,447
  • 1
  • 21
  • 39