2016-08-27 1 views
12

Ich implementiere meinen eigenen signal/Schlitz (Observer Muster, Qt Stil) Mechanismus, so dass ich eine property haben kann, die benachrichtigt ... Zeug ... das hat sich geändert.Implementieren von Signalen (Beobachtermuster): ist veränderbar oder const_cast notwendig?

Ich denke, C++ 11 bietet alles, was notwendig ist, um eine sehr erfolgreiche und funktionsreiche Implementierung zu ermöglichen. Das "Problem", auf das ich stoße, ist, wenn ich mit einem Signal eines const Objekts "verbinden" möchte, brauche ich die signal::connect Funktion, const zu sein, aber die Liste der Rückrufe/Beobachter zu ändern. Es gibt zwei einfache Möglichkeiten, dies zu beheben:

  1. const_cast die Listen innerhalb connect.
  2. Machen Sie die Listen mutable.

Beide scheinen mir wie die gleiche Sache (und dies vor, zum Beispiel in this question gefragt wurde), und völlig in Ordnung, logisch, aber stilistisch fragwürdig. Daher die Frage. Gibt es einen Weg um dies oder ist dies eine wirklich begründete Verwendung von const_cast/?

Einige prelimenary Code, wie ich es haben jetzt:

template<typename... ArgTypes> 
class signal 
{ 
public: 
    template<typename Callable> 
    void connect(Callable&& callback) const 
    { 
    std::lock_guard<std::mutex> lock(slots_mutex); 
    slots.emplace_back(callback); 
    } 

    void emit(ArgTypes... arguments) const 
    { 
    std::lock_guard<std::mutex> lock(slots_mutex); 
    for(auto&& callback : slots) 
    { 
     callback(arguments...); 
    } 
    } 

private: 
    // mutable here allows to connect to a const object's signals 
    mutable std::vector<std::function<void(ArgTypes...)>> slots; 
    std::mutex slots_mutex; 

}; 

Hinweis ich diesen Code nicht getestet; Dies ist nur eine Reflektion meines momentanen Gemütszustandes.

+2

Nicht getesteter Code ... tsk tsk ... –

+0

@Arnav Ich schreibe jetzt buchstäblich die Tests, ich musste nur dieses Designproblem aus dem Weg räumen: p. – rubenvb

+0

Ich fürchte, ich verstehe nicht, warum das 'Signal' selbst' const' Methoden offenlegen sollte. Warum sollte der Benutzer von 'signal' nicht entscheiden, ob er veränderbar (oder nicht) sein will? –

Antwort

9

mutable ist normalerweise die bessere Wahl für solche Fälle.

vermeiden (const) Gießen, wann immer Sie kann, ist es anfällig für undefiniertes Verhalten schlagen, während mutable garantiert nicht 1) sein.


mutable Klasse Mitglieder garantiert nicht emittiert Codes .text Segment zum Beispiel zu gehen.

2

Es gibt zwei einfache Möglichkeiten, um dies zu beheben:

  1. const_cast die Listen innerhalb connect.
  2. Machen Sie die Listen mutable.

In der Tat gibt es eine dritte Wahl ist (die ein allgemeiner Zweck-wäre dieses Problem zu umgehen C hatte ++ nicht das mutable Schlüsselwort zur Verfügung gestellt) - Sie die betreffenden Daten aus dem syntaktischen Objekt bewegen können:

class X 
{ 
    mutable int   i1_; 

    // Data pointed to by i2_ semantically belongs to this object 
    // but doesn't constitute a syntactical part of it so it is not 
    // subject to const-correctness checks by the compiler. 
    std::unique_ptr<int> i2_; 

public: 
    void constFunc() const { 
     i1_ = 123; 
     *i2_ = 456; 
    } 
}; 

Trotz der Verfügbarkeit dieser zusätzlichen Option, stimme ich immer noch mit πάντα ῥεῖanswer, dass das mutable Schlüsselwort die richtige Wahl für solche Fälle ist. Es dokumentiert explizit in einer standardisierten Weise (z. B. erlaubt es, grepped zu werden), dass die konzeptionellen const Operationen dieser Klasse nicht technisch nicht-mutierend sein können.Dies ist zum Beispiel gut zu wissen, wenn es um die Thread-Sicherheit von const Funktionen der Klasse geht.

0

Das Signal signal :: connect ändert signal :: slot. Also, ich denke, das einzige, was Sie tun müssen, ist Ihr Design zu ändern. Lassen Sie signal :: connect veränderbar und Anrufer halten veränderbar Signal Zeiger.