Prüfen ob bei einem Input in einer Custom Node ein gültiger Wert anliegt.

  • Hi zusammen.


    Ich versuche meine eigenen Nodes gerade ein wenig vor Fehleingaben und Exceptions abzusichern.


    Was ich ein wenig mit Entsetzen feststellen musste ist, dass UE Try...Catch nicht unterstützt, höchstens mit Tricks, aber dann auch nur auf eigene Gefahr.


    Ich habe nun mit einer IF...Then...Else eben halt abgefragt und mit UE_LOG halt eine Meldung ausgegeben, wenn der Wert blöd ist.


    Kann man machen, hat aber den Nachteil, es wird halt erst zur Laufzeit ein Hinweis im Output.log ausgegeben.


    Schöner würde ich es finden, wenn bei der Node schon ein ERROR-Hinweis stehen würde, wenn man einen falschen Wert am Input anplugt, oder wie auch immer ein Hinweis aussehen mag. Weil meine derzeitige Methode hat ja auch den großen Nachteil, das Programm läuft halt trotzdem. Irgendwo wird dann halt eine Meldung registriert, aber Konsequenzen hat es halt nicht. Wenn man darauf dann nicht explizit achtet, dann kann es schnell zu Bugs kommen.


    Da muss es doch bessere Möglichkeiten geben.

  • Na eigentlich meinte ich so etwas wie, wenn ich zum Beispiel eine Funktion habe, sagen wir eine ganz einfache, A / B. Wenn B jetzt 0 ist, dass das vorzeitig angezeigt wird.


    Da scheint es aber leider nichts zu geben.


    Erstaunlich finde ich, dass zum Beispiel try...catch nicht funktioniert in Verbindung mit Unreal, obwohl das ja eigentlich eine grundlegende Funktion von C++ ist. Ich finde das immer recht praktisch, wenn eine Exception ausgelöst wird, dann auch der genaue Fehlertext ausgegeben werden kann, was nun falsch gelaufen ist.


    Ich habe mich gestern den ganzen Tag mit den Stichpunkten von Phoenix-100 befasst. Aber so wirklich anders ist das auch nicht.


    Bisher hatte ich das ja so gemacht.


    Code
    if (B == 0){
        UE_LOG(LogTemp, Error, TEXT("Division by zero (B = 0)"));
        return 0;
    }    

    Also, da steckt dann ja ein Text hinter, der von mir formuliert wurde und dann natürlich auch eventuell andere Fehler, die entstehen können ignoriert. Also es gibt dann nur diesen Fehler.


    Alle anderen Methoden bauen eigentlich ähnlich auf. Die Fehlerabfrage ist lediglich ein wenig anders.


    Ich habe mir dann gewisse Codesnippets aus der original Engine angeschaut, also wie lösen sie es bei der original Division-Node.


    Und eigentlich machen sie auch da genau dasselbe. Da ist auch eine IF-Abfrage, wenn der Divisor = 0 ist, dann löst er einen Fehler aus.


    Also ist es offensichtlich gar nicht anders möglich. Weil, wenn die Programmierer der Engine das so machen, dann wird das wohl auch das entsprechende Vorgehen zu sein.

  • Hey zusammen


    Häufig ergeben sich die Paramter einer Blueprit Node zur Laufzeit, das beduetet eine Prüfung zur Compile time ist häufig gar nicht möglich.

    Der übliche Ansatz sind Asserts, wenn durch null dividert wird, wird bei check die Ausführung unterbrochen und in der IDE genau die Zeile geöffnet, wo das passiert ist.

    Ansonsten hast du den Stack auch zur Verfügung, mit der Nachricht vom Ensure und der Zeile wo es passiert ist.


    Code
    LogOutputDevice: Error: === Handled ensure: ===
    LogOutputDevice: Error: Ensure condition failed: B != 0 [File:.../Private/ActorComponentB.cpp] [Line: 37]
    LogOutputDevice: Error: Division by Zero
    LogOutputDevice: Error: Stack: 


    Der Ansatz ist es diese ungültigen Zustände zu finden. Also bei einer Division geht man davon aus, dass der Programmierer da nicht durch null teilt. Falls doch wird die ausführung unterbrochen, und der fehlerhafte Code angzeigt und das Problem kann behoben werden. Deine Log message könnte halt übersehen werden, ein check nicht.


    Du kannst try - catch blocks nutzen. Die Engine nutzt diese kaum. In diesem Beispiel sehe ich da auch kein Vorteil von Exception, weil es halt ein Programmierfehler ist.


    Gruss

  • Hey zusammen


    Häufig ergeben sich die Paramter einer Blueprit Node zur Laufzeit, das beduetet eine Prüfung zur Compile time ist häufig gar nicht möglich.

    Der übliche Ansatz sind Asserts, wenn durch null dividert wird, wird bei check die Ausführung unterbrochen und in der IDE genau die Zeile geöffnet, wo das passiert ist.

    Ja, mein Beispiel war wohl etwas blöd gewählt. Natürlich kann man Division by zero nicht vorab prüfen, denn es kann ja auch eine veränderbare Variable übergeben werden, die irgendwann mal zufällig 0 ist oder so.

    Der Ansatz ist es diese ungültigen Zustände zu finden. Also bei einer Division geht man davon aus, dass der Programmierer da nicht durch null teilt. Falls doch wird die ausführung unterbrochen, und der fehlerhafte Code angzeigt und das Problem kann behoben werden. Deine Log message könnte halt übersehen werden, ein check nicht.

    Was mir halt aufgefallen ist, dass Check, Verify und Ensure sehr wenig darüber zu finden ist im Internet. Also bis auf die offizielle Doku habe ich da nichts gefunden. Und die offizielle Doku der Unreal Engine, gerade was die C++-Api angeht, finde ich relativ unlustig und unschön zu lesen, weswegen ich mich aufgrund deines Hinweises auch versucht habe weiter zu hangeln, wo ich dann wusste wonach ich suchen muss. Allerdings kommt da bei der Google-Suche relativ viel Blödsinn raus, was vielleicht auch an den allgemeinen Such-Keywords liegen mag. Check, Verify usw., das ist dann ja doch in vielen Texten zu finden.


    Du kannst try - catch blocks nutzen. Die Engine nutzt diese kaum. In diesem Beispiel sehe ich da auch kein Vorteil von Exception, weil es halt ein Programmierfehler ist.

    Nun, ich habe ja vorher viel mit C# programmiert. Try...Catch hatte da schon einen sehr guten Vorteil, da in dem Exception-String auch sehr genau zu lesen war, was jetzt schiefgelaufen ist. Ich weiß allerdings nicht, ob das bei C++ auch so ist. Ich programmiere ja erst seit kurzem mit C++ und eigene Nodes erstellen funktioniert schon recht gut. Allerdings sind die noch, sagen wir mal vorsichtig, recht anwendungsunsicher. Das möchte ich halt ändern.


    Aber ich habe ja Zeit. Davon ausgehend, dass ich die nächsten 5 Jahre wohl noch überleben werde, vielleicht auch länger, habe ich ja noch ein wenig mir Tipps, Tricks und den Orieginalcode anzuschauen. :D

  • Na so ganz überzeugt mich das aber noch nicht.


    Ich habe es jetzt mal mit


    Code
    check(B != 0);

    probiert.


    Nun, wenn der Wert nicht = 0 ist funktioniert es, wenn der Wert gleich 0 ist stürzt die gesamte Engine ab.


    Try... Catch funktioniert nicht, weil die Engine keine Exceptions benutzt, was dann mein gewohntes Umfeld wäre. Also bleibt wirklich nur das, was ich von Anfang an gemacht habe.


    Diverse Foren, die ich inzwischen gefunden habe, sagen auch aus, dass man da selber etwas basteln muss, also alle Möglichkeiten, die so passieren können, einzeln abfragen.


    Kommt mir etwas komisch vor. Aber gut, ich nehme es einfach mal so hin.

  • Nun, wenn der Wert nicht = 0 ist funktioniert es, wenn der Wert gleich 0 ist stürzt die gesamte Engine ab.

    Genau so ist, das ist die Konsequenz. Nun crashed die Engine. Der Entwickler sieht nun in welcher Zeile welcher check fehlgeschlagen hat und nun kann das repariert werden. Ziel erreicht oder nicht? Wenn du nicht möchtest, dass die Engine crashed kannst du ensure nutzen.


    Try... Catch funktioniert nicht, weil die Engine keine Exceptions benutzt, was dann mein gewohntes Umfeld wäre. Also bleibt wirklich nur das, was ich von Anfang an gemacht habe.

    Check und ensure empfinde ich da in jedem Fall als überlegen. Ich verstehe nicht wieso die selber basteln müssen. Die asserts sind ideal zum Absichern der Annahme in der Methode und bieten umfangreiche Möglichkeiten.


    Kannst du genauer beschreiben was dich stört?

  • Genau so ist, das ist die Konsequenz. Nun crashed die Engine. Der Entwickler sieht nun in welcher Zeile welcher check fehlgeschlagen hat und nun kann das repariert werden. Ziel erreicht oder nicht? Wenn du nicht möchtest, dass die Engine crashed kannst du ensure nutzen.

    Ich glaube, du verstehst nicht ganz. Die gesamte Engine stürzt komplett ab. Nichts mit Meldung oder so. Also dieses typische Send and Close oder Send and restart Ding.


    Das ist übrigens bei Check, Verify und Ensure so. Also alle Möglichkeiten, die dort beschrieben werden, führen zu einem Totalabsturz, sobald sie ausgelöst werden. So sollte das dann auch nicht sein. Fehlermeldung ja, aber doch nicht, dass ich da einen Crashreport an Epic senden will und die Entwicklungsumgebung neu starten muss. Etwas dazwischen halt.

  • Doch ich verstehe. Genau das soll passieren. Wir sind in einem Zustand der nicht sein darf, fatal, keine Erholung möglich. Anstatt das jetzt durchzuschleppen und dann beim nullptr oder was auch immer zu crashen, crashen wir lieber gleich beim check. Der Programmierer findet so schneller sein Fehler und wir crashen kontrolliert.


    Du hast schon recht, das ist vermutlich seltener der Fall. Häufiger hast du ensure, dann crashed die Engine nicht, eben für nicht fatale Zustände. Du musst aber natürlich sicherstellen, dass du das auffängst.

    Beispiel: Ich habe hier einen nullptr gebaut. Nun wird geloggt, wo genau der aufgetreten ist mit dem stack.

    Crashen tut die Engine hier nicht.


    Code
        AActor* MyPointer = nullptr;
    
        if (ensure(MyPointer))
        {
            MyPointer->GetName();
        }
  • Die IDE sollte dadurch nicht crashen, lediglich die Engine. Hot Reload ist sowieso ziemlich fehleranfällig, was das compilieren aus der IDE sinnvoll macht und damit wird sowieso ein neustart aus der Engine notwendig.


    Aber nochmal, die Engine crashed im Beispiel so oder so, sie crashed nun einfach früher.


    Meistens handelt es aber nicht um fatale Fehler, und hier kannst du ensure nutzen, dann crashed die Engine nicht und du hast den Error im output log.


    Gruss

  • Nun, ich habe jetzt verschiedene Methoden ausprobiert. Mit meinem UE_LOG, aber statt Error halt Fatal benutzt, das soll das Spiel dann ebenfalls zum Beenden bringen. Ich habe einfach Error benutzt, und danach GameExit, und halt und in diversen Kombinationen deine Vorschläge. Immer wenn ich das Spiel unterbrechen will, egal wie, stürzt alles komplett ab, inkl. IDE usw.


    Ich befürchte, da muss ein gravierenderer Fehler vorliegen, gar nicht mal an meinen Ideen, die ich da ausprobiert habe. Ich bin mir nur nicht so ganz sicher, ob das jetzt an meinem Rechner liegt, oder eben halt an UE5, an der UE5-Api, vielleicht auch an Visual Studio 2022.


    Das ist natürlich jetzt irgendwie schwierig, das herauszufinden.