Last active
February 19, 2022 21:20
-
-
Save kebby/aeb8bda4422da3687364be85c13babff to your computer and use it in GitHub Desktop.
simple macro based system for adding property metadata to
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
// We want metadata for struct fields so we can have things like automatic serialization, | |
// validation and UI creation and stuff | |
struct PropertyDef { const char* name; }; | |
struct Property_int : PropertyDef { int& value; const int deflt = 0, min = INT_MAX, max = INT_MIN; }; | |
struct Property_float : PropertyDef { float& value; const float deflt = 0, min = FLT_MAX, max = FLT_MIN, step=0; }; | |
struct Property_bool : PropertyDef { bool& value; const bool deflt = false; }; | |
struct Property_String : PropertyDef { String& value; String deflt = ""; }; | |
struct Property_Vec2 : PropertyDef { Vec2& value; const Vec2 deflt = Vec2(0), min = Vec2(FLT_MAX), max = Vec2(FLT_MIN); }; | |
struct Property_Vec3 : PropertyDef { Vec3& value; const Vec3 deflt = Vec3(0), min = Vec3(FLT_MAX), max = Vec3(FLT_MIN); }; | |
struct Property_Vec4 : PropertyDef { Vec4& value; const Vec4 deflt = Vec4(0), min = Vec4(FLT_MAX), max = Vec4(FLT_MIN); }; | |
struct PropTable; | |
// oh look, it's the Visitor Pattern | |
struct PropVisitor | |
{ | |
virtual ~PropVisitor() {}; | |
virtual void Begin(const char *, int) {}; | |
virtual void End() {}; | |
virtual void Prop(const Property_int&) = 0; | |
virtual void Prop(const Property_float&) = 0; | |
virtual void Prop(const Property_bool&) = 0; | |
virtual void Prop(const Property_String&) = 0; | |
virtual void Prop(const Property_Vec2&) = 0; | |
virtual void Prop(const Property_Vec3&) = 0; | |
virtual void Prop(const Property_Vec4&) = 0; | |
virtual void Nest(const char *,PropTable&) = 0; | |
}; | |
// base class for structs with properties in it. Could use templates instead of OOP | |
// but it's nice to have the stuff at runtime. | |
struct PropTable | |
{ | |
virtual ~PropTable() {}; | |
virtual void Properties(PropVisitor&) = 0; | |
}; | |
// these macros create the Properties() override that calls the visitor for all declared fields | |
#define PROP_BEGIN(TT) typedef TT _TYPE; void Properties(PropVisitor& visitor) override { visitor.Begin(#TT, sizeof(TT)); | |
#define PROP(type, name, def) visitor.Prop(Property_##type { #name, name, def }); | |
#define PROP_MINMAX(type, name, def, min, max) visitor.Prop(Property_##type { #name, name, def, min, max }); | |
#define PROP_NESTED(name) visitor.Nest(#name, name); | |
#define PROP_END() visitor.End(); } | |
// test "serializer", just dumps the contents to the debug channel | |
struct PropVisitor_Dump : public PropVisitor | |
{ | |
void Begin(const char* name, int size) override { DPrintF("PropTable %s (%db):\n", name, size); }; | |
void End() override {}; | |
void Prop(const Property_int& p) override { DPrintF("- Int %s: %d\n", p.name, p.value); } | |
void Prop(const Property_float& p) override { DPrintF("- Float %s: %f\n", p.name, p.value); } | |
void Prop(const Property_bool& p) override { DPrintF("- Bool %s: %s\n", p.name, p.value?"true":"false"); } | |
void Prop(const Property_String& p) override { DPrintF("- String %s: %s\n", p.name, (const char*)p.value); } | |
void Prop(const Property_Vec2& p) override { auto v = p.value; DPrintF("- Vec2 %s: (%f, %f)\n", p.name, v.x, v.y); } | |
void Prop(const Property_Vec3& p) override { auto v = p.value; DPrintF("- Vec3 %s: (%f, %f, %f)\n", p.name, v.x, v.y, v.z); } | |
void Prop(const Property_Vec4& p) override { auto v = p.value; DPrintF("- Vec4 %s: (%f, %f, %, %f)\n", p.name, v.x, v.y, v.z, v.w); } | |
void Nest(const char *name, PropTable& t) override { DPrintF("- Nested %s: ", name); PropVisitor_Dump v; t.Properties(v); } | |
}; | |
// visitor that fills all fields with their default values | |
struct PropVisitor_SetDefaults : public PropVisitor | |
{ | |
void Prop(const Property_int& p) override { p.value = p.deflt; } | |
void Prop(const Property_float& p) override { p.value = p.deflt; } | |
void Prop(const Property_bool& p) override { p.value = p.deflt; } | |
void Prop(const Property_String& p) override { p.value = p.deflt; } | |
void Prop(const Property_Vec2& p) override { p.value = p.deflt; } | |
void Prop(const Property_Vec3& p) override { p.value = p.deflt; } | |
void Prop(const Property_Vec4& p) override { p.value = p.deflt; } | |
void Nest(const char* name, PropTable& t) override { PropVisitor_SetDefaults v; t.Properties(v); } | |
}; | |
// this is the important part - how to actually _use_ it. Syntax should be simple | |
// and boilerplate reduced to a minimum. | |
struct Inner : PropTable | |
{ | |
int yo1; | |
int yo2; | |
PROP_BEGIN(Inner) | |
PROP(int, yo1, 1); | |
PROP(int, yo2, 2); | |
PROP_END() | |
}; | |
struct MyProps : PropTable | |
{ | |
int someInt; | |
bool someBool; | |
float myFloat; | |
String lol; | |
Vec3 isthisacolor; | |
Inner inner; | |
PROP_BEGIN(MyProps) | |
PROP_MINMAX(int, someInt, 4, 0, 255); | |
PROP_MINMAX(float, myFloat, 31.2, 0, 255); | |
PROP(bool, someBool, true); | |
PROP(String, lol, "mydefault"); | |
PROP(Vec3, isthisacolor, Vec3(0.5)); | |
PROP_NESTED(inner); | |
PROP_END() | |
}; | |
// test function - create a structure, fill with defaults, dump | |
void MyTest() | |
{ | |
MyProps p {}; | |
PropVisitor_SetDefaults d; | |
PropVisitor_Dump v; | |
p.Properties(d); // set defaults | |
p.Properties(v); // dump | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment