Signal/Slot, public signal?
-
314159265358979 schrieb:
Membervariablen public machen, wo es Sinn macht.
Und wo wäre das?
-
Nexus schrieb:
Brauchst du ein Unsubscribe überhaupt? Du kannst ja auf einem
connection-Objekt direktdisconnect()aufrufen...Habe ich mich auch gefragt. Es kommt mir irgendwie komisch vor wenns nur ein subscribe...(..) gibt. So unsymmetrisch.
Technisch ist das unsubscribe nicht nötig.
-
std::pair zum Beispiel. Oder Server und Port eine Socketklasse.
-
theta schrieb:
Habe ich mich auch gefragt. Es kommt mir irgendwie komisch vor wenns nur ein subscribe...(..) gibt. So unsymmetrisch.
Bei ähnlichen Klassen bei mir habe ich meist ein
Connect()für die Registrierung sowie einClearConnections(), um alle Slots auf einmal zu entfernen. Aber keinDisconnect()für einen einzelnen Slot.Falls ein
unsubscribe()keine zusätzliche Funktionalität bietet, würde ich es nicht anbieten. Sonst fragt sich der Benutzer, wieso er deine Methode aufrufen soll, wenn er den Slot mitconnection::disconnect()ebenfalls trennen kann...
-
@Nexus:
Und was hast Du wie getypedef'd?
-
Hier mal ein Beispiel einer Timer-Klasse, die beim Ablaufen der Zeit Callbacks auslösen kann (nur der relevante Teil des APIs):
class TriggeringTimer { public: typedef std::tr1::function< void(TriggeringTimer&) > Listener; public: Connection Connect(const Listener& listener); void ClearConnections(); };Wobei die Klasse halt nur ein einzelnes Signal enthält, daher kann ich allgemeine Namen wie "Listener" verwenden.
Ein Fall, in dem ich mehrere Signale hatte: Vor einiger Zeit schrieb ein paar GUI-Klassen für SFML, allerdings entwickelte ich sie nie ganz fertig. Das sah dann z.B. folgendermassen aus: Keine
ClearConnections()-Funktionen, und dietypedefs sind spezifischer:class TextField { public: Connection ConnectTextEvent(const TextEventListener& listener); Connection ConnectClick(const ClickListener& listener) };Ursprünglich hatte ich Bezeichner wie
AddTextEventListener(), aber das war mir auf Dauer zu umständlich.
-
@314159265358979: std::pair ist nicht das beste Beispiel für den klassischen Einsatzzweck von Objektorientierung. Die üblichen Überlegungen wie Schnittstellen-Design oder Zugriffskontrolle fallen gar nicht an, dank der Einfachheit der Datenstruktur.
Und
Oder Server und Port eine Socketklasse.
Das hat sich mir nicht erschlossen.
-
@Nexus: Danke.
-
Markward schrieb:
Das hat sich mir nicht erschlossen.
class socket { public: const std::string server; const short port; }
-
Das sind keine Membervariablen, das sind Konstanten. Bäh.
-
Das sind (konstante) Membervariablen.
-
Konstante Variablen, soso. Na wenn du das so siehst

Ich persönlich halte es aber für sinnvoll, (variable!) Membervariablen grundsätzlich hinter einer Methode zu verbergen. Einschränkung dabei ist, dass die Klasse schon irgendetwas "können" muss. Für jeden Wert, der sich die ganze Anwendung über vermutlich nicht ändert und halt einmal im Konstruktor initialisiert wird einen Getter und Setter anzubieten ist fraglich (siehe std::pair, color_rgb o.ä.).
Sobald aber ein Wert potentiell irgendwelchen implementierungsabhängigen Manipulationen unterworfen ist, sollte man den Wert nach außen auch nur durch eine Methode als saubere Schnittstelle weiterreichen. Und Datenelemente daher private machen. Ansonsten das, was Meyers sagt.
Aber die Diskussion gabs hier bestimmt auch schon öfter.
-
314159265358979 schrieb:
Markward schrieb:
Das hat sich mir nicht erschlossen.
class socket { public: const std::string server; const short port; }Also ich würde nichtmal sowas public machen.
Mal ganz davon abgesehen, dass der Name "socket" äusserst fraglich ist, für ein Ding das per Definition immer einen Port "hat" und einen Server "per Namen kennt". Das riecht eher nach einer "Connection".
-
Ein Socket ist eine Eindeutige Identifizierung des Endpunkts einer Verbindung. Zu eindeutig gehört nunmal auch Host und Port.
-
Und was ist mit Serversockets?
-
Was soll denn ein Serversocket sein? Socket ist Socket.
-
314159265358979 schrieb:
Ein Socket ist eine Eindeutige Identifizierung des Endpunkts einer Verbindung. Zu eindeutig gehört nunmal auch Host und Port.
Nein.
Ein Socket ist ein Socket.
Der kann auch "unbound" sein.Was das Thema Endpunkt angeht: was soll der String "server" da bitte bedeuten?
Soll das die lokale Adresse sein auf die der Socket gebunden ist? Wieso heisst der dann "server" und nicht "host" o.ä. Und wieso als String? Du kannst einen Socket schliesslich nicht auf eine URL binden, sondern nur auf eine Adresse (bzw. auch auf mehrere, wenn man z.B. auf 0.0.0.0 bindet).
Davon abgesehen kann man sich die Info auf welchen Port/Adresse ein Socket gebunden ist vom System holen. Die Infos permanent in Membervariablen abzuspeichern ist also redundant.
-
Ist ein wenig OT das Thema Socket.

-
hustbaer schrieb:
Der kann auch "unbound" sein.
Wie du den verwendest, zeigst du mir.
hustbaer schrieb:
Was das Thema Endpunkt angeht: was soll der String "server" da bitte bedeuten?
Soll das die lokale Adresse sein auf die der Socket gebunden ist? Wieso heisst der dann "server" und nicht "host" o.ä. Und wieso als String?
Ich bin hier so frei und quetsche hier nen Resolver mit nem Socket in eine Klasse. Sehr komfortabel. gethostbyname() ist ja auch nur ein Aufruf, spricht für mich nichts dagegen, das dazu zu geben.
hustbaer schrieb:
Du kannst einen Socket schliesslich nicht auf eine URL binden, sondern nur auf eine Adresse (bzw. auch auf mehrere, wenn man z.B. auf 0.0.0.0 bindet).
Ich binde ihn auf die erste IP, auf die die URL verweist.
hustbaer schrieb:
Davon abgesehen kann man sich die Info auf welchen Port/Adresse ein Socket gebunden ist vom System holen. Die Infos permanent in Membervariablen abzuspeichern ist also redundant.
Mag sein. Ich weiß zwar nicht wie, aber ich glaubs dir einfach mal.
-
@theta:
Ja, ist OT
Zurück zum Thema:
Ich mache das - in meinem aktuellen Projekt - im Prinzip so wie du, mit zwei (IMO unwesentlichen) Unterschieden:
- Ich verwende nicht boost::signals(2) sondern was selbstgestricktes
- Ich hab keine public Member, dafür aber nen Getter der nen shared_ptr<Event> zurückgibt (Event ist bei mir das, was bei signals(2) das "signal" ist)
Ändert aber nix daran, dass man vollen Zugriff auf das "signal" (Event) hat.
Register/Unregister Funktionen führen IMO zu viel zu viel Code-Duplizierung. Einerseits muss man innerhalb der Klasse die ganzen Attach/Detach Funktionen schreiben. Andrerseits kann man auch ausserhalb der Klasse viel weniger Code wiederverwenden. Da es z.B. kein gemeinsames Interface gibt, kann man auch keine Guard-Klassen schreiben die ein Attach im Ctor und ein Detach im Dtor machen. Zu erlauben dass das "signal" (in dem Fall die Klasse mit den Attach/Detach Funktionen) zerstört wird, während noch Listener draufhängen, ist in der Variante auch nicht einfach.
In Summe sind es mir einfach zu viele Nachteile.
Das einzige was mich an der "signal freilegen" Variante stört, ist, dass man von ausserhalb der Klasse das "signal" triggern kann (
node.value_changed();). OK, muss man halt einfach nicht machen. In grösseren Projekten, mit z.T. mässig disziplinierten Mitarbeitern, könnte das aber ein echter Nachteil sein.Wenn man eine selbstgebackene Implementierung schreibt, könnte man das allerdings umgehen, indem man den Teil wo sich die "Listener" draufhängen können aus dem signal "raustrennt".
Grob skizziert:
class connection_point { public: connection attach(...); void detach(connection c); // ... // vermutlich wird hier ein friend class event; // angebracht sein }; class event { public: void fire(); connection_point& get_connection_point() const; // ... }; class foo { public: connection_point& value_changed() { return m_value_changed_event.get_connection_point(); } int get_value() const { return m_value; } void set_value(int val) { if (val != m_value) { m_value = val; m_value_changed_event->fire(); } } private: int m_value; event m_value_changed_event; };