Listener für std::list::push_back



  • Hi,

    ich möchte einen Listener auf std::list::push_back haben.

    Mein erster Gedanke (sorry, ich bin seit ner geraumer Zeit in java unterwegs) wär von std::list zu erben, push_back zu überschreiben ...

    Nur sollte man von std::list nicht erben und push_back ist ja auch nicht virtual.

    Trotzdem möchte ich
    - einen container mit listener
    - diesen container als drop-in replacement für std::list. Also z.B. für den Aufruf der Funktion void addStuff( std::list<int>& list) verwenden können.

    Geht das?



  • Nö. Warum baust Du nicht an addStuff was ran?



  • naja, weil addStuff einfach da ist und nicht verändert werden kann.



  • Dann würde ich sagen hast du ein Problem.



  • was müsste denn die signatur von addStuff sein, damit jeder std-like container verwendet werden kann?

    Wenn ich nicht gleich

    template <typename T>
    addStuff(T& container)
    

    haben will? Gibts da ein trait oder sowas?



  • kartoffelsack schrieb:

    Gibts da ein trait oder sowas?

    Kurz gesagt, nimm wieder Java.


  • Mod

    haben will? Gibts da ein trait oder sowas?

    Naja, was klassifiziert einen STL-Container? Ich denke du meinst sequentielle; diese haben alle ein Memberfunktionstemplate namens emplace_back . Mit der Annahme dass die Elemente default-constructible sind:

    template <typename T>
    using void_t = void;
    
    #define CHECK_EXPR(...) void_t<decltype(__VA_ARGS__)>
    
    template <typename T>
    auto addStuff(T& t) -> CHECK_EXPR(t.emplace_back()) {} // Falls die Elemente nicht default-constructible sind, den Bedingungs-Ausdruck ändern.
    
    #include <vector>
    #include <list>
    #include <set>
    
    int main()
    {
    	std::vector<int> v;
    	std::list<int> l;
    	addStuff(v);
    	addStuff(l);
    	std::set<int> s;
    	addStuff(s); // Fehler
    }
    


  • Wozu überhaupt std::list? Willst du dein Programm künstlich langsam machen?



  • Kommt wahrscheinlich daher, dass das List-Interface in Java für alle sequentiellen Collections verwendet wird. In C++ verwendet man aber std::vector, ausser man hat gute Gründe dagegen.

    kartoffelsack, in C++ sind viele Paradigmen grundsätzlich verschieden von Java. Zum Beispiel ist der normale Weg, um Funktionalität zu erweitern, eben nicht Vererbung und Methoden-Überschreibung. Du hast halt auch viele Abstraktionsmittel, die dir in Java fehlen (freie Funktionen, Templates, Typedef, Namensräume und ADL).

    Die STL ist komplett anders entworfen als Java Collections, entsprechend musst du anders damit arbeiten.



  • @überlisted: addStuff könnte ja auch in der Mitte was hinzufügen. Deswegen list.

    Egal.

    Angenommen ich will einen container schreiben, der das registrieren von listeners für push_back etc. erlaubt. Ansonsten soll sich der container wie ein std container verhalten. Wich mach ich das am besten?

    template <typename T>
    class ObservableContainer
    :private T //has-a
    {
    public:
      //alles was ich nicht ändern will per using verfügbar machen
      using T::size;
      //...
      //gibts es auch was einfacheres als jedes einzelne element von T explizit 
      //mit "using" zu spezifizieren? Speziell hätte ich gern alle members automatisch da.
    
      //alles was ich ändern will, neu implementieren ...
    
      auto push_back(T::value_type item) -> decltype(T::push_back(item))  //ich weiß die Zeile stimmt nicht ganz. Darum gehts mir aber grad nicht.
      {
    
         T::push_back(item);
         //call observer
      }
    };
    };
    


  • Davon abgesehen gibt es auch keinen ernsthaften Grund, warum man einen Container ueberschreiben soll, schliesslich ist ein Container nur zum Aufbewahren da. Fuer Extra-Funktionalitaet ist die Klasse zustaendig, die den Container enthaelt und nach aussen hin kapselt.



  • Du denkst immer noch zu stark in Java-Mustern. Nur weil du bei push_back() einen Listener aufrufen willst, musst du nicht gleich eine neue Klasse schreiben, welche die komplette Schnittstelle dupliziert.

    Entweder du schreibst dir eine freie Funktion, welche push_back() und den Listener aufruft. Vielleicht auch eine Mikro-Klasse, wenn du State für den Listener brauchst.

    Oder du kapselst gleich richtig, schreibst eine neue Klasse, aber bietest nur wenige Operationen an. Das musst du ohnehin, wenn du Invarianten erhalten willst; sonst könnte man mit insert(), resize(), emplace() und all den anderen Einfügefunktionen einfach an deinem Listener vorbeiprogrammieren.


Anmelden zum Antworten