ein Wrapper?



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



  • @trustee sagte in ein Wrapper?:

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

    Ja, auch Clang stört sich daran. Schlägt std::move() vor. Meinst Du diese Richtung? Oder den Inhalt der Methode?



  • Was bringt dir dieser Wrapper? Zumal du lieber ne Ausnahme im Konstruktor werfen solltest, wenn die Datei nicht existiert.



  • Eine Klasse sollte eine Sache machen. Nicht Sound abspielen, Dateien laden, Konsolenausgaben machen. Und dann auch noch im Konstruktor!



  • Du meinst, nicht Sound abspielen + Datei laden + Ausgabe in einer Klasse machen? Nur eines davon in einer Klasse?


  • Gesperrt

    @trustee sagte in ein Wrapper?:

    Eine Klasse sollte eine Sache machen. Nicht ...

    Macht sie. Hast du play() und stop() übersehen?

    @lemon03 für paar Verbesserungsvorschläge müssten wir mehr über die Anwendungsdomäne wissen...



  • Oh, danke. Ging mir nur darum, ob man dies schon als Wrapper bezeichnen kann. Eine Schnittstelle für PlaySound().

    Die Aufgabe besteht nur darin, bei Aufforderung einen Sound abzuspielen. Die Anwendungsdomäne ist ein Konsolenanwendung. Was ich jetzt gerne wissen würde, wie das mit der I/O im Konstruktor gemeint ist. Eine Methode, die ein file prüft, sollte nicht im Konstruktor aufgerufen werden?



  • Damit wird deine Ausgabe im Fehlerfall gemeint sein. Huber Alfonsius hat bereits eine Alternative aufgezeigt.
    Was passiert bei dir eigentlich im Fehlerfall? Dann hast du eine Ausgabe, aber dein Objekt wird trotzdem erstellt und du kannst hinterher auf ein fehlerhaft erstelltes Objekt zugreifen.

    Man könnte sich auch fragen, warum eine Klasse namens Sound irgendwie einen Programmpfad zusammen bastelt. Das schränkt nur die Nutzbarkeit ein.



  • @schlangenmensch sagte in ein Wrapper?:

    Was passiert bei dir eigentlich im Fehlerfall? Dann hast du eine Ausgabe, aber dein Objekt wird trotzdem erstellt und du kannst hinterher auf ein fehlerhaft erstelltes Objekt zugreifen.

    Warum sollte man das tun? Diese simple Fehlermeldung soll doch nur dazu führen, das man die Anwendung schließt und im Code den Namen korrigiert.

    PS: Eigentlich will ich gar keine Ausnahmen werfen. Ich will nur etwas niederschwelliger hingewiesen werden (*), wenn etwas in meinen Code nicht stimmt und ich dies korrigieren kann.

    edit: (*) von meinem Code bezüglich



  • @lemon03 Weil Sinn von Softwareentwicklung unter anderem ist, Sachen generisch verwenden zu können. Eine Klasse die genau ein Sound File abspielt, wofür soll die gut sein. Du willst doch mindestens das Soundfile, dass abgespielt wird dynamisch angeben können und wenn dann ein Fehler in der Datei auftaucht, den korrigieren können ohne das Programm neu zu kompilieren.
    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. Jenachdem wie du das dann verarbeitest bekommt der Anwender deiner Software davon gar nix mit.
    Weiterhin bleibt es Sinnlos eine Klasse zu schreiben, die genau ein Soundfile abspielen kann.



  • Die Klasse barucht gar nicht aufwändiger zu sein, weil PlaySound() nicht mehr kann, als play() und stop(). Und ein bisschen Event-Kram. Ich war selbst etwas enttäuscht, aber für den momentanen Gebrauch benötige ich auch nicht mehr. Wenn doch, muss ich auf PlaySound() ganz verzichten und ein Klasse um was anderes wickeln.

    Und von der Kasse können beliebig viele Objekte mit der selben Anzahl unterschiedlicher Sounds erzeugt werden. Sie können aber nicht gleichzeitig laufen, weil PlaySound() dies nicht macht. Sie werden aber anhand der Objektbezeichnung erkannt.



  • @schlangenmensch sagte in ein Wrapper?:

    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.

    Natürlich muss man nicht, man kann natürlich auch weitermachen und sehen was passiert (experimental imersion).



  • Sorry für 3xpost,

    @schlangenmensch sagte in ein Wrapper?:

    Oder beliebig anders verarbeiten. Das ist niederschwellig. Jenachdem wie du das dann verarbeitest bekommt der Anwender deiner Software davon gar nix mit.

    zB den naheliegendsten File-Namen nehmen, der existiert?



  • So wie Deine Klasse jetzt aussieht kann man nichtmal feststellen, ob eine Instanz davon gültig ist (= das angegebene file abspielen kann) oder nicht. Also sollte der Konstruktor gefälligst werfen, wenn etwas nicht passt.

    Ach bietet PlaySound() mehr Möglichkeiten, als bloß aus Dateien abzuspielen - Deine Klasse schränkt das sehr ein. Vielleicht sollte sie eher mit einem SoundObject arbeiten, als mit einer Zeichenkette und Player statt Sound heißen.



  • 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?


Anmelden zum Antworten