## How to use `tuple_tree_generator` `tuple_tree_generator` takes a description of a struct/enum and generates boilerplate required to make them YAML serializable/deserializable, compatible with `KeyedObjectContainer`s, and so on. This document explains how to add an enum or a class to rev.ng model. **Note**: Never include the generated headers anywhere apart from the corresponding _hand-written_ header, which is the only file that should be included anywhere else (even in other headers generated from the same schema). ### Defining an enum Enums can be defined by adding ```yaml name: type: enum doc: members: - name: doc: - name: doc: ``` to the corresponding schema (in this case `include/revng/Model/model-schema.yml`), and making a new header: ```cpp #pragma once // #include "revng/Model/Generated/Early/.h" // #include "revng/Model/Generated/Late/.h" ``` **Note**: The header must be named `.h`, where `` is the same across the schema and the header internals (namely the `Early` and `Late` include statements). The autogenerated header file will contain something similar to this: ```cpp namespace { enum Values { Invalid = 0 Member_1_Name = 1; Member_2_Name = 2; // . . . }; } ``` Note that an `Invalid` and `Count` members are **always** present, you don't need to add them manually. You can refer to the enum with `EnumName::Values` and to its values with `EnumName::Values::MemberName` or `EnumName::MemberName`. ### Defining a struct Structs can be defined by adding ```yaml name: type: struct doc: fields: - name: type: doc: - name: type: doc: key: # List the fields that uniquely identify an object inside a container here. - Index # These are also used for building the appropriate `TupleTreeReference` -- a "path" inside the tuple tree. ``` to the corresponding schema (in this case `include/revng/Model/model-schema.yml`), and making a new header: ```cpp #pragma once // // Include the dependencies of this new type here: #include "revng/ADT/Something.h" // Note that you can use *wrapper* headers generated from the same schema. Their order in the schema does *not* matter. #include "revng/Model/.h" #include "revng/Model/Generated/Early/.h" class model:: : public model::generated:: { public: // Note: don't forget to inherit constructors from autogenerated class using model::generated::::; // You can also add any extra methods you want here, // but no fields are allowed - please use the scheme for those. }; #include "revng/Model/Generated/Late/.h" ``` **Note**: The header must be named `.h`, where `` is the same across the schema and the header internals (namely the class name, as well as `Early` and `Late` include statements). #### Sequence members You can define a member composed of a sequence of items as follows ```yaml name: MyStruct type: struct doc: Did you know `doc` is a required field everywhere it's available? fields: - name: MyArray doc: | Please don't use non-KOC containers in the model and other tuple trees that take advantage of invalidation. sequence: type: SortedVector elementType: int ``` The sequence type must be instantiable with one type parameter (the element type)! Which means you cannot currently use `std::pair` and friends. Note that, if the specified `elementType` has `abstract: true` set in its *class* definition (see [here](#upcastable-structs) for more details), like here: ```yaml name: MyStruct type: struct doc: Did you know `doc` is a required field everywhere it's available? fields: - name: MyArray doc: | Please don't use non-KOC containers in the model and other tuple trees that take advantage of invalidation. sequence: type: SortedVector elementType: MyAbstractType # <-- note the different type ``` the sequence will automatically be promoted to an upcastable pointer. In this case: ```cpp SortedVector> ``` #### Reference members Members can also specify references like this: ```yaml name: MyStruct type: struct doc: Did you know `doc` is a required field everywhere it's available? fields: - name: MyRef doc: | These references are polymorphic, feel free to use abstract types, like here. referenceTo: model::TypeDefinition ``` This gets translated in C++ as a ```cpp TupleTreeReference ``` where `model::Binary` is the root type of the `TupleTree` this struct is a part of. #### Upcastable structs It is possible to define polymorphic classes. To do so, first define a common base class (like `Type`). This class has to be marked with the attribute `abstract`. **Note**: this will mean that the class cannot be instantiated on its own. If that's desirable, please add an inheritor that doesn't add any extra fields to use instead. Example (`TypeDefinition.h`): ```YAML name: TypeDefinition type: struct abstract: true # <-- notice this key: - Kind # . . . fields: - name: Kind type: model::TypeDefinitionKind::Values # . . . ``` Note that the `Kind` field is required! You will get a compilation time error if one is not present. Also, the `Kind` enum is automatically added to the schema (so no need to do so manually). **Note**: It's only added to the schema, you still have to make a header for it (see [how to define an enum](#defining-an-enum) for more details). With the base out of the way, adding derived structs is as easy as making normal structs (see [here](#defining-a-struct)) with an extra `inherits: ` property. For example: ```yaml name: StructDefinition type: struct inherits: TypeDefinition # <-- notice this fields: # . . . ``` Note, that derived objects will contain all the fields of their parent, so the same names must not be used again.