Den << Operator überladen
-
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)
-
Deine Klasse muß natürlich die entsprechenden Funktionen anbieten, die dann von den Manipulator-Funktionen (myendl und eventuell andere) aufgerufen werden. Im Zweifelsfall sieh dir einfach mal die Stream-Klassen der Standard-Library an. Daraus kann man sehr viel lernen (der Header <ostream> dürfte da ein guter Startpunkt sein).
-
Ok, ich habs hingekriegt, meine endgültig Lösung sieht jetzt so aus:
//dbg.h class dbgObject { public: dbgObject(); dbgObject& operator<<(std::string); dbgObject& operator<<(int zahl); dbgObject& Flush(); dbgObject& operator<<(dbgObject& (*pf)(dbgObject&)); private: HWND hwnd_dbgWnd_; std::string akt_zeile; }; dbgObject& myendl(dbgObject& test); //dbg.cpp dbgObject& myendl(dbgObject& test) { test.Flush(); return test; } dbgObject& dbgObject::operator<<(std::string text) { akt_zeile += text; return *this; } dbgObject& dbgObject::operator<<(int zahl) { std::stringstream text; text << zahl; return operator<<(text.str()); } dbgObject& dbgObject::operator<<(dbgObject& (*pf)(dbgObject&)) { return pf(*this); } dbgObject& dbgObject::Flush() { SendDlgItemMessage(hwnd_dbgWnd_,IDD_DBG_LIST, LB_ADDSTRING,0,(LPARAM) akt_zeile.c_str()); //Nach unten scrollen if(autoscroll_) SendDlgItemMessage(hwnd_dbgWnd_,IDD_DBG_LIST,WM_VSCROLL,(WPARAM) SB_BOTTOM,0); akt_zeile = ""; return *this; } //main.cpp int main() { dbgObject dbg; ... dbg << "bla" << " bla " << 4 << myendl; ... return 0; }Eine letzte Frage habe ich noch: Woher kriegt die freie Funktion myendl ihren Parameter &dbgObject und warum muss ich nicht schreiben dbg << myendl() ???
-
Z2 schrieb:
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.
Das geht mit enums genauso einfach. Nur braucht man dafür nen Template-"Trick", die im Standard ja wirklich rar gesät sind. Das könnte eine Erklärung sein.
-
flammenvogel schrieb:
Eine letzte Frage habe ich noch: Woher kriegt die freie Funktion myendl ihren Parameter &dbgObject und warum muss ich nicht schreiben dbg << myendl() ???
Du übergibst nur einen Funktionszeiger und hier
dbgObject& dbgObject::operator<<(dbgObject& (*pf)(dbgObject&)) { return pf(*this); // pf ist ein Funktionszeiger }wird die Funktion dann aufgerufen.
-
ok, jetzt versteh ichs
thk