Operator << anpassen



  • Hallo,

    ich möchte für folgende Logfile-Klasse einen "<<"-Operator einführen, der
    einfach auf den Stream des Files umleitet.

    LogFile logfile;
    logfile << "String und die Zahl " << 123 << "\n"
    
    class LogFile
    {
    protected:
    	std::ofstream logStream;
    };
    

    Ich finde im Netz immer nur Beispiele, wie man eigene Klassen in einen Stream schreiben kann. Wie sollte mein Operator aussehen?

    Vielen Dank!



  • Die primitive Lösung:

    typedef std::ofstream LogFile;
    

    Die direkte Lösung:

    template<typename T>
    LogFile& operator<<(LogFile& log,const T& val)
    {
      log.logStream<<val;
      return log;
    }
    //oder als Methode:
    template<typename T>
    LogFile& LogFile::operator<<(const T& val)
    {
      logStream<<val;
      return *this;
    }
    

    Die bessere Variante ist es vermutlich, deine LogFile-Klasse in die IOStream-Hierarchie einzugliedern (dazu benötigst du eine eigene Stream-Buffer-Klasse als Ziel).



  • Beeindruckend. Das mit den Templates wäre mich wohl nicht eingefallen.
    Mir war wichtig, daß ich testen kann, ob das File wirklich offen ist.

    Ist doch 'ne nützliche kleine Sache. Wer's mal ausprobieren will:

    #include <fstream>
    using namespace std;
    
    class LogFile
    {
    public:
    
    	 LogFile() { logStream.open( "logfile.txt", ios::out );	};
    	~LogFile() { logStream.close(); };
    	ofstream &stream() { return logStream; };
    
    	template<typename T> LogFile& operator<<(const T& val)
    	{ 
    		if(logStream) logStream<<val; 
    		return *this; 
    	};
    
    protected:
    	ofstream logStream;
    };
    
    int main()
    {
    	LogFile logfile;
    	logfile << "Test";
    }
    

    Danke!



  • stimpleton schrieb:

    Mir war wichtig, daß ich testen kann, ob das File wirklich offen ist.

    Also dafür reicht die Primitv-Variante mit dem typedef völlig aus (wenn du an einen nicht einsatzbereiten Stream Ausgaben schickst, ignoriert er sie einfach). Der einzige Vorteil deiner Klasse ist imho, daß sie automatisch ein File öffnet, wenn du ein Objekt anlegst (mit dem Nachteil, daß du sie nicht wie einen vollwertigen ostream verwenden kannst).



  • Die vorgeschlagene Konstruktion kann z.B. keine Manipulatoren wie das übliche std::endl aufnehmen. Ändert die Template-Methode noch zu:

    template<typename T> std::ostream& operator<<(const T& val)
        {
            return logStream << val;        // if( logStream) .. ist nicht notwendig; das passiert intern sowieso
        };
    

    dann funktioniert auch:

    int main()
    {
        LogFile logfile;
        logfile << "Test" << endl;
    }
    

    die einzige Einschränkung ist nur, dass ein Manipulator nicht unmittelbar hinter logfile stehen darf.

    stimpleton schrieb:

    class LogFile
    {
    public:
    	 LogFile() { logStream.open( "logfile.txt", ios::out );	};
    	~LogFile() { logStream.close(); };
    

    Besser:

    class LogFile
    {
    public:
        LogFile() : logStream("logfile.txt") {}
        // kein Destruktor explizit angeben, da unnötig
    

    .. der Destruktor von ofstream macht ohnehin alles richtig. Das gehört zu der Wesensart von C++ 😉

    Gruß
    Werner


  • Mod

    Werner Salomon schrieb:

    Die vorgeschlagene Konstruktion kann z.B. keine Manipulatoren wie das übliche std::endl aufnehmen.

    wieso?



  • Hallo,
    für die Standardmanipulatoren wie std::endl bedarf es Überladungen der Form:

    LogFile& operator<<(LogFile& lf, std::ostream& (*m)(std::ostream&));
    

    Ein einziges Template reicht da nicht, da std::endl eine überladene Funktion ist und T somit nicht eindeutig hergeleitet werden kann.


  • Mod

    ja richtig. Hätte den Compiler anschmeißen sollen. Jedenfalls ist das besser als der andere Vorschlag.



  • camper schrieb:

    ja richtig. Hätte den Compiler anschmeißen sollen. Jedenfalls ist das besser als der andere Vorschlag.

    Ich bin mir allerdings nicht sicher, ob der Standard garantiert, dass die Manipulatoren als Funktionen implementiert sind (und habe gerade keinen Standard zur Hand, so dass ich nicht nachschauen kann). Habe bisher allerdings noch keine Standard-Lib-Impl gesehen, bei der der Ansatz nicht funktioniert hat.


Anmelden zum Antworten