facebook releases Folly, an open source C++ library



  • DrakoXP schrieb:

    krümelkacker, du hast da was falsch verstanden in Bezug auf const und Zeiger.

    Nein, habe ich nicht.

    tntnet schrieb:

    Kannst Du das ein wenig genauer erläutern. Ich scheine "const" auch nicht ganz verstanden zu haben 😕 .
    [...]
    In Deinem Beispiel ist x eine konstante Referenz. Eine konstante Referenz kann ich auch nur einer Konstanten Referenz zuweisen. Also ist y in der Zeile "auto y = x" auch eine konstante Referenz.

    x ist ein Lvalue vom Typ const DiscriminatedPtr<int,double>
    y ist ein Lvalue vom Typ DiscriminatedPtr<int,double>

    Zeus schrieb:

    Jein, wenn ich eine konstanten Smartpointer bekommen, sollte ich davon auch nur einen konstenten raw-Pointer bekommen - semantisch richtig, dass C++ die Kopie auf ein nicht-konstenten Pointer erlaubt, ist ein Kritik von der D Community 😉

    Das ist Unsinn.

    Leute, leute! Ihr enttäuscht mich! Das kann doch nicht so schwer sein! Der Sinn von DiscrimiatedPtr<...> ist ein etwas typsicherer void-Zeiger. Er kann auf Objekte verschiedener Typen zeigen und weiß sogar noch, um welchen Typ es sich da handelt. Da sollte man erwarten, dass sich DiscriminatedPtr<int> ähnlich wie int* verhält. Stattdessen gibt mir ein konstanter Discriminated<int> einen const int* zurück. Das ist einfach Schwachsinn!

    Das Verhalten eines normalen rohen Zeigers ist äquivalent zu:

    template<class T>
    class pointer
    {
    public:
      ...
      T& operator*() const {return *ptr;}
      T* operator->() const {return ptr;}
      ...
    private:
      T* ptr;
    };
    

    Das heißt: Unabhängig davon, ob ich auf den Zeiger const oder non-const-Zugriff habe, bekomme ich ein T& beim dereferenzieren! Ja, das ist wirklich so:

    #include <iostream>
    using namespace std;
    void wasbinich(const int&) {cout<<"const int\n";}
    void wasbinich(      int&) {cout<<"      int\n";}
    int main()
    {
      int i = 42;
      int*       p = &i;
      int* const q = &i;
      wasbinich(*p);
      wasbinich(*q);
    }
    

    Ausgabe:

    int
          int
    

    Tada!

    Bitte Hirn einschalten und dann posten.



  • Ja, die const-Overloads im DiscriminatedPtr sind reichlich sinnlos.
    "Korrekt" ist mMn. was shared_ptr macht.

    D.h. ein DiscriminatedPtr<int, double> müsste implizit zu DiscriminatedPtr<int const, double const> konvertierbar sein, aber nicht umgekehrt.
    Und der DiscriminatedPtr<int const, double const> darf dann natürlich auch nur int const* bzw. double const* hergeben.



  • Findet ihr?

    #include <iostream>
    using namespace std;
    
    class fun
    {
    public:
    	void print() {
    		cout << "Side-effect";
    	}
    
    	void print() const {
    		cout << "Side-effect free";
    	}
    };
    
    template<typename T> class pointer
    {
    public:
    	pointer(T* ptr) : ptr(ptr) {
    
    	}
    	T* get() {
    		return ptr;
    	}
    
    	T* get() const {
    		return ptr;
    	}
    private:
    	T* ptr;
    };
    
    void invoke(const pointer<fun>& ptr) {
    	ptr.get()->print();
    }
    
    int main()
    {
    	fun* f = new fun();
        pointer<fun> p = f;
        invoke(p);
    }
    


  • Was it der Sinn deines Beitrags? Was willst du uns damit sagen? Dass du nicht verstanden hast was wir meinen? 🤡



  • hustbaer schrieb:

    Dass du nicht verstanden hast was wir meinen? 🤡

    Höchstwahrscheinlich 🙄



  • Naja komm dein Beispiel ist Quatsch, und das solltest du auch wissen wenn du den letzten Beitrag von kk gelesen hättest.

    Klar geht das so wie du es zeigst, aber es bleibt trotzdem Quatsch, und es bietet vor allen keinerlei Sicherheit.

    void invoke(const pointer<fun>& ptr) {
        pointer<fun> p2 = ptr;
        p2.get()->print();
    }
    

    EDIT: OK, du zeigst die Variante wo get immer "T*" und nie "T const"* zurück gibt. Also geht es dort nicht, aber es geht eben so sowieso nicht. /EDIT

    Achja, hier die sinnvolle, funktionierende Variante:

    int main() 
    { 
         fun* f = new fun(); 
         pointer<fun const> p = f; 
         invoke(p); 
    }
    


  • Bloops schrieb:

    Nur warum veröffentlicht Facebook das?

    Weil sie viel auf Open Source setzen und viele ihrer Bibliotheken Open Source machen wollen. Warum auch nicht, wenn man von der Community gratis Bugfixes und neue Features bekommen kann. FB will wohl noch mehr ihrer Bibliotheken veröffentlichen, das ist nur der Anfang.

    http://herbsutter.com/2012/06/02/facebook-folly-oss-c-libraries/?utm_source=tf&utm_medium=t



  • Ich verstehe zwar grundsätzlich, worauf krümelkacker hinaus will und stimme auch zu, aber stimmt das hier wirklich:

    void foo(DiscriminatedPtr<int,double> const& x)
    {
      x.get_nothrow<int>(); // --> const int*
      auto y = x;
      y.get_nothrow<int>(); // --> int*
    }
    

    😕
    Wieso geht denn hier beim Kopieren die Constqualifizierung des Zeigers verloren?

    Edit: Ach ja. Ich bin ja so doof. Hat sich erledigt.



  • Tachyon schrieb:

    Ich verstehe zwar grundsätzlich, worauf krümelkacker hinaus will, aber stimmt das hier wirklich:

    void foo(DiscriminatedPtr<int,double> const& x)
    {
      x.get_nothrow<int>(); // --> const int*
      auto y = x;
      y.get_nothrow<int>(); // --> int*
    }
    

    😕
    Wieso geht denn hier beim Kopieren die Constqualifizierung des Zeigers verloren?

    const/nonconst-Überladung, analog zu dem hier:

    void bar(std::vector<int> const& v)
    {
      v.begin(); // --> const_iterator
      auto y = v; //std::vector<int>, ohne const!
      y.begin(); // --> iterator
      auto const z = v; //std::vector<int> const
      z.begin(); // --> ah, const_iterator!
    }
    

    😉



  • Ja. Bin inzwischen selbst drauf gekommen. Nachdenken hilft, wie bereits weiter oben geschrieben wurde.



  • hustbaer schrieb:

    Naja komm dein Beispiel ist Quatsch, und das solltest du auch wissen wenn du den letzten Beitrag von kk gelesen hättest.

    Natürlich ist es Quatsch, ich hab ja KK Rat befolgt und den Const-Q. entfernt!



  • Zeus schrieb:

    hustbaer schrieb:

    Naja komm dein Beispiel ist Quatsch, und das solltest du auch wissen wenn du den letzten Beitrag von kk gelesen hättest.

    Natürlich ist es Quatsch, ich hab ja KK Rat befolgt und den Const-Q. entfernt!

    Was daran nix ändert dass der Code Quatsch ist.

    Mit entferntem Const-Qualifizierer sieht man es bloss schneller.
    Mit nicht entferntem Const-Qualifizierer ist es deswegen aber nicht weniger Qutasch.



  • hustbaer schrieb:

    Zeus schrieb:

    hustbaer schrieb:

    Naja komm dein Beispiel ist Quatsch, und das solltest du auch wissen wenn du den letzten Beitrag von kk gelesen hättest.

    Natürlich ist es Quatsch, ich hab ja KK Rat befolgt und den Const-Q. entfernt!

    Was daran nix ändert dass der Code Quatsch ist.

    Mit entferntem Const-Qualifizierer sieht man es bloss schneller.
    Mit nicht entferntem Const-Qualifizierer ist es deswegen aber nicht weniger Qutasch.

    Was für ein Quatsch 🙄



  • Zeus, ich versteh' dich nicht. Es wurde nu schon ca. 3x erklärt warum die in DiscriminatedPtr implementierte Variante keinen Sinn macht.

    const T* ist eben nicht das selbe wie T* const .



  • hustbaer schrieb:

    Zeus, ich versteh' dich nicht. Es wurde nu schon ca. 3x erklärt warum die in DiscriminatedPtr implementierte Variante keinen Sinn macht.

    Ich seh keine Erklärungen.

    Wo besteht die Gemeinsamkeit zwischen den Code?

    template <typename... Types>
    class DiscriminatedPtr {
    
      template <typename T>
      const T* get_nothrow() const noexcept ...
    
      template <typename T>
      const T* get() const ...
    
    };
    
    #include <iostream>
    using namespace std;
    void wasbinich(const int&) {cout<<"const int\n";}
    void wasbinich(      int&) {cout<<"      int\n";}
    int main()
    {
      int i = 42;
      int*       p = &i;
      int* const q = &i;
      wasbinich(*p);
      wasbinich(*q);
    }
    

    hustbaer schrieb:

    const T* ist eben nicht das selbe wie T* const .

    Danke hustbaer genau so iss es!
    Das isst es!

    const int* q = &i;
      wasbinich(*q);
    

    krümelkacker schrieb:

    Da hat scheinbar jemand "const" nicht ganz verstanden; denn "const T*" bringt absolut gar nichts, da man den Zeiger ja kopieren kann

    IMO steht das Const nicht dafür da. Was ist daran so schlecht die Const Correctness auf das Objekt zu delegieren.

    #include <iostream>
    using namespace std;
    
    class fun {
    public:
        void print()       {    cout << "Side-effect";       }
        void print() const {    cout << "Side-effect free";  }
    };
    
    template<typename T> class pointer {
    public:
        pointer(T* ptr) : ptr(ptr) {}
        T* get() { return ptr; }
        T* get() const { return ptr; }
    private:
        T* ptr;
    };
    
    void invoke(const pointer<fun> ptr) {
        ptr.get()->print();
    }
    
    void invoke(const fun* ptr) {
        ptr->print();
    }
    
    int main()
    {
        fun* f = new fun();
        pointer<fun> p = f;
        invoke(p);
        invoke(f);
    }
    

    Den Entwickler, der die Pointer-Klasse sollte man liquidieren, hat der tatsächlich gewackt eine Klasse wie ein Sprachmittel zu bennen, dass sich garnicht so wie ein solches benimmt.



  • Zeus schrieb:

    IMO steht das Const nicht dafür da. Was ist daran so schlecht die Const Correctness auf das Objekt zu delegieren.

    Daran wäre gar nichts schlecht, wenn man es richtig machen würde, was hier aber nicht der Fall ist.

    Und falschrum macht es keinen Sinn. Es ist nicht intuitiv, man kann es leicht umgehen (ohne irgendwelche Casts) und man kann sich nichtmal darauf verlassen dass es nicht "unabsichtlich automatisch" umgangen wird.

    http://ideone.com/Nmm8s

    #include <iostream>
    using namespace std;
    
    class fun {
    public:
            void quack()       { cout << "mutable\n"; }
            void quack() const { cout << "const\n"; }
    };
    
    template<typename T> class broken_pointer {
    public:
            explicit broken_pointer(T* ptr) : ptr(ptr) {}
            T* get() { return ptr; }
            T const* get() const { return ptr; }
            T* operator -> () { return ptr; }
            T const* operator -> () const { return ptr; }
    private:
            T* ptr;
    };
    
    template<typename T> class good_pointer {
    public:
            template <typename U>
            explicit good_pointer(U* ptr) : ptr(ptr) {}
            template <typename U>
            good_pointer(good_pointer<U> const& ptr) : ptr(ptr.get()) {}
            T* get() const { return ptr; }
            T* operator -> () const { return ptr; }
    private:
            T* ptr;
    };
    
    template <typename DUCK>
    void invoke(DUCK d) {
            d->quack();
    }
    
    void test(fun const* native, fun* const reverse_native, const broken_pointer<fun> broken, good_pointer<const fun> good, const good_pointer<fun> reverse_good) {
            cout << "const T*     : "; invoke(native);
            cout << "T* const     : "; invoke(reverse_native);
            cout << "broken<const>: compiliert leider nicht, tjoah\n";
            cout << "const broken : "; invoke(broken);
            cout << "good<const>  : "; invoke(good);
            cout << "const good   : "; invoke(reverse_good);
    }
    
    int main(int /*argc*/, char** /*argv*/) {
            fun* native = new fun();
            test(native, native, broken_pointer<fun>(native), good_pointer<fun>(native), good_pointer<fun>(native));
            delete native;
    }
    

    Frage: was bringt der sog. "const support" in broken_pointer ?

    Meine Antwort: nix ausser Verwirrung. Also ganz weglassen, dann ist wenigstens klar dass die Klasse das nicht unterstützt. Bzw. nur teilweise, weil die Konvertierung von ptr<T> nach ptr<const T> nicht implizit unterstützt wird (explizit ist sie ja immer noch mit ptr<const T>(other.get()) möglich).

    Kannst du mir eine sinnvolle Anwendung dafür nennen? Wenn ja immer her damit, ich kann mir nämlich keine vorstellen.

    Den Entwickler, der die Pointer-Klasse sollte man liquidieren, hat der tatsächlich gewackt eine Klasse wie ein Sprachmittel zu bennen, dass sich garnicht so wie ein solches benimmt.

    Öhm...
    Also...
    Wenn du mir den Sinn des Verhaltens von const broken_pointer<T> erklären kannst, dann könnte man vielleicht anfangen darüber nachzudenken ob der Name klug gewählt ist. Wenn nicht, dann erübrigt sich mMn. die Diskussion, weil sinnlos eben sinnlos bleibt.



  • Vielleicht sollten wir beide eine neuen Auflage von Andrei Alexandrescu Modernes C++ Design lesen. Er kann bestimmt dir viel besser ConstPolicy für seine SmartPointer erläutern.

    ////////////////////////////////////////////////////////////////////////////////
    /// @note These policy classes are used in LockingPtr and SmartPtr to define
    ///  how const is propagated from the pointee.
    ////////////////////////////////////////////////////////////////////////////////[/
    template< class T > struct DontPropagateConst
    {
        typedef T Type;
    };
    
    template< class T > struct PropagateConst
    {
        typedef const T Type;
    };
    


  • Zeus schrieb:

    Vielleicht sollten wir beide eine neuen Auflage von Andrei Alexandrescu Modernes C++ Design lesen. Er kann bestimmt dir viel besser ConstPolicy für seine SmartPointer erläutern.

    ////////////////////////////////////////////////////////////////////////////////
    /// @note These policy classes are used in LockingPtr and SmartPtr to define
    ///  how const is propagated from the pointee.
    ////////////////////////////////////////////////////////////////////////////////[/
    template< class T > struct DontPropagateConst
    {
        typedef T Type;
    };
    
    template< class T > struct PropagateConst
    {
        typedef const T Type;
    };
    

    Das Ding beschreibt aber, wie das const von dem Ding, auf das gezeigt wird (eben der Pointee) weitergereicht wird. Mit der Constness des Pointers hat das nichts zu tun.



  • Tachyon schrieb:

    Das Ding beschreibt aber, wie das const von dem Ding, auf das gezeigt wird (eben der Pointee) weitergereicht wird. Mit der Constness des Pointers hat das nichts zu tun.

    Als Hilfestellung in welchen Fällen kann der SmartPointer die Constness auf dem Pointee weiterleiten?



  • Propagieren von const macht nur bei so Dingen wie copy_ptr oder unique_ptr Sinn. Und ratet mal warum: Diese Zeiger verhalten sich nicht wirklich wie Zeiger in dem Sinne, dass ich einen Zeiger kopieren kann, wobei sich die Kopie auf denselben Pointee bezieht. Man kann sie als Wrapper mit Wertesemantik betrachten.

    Zeus schrieb:

    Ich seh keine Erklärungen.

    Schade.

    Zeus schrieb:

    Wo besteht die Gemeinsamkeit zwischen den Code?

    template <typename... Types>
    class DiscriminatedPtr {
    
      template <typename T>
      const T* get_nothrow() const noexcept ...
    
      template <typename T>
      const T* get() const ...
    
    };
    
    #include <iostream>
    using namespace std;
    void wasbinich(const int&) {cout<<"const int\n";}
    void wasbinich(      int&) {cout<<"      int\n";}
    int main()
    {
      int i = 42;
      int*       p = &i;
      int* const q = &i;
      wasbinich(*p);
      wasbinich(*q);
    }
    

    Weil du da oben const T* geschrieben hast, gibt es leider nicht viele Gemeinsamkeiten. Du solltest hier erkannt haben, dass ein int* const dir Schreibzugriff auf den int gibt, genauso wie DiscriminatedPtr<int> const dir Schreibzugriff auf den int geben sollte, was allerdings die originale DiscriminatedPtr Implementierung und das, was du hier zeigst, aus irgendwelchen Gründen nicht tut. Dass diese Implementierung das nicht tut, stellt im übrigen keinen Gewinn dar, da du den Zeiger kopieren kannst, dadurch einen zweiten non-const Zeiger hast, der dann doch Schreibzugriff auf das int-Objekt erlaubt:

    DiscriminatedPtr<int> const q (&i);
    *q.get() = 42; // klappt bei dir nicht. Dieser "Schutz" bringt aber nichts weil...
    DiscriminatedPtr<int> p = q;
    *p.get() = 42; // Upps! Aufeinmal kann ich denselben!!! int doch verändern!!!
    

    Zeus schrieb:

    hustbaer schrieb:

    const T* ist eben nicht das selbe wie T* const .

    Danke hustbaer genau so iss es!
    Das isst es!

    const int* q = &i;
      wasbinich(*q);
    

    Dieses Beispiel hat mit gar nichts zu tun. Der Typ von q würde DiscriminatedPtr<const int> entsprechen, wir reden hier aber von DiscriminatedPtr<int> const . Bekomm das mal bitte auf die Reihe.

    Zeus schrieb:

    krümelkacker schrieb:

    Da hat scheinbar jemand "const" nicht ganz verstanden; denn "const T*" bringt absolut gar nichts, da man den Zeiger ja kopieren kann

    IMO steht das Const nicht dafür da. Was ist daran so schlecht die Const Correctness auf das Objekt zu delegieren.

    Es ergibt keinen Sinn, wie hier zum vierten mal erklärt!


Anmelden zum Antworten