ein Wrapper?



  • Ich würde ja gerne, aber ich missverstehe Dich noch. Doch schon bei der Objekt Erzeugung wird gefehlert, wenn der übergebene Name nicht stimmt. Wie ist das denn gemeint, das der Fehler im Konstruktor werfen soll?

    Der Konstruktor soll gar nicht erst vollständig ausgeführt werden, so das kein Objekt erstellt wurde?



  • @lemon03 sagte in ein Wrapper?:

    Doch schon bei der Objekt Erzeugung wird gefehlert, wenn der übergebene Name nicht stimmt.

    Sound s{ "foobar" };
    // wie weiß ich jetzt hier, ob s bei
    s.play();
    // tatsächlich Töne ausspuckt oder nicht?
    

    Zumal deine Klasse ja auch den Rückgabewert von PlaySound schluckt.

    @lemon03 sagte in ein Wrapper?:

    Wie ist das denn gemeint, das der Fehler im Konstruktor werfen soll?

    if (!file_exists) // 1)
        throw std::runtime_error("emotional crysis: favourite music not found!");
    

    @lemon03 sagte in ein Wrapper?:

    Der Konstruktor soll gar nicht erst vollständig ausgeführt werden, so das kein Objekt erstellt wurde?

    Ja.

    1) es gibt in <filesystem> sicher elegantere Möglichkeiten, als zu versuchen, das File zu öffnen.



  • Ok, dann danke ich mal. Werde ich wohl einige Stellen im Projekt umschreiben müssen. Aber das ist ok. Wollte ich ja auch irgendwie. Danke auch für <filesystem>.

    Wegen PlaySound() und dessen Möglichkeiten später dann.



  • @schlangenmensch sagte in ein Wrapper?:

    Außerdem soll ja irgendwann vlt. auch mal jemand anderes deinen Code verwenden können. Bei einer Fehlerhaften Eingabe will ich als Entwickler ganz bestimmt keine Ausgabe, die mir sagt, dass ich jetzt das Programm beenden muss, sondern eben die Möglichkeit damit umzugehen, wie ich es für richtig halte.

    Edit: Ich habe deinen Edit zu spät gesehen. Du kannst ja in deinem Aufrufer die Ausnahme fangen und eine entsprechende Ausgabe machen. Oder beliebig anders verarbeiten. Das ist niederschwellig.

    Wäre es eine Idee, dem Anwender die Möglichkeit zu geben, den Namen zur Laufzeit zu korrigieren? Mit einer simplen Eingabe, die beim Fehlerfall kommt?



  • Ja, sowas. Datei ist Fehlerhaft, also beschwert sich die Klasse (exception ist der Weg, der zu gehen ist, meiner Meinung nach). Der Aufrufer kann dann zum Beispiel nach einem korrigierten Namen fragen.
    Man könnte sich dann auch überlegen, ob man zwischen 'Datei existiert nicht' und 'Datei liegt nicht in einem abspielbaren Format vor' unterscheiden möchte.



  • Bis ich etwas besseres habe, nur für den Übergang um den Schnitzer ungültiges Objekt, wäre das ok?

    class Sound
    {
    public:
    
        Sound( const std::string& sound_name ): name( sound_name )
        {
            if ( !getSoundFile() )
            {
                //Korrektur oder
                //Ausnamhme werfen?
            }
        }
    
        void play() const
        {
            PlaySound( file_name.c_str(), NULL, SND_ASYNC );
        }
    
        static void stop()
        {
            PlaySound( NULL, 0, 0 );
        }
    
    private:
    
        std::string name;
        std::string file_name;
    
        bool getSoundFile()
        {
            const std::string suffix = ".wav";
            const std::string filename = Files::soundsFolder() + name + suffix;
            std::ifstream file( filename );
            if ( !file )
            {
                std::cerr << "Sound::getSoundFile(): file error\n";
                std::cerr << "file: " << filename;
                _pressKey();
                return false;
            }
            file_name = filename;
            return true;
        }
    };
    

    Und wenn ich jetzt eine Korrektur oder einen Vorschlag machen möchte, wie komme ich wieder zu der Stelle, wo dieses Objekt erzeugt werden sollte? Also wieder vor den Konstruktor Aufruf?



  • Der Aufrufer muss sich halt überlegen, was er macht, wenn eine Exception geworfen wird. Also, ob er z.B. dann nochmal eine Abfrage nach dem Dateinamen macht.

    Als kleine Anregung eine Programm, dass auf die Eingabe 42 wartet:

    #include <iostream>
    #include <string>
    
    void input42()
    {
        std::cout << "input number: ";
        std::string input;
        std::cin >> input;
    
        if(input != "42" )
            throw("Not 42");    
        else
            std::cout << "\n congrats, input correct";
    }
    
    bool input()
    {
        try
        {
            input42();
            return true;
        }catch(...)
        {
            return false;
        }
    }
            
    
    int main()
    {
      while(!input());
    }
    


  • Ich meinte eher,

    //code
    Sound s( "test" );
    //code
    s.play();
    //code
    

    Wenn "test" falsch ist, korrigiert wird, wie komme ich wieder zu

    //code
    Sound s( "test" );
    //code
    s.play();
    //code
    

    ?
    Ich könnte nach der Korrektur aus if ( !getSoundFile() ) springen und weitermachen, aber wenn auch die Korrektur falsch ist?



  • @lemon03

    z.B.

    for(;;)
    {
      try
      {
        Sound s( "test_der_sich_woher_auch_immer_aendert" );
        s.play();
        break;
      }
      catch(...)
      {
      }
    }
    

    Aber wäre nicht irgendwas wie 'setSoundFile' hier günstiger?



  • Bitte ohne Endlosschleife und ohne try/catch. Ausnahme werfen ist kein Problem. Deshalb das oder im obigen Kommentar.

    Geht drum, wenn ich keine Ausnahme werfen möchte, sondern zur Laufzeit korrigieren will, wie mache ich das? Ich kann ja schlecht einen Konstruktor rekursiv aufrufen?

    Sound s( "test_der_sich_woher_auch_immer_aendert" );
    

    Das kann passieren, wenn ich oder ein potentieller jemand im Code beim programmieren den Namen des Sound falsch schreibt. Oder vergessen hat, die Datei in den richtigen Ordner zu kopieren etc.



  • Wie willst du zur Laufzeit korrigieren, was du zur 'Entwicklungszeit' falsch geschrieben hast?



  • Ich dachte, ich greife damit einen Vorschlag auf? Darum geht es doch? Nicht den Code korrigieren und neu kompilieren, sondern zur Laufzeit ändern.

    @schlangenmensch sagte in ein Wrapper?:

    Außerdem soll ja irgendwann vlt. auch mal jemand anderes deinen Code verwenden können. Bei einer Fehlerhaften Eingabe will ich als Entwickler ganz bestimmt keine Ausgabe, die mir sagt, dass ich jetzt das Programm beenden muss, sondern eben die Möglichkeit damit umzugehen, wie ich es für richtig halte.

    Mein Plan wäre dann, den Konstruktor mit dem korrigierten string neu aufzurufen. Das geht dann wahrscheinlich nicht. Also bleibe ich beim Aussteigen, nur das jetzt kein Objekt mehr erzeugt wird.



  • Du willst normalerweise keinen Dateinamen fest in den Source Code einprogrammieren.
    Zur Laufzeit kannst du z.B. Eingaben von einem Anwender auf Korrektheit überprüfen.

    Es ging mir dabei darum, dass die Klasse für verschiedene Anwendungsfälle verwenden kannst. Darum die Idee, die Klasse nur eine Mitteilung raus geben zu lassen, dass ein Fehler aufgetreten ist. Wie mit dem Fehler umgegangen wird, ist dann in der Verantwortung vom Aufrufer. Das heißt, je nach Anwendung kannst du das Programm beenden, oder irgendwas anderes machen. Das soll aber nicht die "Sound Abspiel Klasse" entscheiden.



  • Danke. Wenn ich ein File lesen will, eine Tabelle, einen Spielstand, irgendwas, was nicht im Code festgehalten ist, wie muss ich das denn sonst machen, außer den Dateinamen anzugeben? Es ist ja auch nicht der Dateiname selbst, der wird erzeugt, aber es muss doch einen Namen geben, damit ich und der Code weiß, was gelesen werden muss?

    Bezüglich der Fehlerbehandlung kam mir eine Struktur, vielleicht eine Klasse oder ein namespace in den Sinn. Dort wird anhand der Parameter, die man übergibt, entschieden, wie reagiert wird. Wäre das praktikabel?

    Vielleicht sollte ich kurz erläutern, worin meine Anwendung besteht. Es ist kein Programm, was abläuft, der Anwender gibt ein bisschen was ein und guckt dann zu.
    Es werden Funktionen und Möglichkeiten angeboten, selbst etwas zu programmieren. Der ausführbare Code muss erst vom Anwender geschrieben werden. Und meine Fehlermeldungen sind eher wie Syntaxfehler zu verstehen. Dies oder jenes klappt so nicht und muss korrigiert werden.



  • @lemon03 sagte in ein Wrapper?:

    Danke. Wenn ich ein File lesen will, eine Tabelle, einen Spielstand, irgendwas, was nicht im Code festgehalten ist, wie muss ich das denn sonst machen, außer den Dateinamen anzugeben? Es ist ja auch nicht der Dateiname selbst, der wird erzeugt, aber es muss doch einen Namen geben, damit ich und der Code weiß, was gelesen werden muss?

    Ja, aber in aller Regel wird der Name durch den Programmablauf bestimmt. Bei einem Spielstand entscheidet der Spieler, welchen er laden möchte. Bei einer zu bearbeitenden Textdatei, bestimmt auch der Anwender, welche er öffnen will. Wenn es ein Sound zum Abspielen bei einer bestimmten Aktion ist, könnte ich mir vorstellen, dass der aus einer Config Datei kommt, damit man den, ohne neu zu kompilieren, ändern kann.

    Bezüglich der Fehlerbehandlung kam mir eine Struktur, vielleicht eine Klasse oder ein namespace in den Sinn. Dort wird anhand der Parameter, die man übergibt, entschieden, wie reagiert wird. Wäre das praktikabel?

    Das kann unter umständen Sinnvoll sein. Aber die übergeordnete Klasse muss erstmal entscheiden, was sie macht, wenn ein Fehler auftritt. Zum Beispiel könnte sie eine Klasse aufrufen, die den Fehler irgendwo logt o.ä.

    Vielleicht sollte ich kurz erläutern, worin meine Anwendung besteht. Es ist kein Programm, was abläuft, der Anwender gibt ein bisschen was ein und guckt dann zu.
    Es werden Funktionen und Möglichkeiten angeboten, selbst etwas zu programmieren. Der ausführbare Code muss erst vom Anwender geschrieben werden. Und meine Fehlermeldungen sind eher wie Syntaxfehler zu verstehen. Dies oder jenes klappt so nicht und muss korrigiert werden.

    Also wird das eine Art Skript Interpreter? Aber grade der soll sich ja nicht beenden, wenn ein Fehler auftritt, sondern dem Anwender die Möglichkeit bieten, seinen Code zu korrigieren, oder eventuell noch weitere Fehler finden? Aber wie genau das Verhalten im Fehlerfall sein soll, ist im endeffekt deine Design Entscheidung.



  • @schlangenmensch sagte in ein Wrapper?:

    Ja, sowas. Datei ist Fehlerhaft, also beschwert sich die Klasse (exception ist der Weg, der zu gehen ist, meiner Meinung nach). Der Aufrufer kann dann zum Beispiel nach einem korrigierten Namen fragen.
    Man könnte sich dann auch überlegen, ob man zwischen 'Datei existiert nicht' und 'Datei liegt nicht in einem abspielbaren Format vor' unterscheiden möchte.

    Wäre für mich kein klassischer Fall von Exceptions. Fände ich persönlich übertrieben.

    Nicht mal die STL selber wirft da ne Exception.
    Wenn eine Datei nicht existiert ist das aus meiner Sicht ein übliches und nicht seltenes Verhalten. Also eher keine Ausnahme.

    std::ifstream MySoundFile( "./nichtexistierendeDatei.mp3", std::ios::binary );
    

    wirft auch keine Exception, sondern man kann mit diversen Funktionen nachprüfen ob der Datenstrom lesbar/schreibbar ist.

    In dem Fall des SoundFiles würde ich dann das tatsächliche Öffnen der Datei im Konstruktor auch unterlassen und stattdessen das Prüfen/Öffnen der Datei spätestens beim "play"-Aufruf machen und im Fehlerfall einen bool oder einen Errorcode zurückliefern und zusätzlich eine Funktion "isValidFile" oder ähnliches anbieten, falls der Nutzer wünscht, die Gültigkeit der Datei vor dem "play"-Aufruf zu prüfen.



  • @schlangenmensch sagte in ein Wrapper?:

    Also wird das eine Art Skript Interpreter?

    Eher eine, wie man früher bei Interpretersprachen sagte, "Befehlserweiterung". Etwas wie eine Bibliothek, nur auf einem sehr niedrigeren Niveau. Und nur für eine bestimmte Oberfläche und einen bestimmten Anwendungszweck.



  • Vielleicht sollte ich mal nachholen, ein Beispiel für die Anwendung zu zeigen. Nur um eine Vorstellung zu haben.

    Dies war eine kleine Fingerübung für den bisherigen Code.
    towers of hanoi in windows console



  • @lemon03 Ich sehe folgende Möglichkeiten:

    • Verbesserungsabfrage im ctor (NICHT zu empfehlen)
    • Funktion Sound::is_ready oder so hinzufügen und die Abfrage in einer Schleife bei der Erstellung übernehmen (vermutlich mein Weg)
    • exception abfangen und Abfrage starten

    Vielleicht habe ich aber noch eine Möglichkeit übersehen.



  • @ sagte in ein Wrapper?:

    I/O im Konstruktor ist sehr unschön, meiner Meinung nach. 🤠

    Wieso?
    Bzw. genauer: An welchem Teil störst du dich?

    Ich sehe da genau in (vermutliches) Problem, und das ist der Aufruf der _pressKey() Funktion. Checken ob ein File existiert im Ctor ist OK. Logging im Ctor ist OK. Im Fehlerfall (und nur im Fehlerfall) darauf zu warten dass der Benutzer ne Taste drückt (was _pressKey() wohl vermutlich macht), ist mMn. nicht OK.

    Der Grund dafür hat aber nicht viel mit "I/O im Konstruktor" zu tun.


Anmelden zum Antworten