type-printer: Probleme mit Zeigern auf Zeiger auf Arrays u.ä.



  • Möp!

    Ich bastle gerade einen type-Printer. Der lässts sich so verwenden:

    print_types<char>(); // gibt "char" aus
    print_types<char, int, double>(); // gibt "char, int, double" aus
    
    struct test {};
    print_types<void(const int test::**)>();
    // gibt "void(const int {non-specialized}::**)" aus
    

    Diese Funktion ruft für jeden Typen die statische Funktion void print(std::ostream& ) in der Klasse type_printer<T> auf. type_printer ist spezialisiert für jeden built-in type, für const, volatile, referenzen, zeiger und viele mehr.

    Das Problem dabei, angenommen, man ruft print_types wie folgt auf:

    print_types<int(**)[]>();
    

    In diesem Fall wird (richtigerweise) zuerst die Spezialisierung für T* aufgerufen, danach die für einen Zeiger auf ein Array. Die Ausgabe sieht dann aber so aus: "int()[]".
    Dasselbe Problem habe ich bei Zeigern auf Memberfunktionen.
    Hat jemand eine Idee, wie man dieses Problem elegant lösen kann? Ich möchte beliebig viele Zeiger verschachteln können bei richtiger Ausgabe.



  • 314159265358979 schrieb:

    Ich möchte beliebig viele Zeiger verschachteln können bei richtiger Ausgabe.

    Was, wenn du nur für Zeiger und (mit niedrigerer Priorität) für Array spezialisierst? So wie ich das mitbekommen habe hast du 'ne extra Spezialisierung für Zeiger-Array



  • Kannst du mir zeigen, was du meinst?
    (Nein, ich habe eine Spezialisierung für Zeiger auf Array.)



  • Die von dir beschriebene Reihenfolge:

    • Zeiger
    • Zeiger auf Array

    Was geschieht wenn du für "Zeiger auf Array" keine Spezialisierung hast sondern nur für "Zeiger" und für "Array"?



  • EOutOfResources schrieb:

    Die von dir beschriebene Reihenfolge:

    • Zeiger
    • Zeiger auf Array

    Was geschieht wenn du für "Zeiger auf Array" keine Spezialisierung hast sondern nur für "Zeiger" und für "Array"?

    int[]**



  • Hm...
    Du könntest die Anzahl an Zeigern im Voraus zählen (mit TMP) und dann (wenn es sich um so einen Fall handelt) zuerst den Typ und ggf. Spezifikatoren (in diesem Fall nur int ) ausgeben, dann halt die erste Klammer, dann mit TMP die Zeiger ausgeschrieben, dann die geschlossene Klammer und dann das Array-Zeichen. Wenn es sich nicht um ein Array handelt, lässt du die Klammern und das Array-Zeichen weg.



  • 314159265358979 schrieb:

    (Nein, ich habe eine Spezialisierung für Zeiger auf Array.)

    Er meint, was passiert, wenn du die wegmachst. Außerdem können wir dir ohne deine Implementierung schlecht helfen (wenigstens für die beiden Überladungen).



  • wxSkip schrieb:

    Er meint, was passiert, wenn du die wegmachst.

    Habe ich doch gepostet: int[]**

    wxSkip schrieb:

    Außerdem können wir dir ohne deine Implementierung schlecht helfen (wenigstens für die beiden Überladungen).

    template <typename T>
    struct type_printer<T*>
    {
    	static void print(std::ostream& os)
    	{
    		type_printer<T>::print(os);
    		os << '*';
    	}
    };
    
    template <typename T>
    struct type_printer<T(*)[]>
    {
    	static void print(std::ostream& os)
    	{
    		type_printer<T>::print(os);
    		std::cout << "(*)[]";
    	}
    };
    
    template <typename T, int N>
    struct type_printer<T(*)[N]>
    {
    	static void print(std::ostream& os)
    	{
    		type_printer<T>::print(os);
    		std::cout << "(*)[" << N << ']';
    	}
    };
    


  • Irgendwie scheint mir deine Idee nicht der richtige Weg zu sein, EOutOfResources, oder ich mach grundlegend irgendwas falsch. Alleine mein Type-Trait, um rauszufinden, ob ein Zeiger nur auf ein T zeigt, ist riesig:

    template <typename T>
    struct is_raw_pointer
    {
    	static const bool value = false;
    };
    
    template <typename T>
    struct is_raw_pointer<T*>
    {
    	static const bool value = true;
    };
    
    template <typename T>
    struct is_raw_pointer<T* const>
    {
    	static const bool value = true;
    };
    
    template <typename T>
    struct is_raw_pointer<T* volatile>
    {
    	static const bool value = true;
    };
    
    template <typename T>
    struct is_raw_pointer<T* const volatile>
    {
    	static const bool value = true;
    };
    
    template <typename T>
    struct is_raw_pointer<T**>
    {
    	static const bool value = is_raw_pointer<T*>::value;
    };
    
    template <typename T>
    struct is_raw_pointer<T* const*>
    {
    	static const bool value = is_raw_pointer<T*>::value;
    };
    
    template <typename T>
    struct is_raw_pointer<T* volatile*>
    {
    	static const bool value = is_raw_pointer<T*>::value;
    };
    
    template <typename T>
    struct is_raw_pointer<T* const volatile*>
    {
    	static const bool value = is_raw_pointer<T*>::value;
    };
    


  • Sorry, hatte die Seite wohl zu lange offen vor dem Posten.
    Mir gefällt die Idee nicht, die Zeichen direkt in den Stream zu schreiben. Ich würde irgendwie versuchen, mir von der verwaltenden Klasse (print_types) aus den Typ zu einem String zusammenzubasteln. Im konkreten Fall: Erst der Typ, dann die Pointer im Array, dann evtl. eine Klammer auf, dann die Pointer auf das Array, Klammer zu und dann die Array-Klammern dahinter. Evtl. rekursiv lösen für Typen wie int *(*(*)[5])[7] (geht das?).


  • Mod

    Besonders viele Spezialisierungen braucht man eigentlich nicht, bloß Zeiger auf Memberfunktionen sind ein bisschen umständlich

    #include <string>
    #include <iostream>
    #include <typeinfo>
    #include <boost/lexical_cast.hpp>
    using std::string;
    template <typename...> struct pretty_args;
    
    template <typename T>                               struct pretty_name                                   { static string get() { return typeid(T).name(); } };
    template <typename T>                               struct pretty_name<const T>                          { static string get() { return "const " + pretty_name<T>::get(); } };
    template <typename T>                               struct pretty_name<volatile T>                       { static string get() { return "volatile " + pretty_name<T>::get(); } };
    template <typename T>                               struct pretty_name<const volatile T>                 { static string get() { return "const volatile " + pretty_name<T>::get(); } };
    template <typename T>                               struct pretty_name<T*>                               { static string get() { return "pointer to " + pretty_name<T>::get(); } };
    template <typename T>                               struct pretty_name<T&>                               { static string get() { return "lvalue-reference to " + pretty_name<T>::get(); } };
    template <typename T>                               struct pretty_name<T&&>                              { static string get() { return "rvalue-reference to " + pretty_name<T>::get(); } };
    template <typename T, std::size_t N>                struct pretty_name<T[N]>                             { static string get() { return "array of " + boost::lexical_cast<std::string>(N) + " " + pretty_name<T>::get(); } };
    template <typename T>                               struct pretty_name<T[]>                              { static string get() { return "array of unknown size of " + pretty_name<T>::get(); } };
    template <typename T, typename... Args>             struct pretty_name<T(Args...)>                       { static string get() { return "function taking (" + pretty_args<Args...>::get() + ") returning " + pretty_name<T>::get(); } };
    template <typename T, typename C>                   struct pretty_name<T C::*>                           { static string get() { return "pointer to member " + pretty_name<T>::get() + " of class " + pretty_name<C>::get(); } };
    template <typename T, typename C, typename... Args> struct pretty_name<T (C::*)(Args...) const>          { static string get() { return "pointer to const member " + pretty_name<T(Args...)>::get() + " of class " + pretty_name<C>::get(); } };
    template <typename T, typename C, typename... Args> struct pretty_name<T (C::*)(Args...) volatile>       { static string get() { return "pointer to volatile member " + pretty_name<T(Args...)>::get() + " of class " + pretty_name<C>::get(); } };
    template <typename T, typename C, typename... Args> struct pretty_name<T (C::*)(Args...) const volatile> { static string get() { return "pointer to const volatile member " + pretty_name<T(Args...)>::get() + " of class " + pretty_name<C>::get(); } };
    template <>                             struct pretty_args<>           { static string get() { return ""; } };
    template <typename T>                   struct pretty_args<T>          { static string get() { return pretty_name<T>::get(); } };
    template <typename T, typename... Args> struct pretty_args<T, Args...> { static string get() { return pretty_name<T>::get() + ", " + pretty_args<Args...>::get(); } };
    


  • Ich möchte aber keine Worte sondern C++-Typen 😉


  • Mod

    314159265358979 schrieb:

    Ich möchte aber keine Worte sondern C++-Typen 😉

    Und ich will das du mitdenkst und selber überlegst, wie aus Worten Symbole werden. Das ist ziemlich trivial.



  • camper schrieb:

    314159265358979 schrieb:

    Ich möchte aber keine Worte sondern C++-Typen 😉

    Und ich will das du mitdenkst und selber überlegst, wie aus Worten Symbole werden. Das ist ziemlich trivial.

    Ich weiß es nicht... Du hast hier auch nichts wirklich anders gemacht als ich - außer dass du statt eines Sterns hinten vorne "pointer of" hinschreibst. Ich habe keine Ahnung, habe schon 1 1/2 Stunden lang überlegt, hilf mir doch ein bisschen auf die Sprünge! 🙂


  • Mod

    Du benötigst für die Darstellung des abgeleiteten Typs zwei Strings (jeweils der Teil, der vor bzw. nach dem gedachten Namen kommt sowie die Information, ob der letzte Deklarator rechts oder links steht; folgt links auf rechts brauchst du ein extra Klammerpaar. Ungefähr so

    #include <string>
    #include <iostream>
    #include <typeinfo>
    #include <boost/lexical_cast.hpp>
    
    struct type_derivate
    {
        std::string left;
        std::string right;
        bool right_last;
    };
    
    type_derivate derive_left(const type_derivate& c, const std::string& symbol)
    {
          return { c.left + ( c.right_last ? "(" : "" ) + symbol, ( c.right_last ? ")" : "" ) + c.right };
    }
    
    type_derivate derive_right(const type_derivate& c, const std::string& symbol)
    {
          return { c.left, symbol + c.right, true };
    }
    
    template <typename...>
    struct pretty_args;
    
    template <typename T>
    struct pretty_name
    {
        static type_derivate get() { return { typeid(T).name() }; }
    };
    
    template <typename T>
    struct pretty_name<const T>
    {
        static type_derivate get() { return derive_left( pretty_name<T>::get(), " const" ); }
    };
    
    template <typename T>
    struct pretty_name<volatile T>
    {
        static type_derivate get() { return derive_left( pretty_name<T>::get(), " volatile" ); }
    };
    
    template <typename T>
    struct pretty_name<const volatile T>
    {
        static type_derivate get() { return derive_left( pretty_name<T>::get(), " const volatile" ); }
    };
    
    template <typename T>
    struct pretty_name<T*>
    {
        static type_derivate get() { return derive_left( pretty_name<T>::get(), "*" ); }
    };
    
    template <typename T>
    struct pretty_name<T&>
    {
        static type_derivate get() { return derive_left( pretty_name<T>::get(), "&" ); }
    };
    
    template <typename T>
    struct pretty_name<T&&>
    {
        static type_derivate get() { return derive_left( pretty_name<T>::get(), "&&" ); }
    };
    
    template <typename T, std::size_t N>
    struct pretty_name<T[N]>
    {
        static type_derivate get() { return derive_right( pretty_name<T>::get(), "[" + boost::lexical_cast<std::string>(N) + "]" ); }
    };
    
    template <typename T>
    struct pretty_name<T[]>
    {
        static type_derivate get() { return derive_right( pretty_name<T>::get(), "[]" ); }
    };
    
    template <typename T, typename... Args>
    struct pretty_name<T(Args...)>
    {
        static type_derivate get() { return derive_right( pretty_name<T>::get(), "(" + pretty_args<Args...>::get() + ")" ); }
    };
    
    template <typename T, typename C>
    struct pretty_name<T C::*>
    {
        static type_derivate get() { return derive_left( pretty_name<T>::get(), " " + pretty_name<C>::get().left + "::*" ); }
    };
    
    template <typename T, typename C, typename... Args>
    struct pretty_name<T (C::*)(Args...)>
    {
        static type_derivate get() { return derive_left( pretty_name<T(Args...)>::get(), pretty_name<C>::get().left + "::*" ); }
    };
    
    template <typename T, typename C, typename... Args>
    struct pretty_name<T (C::*)(Args...) const>
    {
        static type_derivate get() { auto x = pretty_name<T(Args...)>::get(); x.right += " const"; return derive_left( x, pretty_name<C>::get().left + "::*" ); }
    };
    
    template <typename T, typename C, typename... Args>
    struct pretty_name<T (C::*)(Args...) volatile>
    {
        static type_derivate get() { auto x = pretty_name<T(Args...)>::get(); x.right += " volatile"; return derive_left( x, pretty_name<C>::get().left + "::*" ); }
    };
    
    template <typename T, typename C, typename... Args>
    struct pretty_name<T (C::*)(Args...) const volatile>
    {
        static type_derivate get() { auto x = pretty_name<T(Args...)>::get(); x.right += " const volatile"; return derive_left( x, pretty_name<C>::get().left + "::*" ); }
    };
    
    template <>
    struct pretty_args<>
    {
        static std::string get() { return ""; }
    };
    
    template <typename T>
    struct pretty_args<T>
    {
        static std::string get() { auto x = pretty_name<T>::get(); return x.left + x.right; }
    };
    
    template <typename T, typename... Args>
    struct pretty_args<T, Args...>
    {
        static std::string get() { return pretty_args<T>::get() + ", " + pretty_args<Args...>::get(); }
    };
    
    template <typename T>
    std::string get_name()
    {
        auto x = pretty_name<T>::get();
        return x.left + x.right;
    }
    
    struct Foo {};
    
    int main()
    {
        std::cout << get_name<int[]>() << '\n';
        std::cout << get_name<int* (*&)[10]>() << '\n';
        std::cout << get_name<int (int,void*)>() << '\n';
        std::cout << get_name<int Foo::*>() << '\n';
        std::cout << get_name<void (Foo::*)()>() << '\n';
        std::cout << get_name<void (Foo::*)(int)>() << '\n';
        std::cout << get_name<void (Foo::*)(int)const>() << '\n';
    }
    


  • Es gibt da ein paar Dinge, die mir an deiner Lösung nicht gefallen.
    - Deine Lösung hat State. Ich weiß nicht, wie gut die Compiler das wegoptimieren können, aber ich finde ein type-printer sollte keinen runtime-overhead bringen.
    - String-Konkatenation: Dabei werden Kopien erzeugt. Ich habe bewusst std::ostream gewählt, da das lediglich zu Debug-Zwecken gedacht ist, da brauche ich strings nicht.
    - typeid: Auch typeid habe ich bewusst weggelassen. Erstens wegen des Runtime-Overheads, zweitens: Was fängt man denn mit sowas an: "St6vectorIcSaIcEE". Das ist ein std::vector<char> beim GCC. Der ist noch recht einfach zu entziffern, aber sobald da kompliziertere Typen stehen, bringt das nichts mehr.

    Ich strebe eine Lösung ausschließlich mit TMP an. Aber danke schon mal für die Grundidee! 🙂

    Mein Problem wird sein, dass ich mich wohl einfach zu wenig mit den Deklarator-Regeln auskenne. Kann hier vielleicht jemand Licht ins Dunkle bringen?



  • ...



  • 314159265358979 schrieb:

    Es gibt da ein paar Dinge, die mir an deiner Lösung nicht gefallen.
    - Deine Lösung hat State. Ich weiß nicht, wie gut die Compiler das wegoptimieren können, aber ich finde ein type-printer sollte keinen runtime-overhead bringen.

    Campers Idee geht auch ohne State, right_last muß nur auch compilezeitkonstant werden.
    Eine spannende Aufgabe, viel Spaß.

    314159265358979 schrieb:

    - String-Konkatenation: Dabei werden Kopien erzeugt. Ich habe bewusst std::ostream gewählt, da das lediglich zu Debug-Zwecken gedacht ist, da brauche ich strings nicht.

    Oder in einen laufenden Iterator schreiben, der wenn man es drauf anlegte, auch ein roher char* sein könnte.


  • Mod

    314159265358979 schrieb:

    Ich strebe eine Lösung ausschließlich mit TMP an. Aber danke schon mal für die Grundidee! 🙂

    Das setzt voraus, dass du irgendwie an Literale für die Grundtypen herankommst. Hier sehe ich keine Möglichkeit, als für jeden einzelnen Typen zu spezialisieren (für einfache Typen kein Problem, aber bei Klassen und enums wird es schwierig).
    Ich habe gerade mit so etwas experimentiert:

    #include <iostream>
    
    template <std::size_t... Indices> struct indices {};
    template <typename> struct next_indices;
    template <std::size_t... Indices> struct next_indices<indices<Indices...>>
    {
        typedef indices<0, (Indices+1)...> type;
    };
    
    template <std::size_t N> struct get_indices
    {
        typedef typename next_indices<typename get_indices<N-1>::type>::type type;
    };
    template <> struct get_indices<0>
    {
        typedef indices<> type;
    };
    
    template <const char*, std::size_t left, std::size_t right, typename = typename get_indices<right-left>::type>
    struct substr;
    
    template <const char* s, std::size_t left, std::size_t right, std::size_t... i>
    struct substr<s, left, right, indices<i...>>
    {
        static const char str[right - left + 1];
    };
    
    template <const char* s, std::size_t left, std::size_t right, std::size_t... i>
    const char substr<s, left, right, indices<i...>>::str[right - left + 1] = { ( s[ i + left ] )... };
    
    extern const char s[] = "abcdef";
    int main()
    {
        std::cout << substr<s, 5, 6>::str << '\n';
    }
    

    Idee war, gccs __PRETTY_FUNCTION__ die Arbeit machen zu lassen (siehe ungefähr so aus: funcname [ with T = ... ]), dummerweise ist __PRETTY_FUNCTION__ ebenso wie __func__ kein Literal.


  • Mod

    Soweit klappt das erst mal, nur an die Klassen-/enum-Namen komme ich nicht heran, da wird erst mal unknonw, bzw. class::* ausgegeben:

    #include <iostream>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/at.hpp>
    #include <boost/mpl/if.hpp>
    #include <boost/mpl/find.hpp>
    #include <boost/mpl/begin_end.hpp>
    #include <type_traits>
    
    template <std::size_t... Indices> struct indices {};
    template <typename> struct next_indices;
    template <std::size_t... Indices> struct next_indices<indices<Indices...>>
    {
        typedef indices<0, (Indices+1)...> type;
    };
    
    template <std::size_t N> struct get_indices
    {
        typedef typename next_indices<typename get_indices<N-1>::type>::type type;
    };
    template <> struct get_indices<0>
    {
        typedef indices<> type;
    };
    
    template <char... c> struct makestr_ { static const char str[sizeof...c + 1]; };
    template <char... c> constexpr char makestr_<c...>::str[sizeof...c + 1] = { c... };
    template <char... c> struct makestr { typedef makestr_<c...> type; };
    // makestr<"abcdef"> sobald das unterstützt wird
    
    template <typename s1, typename s2, typename i1, typename i2> struct concat_;
    
    template <typename s1, typename s2, std::size_t... i1, std::size_t... i2>
    struct concat_<s1, s2, indices<i1...>, indices<i2...>>
    {
        static const char str[sizeof... i1 + sizeof... i2];
    };
    
    template <typename s1, typename s2, std::size_t... i1, std::size_t... i2>
    constexpr char concat_<s1, s2, indices<i1...>, indices<i2...>>::str[] = { s1::str[i1]..., s2::str[i2]... };
    
    template <typename s1, typename s2> struct concat
    {
        typedef concat_<
            typename s1::type,
            typename s2::type,
            typename get_indices<sizeof s1::type::str-1>::type,
            typename get_indices<sizeof s2::type::str>::type> type;
    };
    
    typedef boost::mpl::vector<
        void,
        char,signed char, unsigned char, short, unsigned short, int, unsigned, long, unsigned long, long long, unsigned long long,
        float, double, long double> fundamental_types;
    typedef boost::mpl::vector<
        makestr<'v','o','i','d'>,
        makestr<'c','h','a','r'>,
        makestr<'s','i','g','n','e','d',' ','c','h','a','r'>,
        makestr<'u','n','s','i','g','n','e','d',' ','c','h','a','r'>,
        makestr<'s','h','o','r','t'>,
        makestr<'u','n','s','i','g','n','e','d',' ','s','h','o','r','t'>,
        makestr<'i','n','t'>,
        makestr<'u','n','s','i','g','n','e','d'>,
        makestr<'l','o','n','g'>,
        makestr<'u','n','s','i','g','n','e','d',' ','l','o','n','g'>,
        makestr<'l','o','n','g',' ','l','o','n','g'>,
        makestr<'u','n','s','i','g','n','e','d',' ','l','o','n','g',' ','l','o','n','g'>,
        makestr<'f','l','o','a','t'>,
        makestr<'d','o','u','b','l','e'>,
        makestr<'l','o','n','g',' ','d','o','u','b','l','e'>> fundamental_type_names;
    
    template <typename L, typename R, bool b> struct type_name_parts
    {
        typedef L left;
        typedef R right;
        static constexpr bool right_last = b;
    };
    
    template <typename P> struct name_from_parts : concat<typename P::left, typename P::right> {};
    
    template <typename P, typename S> struct derive_left : type_name_parts<
            concat<
                typename boost::mpl::if_c<P::right_last, concat<typename P::left, makestr<'('>>, typename P::left>::type,
                S
            >,
            typename boost::mpl::if_c<P::right_last, concat<makestr<')'>, typename P::right>, typename P::right>::type,
            false> {};
    
    template <typename P, typename S> struct derive_right :  type_name_parts<typename P::left, concat<S, typename P::right>, true> {};
    
    template <typename P, typename S> struct mem_fun_suffix : type_name_parts<typename P::left, concat<typename P::right, S>, true> {};
    
    template <typename> struct pretty_name;
    
    template <typename...> struct pretty_args;
    template <> struct pretty_args<> : makestr<> {};
    template <typename T> struct pretty_args<T> : name_from_parts<pretty_name<T>> {};
    template <typename T, typename... Args> struct pretty_args<T, Args...>
        : concat<concat<pretty_args<T>,makestr<',',' '>>, pretty_args<Args...>> {};
    
    template <typename T> struct pretty_name : type_name_parts<
            typename boost::mpl::if_<
                std::is_same<
                    typename boost::mpl::end<fundamental_types>::type,
                    typename boost::mpl::find<fundamental_types,T>::type>,
                makestr<'u','n','k','n','o','w','n'>,
                typename boost::mpl::at<fundamental_type_names, typename boost::mpl::find<fundamental_types, T>::type::pos>::type
            >::type,
            makestr<>,
            false> {};
    
    template <typename T> struct pretty_name<const T>
        : derive_left<pretty_name<T>, makestr<' ','c','o','n','s','t'>> {};
    template <typename T> struct pretty_name<volatile T>
        : derive_left<pretty_name<T>, makestr<' ','v','o','l','a','t','i','l','e'>> {};
    template <typename T> struct pretty_name<const volatile T>
        : derive_left<pretty_name<T>, makestr<' ','c','o','n','s','t',' ','v','o','l','a','t','i','l','e'>> {};
    template <typename T> struct pretty_name<T*>
        : derive_left<pretty_name<T>, makestr<'*'>> {};
    template <typename T> struct pretty_name<T&>
        : derive_left<pretty_name<T>, makestr<'&'>> {};
    template <typename T> struct pretty_name<T&&>
        : derive_left<pretty_name<T>, makestr<'&','&'>> {};
    template <typename T, std::size_t N> struct pretty_name<T[N]>
        : derive_right<pretty_name<T>, makestr<'[','0'+N,']'>> {};            // TODO
    template <typename T> struct pretty_name<T[]>
        : derive_right<pretty_name<T>, makestr<'[',']'>> {};
    template <typename T, typename... Args> struct pretty_name<T(Args...)>
        : derive_right<pretty_name<T>, concat<concat<makestr<'('>,pretty_args<Args...>>, makestr<')'>>> {};
    template <typename T, typename C> struct pretty_name<T C::*>
        : derive_left<pretty_name<T>, makestr<' ','c','l','a','s','s',':',':','*'>> {};
    template <typename T, typename C, typename... Args> struct pretty_name<T (C::*)(Args...)>
        : derive_left<pretty_name<T(Args...)>, makestr<'c','l','a','s','s',':',':','*'>> {};
    template <typename T, typename C, typename... Args> struct pretty_name<T (C::*)(Args...) const>
        : derive_left<mem_fun_suffix<pretty_name<T(Args...)>, makestr<'c','o','n','s','t'>>,
              makestr<'c','l','a','s','s',':',':','*'>> {};
    template <typename T, typename C, typename... Args> struct pretty_name<T (C::*)(Args...) volatile>
        : derive_left<mem_fun_suffix<pretty_name<T(Args...)>, makestr<'v','o','l','a','t','i','l','e'>>,
              makestr<'c','l','a','s','s',':',':','*'>> {};
    template <typename T, typename C, typename... Args> struct pretty_name<T (C::*)(Args...) const volatile>
        : derive_left<mem_fun_suffix<pretty_name<T(Args...)>, makestr<'c','o','n','s','t',' ','v','o','l','a','t','i','l','e'>>,
              makestr<'c','l','a','s','s',':',':','*'>> {};
    
    template <typename T>
    std::string get_name()
    {
        return name_from_parts<pretty_name<T>>::type::str;
    }
    
    int main()
    {
        std::cout << get_name<int>() << '\n';
        std::cout << get_name<int const>() << '\n';
        std::cout << get_name<int*>() << '\n';
        std::cout << get_name<int&&>() << '\n';
        std::cout << get_name<int[]>() << '\n';
        std::cout << get_name<int(void)>() << '\n';
        std::cout << get_name<int(*)[5]>() << '\n';
        std::cout << get_name<void(int)>() << '\n';
        std::cout << get_name<int&(int*, char)>() << '\n';
        std::cout << get_name<int&(int*, char, int&(*&)(long,long long))>() << '\n';
        struct Foo;
        std::cout << get_name<int Foo::*>() << '\n';
        std::cout << get_name<int (Foo::*)()>() << '\n';
        std::cout << get_name<int (Foo::*)(int, char)const volatile>() << '\n';
    }
    

Log in to reply