Seite 1 von 1

Was erlauben C++?

Verfasst: 27.12.2024, 18:22
von Alexander Kornrumpf
In einem anderen Forum postete jemand sinngemäß diesen code (minimal verändert)

int dx = (!direction && x > 20) ? -1
: (direction && x < 200) ? 1
: 0 & (direction = !direction);


Falls nicht offensichtlich, der "Trick" um den es geht ist wie man die Zuweisung in einen Zweig des ? Operator bekommt. Ihr müsst nicht diskutieren, warum das einen bescheuerte Idee ist, darüber waren sich sofort alle einig. Mich hat aber als langjähriger Mitleser hier eher mal interessiert ob das überhaupt so funktioniert, wie der Autor sich das vorstellt oder nicht einfach wegoptimiert wird.

Ich habe godbolt dann auch in der Tat dazu bekommen, mit und ohne die Zuweisung den gleichen Text zu erzeugen

https://godbolt.org/z/cfnYMaWE3

was jetzt naiv erstmal bedeutet, dass die Zuweisung nichts tut. Ich war dann aber im zweiten Nachdenken nicht mehr sicher ob das jetzt wirklich einfach 0 & ... zu 0 short-circuited oder ob der Compiler das in meinem Beispiel aus einem anderen Grund darf, oder ob er einfach einen Logik Trick gefunden hat, den ich nicht verstehe.

Weiß ich nicht wie lange der Link gültig bleibt. FWIW:


f(bool, int):
cmp esi, 20
setle al
or al, dil
je .L1
xor edi, 1
cmp esi, 199
setle al
or eax, edi
.L1:
ret


Also Fragen:

1) Ist die Zuweisung noch irgendwie im Ergebnis implizit und ich sehe sie nur nicht

oder

2) Hat der Compiler in der Tat die Zuweisung wegoptimiert [wie von mir erahnt] und wenn ja, warum durfte er das?

Bevor jemand meckert, natürlich habe ich c++ bitwise short circuit gegooglet, aber die Ergebnisse sind so furchtbar (Beispiel: https://stackoverflow.com/questions/968 ... rt-circuit) da frage ich lieber hier.

Re: Was erlauben C++?

Verfasst: 27.12.2024, 18:45
von antisteo
0 & irgendwas ist 0, also don'tcare.

Der C++-Compiler scheint nicht beachtet zu haben, dass die Zuweisung ja trotzdem einen Seiteneffekt hat, der in jedem Fall ausgefürt werden muss.

Gleichzeitig ist es ja so, dass Funktionsaufrufe auch wegoptimiert werden sollen, auch wenn sie Seiteneffekte haben, da die Seiteneffekte ja lediglich die Aufgabe haben, z.B. Caches zu befüllen, es aber um das Rückgabeergebnis geht.

Bau lieber ein großes IF draus, ist lesbarer und sicherer.

Re: Was erlauben C++?

Verfasst: 27.12.2024, 18:48
von Alexander Kornrumpf
antisteo hat geschrieben: 27.12.2024, 18:45 0 & irgendwas ist 0
Ja, weiß ich. War hier nicht die Frage.
Bau lieber ein großes IF draus, ist lesbarer und sicherer.
Es ist nicht mein Code, wie ich schon schrieb.

Re: Was erlauben C++?

Verfasst: 27.12.2024, 20:04
von NytroX
Soweit ich weiß ist das undefined behaviour.
Nur die Operatoren &&, || und ?: garantieren die Reihenfolge und machen short-circuits, ansonsten sind Seiteneffekte Glückssache.
Theoretisch darf der Compiler auch jeden Funktionsaufruf anders auswerten.
Also vielleicht macht er die Zuweisung, oder auch nicht.
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
[Note 5: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. — end note]
Clang und GCC machen da sogar auch was anderes, Clang hat ein conditional-move drin - siehe hier:
https://godbolt.org/z/17varqYfn

Re: Was erlauben C++?

Verfasst: 27.12.2024, 20:41
von Krishty
Alexander Kornrumpf hat geschrieben: 27.12.2024, 18:221) Ist die Zuweisung noch irgendwie im Ergebnis implizit und ich sehe sie nur nicht
Ja ist sie. Der fünfte Assembler-Befehl lautet xor edi, 1. Das ist direction = !direction.

x86 gibt das Ergebnis in EAX zurück, und EAX wird mit EDI verschmolzen via or eax, edi in der letzten Zeile.

Wenn du den Parameter als bool & auslegst, so dass der Compiler gezwungen ist, die Nebenwirkungen öfter zu realisieren, siehst du es etwas deutlicher.

Rest nachher

Re: Was erlauben C++?

Verfasst: 27.12.2024, 20:45
von Krishty
NytroX hat geschrieben: 27.12.2024, 20:04 Soweit ich weiß ist das undefined behaviour.
Nur die Operatoren &&, || und ?: garantieren die Reihenfolge und machen short-circuits, ansonsten sind Seiteneffekte Glückssache.
Theoretisch darf der Compiler auch jeden Funktionsaufruf anders auswerten.
Also vielleicht macht er die Zuweisung, oder auch nicht.
Einspruch!

C++ hat das Konzept von Sequence Points. Das sind festgelegte Punkte, an denen alle Wirkungen realisiert sein müssen. Der häufigste Sequence Point ist das Semikolon.

int i = 0;
i++;


i hat nach dem ersten Semikolon den Wert 0 und nach dem zweiten den Wert 1.

Neben Semikolon sind bspw. auch if, && und Funktionsaufrufe Sequence Points. Damit sowas wie if(i-- && foo(i++)) funktioniert wie man erwartet.

Variablen dürfen IIRC innerhalb eines Sequence Points nur einmal modifiziert und ausgewertet werden*. Sonst Undefined Behavior. Bspw. int j = i++
+ ++i;
das meinst du.

Operator ? : hat AFAIK einen Sequence Point nach dem Auswerten des vorderen Ausdrucks. Das Verketten von ? : ist also legal, sogar wenn die Verkettung mehrfach die selbe Variable liest und schreibt. Jedes Erreichen eines ? muss dann alle Variablen-Werte realisiert haben. Nur innerhalb des selben ? : die Variable mehrfach modifizieren etc. wäre undefined.

Re: Was erlauben C++?

Verfasst: 27.12.2024, 20:50
von Krishty
Und jetzt ganz abschließen: Die Sequence Points bedeuten, dass der Compiler die Invertierung von direction realisiert haben muss, wenn das innerste ? erreicht wird. Unterstrichen:

int dx = (!direction && x > 20) ? -1
: (direction && x < 200) ? 1
: 0 & (direction = !direction)
;


Das wird nur erreicht, wenn die erste Prüfung false ergibt. Darum ist xor edi, 1 im Assembly hinter dem Sprung je .L1.

Re: Was erlauben C++?

Verfasst: 27.12.2024, 22:57
von dot
Krishty hat geschrieben: 27.12.2024, 20:45 C++ hat das Konzept von Sequence Points. Das sind festgelegte Punkte, an denen alle Wirkungen realisiert sein müssen. Der häufigste Sequence Point ist das Semikolon.
nicht mehr; seit C++11 basiert die Spezifikation auf sequenced before relationships ;)

aber ja [expr.cond]/1
The first expression is sequenced before the second or third expression ([intro.execution]).

Re: Was erlauben C++?

Verfasst: 27.12.2024, 23:19
von Alexander Kornrumpf
Krishty hat geschrieben: 27.12.2024, 20:41
Alexander Kornrumpf hat geschrieben: 27.12.2024, 18:221) Ist die Zuweisung noch irgendwie im Ergebnis implizit und ich sehe sie nur nicht
Ja ist sie. Der fünfte Assembler-Befehl lautet xor edi, 1. Das ist direction = !direction.

x86 gibt das Ergebnis in EAX zurück, und EAX wird mit EDI verschmolzen via or eax, edi in der letzten Zeile.
Danke. Verstehe ich.

Wenn du den Parameter als bool & auslegst, so dass der Compiler gezwungen ist, die Nebenwirkungen öfter zu realisieren, siehst du es etwas deutlicher.

Rest nachher
Hatte ich tatsächlich probiert, aber nicht besonders systematisch, oder so dass ich mir zugetraut hätte das Ergebnis zu interpretieren. Das Beispiel ist natürlich auch insofern künstlich als dass man ja dx nicht wegwerfen würde wenn man schon diesen Zauber macht.