Den << Operator überladen
-
@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.
-
Guckst du mein Beispiel oben? Kann ich auch und brauche keine Funktion.
War diese Klasse nicht, was der OP wollte? Siehst du darin einen Nachteil?Greetz, Swordfish
-
Ist ziemlich dicht an dem geforderten dran (abgesehen davon, daß du bei deinem endl noch explizit das Objekt vorher angeben mußt; was aber eigentlich auch egal ist).
Unabhängig davon war meine Bemerkung an Michael E. gerichtet, der fragte, worum ich geschrieben habe, daß myendl eine Funktion sein soll.
-
Z2 schrieb:
Zu welchem Nutzen? Damit man soetwas:
std::cout << std::endl;schreiben kann.
Dafür muss std::endl doch keine Funktion sein. Das geht auch, wenn man op<< für endl überlädt, wenn endl ein enum ist.
Unabhängig davon war meine Bemerkung an Michael E. gerichtet, der fragte, worum ich geschrieben habe, daß myendl eine Funktion sein soll.
Das hast du mir aber noch nicht hinreichend begründet. Dein einziges Argument ist, dass der Standard auch so verfährt. Nur wieso macht er das? Was bringt das? Oder ist das nur ne willkürliche Konvention?
-
Was weiß ich, warum der Standard es so macht wie er es macht? Aber im Standard funktioniert es und zwar auf sehr zufriedenstellende Weise. Und da wir hier die gleiche Aufgabenstellung haben, ist es doch erst mal die naheliegenste Lösung ihn zu kopieren, bevor man sich über andere Lösungen Gedanken macht, oder etwa nicht?
Na gut, ich will mal versuchen zu argumentieren, welche Vorteile Funktionen an dieser Stelle haben, nämlich: Erweiterbarkeit. Wenn du einen zusätzlichen Manipulator brauchst, definierst du einfach eine zusätzliche Funktion ohne die ursprüngliche Klasse in irgendeiner Weise ändern zu müssen. Das geht mit allen anderen Lösungen nicht oder zumindest nicht so einfach.
-
Oh man, kaum ist man ein paar Stunden weg, schon ist der ganze Thread eine Seite länger.

@Swordfish: Keine schlechte Lösung, sie hat leider nur einen Harken, ich habe kein Editfeld in dem ich neue Zeilen mit \r\n erzeugen kann, sondern eine Liste. Um deine Lösung zu benutzen müsste ich bei jedem Aufruf des << Operators den String nach \r\n durchsuchen, und bei Bedarf den Teilstring flushen (was äußerst ineffizent wäre), oder sehe ich das falsch?
@MichaelE: Hmm, ein Enum benutzten... Das wäre natürlich eine Möglichkeit, wenn ich dann den Code zum flushen in eine entsprechend überladene operator << Funktion einbaue. Werde ich auch mal ausprobieren. Am liebsten wäre mir aber, wie Z2 richtig vermutete, eine Funktion

@Z2: Wie soll ich den String flushen, wenn myendl eine freie Funktion ist? Dann habe ich ja keinen Zugriff auf die Membervariabeln... (Es lässt sich kompelieren, wenn ich myendl eine freie Funktion ist)