2

As far as I understand, things like ++i++ are not allowed in C/C++ because they break a rule that variables cannot be written to multiple times in a single statement. The result is undefined behaviour.

Could someone confirm if this also applies to my statement: ++i %= j?

I would like a for loop where I increment i around a circular buffer (size j) starting at an arbitrary point p until I get back to point p.

for(int i = p+1 ; i != p; ++i %= j  )
{
    if (buffer[i].ready()) 
    {
        buffer[i].do_something();
        p = i;
        break;
    }
}

I could do it in more lines of code, but would rather not.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
Stewart
  • 4,356
  • 2
  • 27
  • 59
  • 2
    It is well-defined. – Edgar Rokjān Jun 22 '17 at 12:07
  • 4
    `++i %= j` doesn't appear in the code snippet. – Bathsheba Jun 22 '17 at 12:09
  • Fixed. You're right. – Stewart Jun 22 '17 at 12:11
  • 4
    Why not `i = (i + 1) % j` and avoid *smart* code? – pmg Jun 22 '17 at 12:14
  • 7
    If you have to ask if it's well-defined, it's *bad* code. – Andrew Henle Jun 22 '17 at 12:17
  • 4
    Just for the record (since everyone answered about C++ and the question includes C). In C `++i %= j` will not even compile because `++i` is not an lvalue. Both gcc and clang correctly reject it. – Art Jun 22 '17 at 12:55
  • I'd like to second what @Art said. You are touching on a point where C and C++ subtly differ. C says ++i is an rvalue while C++ says ++i is a lvalue referencing i. Another example where C and C++ disagree similarly is on assignment operators. In C++, the expression x = y (typically) yields an lvalue reference to x, whereas in C the expression yields an rvalue. Writing code on basic types like ints that differs in C and C++ is begging for trouble. In this case, the compiler will likely save you with syntax errors for C, but there are probably other examples that would pass. Not good! – jschultz410 Jun 22 '17 at 16:30
  • Another valid variant: `for (int i = p+1 ; i != p; ++i, i %= j )` – jschultz410 Jun 22 '17 at 16:35
  • I also wanted to point out that you likely have a subtle bug in your code when `p == j - 1` because you add one but don't mod when you initialize `i`. You also don't process `p` itself. Is that what you want? – jschultz410 Jun 22 '17 at 17:24
  • If in doubt better avoid it, especially when safe variant is short and readable – Slava Jun 22 '17 at 17:45

5 Answers5

4

In your code you say ++i % j, which means 'Increment i (and store the new value in i), then calculate the modulo of i and j. But that value is not stored anywhere.

To get a wrap around loop, you can use i = (i+1)%j

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
HeavensInc
  • 78
  • 6
2

The behaviour is undefined pre C++17.

++i %= j is equivalent to i = ++i % j.

This is a dressed up version of i = ++i, and everyone knows that is UB.

Community
  • 1
  • 1
Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Please downvote the living daylights out of this if I'm wrong! Unless I need more coffee I can't see any other way of rationalising this. – Bathsheba Jun 22 '17 at 12:22
  • 1
    According to Nathan Oliver, only the sequencing of the operands relative to each-other is unspecified prior to C++17. They are still both evaluated before the compound assignment. All well defined. (No need to downvote however, it still looks weird) – StoryTeller - Unslander Monica Jun 22 '17 at 12:26
  • Of course. But it isn't a dressed-up version of it. – StoryTeller - Unslander Monica Jun 22 '17 at 12:27
  • So you're saying that "++i %= j is equivalent to i = ++i % j" is incorrect? I'm being a bit naughty here - punting an answer and watching out for downvotes to prove its falsehood or otherwise. – Bathsheba Jun 22 '17 at 12:27
  • Yes. Because the standard clearly specifies the compound assignment is done as a single evaluation, not as two (end of the first quote in my answer, not in bold). – StoryTeller - Unslander Monica Jun 22 '17 at 12:29
  • That only applies from C++17 owards no? – Bathsheba Jun 22 '17 at 12:29
  • I don't know, I only have the C++1z draft handy. Not impossible that like the relative order of lhs and rhs, this too is different in C++14. – StoryTeller - Unslander Monica Jun 22 '17 at 12:30
  • 2
    @StoryTeller From [here](https://stackoverflow.com/a/4176333/4342498) if `++i = 2;` is UB then `++i 2;` is also UB – NathanOliver Jun 22 '17 at 12:30
  • @Bathsheba Yes, it's not equivalent. In the former, the calculation of `%=` is sequenced after calculation of both `++i` and `j`. Yes, the relative order of `++i` and `j` is undefined, but we don't care. For the latter, we have operator `=` and two operands: `i` and `++i % j`, and calculation of `i` may or may not intersect with calculation of `++i % j`, causing a UB. – alexeykuzmin0 Jun 22 '17 at 12:31
  • @Bathsheba So, is `i = ++i;` defined behavior in C++17? For both scalars and classes? Is `i = i++;` still UB for scalars, even in C++17? What about for classes? I'm still living in ANSI C world over here ... – jschultz410 Jun 22 '17 at 18:37
  • @Bathsheba [This answer](https://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points/4183735#4183735) says that `i = ++i;` is defined behavior since C++11, even for scalars. – jschultz410 Jun 22 '17 at 19:22
  • Hum. There's lots of complexity here. On the grounds that I've hit the rep count and deleting this question today would not impact my score, I think I should wiki this so a good answer could emerge. What do you all think? – Bathsheba Jun 22 '17 at 19:25
  • @Bathsheba I agree that there's lots of complexity here between the various versions of C++ on these kinds of questions. I'm too clueless about SE to advise you well about your specific question. – jschultz410 Jun 22 '17 at 19:53
  • And in C++17, even `++i = ++i;` and more complicated variants are now well defined. Ick. – jschultz410 Jun 23 '17 at 17:04
2

Yes, your code is well defined (in C++17 anyway). Quoting the standard:

[expr.ass]

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

[expr.pre.incr]

The operand of prefix ++ is modified by adding 1. The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type other than cv bool, or a pointer to a completely-defined object type. The result is the updated operand; it is an lvalue, and it is a bit-field if the operand is a bit-field. The expression ++x is equivalent to x+=1.

The text in bold means your code pretty much has the following semantics:

auto& __j = j;   // refer to j
auto& __i = ++i; // refer to i after the increment
__i %= __j;

If the expression looks fishy to you despite the standards reassurance, you can always sequence it yourself with the comma operator.

for(... ; ... ; (++i, i %= j))
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Looks like you're quoting C++17. C++14 does not have *The right operand is sequenced before the left operand* – NathanOliver Jun 22 '17 at 12:20
  • @NathanOliver - Good to know. Does the rest still apply? – StoryTeller - Unslander Monica Jun 22 '17 at 12:22
  • I believe so, also your links and quotes are swapped around. – NathanOliver Jun 22 '17 at 12:22
  • @StoryTeller Oh, joy! Does that mean `++i = ++i;` is now defined behavior in C++17??? – jschultz410 Jun 22 '17 at 19:29
  • Even more interestingly (to me at least), when i is a class, `++i = ++i;` is no longer equivalent to `i.operator=(++i, ++i);` – jschultz410 Jun 22 '17 at 19:33
  • @jschultz410 - No need for phrasing your comment in such a manner. Yes `++i = ++i` is sequenced in a well defined manner in c++17(for primitives). That is fact, and stating it doesn't make me an advocate for writing such code. Operator overloading is not covered by these paragraphs, but by a different section entirely. – StoryTeller - Unslander Monica Jun 23 '17 at 08:43
  • @StoryTeller It was not my intention to imply that you support writing that kind of code. I was making fun of the ridiculous kinds of statements that the ever expanding sequencing of operations the C++ standard is allowing. Why don't they just go all the way, as some other languages have, and fully define things like `x = (++i + ++x) * (++i - ++x);`??? It certainly can be done, and wouldn't break any old code. – jschultz410 Jun 23 '17 at 17:15
2

The code ++i %= j is identical to the following code:

operator %= (++i, j);

In the standard (§1.9/15) it's stated that

The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.

While "value computations" includes:

  • value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and
  • initiation of side effects.

So, here the compiler is forced to first calculate ++i and j (in any order), including the side-effect of ++, and call operator %= only after these calculations finished. So, it's a well-defined behavior since at least C++11.

Please see this answer for more details.

alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51
  • From [here](https://stackoverflow.com/a/4176333/4342498) if `++i = 2;` is UB then `++i 2;` is also UB – NathanOliver Jun 22 '17 at 12:31
  • @NathanOliver, please explain. I totally agree that `++i %= j` is **well-defined**. Moreover, I reference exactly the same answer as you do. – alexeykuzmin0 Jun 22 '17 at 12:33
  • There is no sequence point between the `++i` and the `%= j`. Since those are unsequenced the behavior is undefined. – NathanOliver Jun 22 '17 at 12:35
  • @NathanOliver Agree that there's no sequence point because we don't have any sequence points in modern c++. But since `The value computations of the operands of an operator are sequenced before the value computation of the result of the operator`, calculations of both `++i` and `j` are sequenced before calculation of `operator %=`, as its operands. Yes, relative order of `++i` and `j` is undefined (and they may even overlap), but who cares? If I have a mistake in my reasoning, please point exactly on it. – alexeykuzmin0 Jun 22 '17 at 12:40
  • What is going to happen first, the `++i` or the `%=`? We don't know, they are handled in a unspecified sequence. Because of that the result is undefined. – NathanOliver Jun 22 '17 at 12:43
  • @NathanOliver, Isn't `++i` an operand of `%=`? If yes, then calculation of `++i` should be sequenced before calculation of `%=`, and the sequence is not unspecified. – alexeykuzmin0 Jun 22 '17 at 12:46
  • It may or may not be. We don't know. Lets say `i` is 2 and `j` is 5. When you have `++i %= j` the compiler can chose to do `3 %= 5` (applying the `++` first) or `++(2 %= 5)`. Since both are valid and both produce different results it is UB. – NathanOliver Jun 22 '17 at 12:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/147364/discussion-between-alexeykuzmin0-and-nathanoliver). – alexeykuzmin0 Jun 22 '17 at 12:53
  • @NathanOliver - The grammar rules only allow the first of those options to be viable. The grammar for [assignment expressions](http://eel.is/c++draft/expr.ass#1) lists the first operand as a logical-or-expression. If you follow the grammar (and links) long enough, you will see that a unary-expression (like ++) is a logical-or-expression. It must be `(++i) %= j`. – StoryTeller - Unslander Monica Jun 22 '17 at 13:04
  • I don't see how `++++i` can be well-defined but `++i = 2` not be, the first is `auto& __i = ++i; ++__i;` and the second is `auto& __i = ++i; __i = 2;` as per the requirements of sequencing before. Similar arguments hold for compound assignments, unless I'm sorely mistaken. – Passer By Jun 22 '17 at 16:06
  • @alexeykuzmin0 I'm not sure exactly what to think anymore. I'm not sure which one is right. – NathanOliver Jun 22 '17 at 16:25
  • 1
    @PasserBy I think it has to do with (at least pre C++11) that the ++ and = can happen in either order. – NathanOliver Jun 22 '17 at 16:26
  • 1
    @NathanOliver It's stated (albeit not very obviously) that this pertains to post-c++11, I'm pretty sure it's UB before that – Passer By Jun 22 '17 at 17:47
  • 1
    Based on the answer everyone is pointing at, it seems `++i %= j;` was UB before C++11, but has been well defined starting in C++11 forwards. Ugh. – jschultz410 Jun 22 '17 at 19:21
0

Others have already answered that your code is valid C++11 and later, but is undefined behavior for earlier versions of C++ and won't even compile in C (ignoring class member fcn calls, of course). All of which says you should probably use a more explicit form to achieve your desired end.

I also wanted to point out that you likely have a subtle bug in your code when p == j - 1 because you add one but don't mod when you initialize i, making you access beyond the end of the array in that case.

You also never process the element at index p. Is that really what you want? If you repeatedly executed this code and none of the other buffers were ready(), then you would keep skipping over checking if buffer[p].ready().

More correct, more universal code (that still doesn't check the element at index p):

int i;

for (i = (p + 1) % j; i != p && !buffer[i].ready(); ++i, i %= j);

if (i != p)
{
  buffer[i].do_something();
  p = i;
}

A version that starts by processing the element at index p, but stops after going over the array at most once:

int i = p;

while (!buffer[i].ready() && (++i, i %= j, i != p));

if (buffer[i].ready())
{
  buffer[i].do_something();
  p = i;
}

There is a subtle point about the above version. If !buffer[p].ready() when we first enter the loop, we go through the whole array, and none the other elements are ready() either, then we will exit the loop with i == p. We will then interrogate buffer[p] again asking if it is ready(). So, it is possible to ask buffer[p].ready() twice, which may or may not be important (e.g. - if it has side effects). Heads up!

Here's a version that avoids that issue, but checks buffer[p].ready() last:

int i = p;

while ((++i, i %= j, i != p) && !buffer[i].ready());

if (i != p || buffer[p].ready())
{
  buffer[i].do_something();
  p = i;
}
jschultz410
  • 2,849
  • 14
  • 22