Skip to content

Instantly share code, notes, and snippets.

@kebby
Last active February 19, 2022 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kebby/aeb8bda4422da3687364be85c13babff to your computer and use it in GitHub Desktop.
Save kebby/aeb8bda4422da3687364be85c13babff to your computer and use it in GitHub Desktop.
simple macro based system for adding property metadata to
// 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