Files
2025-11-18 17:47:54 +01:00
..
2025-11-05 12:02:58 +01:00
2025-04-30 15:08:45 +02:00
2025-10-31 17:25:03 +01:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00
2025-10-31 17:25:03 +01:00
2025-04-17 11:19:17 +03:00
2025-04-30 15:08:45 +02:00
2025-04-15 16:35:42 +03:00
2025-10-31 17:25:03 +01:00
2025-11-18 17:47:54 +01:00
2025-10-31 17:25:03 +01:00
2025-04-30 15:08:45 +02:00
2025-11-05 12:02:58 +01:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00
2025-04-30 15:08:45 +02:00

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 KeyedObjectContainers, 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

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:

#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:

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

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:

#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

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 for more details), like here:

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:

SortedVector<UpcastablePointer<MyAbstractType>>

Reference members

Members can also specify references like this:

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

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):

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 for more details).

With the base out of the way, adding derived structs is as easy as making normal structs (see here) with an extra inherits: <ParentClass> property.

For example:

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.