Static reflection system for C++ - type_of - part 1

Now we are done with name_of<T>() and kind_of<T>(). Let’s start implementing type_of<T>(). Type First we need to define what Type is, we will start simple and store only the name, kind, size and align of the reflected type for now. struct Type { const char *name; TYPE_KIND kind; size_t size; size_t align; }; Later on we can expand it to store more information such as: pointee’s type info in case of pointers....

January 15, 2024 · 2 min · 368 words · Muhammad Abdulfattah

Static reflection system for C++ - kind_of

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{});....

March 20, 2023 · 2 min · 249 words · Muhammad Abdulfattah

Static reflection system for C++ - name_of - part 2

To fix the second issue, we will parse the type name string at runtime and generate a prettified and consistent one across major compilers. We will start by checking the template parameter type, if it is a primitive type, we can just return a string literal with the correct name, otherwise we parse it. inline static constexpr size_t REFLECT_MAX_NAME_LENGTH = 128; template <typename T> inline static constexpr const char * name_of() { if constexpr (std::is_same_v<T, int8_t>) return "i8"; else if constexpr (std::is_same_v<T, int16_t>) return "i16"; else if constexpr (std::is_same_v<T, int32_t>) return "i32"; else if constexpr (std::is_same_v<T, int64_t>) return "i64"; else if constexpr (std::is_same_v<T, uint8_t>) return "u8"; else if constexpr (std::is_same_v<T, uint16_t>) return "u16"; else if constexpr (std::is_same_v<T, uint32_t>) return "u32"; else if constexpr (std::is_same_v<T, uint64_t>) return "u64"; else if constexpr (std::is_same_v<T, float>) return "f32"; else if constexpr (std::is_same_v<T, double>) return "f64"; else if constexpr (std::is_same_v<T, bool>) return "bool"; else if constexpr (std::is_same_v<T, char>) return "char"; else if constexpr (std::is_same_v<T, void>) return "void"; else { constexpr auto _name_of = [](std::string_view type_name) -> const char * { // a static buffer to hold the prettified type name, it lasts for the duration of the application life time....

March 20, 2023 · 5 min · 1008 words · Muhammad Abdulfattah

Static reflection system for C++ - name_of - part 1

Unfortunately, C++ does not provide a way to get type names properly, at least in a consistent manner between major compilers; for example, using typeid(T).name() will return mangled names on GCC and Clang. Fortunately there is a workaround to solve this problem; compilers provide a macro definition that gives you the function name along with its arguments and template information at compile time. MSVC provides __FUNCSIG__, while GCC and Clang provide __PRETTY_FUNCTION__....

March 20, 2023 · 3 min · 523 words · Muhammad Abdulfattah

Static reflection system for C++

Recently I have been experimenting with a simple reflection library for C++. In this blog post series, I will share the details on how I implemented it. It is inspired by golang’s reflect package, although not feature rich like it unfortunately. If you want to take a look at the full implementation up ahead; you can find it here. What is reflection? It is the ability for a process to examine, introspect and modify its own structure and behaviour; more on Wikipedia....

March 20, 2023 · 3 min · 599 words · Muhammad Abdulfattah