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



  • 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';
    }
    

  • Mod

    Habe das Ganze noch ein bisschen vereinfacht und erweitert, man kann jetzt sehr leicht um zusätzliche Typen erweitern (siehe Beispiel in main), Für Template-Namen müsste man noch etwas mehr Aufwand treiben. Wobei es damit prinzipiell ein Problem im Zusammenhang mit verschachtelten Bezeichener gibt, ein vector<...>::iterator ist kein deduzierbarer Kontext, und damit gibt es keinerlei Möglichkeit, diesen allgemein bekannt zu machen. Aber vielleicht ist es ja jetzt schon nützlich:

    #include <string>
    #include <iostream>
    #include <utility>
    #include <type_traits>
    #include <boost/mpl/pair.hpp>
    #include <boost/mpl/map.hpp>
    #include <boost/mpl/at.hpp>
    #include <boost/mpl/if.hpp>
    #include <boost/mpl/has_key.hpp>
    #include <boost/mpl/insert.hpp>
    
    template <char... c> struct literal
    {
        static const char str[sizeof... c + 1];
        typedef literal type;
    };
    template <char... c>
    constexpr char literal<c...>::str[sizeof... c + 1] = { c... };
    
    template <typename... s> struct concat : concat<typename s::type...> {};
    template <char... c1, char... c2, typename... s> struct concat<literal<c1...>,literal<c2...>, s...> : concat<literal<c1..., c2...>, s...> {};
    template <char... c> struct concat<literal<c...>> : literal<c...> {};
    template <> struct concat<> : literal<> {};
    
    template <std::size_t N, typename L = literal<>> struct N_to_literal : N_to_literal<N / 10, concat<literal<('0' + N % 10)>,L>> {};
    template <typename L> struct N_to_literal<0, L> : L {};
    template <> struct N_to_literal<0, literal<>> : literal<'0'> {};
    
    typedef boost::mpl::map<
        boost::mpl::pair<void,               literal<'v','o','i','d'>>,
        boost::mpl::pair<char,               literal<'c','h','a','r'>>,
        boost::mpl::pair<signed char,        literal<'s','i','g','n','e','d',' ','c','h','a','r'>>,
        boost::mpl::pair<unsigned char,      literal<'u','n','s','i','g','n','e','d',' ','c','h','a','r'>>,
        boost::mpl::pair<short,              literal<'s','h','o','r','t'>>,
        boost::mpl::pair<unsigned short,     literal<'u','n','s','i','g','n','e','d',' ','s','h','o','r','t'>>,
        boost::mpl::pair<int,                literal<'i','n','t'>>,
        boost::mpl::pair<unsigned,           literal<'u','n','s','i','g','n','e','d'>>,
        boost::mpl::pair<long,               literal<'l','o','n','g'>>,
        boost::mpl::pair<unsigned long,      literal<'u','n','s','i','g','n','e','d',' ','l','o','n','g'>>,
        boost::mpl::pair<long long,          literal<'l','o','n','g',' ','l','o','n','g'>>,
        boost::mpl::pair<unsigned long long, literal<'u','n','s','i','g','n','e','d',' ','l','o','n','g',' ','l','o','n','g'>>,
        boost::mpl::pair<wchar_t,            literal<'w','c','h','a','r','_','t'>>,
        boost::mpl::pair<float,              literal<'f','l','o','a','t'>>,
        boost::mpl::pair<double,             literal<'d','o','u','b','l','e'>>,
        boost::mpl::pair<long double,        literal<'l','o','n','g',' ','d','o','u','b','l','e'>>> fundamental_types;
    
    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 T, typename M> struct derive_name_parts;
    
    template <typename T, typename M = fundamental_types> struct type_name : concat<typename derive_name_parts<T, M>::left, typename derive_name_parts<T, M>::right> {};
    
    template <typename P, typename S> struct derive_left
        : type_name_parts<
            concat<typename P::left, boost::mpl::if_c<P::right_last, literal<'('>, literal<>>, S>,
            concat<boost::mpl::if_c<P::right_last, literal<')'>, literal<>>, typename P::right>,
            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 M, typename...> struct pretty_args;
    template <typename M> struct pretty_args<M> : literal<> {};
    template <typename M, typename T> struct pretty_args<M, T> : type_name<T, M> {};
    template <typename M, typename T, typename... Args> struct pretty_args<M, T, Args...>
        : concat<pretty_args<M, T>,literal<','>, pretty_args<M, Args...>> {};
    
    template <typename T, typename M> struct derive_name_parts
        : type_name_parts<boost::mpl::if_<boost::mpl::has_key<M, T>, boost::mpl::at<M, T>, literal<'u','n','k','n','o','w','n'>>, literal<>, false> {};
    
    template <typename T, typename M> struct derive_name_parts<const T, M>
        : derive_left<derive_name_parts<T, M>, literal<' ','c','o','n','s','t'>> {};
    template <typename T, typename M> struct derive_name_parts<volatile T, M>
        : derive_left<derive_name_parts<T, M>, literal<' ','v','o','l','a','t','i','l','e'>> {};
    template <typename T, typename M> struct derive_name_parts<const volatile T, M>
        : derive_left<derive_name_parts<T, M>, literal<' ','c','o','n','s','t',' ','v','o','l','a','t','i','l','e'>> {};
    template <typename T, typename M> struct derive_name_parts<T*, M>
        : derive_left<derive_name_parts<T, M>, literal<'*'>> {};
    template <typename T, typename M> struct derive_name_parts<T&, M>
        : derive_left<derive_name_parts<T, M>, literal<'&'>> {};
    template <typename T, typename M> struct derive_name_parts<T&&, M>
        : derive_left<derive_name_parts<T, M>, literal<'&','&'>> {};
    template <typename T, typename M, std::size_t N> struct derive_name_parts<T[N], M>
        : derive_right<derive_name_parts<T, M>, concat<literal<'['>,N_to_literal<N>,literal<']'>>> {};
    template <typename T, typename M> struct derive_name_parts<T[], M>
        : derive_right<derive_name_parts<T, M>, literal<'[',']'>> {};
    template <typename T, typename M, typename... Args> struct derive_name_parts<T(Args...), M>
        : derive_right<derive_name_parts<T, M>, concat<literal<'('>, pretty_args<M, Args...>, literal<')'>>> {};
    template <typename T, typename M, typename C> struct derive_name_parts<T C::*, M>
        : derive_left<derive_name_parts<T, M>, concat<literal<' '>, type_name<C, M>, literal<':',':','*'>>> {};
    template <typename T, typename M, typename C, typename... Args> struct derive_name_parts<T (C::*)(Args...), M>
        : derive_left<derive_name_parts<T(Args...), M>, concat<type_name<C, M>, literal<':',':','*'>>> {};
    template <typename T, typename M, typename C, typename... Args> struct derive_name_parts<T (C::*)(Args...) const, M>
        : derive_left<mem_fun_suffix<derive_name_parts<T(Args...), M>, literal<'c','o','n','s','t'>>, concat<type_name<C, M>, literal<':',':','*'>>> {};
    template <typename T, typename M, typename C, typename... Args> struct derive_name_parts<T (C::*)(Args...) volatile, M>
        : derive_left<mem_fun_suffix<derive_name_parts<T(Args...), M>, literal<'v','o','l','a','t','i','l','e'>>, concat<type_name<C, M>, literal<':',':','*'>>> {};
    template <typename T, typename M, typename C, typename... Args> struct derive_name_parts<T (C::*)(Args...) const volatile, M>
        : derive_left<mem_fun_suffix<derive_name_parts<T(Args...), M>, literal<'c','o','n','s','t',' ','v','o','l','a','t','i','l','e'>>, concat<type_name<C, M>, literal<':',':','*'>>> {};
    
    int main()
    {
        struct Foo;
        std::cout << type_name<int>::type::str << '\n';
        std::cout << type_name<int const>::type::str << '\n';
        std::cout << type_name<int*>::type::str << '\n';
        std::cout << type_name<int&&>::type::str << '\n';
        std::cout << type_name<int[]>::type::str << '\n';
        std::cout << type_name<int(void)>::type::str << '\n';
        std::cout << type_name<int(*)[12345]>::type::str << '\n';
        std::cout << type_name<void(int)>::type::str << '\n';
        std::cout << type_name<int&(int*, char)>::type::str << '\n';
        std::cout << type_name<int&(int*, char, int&(*&)(long,long long))>::type::str << '\n';
        std::cout << type_name<Foo>::type::str << '\n';
        std::cout << type_name<int Foo::*>::type::str << '\n';
        std::cout << type_name<int (Foo::*)()>::type::str << '\n';
        std::cout << type_name<int (Foo::*)(int, char)const volatile>::type::str << '\n';
    
        typedef boost::mpl::insert<
            boost::mpl::insert<fundamental_types, boost::mpl::pair<Foo, literal<'F','o','o'>>>::type,
            boost::mpl::pair<std::string, literal<':',':','s','t','d',':',':','s','t','r','i','n','g'>>>::type more_types;
        std::cout << type_name<Foo, more_types>::type::str << '\n';
        std::cout << type_name<int Foo::*, more_types>::type::str << '\n';
        std::cout << type_name<int (Foo::*)(), more_types>::type::str << '\n';
        std::cout << type_name<std::string (Foo::*)(int, char)const volatile, more_types>::type::str << '\n';
    }
    

    Ausgabe:

    int
    int const
    int*
    int&&
    int[]
    int()
    int(*)[12345]
    void(int)
    int&(int*,char)
    int&(int*,char,int&(*&)(long,long long))
    unknown
    int unknown::*
    int(unknown::*)()
    int(unknown::*)(int,char)const volatile
    Foo
    int Foo::*
    int(Foo::*)()
    ::std::string(Foo::*)(int,char)const volatile
    


  • Schön... glaub ich.
    Ich kenne mich mit boost::mpl nicht aus, und die Deklarator-Regeln verstehe ich immer noch nicht.

    Jetzt ist der Zeitpunkt eingetreten, an dem ich keine Lust mehr habe, weil ich draufkomme, dass mein Ansatz von Grund auf falsch ist und ich's sowieso nicht kapier.


Anmelden zum Antworten