Skip to content

Instantly share code, notes, and snippets.

@e7
Last active May 3, 2018 08:10
Show Gist options
  • Save e7/bff1ad6e0a135062b1249bc7414da12e to your computer and use it in GitHub Desktop.
Save e7/bff1ad6e0a135062b1249bc7414da12e to your computer and use it in GitHub Desktop.
check type
#ifndef CHECK_TYPE_HPP___
#define CHECK_TYPE_HPP___
#include <typeinfo> // typeid
#include <sstream> // std::ostringstream, std::string
#include <type_traits> // std::is_array
#include <utility> // std::move
#if defined(__GNUC__)
#include <memory> // std::unique_ptr
#include <cxxabi.h> // abi::__cxa_demangle
#endif
namespace private_check_type {
template <typename T, bool IsBase = false>
struct check;
/*
Output state management
*/
class output
{
bool is_compact_ = true;
template <typename T>
bool check_empty(const T&) { return false; }
bool check_empty(const char* val)
{
return (!val) || (val[0] == 0);
}
template <typename T>
void out(const T& val)
{
if (check_empty(val)) return;
if (!is_compact_) sr_ += " ";
using ss_t = std::ostringstream;
sr_ += static_cast<ss_t&>(ss_t() << val).str();
is_compact_ = false;
}
std::string& sr_;
public:
output(std::string& sr) : sr_(sr) {}
output& operator()(void) { return (*this); }
template <typename T1, typename... T>
output& operator()(const T1& val, const T&... args)
{
out(val);
return operator()(args...);
}
output& compact(void)
{
is_compact_ = true;
return (*this);
}
};
// ()
template <bool>
struct bracket
{
output& out_;
bracket(output& out, const char* = nullptr) : out_(out)
{ out_("(").compact(); }
~bracket(void)
{ out_.compact()(")"); }
};
template <>
struct bracket<false>
{
bracket(output& out, const char* str = nullptr)
{ out(str); }
};
// [N]
template <size_t N = 0>
struct bound
{
output& out_;
bound(output& out) : out_(out) {}
~bound(void)
{
if (N == 0) out_("[]");
else out_("[").compact()
( N ).compact()
("]");
}
};
// (P1, P2, ...)
template <bool, typename... P>
struct parameter;
template <bool IsStart>
struct parameter<IsStart>
{
output& out_;
parameter(output& out) : out_(out) {}
~parameter(void)
{ bracket<IsStart> { out_ }; }
};
template <bool IsStart, typename P1, typename... P>
struct parameter<IsStart, P1, P...>
{
output& out_;
parameter(output& out) : out_(out) {}
~parameter(void)
{
[this](bracket<IsStart>&&)
{
check<P1> { out_ };
parameter<false, P...> { out_.compact() };
} (bracket<IsStart> { out_, "," });
}
};
// Do output at destruct
struct at_destruct
{
output& out_;
const char* str_;
at_destruct(output& out, const char* str = nullptr)
: out_(out)
, str_(str)
{}
~at_destruct(void)
{ out_(str_); }
void set_str(const char* str = nullptr)
{ str_ = str; }
};
/*
CV-qualifiers, references, pointers
*/
template <typename T, bool IsBase>
struct check
{
output out_;
check(const output& out) : out_(out)
{
# if defined(__GNUC__)
const char* typeid_name = typeid(T).name();
auto deleter = [](char* p)
{
if (p) free(p);
};
std::unique_ptr<char, decltype(deleter)> real_name
{
abi::__cxa_demangle(typeid_name, nullptr, nullptr, nullptr), deleter
};
out_(real_name ? real_name.get() : typeid_name);
# else
out_(typeid(T).name());
# endif
}
};
#define CHECK_TYPE__(OPT) \
template <typename T, bool IsBase> \
struct check<T OPT, IsBase> : check<T, true> \
{ \
using base_t = check<T, true>; \
using base_t::out_; \
check(const output& out) : base_t(out) { out_(#OPT); } \
};
CHECK_TYPE__(const)
CHECK_TYPE__(volatile)
CHECK_TYPE__(const volatile)
CHECK_TYPE__(&)
CHECK_TYPE__(&&)
CHECK_TYPE__(*)
#undef CHECK_TYPE__
/*
Arrays
*/
#define CHECK_TYPE_ARRAY__(CV_OPT, BOUND_OPT, ...) \
template <typename T, bool IsBase __VA_ARGS__> \
struct check<T CV_OPT [BOUND_OPT], IsBase> : check<T CV_OPT, !std::is_array<T>::value> \
{ \
using base_t = check<T CV_OPT, !std::is_array<T>::value>; \
using base_t::out_; \
\
bound<BOUND_OPT> bound_ = out_; \
bracket<IsBase> bracket_ = out_; \
\
check(const output& out) : base_t(out) {} \
};
#define CHECK_TYPE_ARRAY_CV__(BOUND_OPT, ...) \
CHECK_TYPE_ARRAY__(, BOUND_OPT, ,##__VA_ARGS__) \
CHECK_TYPE_ARRAY__(const, BOUND_OPT, ,##__VA_ARGS__) \
CHECK_TYPE_ARRAY__(volatile, BOUND_OPT, ,##__VA_ARGS__) \
CHECK_TYPE_ARRAY__(const volatile, BOUND_OPT, ,##__VA_ARGS__)
#define CHECK_TYPE_PLACEHOLDER__
CHECK_TYPE_ARRAY_CV__(CHECK_TYPE_PLACEHOLDER__)
#if defined(__GNUC__)
CHECK_TYPE_ARRAY_CV__(0)
#endif
CHECK_TYPE_ARRAY_CV__(N, size_t N)
#undef CHECK_TYPE_PLACEHOLDER__
#undef CHECK_TYPE_ARRAY_CV__
#undef CHECK_TYPE_ARRAY__
/*
Functions
*/
template <typename T, bool IsBase, typename... P>
struct check<T(P...), IsBase> : check<T, true>
{
using base_t = check<T, true>;
using base_t::out_;
parameter<true, P...> parameter_ = out_;
bracket<IsBase> bracket_ = out_;
check(const output& out) : base_t(out) {}
};
/*
Pointers to members
*/
template <typename T, bool IsBase, typename C>
struct check<T C::*, IsBase> : check<T, true>
{
using base_t = check<T, true>;
using base_t::out_;
check(const output& out) : base_t(out)
{
check<C> { out_ };
out_.compact()("::*");
}
};
/*
Pointers to member functions
*/
#define CHECK_TYPE_MEM_FUNC__(...) \
template <typename T, bool IsBase, typename C, typename... P> \
struct check<T(C::*)(P...) __VA_ARGS__, IsBase> \
{ \
at_destruct cv_ = base_.out_; \
check<T(P...), true> base_; \
output& out_ = base_.out_; \
\
check(const output& out) : base_(out) \
{ \
cv_.set_str(#__VA_ARGS__); \
check<C> { out_ }; \
out_.compact()("::*"); \
} \
};
CHECK_TYPE_MEM_FUNC__()
CHECK_TYPE_MEM_FUNC__(const)
CHECK_TYPE_MEM_FUNC__(volatile)
CHECK_TYPE_MEM_FUNC__(const volatile)
#undef CHECK_TYPE_MEM_FUNC__
} // namespace private_check_type
/*
Get the name of the given type
check_type<const volatile void *>()
-->
void const volatile *
*/
template <typename T>
inline std::string check_type(void)
{
std::string str;
private_check_type::check<T> { str };
return std::move(str);
}
#endif // CHECK_TYPE_HPP___
@e7
Copy link
Author

e7 commented Dec 12, 2017

#include "check_type.hpp"

#include <iostream>

class Foo {};

template <typename T>
auto func(T&&) -> T;

int main(void)
{
    std::cout << check_type<const volatile void *>()
              << std::endl;
    std::cout << check_type<const volatile void *(&)[10]>()
              << std::endl;
    std::cout << check_type<int [1][2][3]>()
              << std::endl;
    std::cout << check_type<const int [3][10]>()
              << std::endl << std::endl;

    std::cout << check_type<int(unsigned)>()
              << std::endl;
    std::cout << check_type<int(*)(const int *(*)[3][10], Foo&&, int, unsigned)>()
              << std::endl;
    std::cout << check_type<char(*(*const)(const int(&)[10]))[10]>()
              << std::endl << std::endl;

    std::cout << check_type<int Foo::* const(&)[]>()
              << std::endl << std::endl;

    std::cout << check_type<void(Foo::*)(void)>()
              << std::endl;
    std::cout << check_type<void(Foo::*)(void) volatile const>()
              << std::endl;
    std::cout << check_type<void(Foo::*(*)[])(int) const>()
              << std::endl;
    std::cout << check_type<int (Foo::* const)(int, Foo&&, int) volatile>()
              << std::endl << std::endl;

    std::cout << check_type<decltype(func<Foo>)>() << std::endl;
    std::cout << check_type<decltype(func<Foo&>)>() << std::endl;
    std::cout << check_type<decltype(func<Foo&&>)>() << std::endl;

    return 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment