Den << Operator überladen
-
Ein "echter" Stream verwendet std::endl - das schreibt erst ein Newline-Zeichen '\n' und flusht anschließend den Ausgabepuffer (mit dem Ergebnis, daß die Daten aus dem Zwischenspeicher auf Festplatte oder Monitor geschrieben werden).
-
flammenvogel schrieb:
Hallo,
Ich bin gerade dabei eine kleine Klasse zu programmieren die mir bestimmte Sachen über WinApi Funktionen ausgibt.
Das sollte dann in etwa wie folgt aussehen:
cl_dbg dbg; dbg << "x1" << "x2";Damit das so funktioniert, habe ich in meiner Klasse den Streamoperator überladen:
class cl_dbg { public: void operator<<(std::string text); ... private: ... }; void cl_dbg::operator<<(std::string text) { ... return; }Das funktioniert aber nur, wenn ich nur ein element mir << ausgeben will. Wie kann ich den Operator so überladen, das Ausgaben wie im Beispiel von mir oben angegeben funktionieren?
GeorgV schrieb:
class cl_dbg { public: cl_dbg& operator<<(std::string text) { return *this; } ... private: ... };flammenvogel schrieb:
thk, so gehts
Allerdings habe ich noch eine Frage, wie kann ich etwas ähnliches wie std::endl selber bauen?
Das soll funktionieren?
operator << für die Ausgabe muss ein "externer" Operator sein, möglicherweise friend deklariert, denn
auf der linken Seite des Operators steht keine Instanz der Klasse. Das erste Argument ist ostream &,
das zweite eine Kopie oder eine konstante Referenz auf die Klasse. Der Operator gibt die Referenz auf
den ostream im Argument als Rückgabewert zurück. Dies garantiert die Aufreibarkeit von << .
-
flammenvogel schrieb:
Allerdings habe ich noch eine Frage, wie kann ich etwas ähnliches wie std::endl selber bauen?
cl_dbg &myendl(cl_dbg& stream) { .... return stream }
-
@Helmut S.:
Du meinst soetwas wie
#include <ostream> #include <iostream> using namespace std; class vector { friend ostream& operator<<( ostream& os, vector &v ); private: int _x, _y; public: vector( int x, int y ) : _x( x ), _y( y ), x( _x ), y( _y ) { }; vector( const vector &other ) : _x( other.x ), _y( other.y ), x( _x ), y( _y ) { }; ~vector( ) { }; const int &x, &y; void set_x( int x ) { _x = x; } void set_y( int y ) { _y = y; } void set( int x, int y ) { _x = x; _y = y; } }; ostream& operator<<( ostream& os, vector &v ) { os << "[ " << v._x << "; " << v._y << " ]"; return os; } int main( ) { vector v( 12, 3 ); cout << v << endl; }? Das hat nichts mit dem vorliegenden Problem zu tum.
Greetz, Swordfish
-
@CStoll: Ich will den ganzen Kram nicht auf der Konsole oder in einer Datei ausgeben sondern in einem Windowslistenfeld, daher brauch ich nur flush bzw. newline (das ist im Fall einer Liste gleichwertig).
@HelmutS: Doch das funktioniert so, wie GeorgV es beschrieben hat. Und auch die Reihenfolge der Operanden spielt bei der von GregorV gepostet Lösung keine Geige. Ich habe mitlerfeile den << Operator in der Klasse auch für int-Werte überladen und es funktioniert zuverlässig.
@Freak_Coder: Ich werds gleichmal ausprobieren (schonmal thk)
-
@Freak_Coder: Wenn ich jetzt
dbg << "bla" << "bla" << myendl;mache, dann bekomme ich vom Kompiler die Meldung:
main.cpp:86: error: `myendl' undeclared (first use this function) main.cpp:86: error: (Each undeclared identifier is reported only once for each function it appears in.) :: === Build finished: 2 errors, 0 warnings ===
-
Hmm, zeig mal mehr Code. Irgendwas hast du falsch gemacht.
-
Ich habe das Problem mal auf ein Minimalbeispiel reduziert:
//dbg.h class dbgObject { public: dbgObject(); dbgObject operator<<(std::string); dbgObject operator<<(int zahl); dbgObject &myendl(dbgObject& stream); ... private: HWND hwnd_dbgWnd_; bool autoscroll_; ... }; //dbg.cpp dbgObject dbgObject::operator<<(std::string text) { //Text hinzufügen SendDlgItemMessage(hwnd_dbgWnd_,IDD_DBG_LIST, LB_ADDSTRING,0,(LPARAM) text.c_str()); //Nach unten scrollen if(autoscroll_) SendDlgItemMessage(hwnd_dbgWnd_,IDD_DBG_LIST,WM_VSCROLL,(WPARAM) SB_BOTTOM,0); return *this; } dbgObject dbgObject::operator<<(int zahl) { std::stringstream str; str << zahl; return operator<<(str.str()); } dbgObject dbgObject::&myendl(dbgObject& stream) { return stream; }Anmerkung 1: Ich habe die Klasse in dbgObject umbennant.
Anmerkung 2: SendDlgItemMessage ist eine WinApi Funktion -> windows.h
-
flammenvogel schrieb:
dbgObject dbgObject::&myendl(dbgObject& stream) { return stream; }

dbgObject& dbgObject::myendl(dbgObject& stream) { return stream; }
Ich hätte das & normal lieber wie immer an den Datentyp hängen sollen, dann passieren solche Fehler auch nicht.
-
Irgendwie funktioniert es in der abgeänderten Version auch net. (gleicher Fehler)
//dbg.h class dbgObject { public: dbgObject(); dbgObject operator<<(std::string); dbgObject operator<<(int zahl); dbgObject& myendl(dbgObject& stream); private: HWND hwnd_dbgWnd_; bool autoscroll_; }; //dbg.cpp dbgObject dbgObject::operator<<(std::string text) { //Text hinzufügen SendDlgItemMessage(hwnd_dbgWnd_,IDD_DBG_LIST, LB_ADDSTRING,0,(LPARAM) text.c_str()); //Nach unten scrollen if(autoscroll_) SendDlgItemMessage(hwnd_dbgWnd_,IDD_DBG_LIST,WM_VSCROLL,(WPARAM) SB_BOTTOM,0); return *this; } dbgObject& dbgObject::myendl(dbgObject& stream) { return stream; } dbgObject dbgObject::operator<<(int zahl) { std::stringstream str; str << zahl; return operator<<(str.str()); }Kann ich mit deiner Version wirklich schreiben:
dbg << "test" << "test" << myendl;???
-
Ich glaube nicht, daß das so funktionieren kann. Dir fehlt ein Operator:
dbgObject& dbgObject::operator<<(dbgObject& (*pf)(dbgObject&)) { return pf(*this); }Ungetested und von einem Coder, der so früh am Morgen noch nicht richtig wach ist. Also bitte mit Vorsicht und Nachsicht behandeln.
Edit: Soweit ich sehe fehlt dir bei den Rückgabetypen von operator<< überall die Referenz.
-
Jaja, auf die Idee bin ich um 3:00 Uhr auch noch gekommen. Wenn ich so einen Operator habe, kann ich problemlos folgendes schreiben:
dbgObject dbg; dbg << "test" << "bla" << 4 << "bla" << dbg.Flush();Hierbei enstehen allerdings zwei Probleme.
a) ist das immer noch nicht das was ich will und
b) wird das ganze von hinten nach vorne abgearbeitet (zuerst wird geflusht und dann erst wird "bla" undsoweiter angefügt.Als Alternativ (ich finds nicht wirklich schön) ginge ohne den Operator auch noch:
(dbg << "Test" << "Test").Flush()Allerdings bleibt mein Problem das gleiche, weiß jemand nähres darüber wie das bei std::endl geht? (Oder noch besser, wie ich eins nachbauen kann?)
//edit: Vieleicht sollte ich die Dekleration von Flush auch mal mit angeben:
dbgObject& Flush(); //Member von dbgObject,public
-
Dieses Flush ist bloß eine gewöhnlich Member-Funktion, die vermutlich von der endl-Funktion aufgerufen wird und mit dem Manipulator, den du schreiben willst, eigentlich nichts zu tun hat.
Wo ist dein Problem? Hast du es überhaupt mal ausprobiert:
dbg << myendl;
Das müßte damit theoretisch funktionieren. Der endl-Manipulator der Stream-Library ist nämlich auf genau dieselbe Weise implementiert.
-
Hmm, ich hab das mit den Referenzen geändert. Allerdings funktioniert es bei mir irgendwie immer noch nicht. Könnte jemmand von euch den geposten Code mal testen und checken ob der so lauffähig ist?
-
Noch ein Fehler entdeckt: Das myendl muß eine freie Funktion sein, keine Memberfunktion.
-
ich würd's so machen:
#include <string> #include <sstream> #define UNICODE #define _UNICODE #include <windows.h> using namespace std; typedef basic_string< wchar_t, char_traits< wchar_t > > wstring_t; typedef basic_stringstream< wchar_t, char_traits< wchar_t > > wstringstream_t; const unsigned int edit_control_id = 100; class debug_stream_t { private: HWND edit_control; public: const wstring_t endl; debug_stream_t( ) : edit_control ( 0 ), endl( L"\r\n" ) { }; debug_stream_t( HWND edit ) : edit_control( edit ), endl( L"\r\n" ) { clear( ); } HWND set_edit_control( HWND edit ) { HWND temp = edit_control; edit_control = edit; return temp; } void clear( ) { if( !edit_control ) { return; } # pragma warning( disable : 4311 ) SendMessage( edit_control, WM_SETTEXT, 0, reinterpret_cast< long >( L"" ) ); # pragma warning( default : 4311 ) SendMessage( edit_control, EM_EMPTYUNDOBUFFER, 0, 0 ); } debug_stream_t& operator<<( debug_stream_t &ds ) { return ds; } debug_stream_t& operator<<( const wstring_t &str ) { long length = static_cast< long >( SendMessage( edit_control, WM_GETTEXTLENGTH, 0, 0 ) + 1 ); wchar_t *backup_text; if( length > 1 ) { try { backup_text = new wchar_t[ length * sizeof( wchar_t ) ]; } catch( bad_alloc &e ) { throw e; } # pragma warning( disable : 4311 ) SendMessage( edit_control, WM_GETTEXT, length, reinterpret_cast< long >( backup_text ) ); # pragma warning( default : 4311 ) } else { backup_text = L""; } wstring_t text = backup_text + str; # pragma warning( disable : 4311 ) SendMessage( edit_control, WM_SETTEXT, 0, reinterpret_cast< long >( text.c_str( ) ) ); # pragma warning( default : 4311 ) if( length > 1 ) { delete [ ] backup_text; } return *this; } debug_stream_t& operator<<( const wchar_t &ch ) { wstringstream_t wss; wss << ch; return operator<<( wss.str( ) ); } debug_stream_t& operator<<( const int &i ) { wstringstream_t wss; wss << i; return operator<<( wss.str( ) ); } }; long __stdcall main_window_proc( HWND window, unsigned int message, unsigned int first, long second ) { static HWND edit_control; static debug_stream_t debug_stream; switch( message ) { case WM_CREATE: # pragma warning( disable : 4312 ) edit_control = CreateWindow( L"EDIT", 0, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL | ES_READONLY, 10, 10, 572, 354, window, reinterpret_cast< HMENU >( edit_control_id ), GetModuleHandle( 0 ), 0 ); # pragma warning( default : 4312 ) if( !edit_control ) { return -1; } debug_stream.set_edit_control( edit_control ); debug_stream << wstring_t( L"Hello," ) << debug_stream.endl; debug_stream << wstring_t( L"debug" ) << wstring_t( L"_stream_t!" ) << debug_stream.endl; debug_stream << wstring_t( L"an Integer: " ) << 10 << debug_stream.endl; return 0; case WM_CLOSE: PostQuitMessage( 0 ); return 0; } return static_cast< long >( DefWindowProc( window, message, first, second ) ); } int __stdcall WinMain( HINSTANCE instance, HINSTANCE prev_instance, char *cmd_line, int show_state ) { wchar_t main_window_name[ ] = L"debug stream - Sample"; wchar_t main_window_class_name[ ] = L"debug_stream_sample_window_class"; WNDCLASS wc; memset( &wc, 0, sizeof( WNDCLASS ) ); wc.lpszClassName = main_window_class_name; wc.hInstance = instance; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = main_window_proc; wc.hIcon = LoadIcon( 0, IDI_APPLICATION ); wc.hCursor = LoadCursor( 0, IDC_ARROW ); wc.hbrBackground = reinterpret_cast< HBRUSH >( COLOR_APPWORKSPACE + 1 ); if( !RegisterClass( &wc ) ) { MessageBox( 0, L"Could not register Window-Class!", L"Error:", MB_OK ); return EXIT_FAILURE; } HWND main_window = CreateWindow( main_window_class_name, main_window_name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, 0, 0, instance, 0 ); if( !main_window ) { UnregisterClass( main_window_class_name, instance ); MessageBox( 0, L"Could not create the main window!", L"Error:", MB_OK ); return EXIT_FAILURE; } UpdateWindow( main_window ); ShowWindow( main_window, show_state ); MSG msg; while( GetMessage( &msg, main_window, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } return static_cast< int >( msg.wParam ); }Greetz, Swordfish
-
Wieso soll myendl ne Funktion sein? Dafür gibts doch enums.
-
Die Idee war es ähnlich wie bei den Standard Stream-Klassen zu machen. Und da ist endl nun mal gewöhnlich eine Funktion.
-
@Z2: und für welchen Nutzen?
Greetz, Swordfish
-
Zu welchem Nutzen? Damit man soetwas:
std::cout << std::endl;schreiben kann.