Was würdet ihr an C++ verbessern?
-
Der Debugger soll mal Ausdrücke auswerten können und nicht ständig meckern und wenn ich einen Zeiger angebe und das ein Array ist, soll der das selbstständig kapieren und mir die Arraywerte und nicht nur den erste zurückgeben (oder man sollte das via Schlüsselwort oder [] hinter Variablennamen im Überwachungsfenster erzwingen können).
Aber geht ja nicht um MSVC++ hier
-
Eisflamme schrieb:
Der Debugger soll mal Ausdrücke auswerten können und nicht ständig meckern und wenn ich einen Zeiger angebe und das ein Array ist, soll der das selbstständig kapieren und mir die Arraywerte und nicht nur den erste zurückgeben (oder man sollte das via Schlüsselwort oder [] hinter Variablennamen im Überwachungsfenster erzwingen können).
Das geht doch alles, es gibt afaik sogar ne Möglichkeit die Anzeige im Watch Window per Skript für deine eigenen Typen zu erweitern (wie das z.B. die die std Container gemacht ist)
-
off topic geworden.
Noch eine verbesserung ist mir eingefallen.
template<typename tDerived> class Foo { // implementation }; class MyFoo : public Foo<MyFoo> { // leer }; // instance bilden MyFoo my_instance; // // Es soll möglich sein das der Compiler // die MyFoo Klasse automatisch definiert // // folgender masse Foo<identity> my_instance; // das gleiche wie "MyFoo my_instance;" // der Bezeichner identity ist ein möglicher Schlüsselwort(Keyword) // my_instance ist eine variable von unbenannten Type(MyFoo) den der Compiler erstellt hat.
-
Was soll das bringen, wenn
Foo<identity>
das gleiche wieMyFoo
bedeutet?MyFoo
ist ja nicht nur irgendeine Instanziierung desFoo
-Templates, sondern eine davon abgeleitete Klasse.Oder kannst du das etwas genauer erklären, am besten mit einem Anwendungsfall?
-
template<typename tDerived> class Window { Window(Window* parent, ...) {} void (tDerived::*onClick)(); }; // diese Klasse muss Du erstellen. // die Klasse kannst du nicht als Basis Klasse verwenden // sonst würde onClick member function pointer // Probleme machen. // diese Klasse ist nur für einen Zweck da um Objekte zu erstellen class MyWindow : public Window<MyWindow> { MyWindow(Window* parent, ...) : Window<MyWindow>(parent, ...) {} }; // verwendung int main() { MyWindow obj; // oder so, was besser ist Window<identity> obj; }
-
Warum muss diese Klasse erstellt werden?
Warum kann man sie nicht als Basisklasse verwenden?
Warum würdeOnClick
Member-Function-Pointer Probleme machen?Und inwiefern hilft nun dabei dieses
Window<identity>
, welches du bis jetzt nicht wirklich erklärt hast, was es machen soll.Grüssli
-
Ich versuche es zu verdeutlichen.
class Window { Window(Window* parent, ...) {} }; template<typename tDerived> class WindowImpl : public Window { WindowImpl<tDerived>(Window* parent, ...) : Window(parent, ...) {} void (tDerived::*onClick)(); }; class MyWindow : public WindowImpl<MyWindow> { MyWindow(Window* parent, ...) : WindowImpl<MyWindow>(parent, ...) {} };
Dravere schrieb:
Warum muss diese Klasse erstellt werden?
Bedenke dass der template type parammeter von Klasse "WindowImpl", eine Abgeleitete Klasse sein muss.
Dravere schrieb:
Warum kann man sie nicht als Basisklasse verwenden?
Warum würdeOnClick
Member-Function-Pointer Probleme machen?class FrameWindow : public MyWindow { FrameWindow(Window* parent, ...) : Window(parent, ...) { // error type not compatibel onClick = &FrameWindow::this_Click; } // error uns fellt der template parammeter // wenn wir FrameWindow nehmen kommt es in der Abgeleiteten Klasse // zu Fehlern siehe onClick void (???::*onDoubleClick)(); void this_Click() {} };
Dravere schrieb:
Und inwiefern hilft nun dabei dieses
Window<identity>
, welches du bis jetzt nicht wirklich erklärt hast, was es machen soll.Grüssli
der Compiler soll uns die Fehlende Klasse definieren
class Window { Window(Window* parent, ...) {} }; template<typename tDerived> class WindowImpl : public Window { WindowImpl<tDerived>(Window* parent, ...) : Window(parent, ...) {} void (tDerived::*onClick)(); }; int main() { WindowImpl<identity> win; }
aus dem Oberen Quelltext erstellt uns der Compiler
class Window { Window(Window* parent, ...) {} }; template<typename tDerived> class WindowImpl : public Window { WindowImpl<tDerived>(Window* parent, ...) : Window(parent, ...) {} void (tDerived::*onClick)(); }; // achtung Compiler generierter Code class unique_123456789 : public WindowImpl<unique_123456789> { unique_123456789() : WindowImpl<unique_123456789>(Window* parent, ...) {} }; int main() { // unique_123456789 alias WindowImpl<identity> unique_123456789 win; }
-
dot schrieb:
Eisflamme schrieb:
Der Debugger soll mal Ausdrücke auswerten können und nicht ständig meckern und wenn ich einen Zeiger angebe und das ein Array ist, soll der das selbstständig kapieren und mir die Arraywerte und nicht nur den erste zurückgeben (oder man sollte das via Schlüsselwort oder [] hinter Variablennamen im Überwachungsfenster erzwingen können).
Das geht doch alles, es gibt afaik sogar ne Möglichkeit die Anzeige im Watch Window per Skript für deine eigenen Typen zu erweitern (wie das z.B. die die std Container gemacht ist)
Wie das geht?
Wenn in meinem Code irgendwo bla* steht und sich dahinter ein Array verbirgt und ich bla als überwachte Variable eingebe, dann gibt er mir nur den ersten Wert.
-
@Ramsis,
Du redest wirres Zeug ...
Warum muss der Templateparemeter vonWindowImpl
eine abgeleitete Klasse sein? Das ist doch überhaupt gar nicht zwingend.Meine beiden weiteren Fragen hast du mit dem Code auch nicht beantwortet. Ich habe nicht gefragt, wieso man einen Templateparameter braucht, sondern warum man sie nicht als Basisklasse verwenden kann wegen des Member-Function-Pointer.
Deine Aussagen sind irgendwie völlig zusammenhangslos. Ich weiss nicht mal was ich fragen soll, um da Licht ins Dunkle zu bringen, das ist so wirres Zeug. Vielleicht solltest du weiter ausholen, was du eigentlich machen möchtest. Vielleicht auch weniger Code hinschreiben dafür mehr Text und erklären, um was es dir geht.
Grüssli
-
Eisflamme schrieb:
Wenn in meinem Code irgendwo bla* steht und sich dahinter ein Array verbirgt und ich bla als überwachte Variable eingebe, dann gibt er mir nur den ersten Wert.
Dann schreib mal bla,100 dann interpretiert er den Wert als Zeiger auf ein Array auf 100 Elementen
-
Ramsis schrieb:
class FrameWindow : public MyWindow { FrameWindow(Window* parent, ...) : Window(parent, ...) { // error type not compatibel onClick = &FrameWindow::this_Click; } // error uns fellt der template parammeter // wenn wir FrameWindow nehmen kommt es in der Abgeleiteten Klasse // zu Fehlern siehe onClick void (???::*onDoubleClick)(); void this_Click() {} };
Ich verstehe schon garnicht, warum du dich überhaupt auf irgendeinen Symbolnamen festnagelst:
class window{ public: boost::signals::signal<void()> on_click_signal; }; class my_window : public window{ public: my_window(){ this->on_click_signal.connect(boost::bind(my_window::on_click,this)); // oder so ähnlich ;) } void on_click(){ // handle event } };
Das erfüllt doch die gleichen Anforderungen wie der Kauderwelsch mit den abgeleiteten Klassen & Templateparametern oder?
-
Eine Kleinigkeit die noch nicht genannt wurde:
Kommentare im Stil von/* ... */
, aber schachtelbar. Der Ersatz durch ifdef ist derzeit einfach umständlich und einzelne Reihen sind manchmal eben einfach nicht genug.
-
Moin,
ich habe gerade folgendes gefunden:
In C with Classes, it was possible to define a function that would implicitly be called before every call of every member function(except the constructor)and another that would be implicitly called before every return from every member function. They were called call and return functions. They were used to provide synchronization for the monitor class in the original task library...
class monitor : object{ call() {/*grab lock*/} return(){/*release lock*/} };
In dem Text schreibt Stroustrup weiter, dass er die "Leute"(aka C with Classes/C++-User anno 1980) nicht davon überzeugen konnte, dieses Feature zu nutzen, weshalb es später wieder rausflog.
Ich hatte mit Synchronisation, Multihtreading etc. bisher noch nicht allzu viel zu tun. Ehrlich gesagt hab ich davon Null Ahnung. Also daher die Frage:
Worin bestünde der Vorteil dieses Konstrukts gegenüber nem Mutex, der z.B. in nem Wrapper haust und nur diesen einen Methodenaufruf lockt? So:class monitor_wrapper{ private: monitor m; public: void do_something_locked(){ mutex_type mutex; // Evt. als Member? mutex.lock() m.do_something(); mutex.unlock(); } };
Natürlich hat im C with Classes Design keinerlei Kosten für das Erstellen des Mutex. Dafür jedoch zahlt man mit zwei zusätzlichen Methodenaufrufen (call und return), die jedoch höchst wahrscheinlich geinlined werden können. Man bekommt aber ein Problem, sobald eine Methode keinerlei call(), return()-Funktionalität benötigt und ein zusätzlicher Switch eingebaut werden muss. Sehe ich das richtig?
Außerdem sehe bisher außer dem Locking-Mechanismus keinerlei andere sinnvolle Anwendung darin. Wie seht ihr das?* aus Thomas J. Bergin, History of Programming Languages II, 1996
-
Man bekommt aber ein Problem, sobald eine Methode keinerlei call(), return()-Funktionalität benötigt und ein zusätzlicher Switch eingebaut werden muss. Sehe ich das richtig?
Ich sehe da kein Problem. Du kannst ja auch ein Array aus 1 Mio. PODs mit
delete
wegmachen ohne dass 1 Mio. mal der triviale Destruktor aufgerufen wird.
Genau so würde es dann auch triviale call() und return() Funktionen geben - d.h. die würden dann einfach nicht aufgerufen.Und Stroustrup ist Performance-Freak. Du kannst davon ausgehen, dass er es nicht vorgeschlagen hätte, wenn man auch in Klassen wo man es nicht verwendet dafür bezahlen müsste.
Ich hatte mit Synchronisation, Multihtreading etc. bisher noch nicht allzu viel zu tun. Ehrlich gesagt hab ich davon Null Ahnung. Also daher die Frage:
Worin bestünde der Vorteil dieses Konstrukts gegenüber nem Mutex, der z.B. in nem Wrapper haust und nur diesen einen Methodenaufruf lockt?Darin dass man den Code nicht mit Hand schreiben muss?
Ich sehe darin aber eher einen Nachteil: es würde dazu verleiten mehr Klassen "intern" zu synchronisieren. Und das ist schlecht da es a) zu einem "false sense of security" führen kann und b) oft die Performance verschlechtert, da unnötig oft gelockt wird. Wenn man es "aussen" macht kann man "blocken", also die Mutex 1x locken, dann ein paar Memberfunktionen aufrufen und dann 1x unlocken. Und da Mutexen locken/unlocken relativ teuer ist...So:
class monitor_wrapper{ private: monitor m; public: void do_something_locked(){ mutex_type mutex; // Evt. als Member? mutex.lock() m.do_something(); mutex.unlock(); } };
Ja, ganz sicher als Member, sonst geht das schonmal gar nicht. Die Mutex muss ja immer die selbe sein, wenn jeder Aufrufer seine eigene bekommt können sich die ja unmöglich untereinander synchronisieren.
Natürlich hat im C with Classes Design keinerlei Kosten für das Erstellen des Mutex.
Wie kommst du denn auf die Idee? Die Mutex müsste man genau so selbst bereitstellen wie man es auch in C++ muss.
Außerdem sehe bisher außer dem Locking-Mechanismus keinerlei andere sinnvolle Anwendung darin.
Man könnte Klassen-Invarianten damit prüfen.
class foo { // ... void call() { #ifndef NDEBUG if (m_nesting_count == 0) check_invariants(); m_nesting_count++; #endif } void return() { #ifndef NDEBUG m_nesting_count--; if (m_nesting_count == 0) check_invariants(); #endif } };
-
Mit diesem call()/return()-Konstrukt lässt sich aber nur schwer ausdrücken, wenn man das Feature nur für einen Teil der Methoden benötigt. Um beim Locking zu bleiben - bei einer Getter-Methode würde das reserieren und freigeben des Locks möglicherweise mer Zeit benötigen als das eigentliche Lesen der Daten.
Zum Thema: Nachdem ich seit ca. einem Jahr nicht mehr aktiv in C++ programmiere (mein Arbeitgeber setzt eine andere Entwicklungsumgebung ein, die angeblich auch Objektorientierung beherrscht), vermisse ich eigentlich die Eleganz der Sprache.
-
CStoll schrieb:
Mit diesem call()/return()-Konstrukt lässt sich aber nur schwer ausdrücken, wenn man das Feature nur für einen Teil der Methoden benötigt. Um beim Locking zu bleiben - bei einer Getter-Methode würde das reserieren und freigeben des Locks möglicherweise mer Zeit benötigen als das eigentliche Lesen der Daten.
Man muss auch beim lesen synchronisieren!
-
Finde ich jetzt überhaupt nicht schade, dass
call()
undreturn()
nicht Bestandteil von C++ sind. Implizite Features wie "rufe vor und nach allen Funktionen eine Zusatzfunktion auf" sind immer heikel, weil man schnell den Überblick verliert, wo sie zum Zuge kommen. Zum Beispiel will man das bei privaten Funktionen wahrscheinlich nicht.Ausserdem kann man ja lokale Mutexes sehr elegant mit RAII lösen.
void Class::Function() { ScopedLock lock(mutex); // wird am Ende des Scopes automatisch freigegeben - im // Gegensatz zu lock() und unlock() sogar bei Exceptions }
-
rüdiger schrieb:
CStoll schrieb:
Mit diesem call()/return()-Konstrukt lässt sich aber nur schwer ausdrücken, wenn man das Feature nur für einen Teil der Methoden benötigt. Um beim Locking zu bleiben - bei einer Getter-Methode würde das reserieren und freigeben des Locks möglicherweise mer Zeit benötigen als das eigentliche Lesen der Daten.
Man muss auch beim lesen synchronisieren!
Aber nicht so gründlich
Ich habe z.B. einiges mit SQL zu tun - und dort unterscheidet man zwischen shared lock (zum Lesen der Daten - das können mehrere Programme gleichzeitig, ohne sich zu stören) und exclusiv lock (zum Schreiben der Daten - da darf zwischendurch niemand anderes in die Nähe der Daten, um Inkonsistenzen zu vermeiden). Das kannst du sicher analog auch auf "normale" Daten im Programm anwenden, die threadsicher verarbeitet werden müssen.