Members zur Laufzeit auflisten



  • Gibt es eine Möglichkeit Klassen, Funktionen bzw. Methoden einer Klasse zur Laufzeit auflisten zu lassen, also dass man praktisch quasi zur Laufzeit eine Liste erzeugen kann, die man irgendwie weiter verwenden kann, um z.B. diese generisch für eine API oder sowas bereitstellen kann. Keine Ahnung ob das irgendwie Sinn macht, aber rein aus Neugier würde mich das interessieren.



  • Sinn macht es keinen und bei "klassischen" C++-Methoden / -Membern ist das auch nicht möglich. Man kann allerdings Klassen einfach als std::map s von std::string s und std::function s / boost::any s implementieren und dann auf Methoden / Membern zugreifen indem man deren Name als String angibt. Das ist jedoch extrem unpraktisch und bedeutend langsamer.

    Sind dir Polymorphie / virtuelle Funktionen / Interfaces / Duck Typing Begriffe? Ich denke, du brauchst etwas anderes als du zu brauchen glaubst.



  • dixidix schrieb:

    Gibt es eine Möglichkeit Klassen, Funktionen bzw. Methoden einer Klasse zur Laufzeit auflisten zu lassen, also dass man praktisch quasi zur Laufzeit eine Liste erzeugen kann, die man irgendwie weiter verwenden kann, um z.B. diese generisch für eine API oder sowas bereitstellen kann. Keine Ahnung ob das irgendwie Sinn macht, aber rein aus Neugier würde mich das interessieren.

    Ne, C++ hat kein Reflection.



  • Sind dir Polymorphie / virtuelle Funktionen / Interfaces / Duck Typing Begriffe? Ich denke, du brauchst etwas anderes als du zu brauchen glaubst.

    Ich will nicht auf vererbung oder Sowas hinaus, sondern mehr geht es darum Funktionen und Methoden dynamisch bereit zu stellen, die man in eine API einbauen könnte. Ein Beispiel wäre z.B. das ganze in Verbindung mit Lua zu machen. Hier wird glaube ich über Callbacks gearbeitet. Wie das da genau geht, bin ich mir nicht ganz sicher, aber prinzipiell müssten bestimmte Funktionen über eine API-Instanz dem Luainterpreter bekannt gemacht werden, um auf diese in einem Lua-Script zugreifen zu können. Damit man eben nicht jede Änderung im Basiscode in die Schnittstelle eintragen muss, würde dafür eine dynamische Lösung sicher bequemer sein.



  • Gib deiner Klasse eine Methode, die einen Container oder was auch immer mit den benötigten Informationen befüllt. Als Programmierer einer Klasse weißt du ja, welche Member die Klasse hat...



  • dixidix schrieb:

    Sind dir Polymorphie / virtuelle Funktionen / Interfaces / Duck Typing Begriffe? Ich denke, du brauchst etwas anderes als du zu brauchen glaubst.

    Ich will nicht auf vererbung oder Sowas hinaus, sondern mehr geht es darum Funktionen und Methoden dynamisch bereit zu stellen, die man in eine API einbauen könnte. Ein Beispiel wäre z.B. das ganze in Verbindung mit Lua zu machen. Hier wird glaube ich über Callbacks gearbeitet. Wie das da genau geht, bin ich mir nicht ganz sicher, aber prinzipiell müssten bestimmte Funktionen über eine API-Instanz dem Luainterpreter bekannt gemacht werden, um auf diese in einem Lua-Script zugreifen zu können. Damit man eben nicht jede Änderung im Basiscode in die Schnittstelle eintragen muss, würde dafür eine dynamische Lösung sicher bequemer sein.

    Ich selbe arbeite an einer Scriptsprache und bei mir muss man für einen Typen folgendes tun:

    interface.register_type<std::string>("string");
    interface.register_function("string::c_str", &std::string::c_str);
    

    // usw. für alle anderen Methoden
    Einfach nur

    interface.register_type_and_all_members<std::string>("string");
    

    geht leider (noch) nicht. Aber man muss nicht jede Änderung übertragen, auch wenn sich Signaturen oder so ändern; nur das Script muss angepasst werden.



  • Wir brauchen Compile-Time-Reflection!



  • Hier ein simples Beispiel, das schonmal sowas mit Membern machen kann.
    Da ist auch noch erweiterungsspielraum für mehr und kann umgemodelt werden.

    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/mpl.hpp>
    #include <boost/fusion/adapted.hpp>
    #include <boost/fusion/include/at.hpp>
    #include <boost/fusion/include/for_each.hpp>
    #include <boost/mpl/range_c.hpp>
    #include <boost/mpl/for_each.hpp>
    #include <functional>
    #include <iostream>
    
    struct A
    {
        int id;
        std::string name;
        int uid;
    };
    
    BOOST_FUSION_ADAPT_STRUCT (
        A,
        (int, id)
        (std::string, name)
        (int, uid)
    )
    
    template <typename T>
    void list(T const& obj)
    {
        using range =
        boost::mpl::range_c <int, 0, boost::fusion::result_of::size<T>::type::value>;
    
        boost::mpl::for_each <range> (std::bind<void>(
            [&](T const& object, auto Index = {}) {
                std::cout << boost::fusion::extension::struct_member_name<T, decltype(Index)::value>::call() << ": " // name
                          << boost::fusion::at<decltype(Index)>(object) << "\n"; // value
            },
            std::cref(obj),
            std::placeholders::_1
        ));
    }
    
    int main()
    {
        list<A>(A{5, "Hello", 3});
    }
    

    Ausgabe:

    id: 5
    name: Hello
    uid: 3



  • Natürlich kann man dann auch anfangen die Typen durch type_traits zu spalten und so zu identifizieren und gesondert behandeln. Jemand der besser ist als ich, kann damit bestimmt schöne Sachen zaubern.

    Hier ein Beispiel mit Memberfunctions.

    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/mpl.hpp>
    #include <boost/fusion/adapted.hpp>
    #include <boost/fusion/include/at.hpp>
    #include <boost/fusion/include/for_each.hpp>
    #include <boost/mpl/range_c.hpp>
    #include <boost/mpl/for_each.hpp>
    #include <boost/fusion/adapted/struct/detail/extension.hpp>
    #include <functional>
    #include <iostream>
    #include <type_traits>
    #include <boost/type_index.hpp>
    
    struct A
    {
        int id;
        std::string name;
        int uid;
        void f();
    };
    
    using f_type = decltype(&A::f);
    
    BOOST_FUSION_ADAPT_STRUCT (
        A,
        (int, id)
        (std::string, name)
        (int, uid)
        (f_type, f)
    )
    
    template <typename T, typename U>
    typename std::enable_if <std::is_member_function_pointer<typename std::remove_reference<T>::type>::value, void>::type Selector(U const&)
    {
        std::cout << "FUNCTION_TYPE";
    }
    
    template <typename T, typename U>
    typename std::enable_if <!std::is_member_function_pointer<typename std::remove_reference<T>::type>::value, void>::type Selector(U const& obj)
    {
        std::cout << boost::typeindex::type_id_with_cvr<T>().pretty_name();
    }
    
    template <typename T>
    struct Helper
    {
        template<class Index>
        void operator()(Index, T object) const {
            std::cout << boost::fusion::extension::struct_member_name<T, Index::value>::call() << ": ";
            Selector<typename boost::fusion::result_of::at_c<T, Index::value>::type>(object);
            std::cout << "\n";
        }
    };
    
    template <typename T>
    void list(T const& obj)
    {
        using range =
        boost::mpl::range_c <int, 0, boost::fusion::result_of::size<T>::type::value>;
    
        boost::mpl::for_each <range> (
            std::bind<void>(Helper<T>{}, std::placeholders::_1, std::cref(obj))
        );
    }
    
    int main()
    {
        auto inst = A{5, "Hello", 3};
        list<A>(inst);
    }
    

    Ausgabe:

    id: int&
    name: std::string&
    uid: int&
    f: FUNCTION_TYPE



  • Also ich kenne 2 Möglichkeiten um dynamisch an Funktionen aus einem Modul (exe oder dll) zu kommen. Entweder 1. du exportierst deine Funktionen und schaust anschließend in der Exporttable deines Moduls nach (was sogar Sinn macht, wenn du eh vor hast eine DLL zu programmieren, wenn du den Code dann aber statisch linken willst, hast du wieder ein Problem)
    Oder 2. du lässt dir von Visual Studio Debuginformationen generieren (im Debugmodus macht Visual Studio das von selbst) und lädst diese anschließend mit der von Microsoft bereitgestellten Lib um die Debuginfromationen auswerten zu können.
    Beides nicht schön, beantwortet aber denke ich mal deine Frage.

    Hab das mal verwendet, um für UnitTest nur bestimmte Funktionen zu detouren.


Anmelden zum Antworten