Zur Kompilezeit raus bekommen ob mein Methoden-Ptr-Typ const oder non-const ist



  • using int_non_const = int (my_class::*)();
    using void_non_const = void (my_class::*)();
    using int_const = int (my_class::*)() const;
    using void_const = void (my_class::*)() const;
    

    std::is_const liefert immer true, gib es da etwas passendes im Standard oder irgendwie selber bauen?



  • ist das eine gute Lösung?

    template <typename R, typename C, typename ... P>
    constexpr bool is_const_method(R(C::*)(P...) const){ return true;}
    
    template <typename R, typename C, typename ... P>	
    constexpr bool is_const_method(R(C::*)(P...)){ return false; }
    

    irgendwie fuehlen sich diese constexpr Funktionen immer nicht so richtig an



  • Ne, ist keine gute Lösung weils nicht funktioniert:

    struct foo_t
    {
    	void bar() const {}
    	void bar() {}
    };
    

    Hier sind die Antworten für Leute die deine Frage auch schon gestellt haben: How to test if a method is const?



  • @Swordfish

    Es funktioniert oder übersehe ich ein wichtiges Detail?

    #include <iostream>
    
    struct foo_t
    {
    	void bar() const { std::cout << "const" << std::endl; }
    	void bar() { std::cout << "non-const" << std::endl; }
    };
    
    template <typename R, typename C, typename ... P>
    constexpr bool is_const_method(R(C::*)(P...) const) { return true; }
    
    template <typename R, typename C, typename ... P>
    constexpr bool is_const_method(R(C::*)(P...)) { return false; }
    
    int main()
    {
        using bar_const = void (foo_t::*)() const;
        using bar = void (foo_t::*)();
        
        auto bar_const_ptr = static_cast<bar_const>(&foo_t::bar);
        auto bar_ptr = static_cast<bar>(&foo_t::bar);
        
        std::cout << is_const_method(bar_const_ptr) << std::endl;
        std::cout << is_const_method(bar_ptr) << std::endl;
        
        foo_t f;
    
        (&f->*bar_const_ptr)();
        (&f->*bar_ptr)();
        
        return 0;
    }
    
    

    Ausgabe:
    1
    0
    const
    non-const



  • Ja, mit explizitem cast geht's schon. Macht halt dann nicht viel sinn wenn du den Typ sowieso hinschreiben musst.

    Der Adressoperator vor einem Funktionsnamen ist übrigens doppelt gemoppelt.



  • @Swordfish

    wo sollte ich den den Adressoperator weg lassen können?

    mein "Problem" noch mal ein bisschen konkreter:

    Ich möchte: Um Boilerplate-Code absolut zu minimieren nur einmal einen Methoden-Zuordnung
    in einen static constexpr auto ptr mit der richtigen Methode machen - und den Rest aus dem Methoden-Zeiger
    dedukten - irgenwo ist da aber eine Lücken - ich verstehe nicht warum die typen test1 und test2 nicht identisch sind

    #include <iostream>
    
    struct foo_t
    {
      void bar() const { std::cout << "const!" << std::endl; }
      void bar() { std::cout << "non-const!" << std::endl; }
    };
    
    template <typename R, typename C, typename ... P>
    constexpr auto get_const_method_ptr(R(C::*method)(P...) const)
    {
       return static_cast<decltype(method)>(method);
    }
    
    template <typename R, typename C, typename ... P>
    constexpr auto get_non_const_method_ptr(R(C::*method)(P...))
    {
       return static_cast<decltype(method)>(method);
    }
        
    static constexpr auto ptr = get_const_method_ptr(&foo_t::bar);
    using test1 = decltype(ptr);
    using test2 = decltype(get_const_method_ptr(ptr));
    using test3 = decltype(get_const_method_ptr(&foo_t::bar));
    
    static_assert(!std::is_same_v<test1, test2>, "wrong"); // warum sind die typen nicht gleich???
    static_assert(std::is_same_v<test2, test3>, "wrong"); // die sind gleich
        
    int main()
    {
        // obwohl test1 != test2 komme ich hier an die richtige Methode
        test1 t1 = &foo_t::bar; // die const-variante wird aufgerufen
        test2 t2 = &foo_t::bar; // die const-variante wird aufgerufen
        test3 t3 = &foo_t::bar; // die const-variante wird aufgerufen
        auto t4 = get_non_const_method_ptr(&foo_t::bar); // non-const
       
        foo_t f;
        (&f->*t1)();
        (&f->*t2)();
        (&f->*t3)();
        (&f->*t4)();
    }
    

    Ausgabe:
    const!
    const!
    const!
    non-const!



  • @llm sagte in Zur Kompilezeit raus bekommen ob mein Methoden-Ptr-Typ const oder non-const ist:

    wo sollte ich den den Adressoperator weg lassen können?

    Sorry, war der irrigen Annahme, daß der Adressoperator auch bei Memberfunktionen überflüssig ist wie bei freien Funktionen.

    @llm sagte in Zur Kompilezeit raus bekommen ob mein Methoden-Ptr-Typ const oder non-const ist:

    ich verstehe nicht warum die typen test1 und test2 nicht identisch sind

    Ähm. test1 und test2 haben den selben Typ. Du hast in static_assert(!std::is_same_v<test1, test2>, "wrong"); ein ! zuviel.



  • Dieser Beitrag wurde gelöscht!


  • @Swordfish

    Ähm. test1 und test2 haben den selben Typ. Du hast in static_assert(!std::is_same_v<test1, test2>, "wrong"); ein ! zuviel.

    Nein, da kommt false raus - das "zuviele" ! damit das Beispiel trotzdem kompiliert

    zum online kompilieren/ausführen:
    https://onlinegdb.com/H1MJqq8VE

    btw: ist C++17

    also mein Ziel ist es das der User meiner Lib nur einmal den ptr mit dem Methoden-Zeiger setzen muss und ich dann die restlichen, von mir benoetigten Informationen wie z.B. ist es ein const, signature usw. aus diesem ptr dedukte - ist also eher eine "Usibility maximieren" Frage 🙂

    #include <iostream>
    
    struct foo_t
    {
      void bar() const { std::cout << "const!" << std::endl; }
      void bar() { std::cout << "non-const!" << std::endl; }
    };
    
    template <typename R, typename C, typename ... P>
    constexpr auto get_const_method_ptr(R(C::*method)(P...) const)
    {
       return static_cast<decltype(method)>(method);
    }
    
    template <typename R, typename C, typename ... P>
    constexpr auto get_non_const_method_ptr(R(C::*method)(P...))
    {
       return static_cast<decltype(method)>(method);
    }
        
    template<typename C>
    struct method_info{ using type = std::false_type; };
    
    template<typename R, typename C, typename... P>
    struct method_info<R(C::*)(P...) const>
    {
      using type = std::true_type;
      using return_type = R;
      using parameter_types = std::tuple<P...>;
    };    
        
    template<typename R, typename C, typename... P>
    struct method_info<R(C::*)(P...)>
    {
      using type = std::true_type;
      using return_type = R;
      using parameter_types = std::tuple<P...>;
    };
    
    static constexpr auto ptr = get_const_method_ptr(&foo_t::bar);
    using test1 = decltype(ptr); // was ist hier anders...
    using test2 = decltype(get_const_method_ptr(ptr)); // ...als hier?
    using test3 = decltype(get_const_method_ptr(&foo_t::bar));
    
    static_assert(!std::is_same_v<test1, test2>, "wrong"); // warum sind die typen nicht gleich???
    static_assert(std::is_same_v<test2, test3>, "wrong"); // die sind gleich
    
    // warum kann ich hier aus dem ptr nicht die richtige method_info deducten??? (ich lande in der false_type implementation) liegt das an test1 != test2?
    using with_ptr = method_info<decltype(ptr)>::type;
    static_assert(std::is_same_v<with_ptr, std::false_type>, "wrong");
    // so geht es - aber ich wuerde eben gerne auf das weitere get_const_method_ptr verzichten (weniger boilerplate in meinem Echt-Fall)
    using wrapped_ptr = method_info<decltype(get_const_method_ptr(ptr))>;
    static_assert(std::is_same_v<wrapped_ptr::type, std::true_type>, "wrong");
    	
    int main()
    {
        // obwohl test1 != test2 komme ich hier an die richtige Methode
        test1 t1 = &foo_t::bar; // die const-variante wird aufgerufen
        test2 t2 = &foo_t::bar; // die const-variante wird aufgerufen
        test3 t3 = &foo_t::bar; // die const-variante wird aufgerufen
        auto t4 = get_non_const_method_ptr(&foo_t::bar); // non-const
     
        foo_t f;
        (&f->*t1)();
        (&f->*t2)();
        (&f->*t3)();
        (&f->*t4)();
     
        return 0;
    }
    


  • Ich glaube ich habe mein Problem gelöst - bin noch mal nach deinen Kommentaren ganz kritisch durch den Code gegangen und dabei ist mir aufgefallen das auch einfach mit dem ptr constexpr functions dedukten kann die dann das richtige fuer mich machen

    das ist die Lösung meines Problems: https://onlinegdb.com/ByQJK3LNV

    #include <iostream>
    #include <type_traits>
    
    struct foo_t
    {
      void bar(int, double) const { std::cout << "const!" << std::endl; }
      void bar(short, float) { std::cout << "non-const!" << std::endl; }
    };
    
    template <typename R, typename C, typename ... P>
    constexpr auto get_const_method_ptr(R(C::*method)(P...) const)
    {
       return static_cast<decltype(method)>(method);
    }
    
    template <typename R, typename C, typename ... P>
    constexpr auto get_non_const_method_ptr(R(C::*method)(P...))
    {
       return static_cast<decltype(method)>(method);
    }
    
    template <typename R, typename C, typename ... P>
    constexpr bool is_const_method(R(C::*)(P...) const) { return true; }
    
    template <typename R, typename C, typename ... P>
    constexpr bool is_const_method(R(C::*)(P...)) { return false; }
    
    template <typename C, typename R, typename... P>
    std::tuple<P...> method_args(R(C::*)(P...)){ return std::tuple<P...>(); }
    
    template <typename C, typename R, typename... P>
    std::tuple<P...> method_args(R(C::*)(P...) const) {	return std::tuple<P...>(); }    
    
    static constexpr auto bar_const_ptr = get_const_method_ptr(&foo_t::bar); // nur 1 Angabe
    static_assert(std::is_same_v<decltype(bar_const_ptr), void (foo_t::* const)(int, double) const>, "wrong");
    constexpr auto bar_const_is_const = is_const_method(bar_const_ptr);
    static_assert(bar_const_is_const, "wrong");
    using bar_const_parameter = decltype(method_args(bar_const_ptr));
    
    static constexpr auto bar_ptr = get_non_const_method_ptr(&foo_t::bar); // nur 1 Angabe
    static_assert(std::is_same_v<decltype(bar_ptr), void (foo_t::* const)(short, float)>, "wrong");
    constexpr auto bar_is_const = is_const_method(bar_ptr);
    static_assert(!bar_is_const, "wrong");
    using bar_parameter = decltype(method_args(bar_ptr));
    
    int main()
    {
      foo_t f;
      (&f->*bar_const_ptr)(10, 10.10);
      (&f->*bar_ptr)(1, 20.20f);
      return 0;
    }
    


  • @llm sagte in Zur Kompilezeit raus bekommen ob mein Methoden-Ptr-Typ const oder non-const ist:

    using test1 = decltype(ptr); // was ist hier anders...
    using test2 = decltype(get_const_method_ptr(ptr)); // ...als hier?
    

    Ich glaube mir ist dazu der Knoten aufgegangen. decltype bastelt je nach value-Kategorie ein oder zwei & zum Typ dazu: auto and decltpye.



  • Ich glaube mir ist dazu der Knoten aufgegangen.
    decltype bastelt je nach value-Kategorie ein oder zwei & zum Typ dazu: auto and decltpye.

    Danke

    Kennst du ein gutes Tool mit dem man Templates oder solche Return-Type-Situationen gut "debuggen" kann
    ich habe von Templar, Templight und Metashell gehört aber noch Erfahrung kannst du etwas empfehlen?


Anmelden zum Antworten