For example:
int a = 12;
cout << typeof(a) << endl;
Expected output:
int
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);};