Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No side effect with comma operator, return statement, and nullptr? [duplicate]

I have the following test code:

#include <cstdint>
#include <cassert>

enum class Result : std::uint32_t {SUCCESS = 0, INSUCCESS = 1};

void* func(Result& result)
{
    // works great
    /*
    result = Result::INSUCCESS;
    return NULL;
    */

    // error: invalid conversion from ‘long int’ to ‘void*’ [-fpermissive]
    /*
    return result = Result::INSUCCESS, NULL;
    */

    // compiles, but <result> is not set???
    return result = Result::INSUCCESS, nullptr;
}

void testReturnWithSideEffects()
{
    Result result = Result::SUCCESS;
    func(result);
    assert(result == Result::INSUCCESS);
}

There are 2 questions here but I'm mostly interested in the second:

Why is result not set?

Edit: Thanks to everyone for confirming this. The work-around that I have decided to use is to replace:

return result = Result::INSUCCESS, nullptr;

with the following:

return result = Result::INSUCCESS, (void*)NULL;

Additional note: of course my production scenario is with another pointer type (not void*) but I simplified for illustrative purposes.

Another note: from the workaround you can tell that there's something fishy going on with that nullptr. I'm guessing that the example line which doesn't compile, should actually compile, and these 2 matters are probably related somehow.

And a 3rd and final note, to those who outlined the "trickery" or "unreadability" of my code: readability is largely a subjective matter, for instance I can argue that a shorthand like this can make the code more structured which can actually help spot defects.

like image 348
haelix Avatar asked May 09 '14 11:05

haelix


People also ask

Why are the comma operators not working in my assignment?

Note that the comma operators in assignments may appear not to have the normal effect of comma operators because they don't exist within an expression. In the following example, a is set to the value of b = 3 (which is 3), but the c = 4 expression still evaluates and its result returned to console (i. e., 4).

How does the comma operator work in JavaScript?

The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand. The source for this interactive example is stored in a GitHub repository.

How do you evaluate a comma operator?

"The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then the right operand is evaluated; the result has its type and value.97) If an attempt is made to modify the result of a comma operator or to access it after the next sequence point, the behavior is undefined."

What happens when we replace null with nullptr in C++?

In the above program, if we replace NULL with nullptr, we get the output as “fun (char *)”. nullptr is a keyword that can be used at all places where NULL is expected. Like NULL, nullptr is implicitly convertible and comparable to any pointer type. Unlike NULL, it is not implicitly convertible or comparable to integral types.


1 Answers

The second case:

return result = Result::INSUCCESS, nullptr;

looks like a gcc bug, it appears to be ignoring the left hand side of the comma operator in the context of a return statement, if I change the return statement to this (see it live):

return (printf("hello\n"), nullptr);

there is no output but if we do this outside of a return statement we get the expected result. It appears to be fixed in 4.8 but I can reproduce with 4.6 and 4.7.

If we look at the draft C++ standard section 5.18 Comma operator it says (emphasis mine):

A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded value expression (Clause 5).83Every value computation and side effect associated with the left expression is sequenced before every value computation and side effect associated with the right expression. The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right operand is a temporary (12.2), the result is that temporary.

Regardless, as several people have already mentioned this code is clever but hard to read and therefore will be hard to maintain, splitting this into two lines will work fine. I always like to keep in mind this quote from Brian Kernighan (there may be a few other versions of this):

Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?

For the first case, the error is valid, if we look at section 4.10 Pointer conversions which says (emphasis mine going forward):

A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion.

the expression:

result = Result::INSUCCESS, NULL

is not a constant expression since it contains =, what is a constant expression is covered in section 5.19 Constant expressions and says:

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2) [...]

and includes:

an assignment or a compound assignment (5.17); or

Using nullptr is okay since it is a prvalue of std::nullptr_t, we can see that from section 12.14.7 Pointer literals which says:

The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See 4.10 and4.11. —endnote]

like image 117
Shafik Yaghmour Avatar answered Nov 11 '22 23:11

Shafik Yaghmour