We need a way to determine if a type is a struct or enum or an array or a primitive type, etc… So first let’s define an enum called TYPE_KIND as following:

enum TYPE_KIND
{
    TYPE_KIND_I8,
    TYPE_KIND_I16,
    TYPE_KIND_I32,
    TYPE_KIND_I64,
    TYPE_KIND_U8,
    TYPE_KIND_U16,
    TYPE_KIND_U32,
    TYPE_KIND_U64,
    TYPE_KIND_F32,
    TYPE_KIND_F64,
    TYPE_KIND_BOOL,
    TYPE_KIND_CHAR,
    TYPE_KIND_VOID,
    TYPE_KIND_POINTER,
    TYPE_KIND_ARRAY,
    TYPE_KIND_ENUM,
    TYPE_KIND_STRUCT
};

Then we can take advantage of C++’s type_traits to implement kind_of<T>() as following:

#include <type_traits>

template <typename T>
inline static constexpr TYPE_KIND
kind_of()
{
    using Type = std::remove_cvref_t<T>;
    if constexpr (std::is_same_v<Type, int8_t>)
        return TYPE_KIND_I8;
    else if constexpr (std::is_same_v<Type, int16_t>)
        return TYPE_KIND_I16;
    else if constexpr (std::is_same_v<Type, int32_t>)
        return TYPE_KIND_I32;
    else if constexpr (std::is_same_v<Type, int64_t>)
        return TYPE_KIND_I64;
    else if constexpr (std::is_same_v<Type, uint8_t>)
        return TYPE_KIND_U8;
    else if constexpr (std::is_same_v<Type, uint16_t>)
        return TYPE_KIND_U16;
    else if constexpr (std::is_same_v<Type, uint32_t>)
        return TYPE_KIND_U32;
    else if constexpr (std::is_same_v<Type, uint64_t>)
        return TYPE_KIND_U64;
    else if constexpr (std::is_same_v<Type, float>)
        return TYPE_KIND_F32;
    else if constexpr (std::is_same_v<Type, double>)
        return TYPE_KIND_F64;
    else if constexpr (std::is_same_v<Type, bool>)
        return TYPE_KIND_BOOL;
    else if constexpr (std::is_same_v<Type, char>)
        return TYPE_KIND_CHAR;
    else if constexpr (std::is_same_v<Type, void>)
        return TYPE_KIND_VOID;
    else if constexpr (std::is_pointer_v<Type>)
        return TYPE_KIND_POINTER;
    else if constexpr (std::is_array_v<Type>)
        return TYPE_KIND_ARRAY;
    else if constexpr (std::is_enum_v<Type>)
        return TYPE_KIND_ENUM;
    else if constexpr (std::is_compound_v<Type>)
        return TYPE_KIND_STRUCT;
}

Let’s also define a version that works with rvalues; to support literals kind_of(2); and object instances kind_of(T{});.

template <typename T>
inline static constexpr TYPE_KIND
kind_of(T &&)
{
    return kind_of<T>();
}

You can view and edit the source code on compiler explorer here.

In the next article we will discuss how to implement type_of<T>().