Skip to content

Instantly share code, notes, and snippets.

@nlupugla
Created October 22, 2023 20:31
Show Gist options
  • Save nlupugla/fc785b4a62f0babf3bdb62c262bd1e27 to your computer and use it in GitHub Desktop.
Save nlupugla/fc785b4a62f0babf3bdb62c262bd1e27 to your computer and use it in GitHub Desktop.
Godot Metatype
/* 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