Eigenes std::cout



  • Zu dem ersten - ja... Und das war so gemeint, dass der erste Schritt _VLL_ nicht mit dazugehört - der Rest aber mit Sicherheit atomar ist...

    Zu dem Link: Danke - ma gucken, ob mich das weiterbringt - ich versuch ma, mich ne ganz so doof anzustellen und schreib dann später noch mal ;o)

    Ciao



  • unskilled schrieb:

    Zu dem ersten - ja... Und das war so gemeint, dass der erste Schritt _VLL_ nicht mit dazugehört - der Rest aber mit Sicherheit atomar ist...

    Die Werte zu kopieren ist (ohne externe Hilfsmittel) sicher nicht atomar 🙄 Und Wertzuweisung (Schritt 3) auch nur (wenn ueberhaupt) fuer Maschinendatentypen. Ausserdem muessten die 3 Aktionen ZUSAMMEN atomar sein, damit push_back atomar ist 😉

    EDIT: um mir meine Frage mal selbst zu beantworten
    http://www.sgi.com/tech/stl/thread_safety.html
    http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch03s05.html#manual.intro.using.concurrency.thread_safety



  • hmm... Mein nächster Versuch... (wahrscheinlich denkt jz auch der letzte, dass ich doof bin - aber nunja... ^^) (die locks / unlocks dann sind ja kein prob mehr - nur erst ma das prinzip...)

    class hlper
    	{
    		protected:
    			std::filebuf FB;
    		public:
    			hlper (std::string P)
    				{
    					FB.open (P.c_str (), std::ios::out);
    				};
    	};
    class my_cout : private hlper, public std::ostream
    	{
    		private:
    			std::ostream os;
    		public:
    			my_cout (std::string Para) : hlper (Para), std::ostream (&(FB)), os (&(FB)) {};
    			const std::ostream& operator << (const std::stringstream &ausgabe)
    				{
    					os << ausgabe;
    					return *this;
    				};
    			const std::ostream& operator << (const std::ostream &ausgabe)
    				{
    					os << ausgabe;
    					return ausgabe;
    				};
    			const std::ostream& operator << (SOCKET ausgabe)
    				{
    					os << static_cast <int> (ausgabe);
    					return *this;
    				};
    	};
    

    Eigtl geht das ganze auch schon ganz gut (mal abgesehen davon, dass es echt nicht schön ist - aber mein Projekt kann ja nicht nur hübsche Klassen haben ;o) ):

    namespace my
    {
     my_cout cout ("log_cout.txt");
    }
    
    using my::cout;
    SOCKET socket;
    
    cout << socket; //geht
    cout << socket << "...."; //geht nicht mehr - obwohl ich dachte, dass das return *this; richtig wäre -.-
    

    Danke 🤡



  • class my_cout : private hlper, public std::ostream
        {
            private:
                std::ostream os;
            public:
                my_cout (std::string Para) : hlper (Para), std::ostream (&(FB)), os (&(FB)) {};
                const my_cout& operator << (const std::stringstream &ausgabe)
                    {
                        os << ausgabe;
                        return *this;
                    };
                const my_cout& operator << (const std::ostream &ausgabe)
                    {
                        os << ausgabe;
                        return ausgabe;
                    };
                const my_cout& operator << (SOCKET ausgabe)
                    {
                        os << static_cast <int> (ausgabe);
                        return *this;
                    };
        };
    


  • Ach ich Trottel ^^ Danke :>

    btw: das const war auch falsch ^^ weil ich den operator ja nicht als const deklarieren kann...



  • (Der Vollständigkeit halber) hier meine Lösung:

    header

    //includes
    namespace my
    	{
    		class Tbeg	{};
    
    		class Tend	{};
    
    		class Thlp
    			{
    				protected:
    					std::filebuf m_FB;
    					Thlp (std::string P = "stdlog.txt");
    					~Thlp (void);
    			};
    
    		class Tout : private Thlp
    			{
    				private:
    					std::ostream m_os;
    					static CRITICAL_SECTION m_cs;
    					static void lock (void);
    					static void unlock (void);
    				public:
    					Tout (std::string Parameter);
    					~Tout (void);
    					Tout& operator << (const char * ausgabe);
    					Tout& operator << (const int &ausgabe);
    					Tout& operator << (const SOCKET &ausgabe);
    					Tout& operator << (const std::string &ausgabe);
    					Tout& operator << (const Tbeg &muell);
    					void operator << (const Tend &muell);
    			};
    	};
    

    source

    //include header
    
    CRITICAL_SECTION my::Tout::m_cs;
    
    my::Thlp::Thlp (std::string Parameter)
    	{
    		m_FB.open (Parameter.c_str (), std::ios::end);
    	};
    
    my::Thlp::~Thlp (void)
    	{
    		m_FB.close ();
    	};
    
    my::Tout::Tout (std::string Parameter) : Thlp (Parameter), m_os (&m_FB)
    	{
    		InitializeCriticalSection (&m_cs);
    	};
    
    void my::Tout::lock (void)
    	{
    		EnterCriticalSection (&m_cs);
    	};
    
    void my::Tout::unlock (void)
    	{
    		LeaveCriticalSection (&m_cs);
    	};
    
    my::Tout& my::Tout::operator << (const char * ausgabe)
    	{
    		std::cout << ausgabe;
    		m_os << ausgabe;
    		return *this;
    	};
    
    my::Tout& my::Tout::operator << (const int &ausgabe)
    	{
    		std::cout << ausgabe;
    		m_os << ausgabe;
    		return *this;
    	};
    
    my::Tout& my::Tout::operator << (const SOCKET &ausgabe)
    	{
    		std::cout << ausgabe;
    		m_os << ausgabe;
    		return *this;
    	};
    
    my::Tout& my::Tout::operator << (const std::string &ausgabe)
    	{
    		std::cout << ausgabe;
    		m_os << ausgabe;
    		return *this;
    	};
    
    my::Tout& my::Tout::operator << (const Tbeg &muell)
    	{
    		lock ();
    		return *this;
    	};
    void my::Tout::operator << (const Tend &muell)
    	{
    		std::cout << std::endl;
    		m_os << std::endl;
    		unlock ();
    	};
    
    my::Tout::~Tout (void)
    	{};
    

    die Deklaration:

    namespace my
    	{
    		Tout cout ("log_cout.txt");
    		Tbeg beg;
    		Tend end;
    	};
    

    der Aufruf:

    my::cout << my::beg << "..." << my::end;
    

    Bis jetzt hab ich noch keine Fehler (richtige Benutzung vorrausgesetzt ^^) mitbekommen...

    Wenn ihr Lust und Zeit habt, könnt ihr euch ja drüber auslassen (also sagen, wie (doof) ihr es findet ^^).

    Byebye 🙂



  • unskilled schrieb:

    Wenn ihr Lust und Zeit habt, könnt ihr euch ja drüber auslassen (also sagen, wie (doof) ihr es findet ^^).

    Na wenn's funktioniert, dann ist das doch schon mal gut. Ich würde Dir aber trotzdem eine andere Lösung vorschlagen. Mal angenommen, es gibt eine Klasse Mutex mit den üblichen Fähigkeiten - dann brauchst Du für Deine Anwendung nur noch zu schreiben:

    #include <iostream>
    // #include "Mutex.h" (s.u.)
    
    class Lock
    {
    public:
        Lock( std::ostream& out )
            : m_lock( m_out_guard )
            , m_out( out )
        {}
        template< typename T >
        std::ostream& operator<<( const T& x ) const
        {
            return m_out << x;
        }
    private:
        typedef Mutex mutex_type;  // auch boost::mutex wäre möglich
        mutex_type::scoped_lock m_lock;
        std::ostream& m_out;
        static mutex_type m_out_guard;
    };
    Lock::mutex_type Lock::m_out_guard;
    
    // -- Anwendung
    int main()
    {
        using namespace std;
        Lock( cout ) << "sichere " << "und " << " unteilbare " << "Ausgabe " << endl;
    
        return 0;
    }
    

    Der Trick besteht darin, dass der Ausdruck Lock( cout ) ein temporäres Objekt vom Typ 'scoped_lock' erzeugt, welches in seinem Konstruktor das Mutex 'm_out_guard' lockt. Das temporäre Objekt wird erst nach der Abarbeitung des gesamten Ausdrucks wieder beseitigt und mit ihm sein Member 'm_lock', der in seinem Destruktor das Mutex wieder freigibt.

    Der Ausdruck mit dem Template sorgt dafür, dass Du nicht für alle möglichen Typen den Streaming-Operator überladen musst. Es können ja auch ein paar eigene dabei sein - wie z.B. SOCKET.

    Als Mutex kannst Du boost::mutex benutzen oder auch was selbst gestricktes - etwa so:

    #include <windows.h>
    
    template< typename M >
    struct basic_lock
    {
        basic_lock( M& m ) : m_mutex( m )
        {
            m_mutex.do_lock();
        }
        ~basic_lock()
        {
            m_mutex.do_unlock();
        }
    private:
        M& m_mutex;
        // Kopieren verboten
        basic_lock( const basic_lock& );
        basic_lock& operator=( const basic_lock& );
    };
    
    struct Mutex
    {
        Mutex() : m_cs()
        {
            InitializeCriticalSection( &m_cs );   
        }
        ~Mutex()
        {
            DeleteCriticalSection( &m_cs );
        }
        typedef basic_lock< Mutex > scoped_lock;
        friend scoped_lock;
    
    private:
        void do_lock()
        {
            EnterCriticalSection( &m_cs );
        }
        void do_unlock()
        {
            LeaveCriticalSection( &m_cs );
        }
        CRITICAL_SECTION m_cs;
    
        // --   Kopieren verboten
        Mutex( const Mutex& );
        Mutex& operator=( const Mutex& );
    };
    

    Das ganze ist natürlich noch auf H- und Cpp-Dateien zu verteilen.

    Was damit noch nicht geht ist z.B.:

    Lock( cout ) << hex ...;
    

    dann müßte man das Template noch erweitern.

    Falls Du mehr als einen Ausdruck locken möchtest, so geht das auch mit

    { // neuer Scope
            Lock lk( cout ); // lokales Objekt 'lk' lebt solange wie der Scope
            cout << ... ;
            cout << ... ;
        } // erst hier kann ein anderer Thread wieder auf cout schreiben.
    

    Gruß
    Werner



  • Hmm... Also ich find meins schöner - aber das is nun mal Geschmackssache - wahrscheinlich hast du zwar mehr Ahnung von C++, aber vll liegts auch gerad daran, dass mir das nicht so gefällt - kein Plan ^^

    Die Klasse Mutex gibt es bei mir ja auch schon, aber die bekomm ich da nicht so richtig rein - weil sie ja für alle Streams (mein cout, cerr und clog) gelten soll...

    Naja - also danke - ich habs mir zwar ne Zeit lang angeguckt, aber bleib bei meinem 🤡

    Byebye



  • unskilled schrieb:

    ...

    der Name ist Programm



  • klar 😛

    für dich reichts vermutlich aber gerad noch so...



  • lol


Anmelden zum Antworten