Created
October 22, 2023 20:31
-
-
Save nlupugla/fc785b4a62f0babf3bdb62c262bd1e27 to your computer and use it in GitHub Desktop.
Godot Metatype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This gist details a way of representing type data in C++ that I have found useful for implementing structs. | |
* Rather than represent type data as local variables on an instance of some kind of TypeInfo struct, | |
* I represent each type via static methods on a corresponding metatype. Representing the data as metatypes | |
* allows the compiler to understand what our types mean and enables some cool template metaprogramming tricks. | |
* defined at compile time, which allows for many cool template metaprogramming tricks. */ | |
/* Here is an example of a struct whose members have been defined via metatypes. */ | |
struct NamedInt { | |
StringName name = StringName(); | |
struct MemberName { // A metatype that represents the member called "name" defined above | |
inline static const StringName get_name() { return SNAME("name"); } | |
inline static StringName get(const NamedInt &p_struct) { return p_struct.name; } | |
inline static const StringName get_default_value() { return StringName(); } | |
inline static void set(NamedInt &p_struct, const StringName &p_value) { p_struct.name = p_value; } | |
using Type = StringName; | |
inline static const Variant::Type get_variant_type() { return Variant::STRING_NAME; } | |
inline static const StringName get_class_name() { return StringName(); } | |
} | |
int value = 0; | |
struct MemberValue { // A metatype that represents the member called "value" defined above | |
inline static const StringName get_name() { return SNAME("value"); } | |
inline static int get(const NamedInt &p_struct) { return p_struct.value; } | |
inline static const int get_default_value() { return 0; } | |
inline static void set(NamedInt &p_struct, const int &p_value) { p_struct.value = p_value; } | |
using Type = int; | |
inline static const Variant::Type get_variant_type() { return Variant::INT; } | |
inline static const StringName get_class_name() { return StringName(); } | |
} | |
static const StringName get_struct_name() { return SNAME("NamedInt"); } | |
using Layout = StructLayout<NamedInt, MemberName, MemberValue>; // Representing the members as types allows me to pass them as template parameters. | |
NamedInt(const Array &p_array) { Layout::fill_struct(p_array, *this); } // This is a deserializtion function that can be autogenerated thanks to template metraprogramming. | |
}; | |
/* Here's a rough sketch of the StructLayout struct to give you an idea of some of the powerful metaprogramming you can do | |
* when type data is encoded in a metatype. */ | |
template <typename StructType, typename... StructMembers> | |
struct StructLayout { | |
static constexpr uint32_t struct_member_count = sizeof...(StructMembers); | |
inline static const StructInfo &get_struct_info() { | |
static const StringName names[struct_member_count] = { StructMembers::get_name()... }; | |
static const Variant::Type types[struct_member_count] = { StructMembers::get_variant_type()... }; | |
static const StringName class_names[struct_member_count] = { StructMembers::get_class_name()... }; | |
static const Variant default_values[struct_member_count] = { StructMembers::get_default_value_variant()... }; | |
return info; | |
} | |
static void fill_array(Array &p_array, const StructType &p_struct) { | |
p_array.resize(struct_member_count); | |
Variant vals[struct_member_count] = { StructMembers::get(p_struct)... }; | |
for (uint32_t i = 0; i < struct_member_count; i++) { | |
p_array.set(i, vals[i]); | |
} | |
} | |
static void fill_struct(const Array &p_array, StructType &p_struct) { | |
uint32_t i = 0; | |
int dummy[] = { 0, (StructMembers::set(p_struct, p_array.get(i)), i++, 0)... }; | |
(void)dummy; // Suppress unused variable warning | |
} | |
}; | |
/* In the above, I've left out some implementation details for the sake of simplicity. | |
* Also, it's worth mentioning that the metatypes above can be generated easily with simple macros. */ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment