Schlampereien mit std::shared_ptr



  • Hi zusammen

    ich versuche gerade etwas ungewöhnliches und muss mich dafür schon mal im Voraus dafür entschuldigen ( Anfänger bitte dies nicht lesen 😀 ).

    Ich habe ein Modul, welches shared_ptr<T> verwaltet. Was das für Objekte sind, ist unerheblich.
    Es verwaltet diese Objekte, fügt hinzu, entfernt usw.
    Aber die Objekte werden nicht "benutzt", also sie werden weder dort erzeugt, noch entfernt oder Funktionen dieser Objekte aufgerufen. Sie werden nur "gelagert".

    Nun schreibe ich dafür einen Unit test, weil ich dieses Modul im Nachgang neu implementieren möchte.
    Nun möchte ich aber im Unittest diese komplexen Objekte vom Typ "Dummy" nicht erzeugen, weil dies noch das hinzufügen eines Frameworks und einer Bibliothek bedeutet und ich die Unittests so schlank wie möglich halten will.

    #include <memory>
    #include <vector>
    
    class Dummy
    {
    public:
        Dummy() = default;
        ~Dummy() = default;
    };
    
    class DummyStorage
    {
    public:
        DummyStorage() = default;
        ~DummyStorage() = default;
    
        void registerDummy( std::shared_ptr<Dummy> d ) { Storage.push_back( d ); }
        void clear() { Storage.clear(); }
    
    private:
        std::vector<std::shared_ptr<Dummy>> Storage;
    };
    
    
    int main()
    {
        std::shared_ptr<Dummy> d1( (Dummy*)0x01 );
        DummyStorage DS;
        DS.registerDummy( d1 );
        DS.clear();
        return 0;
    }
    

    Deswegen schiebe ich dem std::shared_ptr<Dummy> d1 eine ungültige Adresse unter.
    Nun ergibt sich aber logischerweise beim Beenden der Applikation das Problem, dass d1 versucht sein Objekt zu löschen.

    Nun die Frage: wie kann ich "d1" im Nachgang quasi einen Nullptr unterschieben, ohne das ein Destruktor aufgerufen wird?



  • @It0101

    Hallo,

    geht da nicht einfach ein custom-deleter?

    std::shared_ptr<Dummy> d1( (Dummy*)0x01,
        [](int *) { }); // deleter macht nix
    


  • Danke, das war der richtige Hinweis 😉 Mit den Dingern habe ich bisher nicht gearbeitet...

    Und danke dass du mich nicht verurteilst 😁



  • @It0101 sagte in Schlampereien mit std::shared_ptr:

    class Dummy

    Was sich mir allerdings nicht ganz erschliesst, ist, weshalb eine solche leere Dummy-Klasse, wie du sie in dem Beispiel hast, keine Lösung ist. Du musst das besser wissen, aber für mich sieht das erstmal unkomplizierter und näher an dem aus, was eigentlich getestet werden soll.

    Nebenbei könnte man diesem Dummy-Dummy auch noch einen statischen Zähler in Ctor/Dtor verpassen um zu prüfen, ob die Objekte unter Verwendung von DummyStorage auch genau so oft erzeugt/zerstört werden, wie man das erwartet - um z.B. abzudecken, dass DummyStorage.clear() korrekt arbeitet.



  • @It0101 sagte in Schlampereien mit std::shared_ptr:

    class DummyStorage
    {
    

    Gegenvorschlag:

    template <class T>
    class DummyStorageImpl
    

    Dann kannst du DummyStorageImpl schön ohne Hacks in Unit-Tests testen.

    Und DummyStorage implementierst du einfach mit Hilfe von DummyStorageImpl (dumme 1:1 forwarding Funktionen). Ausser dass du dich bei den forwarding Funktionen vertippst kann dann nicht mehr viel schief gehen. Und wenn du DummyStorage duch nen typedef ersetzt bzw. public von DummyStorageImpl ableitest nichtmal mehr das.



  • @Finnegan sagte in Schlampereien mit std::shared_ptr:

    @It0101 sagte in Schlampereien mit std::shared_ptr:

    class Dummy

    Was sich mir allerdings nicht ganz erschliesst, ist, weshalb eine solche leere Dummy-Klasse, wie du sie in dem Beispiel hast, keine Lösung ist.

    Die Glaskugel sagt: Weil sein Dummy kein Dummy ist sondern ein Foo 😆

    Nebenbei könnte man diesem Dummy-Dummy auch noch einen statischen Zähler in Ctor/Dtor verpassen um zu prüfen, ob die Objekte unter Verwendung von DummyStorage auch genau so oft erzeugt/zerstört werden, wie man das erwartet - um z.B. abzudecken, dass DummyStorage.clear() korrekt arbeitet.

    Das geht auch beim "gecastete Adresse" Hack und zwar mit dem "aliasing constructor":

    struct TestDummy {
        char atLeastWeCanUseAValidAddress[1024];
        // ...
    };
    
    struct Dummy { // <-- da wo eigentlich Foo heissen sollte
        // ...
    };
    
    void fun() {
        auto testDummy = std::make_shared<TestDummy>();
        auto dummy = std::shared_ptr<Dummy>{
            /* share ownership with this: */ testDummy,
            /* but point to this:         */ reinterpret_cast<Dummy*>(&testDummy->atLeastWeCanUseAValidAddress[0]) };
    }
    

    Die "but point to this" Adresse muss dabei nichtmal innerhalb des kontrollierten Objekts liegen, nur ist das der Anwendungsfall den man meistens haben will.



  • @hustbaer an ein Template hatte ich auch gedacht, aber irgendwie schien mir das als zu großer Eingriff in den Quellcode, den man bei Unittests ja eigentlich unbedingt vermeiden will.

    @Finnegan die Dummy-Klasse ist nur fürs "reduzierte Quellcodebeispiel" fürs Forum. In Wirklichkeit ist die Klasse sehr umfangreich.



  • @It0101 sagte in Schlampereien mit std::shared_ptr:

    @Finnegan die Dummy-Klasse ist nur fürs "reduzierte Quellcodebeispiel" fürs Forum. In Wirklichkeit ist die Klasse sehr umfangreich.

    Um Misverständnisse zu vermeiden solltest du das Ding dann im Forum nicht Dummy nennen. Weil sonst, wie man sieht, Leute annehmen du meinst nen Mock/Test-Dummy.


Log in to reply