refactor(autogen): changed the way props and instances are defined via macros
This commit is contained in:
parent
fc998a1c3f
commit
e5f543ef4a
15 changed files with 116 additions and 67 deletions
|
@ -137,10 +137,17 @@ std::string findBaseClass(CXCursor cur) {
|
|||
return baseClass;
|
||||
}
|
||||
|
||||
std::string currentCategory = "";
|
||||
void processField(CXCursor cur, ClassAnalysis* state) {
|
||||
std::optional<std::string> propertyDef = findAnnotation(cur, "OB::def_prop");
|
||||
if (!propertyDef) return;
|
||||
|
||||
// Update current category
|
||||
std::optional<std::string> categoryDef = findAnnotation(cur, "OB::def_prop_category");
|
||||
if (categoryDef) {
|
||||
currentCategory = parseAnnotationString(categoryDef.value())["category"];
|
||||
}
|
||||
|
||||
PropertyAnalysis anly;
|
||||
|
||||
auto result = parseAnnotationString(propertyDef.value());
|
||||
|
@ -150,6 +157,15 @@ void processField(CXCursor cur, ClassAnalysis* state) {
|
|||
anly.fieldName = fieldName;
|
||||
anly.category = result["category"];
|
||||
anly.onUpdateCallback = result["on_update"];
|
||||
|
||||
if (anly.category == "")
|
||||
anly.category = currentCategory;
|
||||
|
||||
// if name field is not provided, use fieldName instead, but capitalize the first character
|
||||
if (anly.name == "") {
|
||||
anly.name = fieldName;
|
||||
anly.name[0] = std::toupper(anly.name[0]);
|
||||
}
|
||||
|
||||
if (result.count("hidden"))
|
||||
anly.flags = anly.flags | PropertyFlags::PropertyFlag_Hidden;
|
||||
|
@ -234,6 +250,7 @@ void processClass(CXCursor cur, AnalysisState* state, std::string className, std
|
|||
anly.explorerIcon = result["explorer_icon"];
|
||||
|
||||
// Find annotated fields
|
||||
currentCategory = "";
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
if (kind != CXCursor_FieldDecl) return CXChildVisit_Continue;
|
||||
|
|
|
@ -2,26 +2,34 @@
|
|||
|
||||
// Markers for the autogen engine to generate getters, setters, lua, etc.
|
||||
|
||||
// Base macros
|
||||
#ifdef __AUTOGEN__
|
||||
#define def_inst(...) clang::annotate("OB::def_inst", #__VA_ARGS__)
|
||||
#define INSTANCE [[ def_inst() ]]
|
||||
#define INSTANCE_WITH(...) [[ def_inst(__VA_ARGS__) ]]
|
||||
#define INSTANCE_SERVICE(...) [[ def_inst(__VA_ARGS__, service) ]]
|
||||
|
||||
#define def_prop(...) clang::annotate("OB::def_prop", #__VA_ARGS__)
|
||||
|
||||
#define def_prop_category(...) clang::annotate("OB::def_prop_category", #__VA_ARGS__)
|
||||
#define cframe_position_prop(...) clang::annotate("OB::cframe_position_prop", #__VA_ARGS__)
|
||||
#define cframe_rotation_prop(...) clang::annotate("OB::cframe_rotation_prop", #__VA_ARGS__)
|
||||
#else
|
||||
#define def_inst(...)
|
||||
#define INSTANCE
|
||||
#define INSTANCE_WITH(...)
|
||||
#define INSTANCE_SERVICE(...)
|
||||
#define def_prop(...)
|
||||
#define def_prop_category(...)
|
||||
#define cframe_position_prop(...)
|
||||
#define cframe_rotation_prop(...)
|
||||
#endif
|
||||
|
||||
// Helper macros
|
||||
#define DEF_INST [[ def_inst() ]]
|
||||
#define DEF_INST_(...) [[ def_inst(__VA_ARGS__) ]]
|
||||
#define DEF_INST_ABSTRACT [[ def_inst(abstract) ]]
|
||||
#define DEF_INST_ABSTRACT_(...) [[ def_inst(__VA_ARGS__, abstract) ]]
|
||||
#define DEF_INST_SERVICE [[ def_inst(service) ]]
|
||||
#define DEF_INST_SERVICE_(...) [[ def_inst(__VA_ARGS__, service) ]]
|
||||
#define DEF_PROP [[ def_prop() ]]
|
||||
#define DEF_PROP_(...) [[ def_prop(__VA_ARGS__) ]]
|
||||
|
||||
// Categories
|
||||
#define DEF_PROP_CATEGORY(CATEGORY) [[ def_prop_category(category=CATEGORY) ]]
|
||||
|
||||
#define AUTOGEN_PREAMBLE \
|
||||
protected: \
|
||||
virtual result<PropertyMeta, MemberNotFound> InternalGetPropertyMeta(std::string name) override; \
|
||||
|
|
|
@ -13,7 +13,7 @@ class DataModel;
|
|||
class Service;
|
||||
|
||||
// The root instance to all objects in the hierarchy
|
||||
class INSTANCE_WITH(abstract) DataModel : public Instance {
|
||||
class DEF_INST_(abstract) DataModel : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
private:
|
||||
void DeserializeService(pugi::xml_node node);
|
||||
|
|
|
@ -31,7 +31,7 @@ enum HandlesType {
|
|||
RotateHandles,
|
||||
};
|
||||
|
||||
class INSTANCE_WITH(abstract) Handles : public Instance {
|
||||
class DEF_INST_(abstract) Handles : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
public:
|
||||
bool nixAxes = false; // XYZ -> ZXY, used with rotation
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
class Part;
|
||||
class Workspace;
|
||||
|
||||
class INSTANCE_WITH(abstract) JointInstance : public Instance {
|
||||
class DEF_INST_ABSTRACT JointInstance : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
std::weak_ptr<Part> oldPart0;
|
||||
|
@ -31,14 +31,10 @@ protected:
|
|||
virtual void breakJoint() = 0;
|
||||
public:
|
||||
|
||||
[[ def_prop(name="Part0", on_update=onUpdated) ]]
|
||||
std::weak_ptr<Part> part0;
|
||||
[[ def_prop(name="Part1", on_update=onUpdated) ]]
|
||||
std::weak_ptr<Part> part1;
|
||||
[[ def_prop(name="C0", on_update=onUpdated) ]]
|
||||
CFrame c0;
|
||||
[[ def_prop(name="C1", on_update=onUpdated) ]]
|
||||
CFrame c1;
|
||||
DEF_PROP_(on_update=onUpdated) std::weak_ptr<Part> part0;
|
||||
DEF_PROP_(on_update=onUpdated) std::weak_ptr<Part> part1;
|
||||
DEF_PROP_(on_update=onUpdated) CFrame c0;
|
||||
DEF_PROP_(on_update=onUpdated) CFrame c1;
|
||||
|
||||
JointInstance(const InstanceType*);
|
||||
~JointInstance();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
|
||||
class INSTANCE Rotate : public JointInstance {
|
||||
class DEF_INST Rotate : public JointInstance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
rp::HingeJoint* joint = nullptr;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
|
||||
class INSTANCE Snap : public JointInstance {
|
||||
class DEF_INST Snap : public JointInstance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
rp::FixedJoint* joint = nullptr;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
|
||||
class INSTANCE Weld : public JointInstance {
|
||||
class DEF_INST Weld : public JointInstance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
rp::FixedJoint* joint = nullptr;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "objects/annotation.h"
|
||||
#include "objects/base/service.h"
|
||||
|
||||
class INSTANCE_SERVICE() JointsService : public Service {
|
||||
class DEF_INST_SERVICE JointsService : public Service {
|
||||
AUTOGEN_PREAMBLE
|
||||
private:
|
||||
std::optional<std::shared_ptr<Workspace>> jointWorkspace();
|
||||
|
|
|
@ -28,7 +28,7 @@ struct PartConstructParams {
|
|||
|
||||
class Snap;
|
||||
|
||||
class INSTANCE_WITH(explorer_icon="part") Part : public Instance {
|
||||
class DEF_INST_(explorer_icon="part") Part : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
protected:
|
||||
// Joints where this part is Part0
|
||||
|
@ -50,36 +50,33 @@ protected:
|
|||
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) override;
|
||||
void onUpdated(std::string);
|
||||
public:
|
||||
[[ def_prop(name="Velocity", on_update=onUpdated) ]]
|
||||
Vector3 velocity;
|
||||
DEF_PROP_CATEGORY(DATA)
|
||||
DEF_PROP_(on_update=onUpdated) Vector3 velocity;
|
||||
[[ def_prop(name="CFrame", on_update=onUpdated), cframe_position_prop(name="Position"), cframe_rotation_prop(name="Rotation") ]]
|
||||
CFrame cframe;
|
||||
[[ def_prop(name="Size", category=PART, on_update=onUpdated) ]]
|
||||
glm::vec3 size;
|
||||
[[ def_prop(name="Color", category=APPEARANCE) ]]
|
||||
Color3 color;
|
||||
[[ def_prop(name="Transparency", category=APPEARANCE) ]]
|
||||
float transparency = 0.f;
|
||||
|
||||
DEF_PROP_CATEGORY(PART)
|
||||
DEF_PROP_(on_update=onUpdated) glm::vec3 size;
|
||||
|
||||
DEF_PROP_CATEGORY(APPEARANCE)
|
||||
DEF_PROP Color3 color;
|
||||
DEF_PROP float transparency = 0.f;
|
||||
bool selected = false;
|
||||
|
||||
[[ def_prop(name="Anchored", category=BEHAVIOR, on_update=onUpdated) ]]
|
||||
bool anchored = false;
|
||||
[[ def_prop(name="Locked", category=BEHAVIOR) ]]
|
||||
bool locked = false;
|
||||
rp::RigidBody* rigidBody = nullptr;
|
||||
DEF_PROP_CATEGORY(BEHAVIOR)
|
||||
DEF_PROP bool anchored = false;
|
||||
DEF_PROP bool locked = false;
|
||||
|
||||
[[ def_prop(name="TopSurface", category=SURFACE) ]]
|
||||
SurfaceType topSurface = SurfaceType::SurfaceStuds;
|
||||
[[ def_prop(name="BottomSurface", category=SURFACE) ]]
|
||||
SurfaceType bottomSurface = SurfaceType::SurfaceInlets;
|
||||
[[ def_prop(name="LeftSurface", category=SURFACE) ]]
|
||||
SurfaceType leftSurface = SurfaceType::SurfaceSmooth;
|
||||
[[ def_prop(name="RightSurface", category=SURFACE) ]]
|
||||
SurfaceType rightSurface = SurfaceType::SurfaceSmooth;
|
||||
[[ def_prop(name="FrontSurface", category=SURFACE) ]]
|
||||
SurfaceType frontSurface = SurfaceType::SurfaceSmooth;
|
||||
[[ def_prop(name="BackSurface", category=SURFACE) ]]
|
||||
SurfaceType backSurface = SurfaceType::SurfaceSmooth;
|
||||
DEF_PROP_CATEGORY(SURFACE)
|
||||
DEF_PROP SurfaceType topSurface = SurfaceType::SurfaceStuds;
|
||||
DEF_PROP SurfaceType bottomSurface = SurfaceType::SurfaceInlets;
|
||||
DEF_PROP SurfaceType leftSurface = SurfaceType::SurfaceSmooth;
|
||||
DEF_PROP SurfaceType rightSurface = SurfaceType::SurfaceSmooth;
|
||||
DEF_PROP SurfaceType frontSurface = SurfaceType::SurfaceSmooth;
|
||||
DEF_PROP SurfaceType backSurface = SurfaceType::SurfaceSmooth;
|
||||
|
||||
|
||||
rp::RigidBody* rigidBody = nullptr;
|
||||
|
||||
inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); }
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "objects/base/instance.h"
|
||||
#include <memory>
|
||||
|
||||
class INSTANCE_WITH(explorer_icon="script") Script : public Instance {
|
||||
class DEF_INST_(explorer_icon="script") Script : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
public:
|
||||
Script();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "objects/base/service.h"
|
||||
#include "lua.h"
|
||||
|
||||
class INSTANCE_SERVICE() ScriptContext : public Service {
|
||||
class DEF_INST_SERVICE ScriptContext : public Service {
|
||||
AUTOGEN_PREAMBLE
|
||||
protected:
|
||||
void InitService() override;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// Container class for server scripts
|
||||
// Also handles/manages running server scripts on run
|
||||
class INSTANCE_SERVICE() ServerScriptService : public Service {
|
||||
class DEF_INST_SERVICE ServerScriptService : public Service {
|
||||
AUTOGEN_PREAMBLE
|
||||
protected:
|
||||
void InitService() override;
|
||||
|
|
|
@ -31,7 +31,7 @@ class Rotate;
|
|||
|
||||
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
||||
|
||||
class INSTANCE_SERVICE(explorer_icon="workspace") Workspace : public Service {
|
||||
class DEF_INST_SERVICE_(explorer_icon="workspace") Workspace : public Service {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
rp::PhysicsWorld* physicsWorld = nullptr;
|
||||
|
|
|
@ -44,10 +44,12 @@ Finally, create a build target that depends on your `AUTOGEN_OUTS`, and make `AL
|
|||
Autogen is an annotation processor, it largely only interacts with C++ annotations (`[[ ... ]]`).
|
||||
It uses libClang to parse through the header and look for classes annotated with `OB::def_inst`. In `annotation.h`, there are various aliases for this command:
|
||||
|
||||
- `INSTANCE` - Simply annotate with `def_inst`, do not do anything more. Equivalent to `[[ def_inst() ]]`
|
||||
- `INSTANCE_WITH(...)` - Same as above, but allows you to pass in arguments to `def_inst`. Equivalent to `[[ def_inst(...) ]]`
|
||||
- `INSTANCE_SERVICE(...)` - Same as above, but adds `, service` to the end of the argument list, marking the instance as a service.
|
||||
Equivalent to `[[ def_inst(..., service) ]]`
|
||||
- `DEF_INST` - Simply annotate with `def_inst`, do not do anything more. Equivalent to `[[ def_inst() ]]`
|
||||
- `DEF_INST_(...)` - Same as above, but allows you to pass in arguments to `def_inst`. Equivalent to `[[ def_inst(...) ]]`
|
||||
- `DEF_INST_SERVICE` - Annotates with `def_inst`, but passes `service` as an argument, marking the instance as a service.
|
||||
- `DEF_INST_SERVICE_(...)` - Same as above, but lets you add additional arguments
|
||||
- `DEF_INST_ABSTRACT - Annotates with `def_isnt`, but passes `abstract`
|
||||
- `DEF_INST_ABSTRACT_(...) - Same as above, but let's you pass additional arguments
|
||||
|
||||
Then, there is the command itself:
|
||||
|
||||
|
@ -71,7 +73,7 @@ Properties are also a key component. Inside every class annotated with `def_inst
|
|||
|
||||
Here are its parameters:
|
||||
|
||||
- `name` - Required parameter, this is the name of the property itself. The name of the field is not taken into consideration (subject to change)
|
||||
- `name` - Option, name of the property. If this is not specified, it will use the field's name, with the first letter capitalized
|
||||
- `hidden` - Flag, marks the property as hidden from the editor.
|
||||
- `no_save` - Flag, the property should not be deserialized nor serialized
|
||||
- `readonly` - Flag, the property cannot be assigned to
|
||||
|
@ -80,6 +82,11 @@ Here are its parameters:
|
|||
|
||||
The type of the property, and conversion to and from it and the datatype system is automatically inferred. `std::string` is interpreted as `Data::String`, and `std::weak_ptr<T>` is also converted to/from `Data::InstanceRef`. In the future, if weird edge-case types are introduced, the code generator may need to be extended. See [Extending Autogen](#extending-autogen)
|
||||
|
||||
Similarly to `def_inst`, `def_prop` also has its aliases:
|
||||
|
||||
- `DEF_PROP` - Annotates the field with `def_prop`. Equivalent to `[[ def_prop() ]]`
|
||||
- `DEF_PROP_(...)` - Same as above, but allows you to pass in arguments. Equivalent to `[[ def_prop(...) ]]`
|
||||
|
||||
In Part, it is necessary to expose the position and rotation components of the CFrame as properties, so there are two commands for this case (these should be added alongside `def_prop` on the CFrame property):
|
||||
|
||||
cframe_position_prop(name=)
|
||||
|
@ -87,26 +94,50 @@ In Part, it is necessary to expose the position and rotation components of the C
|
|||
|
||||
They simply create properties of the component with the specified name. They will inherit the category and update callback from the CFrame property, and will be flagged with NOSAVE. A getter/setter is automatically generated which generates a new CFrame with the specific component changed for you.
|
||||
|
||||
Setting the category of each can be cumbersome and annoying. Rather than setting it on each field, you can use `DEF_PROP_CATEGORY` to apply the specified
|
||||
category to the current field and all that follow it.
|
||||
|
||||
Sometimes, you will need to reference another object in your header. For instance, if you use a specific object type for a `weak_ptr` (e.g.
|
||||
`std::weak_ptr<Part>`). Doing so with an `#include` will unnecessarily add a dependency into the build chain for that translation unit. Instead, you may
|
||||
opt to use a forward-declaration before your class declaration to avoid this overhead.
|
||||
|
||||
However, Autogen cannot tell where this class came from to automatically include its header in the generated file. So, you must include its header in an `#ifdef`
|
||||
block via `__AUTOGEN_EXTRA_INCLUDES__`:
|
||||
|
||||
#ifdef __AUTOGEN_EXTRA_INCLUDES__
|
||||
#include "objects/part.h"
|
||||
#endif
|
||||
|
||||
class Part;
|
||||
|
||||
class DEF_INST SomeObject {
|
||||
AUTOGEN_PREAMBLE
|
||||
public:
|
||||
DEF_PROP std::weak_ptr<Part> myPart;
|
||||
}
|
||||
|
||||
Autogen will automatically define `__AUTOGEN_EXTRA_INCLDUES__` before including `SomeObject`'s header file, so the `#ifdef` block will evaluate, pulling in
|
||||
the necessary dependencies
|
||||
|
||||
### Example
|
||||
|
||||
Here is an example of an instance annotated using Autogen
|
||||
|
||||
class INSTANCE Part {
|
||||
class DEF_INST Part {
|
||||
AUTOGEN_PREAMBLE
|
||||
public:
|
||||
[[ def_prop(name="Color") ]]
|
||||
Color3 color;
|
||||
DEF_PROP_CATEGORY("APPEARANCE")
|
||||
DEF_PROP Color3 color;
|
||||
DEF_PROP CFrame cframe;
|
||||
|
||||
[[ def_prop(name="CFrame"), cframe_position_prop(name="Position") ]]
|
||||
CFrame cframe;
|
||||
|
||||
[[ def_prop(name="ReadOnly", readonly) ]]
|
||||
DEF_PROP_CATEGORY("DATA")
|
||||
DEF_PROP_(name="ReadOnly", readonly)
|
||||
int readOnlyValue;
|
||||
|
||||
[[ def_prop(name="Ephemeral", no_save) ]]
|
||||
DEF_PROP_(no_save)
|
||||
std::string ephemeral;
|
||||
|
||||
[[ def_prop(name="SuperSecretValue", no_save, hidden) ]]
|
||||
DEF_PROP_(name="SuperSecretValue", no_save, hidden)
|
||||
std::string superSecret;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue