Is it possible to print a variable's type in standard C++?

前端 未结 21 1668
南笙
南笙 2020-11-22 01:41

For example:

int a = 12;
cout << typeof(a) << endl;

Expected output:

int
21条回答
  •  情深已故
    2020-11-22 02:05

    As I challenge I decided to test how far can one go with platform-independent (hopefully) template trickery.

    The names are assembled completely at compilation time. (Which means typeid(T).name() couldn't be used, thus you have to explicitly provide names for non-compound types. Otherwise placeholders will be displayed instead.)

    Example usage:

    TYPE_NAME(int)
    TYPE_NAME(void)
    // You probably should list all primitive types here.
    
    TYPE_NAME(std::string)
    
    int main()
    {
        // A simple case
        std::cout << type_name << '\n';
        // -> `void (*)(int)`
    
        // Ugly mess case
        // Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers.
        std::cout << type_name << '\n';
        // -> `void (std::string::*(int *,int,void (*)(std::string)))(volatile int *const*)`
    
        // A case with undefined types
        //  If a type wasn't TYPE_NAME'd, it's replaced by a placeholder, one of `class?`, `union?`, `enum?` or `??`.
        std::cout << type_name << '\n';
        // -> `class? (*)(int,??)`
        // With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.
    }
    

    Code:

    #include 
    #include 
    
    static constexpr std::size_t max_str_lit_len = 256;
    
    template  constexpr char sl_at(const char (&str)[N])
    {
        if constexpr(I < N)
            return str[I];
        else
            return '\0';
    }
    
    constexpr std::size_t sl_len(const char *str)
    {
        for (std::size_t i = 0; i < max_str_lit_len; i++)
            if (str[i] == '\0')
                return i;
        return 0;
    }
    
    template  struct str_lit
    {
        static constexpr char value[] {C..., '\0'};
        static constexpr int size = sl_len(value);
    
        template  struct concat_impl {using type = typename concat_impl::type::template concat_impl::type;};
        template  struct concat_impl> {using type = str_lit;};
        template  using concat = typename concat_impl::type;
    };
    
    template  struct trim_str_lit_impl;
    template  struct trim_str_lit_impl, S>
    {
        using type = str_lit;
    };
    template  using trim_str_lit = typename trim_str_lit_impl, S>::type;
    
    #define STR_LIT(str) ::trim_str_lit<::sl_len(str), ::str_lit::value>
    #define STR_TO_VA(str) STR_TO_VA_16(str,0),STR_TO_VA_16(str,16),STR_TO_VA_16(str,32),STR_TO_VA_16(str,48)
    #define STR_TO_VA_16(str,off) STR_TO_VA_4(str,0+off),STR_TO_VA_4(str,4+off),STR_TO_VA_4(str,8+off),STR_TO_VA_4(str,12+off)
    #define STR_TO_VA_4(str,off) ::sl_at(str),::sl_at(str),::sl_at(str),::sl_at(str)
    
    template  constexpr str_lit make_str_lit(str_lit) {return {};}
    template  constexpr auto make_str_lit(const char (&str)[N])
    {
        return trim_str_lit{};
    }
    
    template  struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow::value;};
    template  struct cexpr_pow {static constexpr std::size_t value = 1;};
    template > struct num_to_str_lit_impl;
    template  struct num_to_str_lit_impl>
    {
        static constexpr auto func()
        {
            if constexpr (N >= cexpr_pow<10,X>::value)
                return num_to_str_lit_impl::func();
            else
                return str_lit<(N / cexpr_pow<10,X-1-Seq>::value % 10 + '0')...>{};
        }
    };
    template  using num_to_str_lit = decltype(num_to_str_lit_impl::func());
    
    
    using spa = str_lit<' '>;
    using lpa = str_lit<'('>;
    using rpa = str_lit<')'>;
    using lbr = str_lit<'['>;
    using rbr = str_lit<']'>;
    using ast = str_lit<'*'>;
    using amp = str_lit<'&'>;
    using con = str_lit<'c','o','n','s','t'>;
    using vol = str_lit<'v','o','l','a','t','i','l','e'>;
    using con_vol = con::concat;
    using nsp = str_lit<':',':'>;
    using com = str_lit<','>;
    using unk = str_lit<'?','?'>;
    
    using c_cla = str_lit<'c','l','a','s','s','?'>;
    using c_uni = str_lit<'u','n','i','o','n','?'>;
    using c_enu = str_lit<'e','n','u','m','?'>;
    
    template  inline constexpr bool ptr_or_ref = std::is_pointer_v || std::is_reference_v || std::is_member_pointer_v;
    template  inline constexpr bool func_or_arr = std::is_function_v || std::is_array_v;
    
    template  struct primitive_type_name {using value = unk;};
    
    template >> using enable_if_class = T;
    template >> using enable_if_union = T;
    template >> using enable_if_enum  = T;
    template  struct primitive_type_name> {using value = c_cla;};
    template  struct primitive_type_name> {using value = c_uni;};
    template  struct primitive_type_name> {using value = c_enu;};
    
    template  struct type_name_impl;
    
    template  using type_name_lit = std::conditional_t::value::template concat,
                                                                                   typename type_name_impl::l::template concat::r>>,
                                                typename primitive_type_name::value,
                                                typename type_name_impl::l::template concat::r>>;
    template  inline constexpr const char *type_name = type_name_lit::value;
    
    template  && !std::is_volatile_v>> using enable_if_no_cv = T;
    
    template  struct type_name_impl
    {
        using l = typename primitive_type_name::value::template concat;
        using r = str_lit<>;
    };
    template  struct type_name_impl
    {
        using new_T_l = std::conditional_t::l::size && !ptr_or_ref,
                                           spa::concat::l>,
                                           typename type_name_impl::l>;
        using l = std::conditional_t,
                                     typename new_T_l::template concat,
                                     con::concat>;
        using r = typename type_name_impl::r;
    };
    template  struct type_name_impl
    {
        using new_T_l = std::conditional_t::l::size && !ptr_or_ref,
                                           spa::concat::l>,
                                           typename type_name_impl::l>;
        using l = std::conditional_t,
                                     typename new_T_l::template concat,
                                     vol::concat>;
        using r = typename type_name_impl::r;
    };
    template  struct type_name_impl
    {
        using new_T_l = std::conditional_t::l::size && !ptr_or_ref,
                                           spa::concat::l>,
                                           typename type_name_impl::l>;
        using l = std::conditional_t,
                                     typename new_T_l::template concat,
                                     con_vol::concat>;
        using r = typename type_name_impl::r;
    };
    template  struct type_name_impl
    {
        using l = std::conditional_t,
                                     typename type_name_impl::l::template concat,
                                     typename type_name_impl::l::template concat<     ast>>;
        using r = std::conditional_t,
                                     rpa::concat::r>,
                                                 typename type_name_impl::r>;
    };
    template  struct type_name_impl
    {
        using l = std::conditional_t,
                                     typename type_name_impl::l::template concat,
                                     typename type_name_impl::l::template concat<     amp>>;
        using r = std::conditional_t,
                                     rpa::concat::r>,
                                                 typename type_name_impl::r>;
    };
    template  struct type_name_impl
    {
        using l = std::conditional_t,
                                     typename type_name_impl::l::template concat,
                                     typename type_name_impl::l::template concat<     amp, amp>>;
        using r = std::conditional_t,
                                     rpa::concat::r>,
                                                 typename type_name_impl::r>;
    };
    template  struct type_name_impl
    {
        using l = std::conditional_t,
                                     typename type_name_impl::l::template concat, nsp, ast>,
                                     typename type_name_impl::l::template concat<     type_name_lit, nsp, ast>>;
        using r = std::conditional_t,
                                     rpa::concat::r>,
                                                 typename type_name_impl::r>;
    };
    template  struct type_name_impl>
    {
        using l = typename type_name_impl::l;
        using r = lbr::concat::r>;
    };
    template  struct type_name_impl>
    {
        using l = typename type_name_impl::l;
        using r = lbr::concat, rbr, typename type_name_impl::r>;
    };
    template  struct type_name_impl
    {
        using l = typename type_name_impl::l;
        using r = lpa::concat::r>;
    };
    template  struct type_name_impl
    {
        using l = typename type_name_impl::l;
        using r = lpa::concat,
                              com::concat>..., rpa, typename type_name_impl::r>;
    };
    
    #define TYPE_NAME(t) template <> struct primitive_type_name {using value = STR_LIT(#t);};
    

提交回复
热议问题