Files
revng-revng/include/revng/Model/README.md
Ivan Krysak cc57e57fd1 TTG: remove support for optional: true fields
After this commit, every non-key field is treated as if it was optional
while every key field (plus every auto-generated `Kind` field) - as if
it was required.
2025-10-13 18:33:10 +03:00

218 lines
6.3 KiB
Markdown

## 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: <Name>
type: enum
doc: <Documentation entry about the enum itself>
members:
- name: <Member_1_Name>
doc: <Documentation entry about the first enum member>
- name: <Member_2_Name>
doc: <Documentation entry about the second enum member>
```
to the corresponding schema (in this case `include/revng/Model/model-schema.yml`), and making a new header:
```cpp
#pragma once
// <copyright notice>
#include "revng/Model/Generated/Early/<Name>.h"
// <feel free to add other related definitions here>
#include "revng/Model/Generated/Late/<Name>.h"
```
**Note**: The header must be named `<Name>.h`, where `<Name>` 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 <Name> {
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: <Name>
type: struct
doc: <Documentation entry about the struct itself>
fields:
- name: <Field1_name>
type: <Field1_type>
doc: <Documentation entry about the first field>
- name: <Field2_name>
type: <Field2_type>
doc: <Documentation entry about the second field>
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
// <copyright notice>
// 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/<AnotherName>.h"
#include "revng/Model/Generated/Early/<Name>.h"
class model::<Name> : public model::generated::<Name> {
public:
// Note: don't forget to inherit constructors from autogenerated class
using model::generated::<ClassName>::<ClassName>;
// 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/<Name>.h"
```
**Note**: The header must be named `<Name>.h`, where `<Name>` 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<UpcastablePointer<MyAbstractType>>
```
#### 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<model::TypeDefinition, model::Binary>
```
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 `<Name>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: <ParentClass>` 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.