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/ .
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.
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
Using gcc's -pedantic flag, it does say
foo.c:5: warning: ISO C forbids
omitting the middle term of a ?:
expression
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.
The K&R BNF shows an expression is required between "?" and ":". I don't think gcc should be compiling that without a diagnostic.
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.