Eigenes std::cout
-
leg dir mal n Bookmark auf die C++-Referenz oder auf cppreference.com oder eine andre Referenz deiner Wahl, da steht sowas drin
und ein nicht-atomares vector::push_back wuerde ich naiverweise erwarten, wenn vec.size() == vec.capacity =>
- neuen Speicher alokieren
- alte Werte rueberkopieren
- neuen Wert hinten anhaengenWuerd mich schon wundern, wenn das in jeder STL-Implementierung standardmaessig threadsafe (ohne overhead durch locks/mutexe/critical sections) ist, aber ich lass mich gern ueberraschen
-
-
"- neuen Speicher alokieren"
-
"- alte Werte rueberkopieren"
-
"- neuen Wert hinten anhaengen"
-
ok - hier könnte es zutreffen...
-
soll er noch im 'alten' Speicher rumoperieren? ich glaube nicht...
-
max. hier - aber das würde ich für komisch halten - warum sollte man mit so ner einfachen (und schnellen) operation denn warten?
Naja - wie auch immer...
'cppreference.com' kenn ich - nur leider bin ich entweder zu doof oder blind oder wie auch immer -.-
-
-
nimm boost::format^^
-
unskilled schrieb:
-
"- neuen Speicher alokieren"
-
"- alte Werte rueberkopieren"
-
"- neuen Wert hinten anhaengen"
-
ok - hier könnte es zutreffen...
-
soll er noch im 'alten' Speicher rumoperieren? ich glaube nicht...
-
max. hier - aber das würde ich für komisch halten - warum sollte man mit so ner einfachen (und schnellen) operation denn warten?
Ich glaub du hast Pumuckl nicht verstanden... damit push_back atomar ist, duerfen diese 3 Aktionen nicht unterbrochen werden, wenn bei einem Vektor hinten was angehaengt wird und dabei neuer Speicher reserviert werden muss. D.h. man braeuchte dazu einen Lock. Aber das wuerde einen ziemlichen Performance-Unterschied ausmachen fuer Leute, die std::vector nur im singlehtreaded-betrieb verwenden. genauso braeuchte es fuer std::list<>::push_back einen Lock, damit nicht mitten im Zeiger-Umbiegen ein anderer Thread dazwischenfunkt.
Jedenfalls wuerds mich interessieren was RHBaum gemeint hat mit "STL ist threadsafe".'cppreference.com' kenn ich - nur leider bin ich entweder zu doof oder blind oder wie auch immer -.-
Auf cppreference.com steht das auch nicht
Aber auf http://cplusplus.com/reference/ ==> du musst einen Stream_buf uebergeben.
-
-
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