Streams und << Operator



  • Ich habe ein Problem mit streams. Kann mir wer helfen? - Wäre sehr flott! - Danke schon mal!

    Gegeben sei eine Klasse mit gewissen Memberfunctions. Diese geben zwischendurch Sachen (Meldungen) aus. Das Ganze gibt eine Konsolen APP. Statt die Meldungen einfach per cout auszugeben, möchte ich machen, dass man wahlweise genau dasselbe auch noch oder nur in eine Logdatei schreiben kann. Wie ist das am Besten zu realisieren? - Ich denke man macht doch am Besten eine Klasse (eigenständige, ohne Vorfahren [oder doch von ostream ableiten?]) mit zwei ostreams drinn. Und dann ein eigener << Operator. Ich habs versucht, aber es geht nicht ganz. Es frisst mir den << Operator nicht. Was ist wohl falsch?

    class COstream
    {
    	private:
    		ostream		*m_ostreamCOUT;
    		ostream		*m_ostreamFile;
    
    	public:
    		COstream(BYTE bUseCOUT, string filename);
    		~COstream();
    		COstream& operator<< (istream&);
    
    };
    
    COstream::COstream(BYTE bUseCOUT, string filename)
    {
    	m_ostreamCOUT = NULL;
    	m_ostreamFile = NULL;
    
    	if (bUseCOUT) m_ostreamCOUT = new ostream (cout);	
    	if (filename != "") m_ostreamFile = new ofstream (filename.c_str());
    }
    
    COstream::~COstream()
    {
    	if (m_ostreamCOUT) delete m_ostreamCOUT;
    	if (m_ostreamFile) delete m_ostreamFile;
    }
    
    COstream& COstream::operator<< (istream& strm)
    {
    	if (m_ostreamCOUT) *m_ostreamCOUT << strm;
    	if (m_ostreamFile) *m_ostreamFile << strm;
    
    	return *this;
    }
    

    Dieser Code ist kompilierbar, aber wenn man nun was drauf ausgeben will, happerts beim Compilieren...

    {
    	COstream	mystream (true, "");
    
    	mystream << "test";
    }
    

    ... Binaerer Operator '<<' : Kein Operator definiert, der einen linksseitigen Operator vom Typ 'class COstream' akzeptiert (oder keine geeignete Konvertierung moeglich)

    Hilfe ist sehr erwünscht! Vielleicht ists ne Kleinigkeit. Oder ist ev. sogar das ganze Konzept schlecht? Würde man gleich besser von einem ostream erben? aber wie dann den zweiten solchen integrieren? ...

    - Adrian



  • eigene Streams erzeugt man, in dem man von std::streambuf ableitet

    Außerdem ist dein Konstruktor nicht Exception Sicher und für bool Werte sollte man doch bool und nicht BYTE nehmen (besonders da groß geschriebene Typen absolut hässliche sind). Außerdem würde ich lieber den Konstruktor mit Parameterüberladung machen, anstelle ein if(filename!="") einzubauen.

    Und C als Klassenprefix ist absolut schlecht (siehe FAQ)



  • Vielen Dank für die Tipps.
    Ja, grosse Typen sind hässlich. Aber ich MUSS da unter VC++ coden, und das ist eh hässlich. Die haben dort alles so grosse Typen. Und C für Klasenprefix ist Vorgabe und auch m_ für Memberfunctions...

    Aber zur Sache: dann sollte ich von streambuf ableiten. Aber wie würdest Du dann machen, dass man dann mit einer Ausgabe auf den Stream diesen quasi an zwei andere weitergeben kann? Ist es daher sinnvoll, von streambuf abzuleiten?

    - Adrian



  • ABader schrieb:

    Ja, grosse Typen sind hässlich. Aber ich MUSS da unter VC++ coden, und das ist eh hässlich. Die haben dort alles so grosse Typen. Und C für Klasenprefix ist Vorgabe und auch m_ für Memberfunctions...

    Quatsch. Ich habe die Assistenten nie verwendet, vielleicht zwingen die einen zu sowas. Aber man kann in MSVC schreiben, was man will. Erstell eine neue Datei und leg los.

    Ich hätte das spontan so geschrieben (von daher ungetestet):

    class Output
    {
        bool m_toCout;
        std::ofstream m_file;
    
        Output(bool toCout, const std::string& filename)
        : m_toCout(toCout), m_file(filename.c_str())
        {
        }
    
        template<typename T>
        Output& operator<<(Output& output, const T& t)
        {
            if (m_toCout)
              std::cout << t;
            m_file << t;
            return *this;
        }
    };
    

    Aber zur Sache: dann sollte ich von streambuf ableiten. Aber wie würdest Du dann machen, dass man dann mit einer Ausgabe auf den Stream diesen quasi an zwei andere weitergeben kann? Ist es daher sinnvoll, von streambuf abzuleiten?

    Klingt nach dem sauberen Weg (hat meiner von oben irgendwelche Einschränkungen?), aber vielleicht solltest du das Problem aus etwas größerer Entfernung betrachten und überlegen, ob es das wirklich wert ist. Wenn du größtenteils Strings schreiben willst, kommst du sogar mit der obigen Lösung ohne Templates aus und kannst für den Fall, dass du wirklich IOStreams-Funktionalität brauchst, einen std::ostringstream verwenden.



  • ABader schrieb:
    Ja, grosse Typen sind hässlich. Aber ich MUSS da unter VC++ coden, und das ist eh hässlich. Die haben dort alles so grosse Typen. Und C für Klasenprefix ist Vorgabe und auch m_ für Memberfunctions...

    Quatsch. Ich habe die Assistenten nie verwendet, vielleicht zwingen die einen zu sowas. Aber man kann in MSVC schreiben, was man will. Erstell eine neue Datei und leg los.

    Nene, ich brauch NIE Assistenten für sowas. Aber mit MUSS meine ich, dass ich für ein Projekt was machen MUSS, und die Leute dort verlangen gewissen Sachen. Und unter anderem eben auch BYTE und BOOL und so... aber eben, ob hässlich oder nicht, ich muss es jez einfach so machen. Auch C für Class Präfix.

    Deine Variante sieht nicht schlecht aus. Ich brauche nicht nur strings, daher macht ein Template durchaus Sinn. Allerdings kann ichs im VC++ nicht kompilieren. Mit Borland C++ Builder gehts ohne Schwierigkeiten. Da es nach VC++ spezifischem Problem aussieht, habe ich im VC++ Forum ein entsprechendes Post gestartet. Vielleicht weiss jemand, wie man auch mit M$ VC++ die STL oder besser Elementtemplates zum Laufen kriegt 🙄

    - Adrian



  • Ach so. Ich dachte, du sähest dich von MSVC zu hässlichen Namen gezwungen 🙂

    Hm ja. Also erstmal hat mein Code einen Bug, den ich grad erst gesehen habe: Natürlich muss der operator<< nur das "const T& t"-Argument haben, das andere ist zu viel. Wenn es trotzdem nicht klappt, hat dein MSVC vielleicht Probleme mit Element-templates; klappt das hier?

    struct Output
    {
        // Jetzt alles public (struct), damit der Compiler weder ein
        // Element-template noch einen template-friend verstehen muss.
        // Verlassen wir uns einfach drauf, dass keiner Unfug damit anstellt.
        bool m_toCout;
        std::ofstream m_file;
    
        Output(bool toCout, const std::string& filename)
        : m_toCout(toCout), m_file(filename.c_str())
        {
        }
    };
    
    template<typename T>
    Output& operator<<(Output& output, const T& t)
    {
        if (output.m_toCout)
            std::cout << t;
        output.m_file << t;
        return output;
    }
    


  • klappt das hier?

    Ja, das klappt. Hey, vielen Dank!
    --> Unterstützt denn VC++ 6 tatsächlich keine Member Function Templates?? Ist das möglich? Kann mir bitte jemand sagen, ob das effektiv zutrifft, oder ob ich einfach was falsch mache?

    Nur "endl" kann ich nicht rausgeben. Ich wollte das folgendermassen ergänzen:

    std::ostream& endl(std::ostream& os)
    { 
        return os.put('\n').flush();
    }
    

    Das funktioniert leider nicht in VC++. Es kommt eine Compiler Fehlermeldung "C1001: INTERNER COMPILER- FEHLER". Das ist echt mühsam. Habe auch das neuste SP reingespielt.
    Aber auch anständige Compiler zeigen, dass es nicht ganz korrekt ist. Statt ein "\n" und flush wird der Pointer auf die Funktion rausgegeben. Irgendwas ist also noch falsch.

    Kannst Du mir nochmals helfen? - Und wenn es geht so, dass es auch in VC++ 6 geht...
    Vielen Dank!!

    - Adrian


Log in to reply