Advanced Types¶
In addition to Inlet’s primitive types (bool, int, double, string), user-defined types and functions can also be defined as part of an input file.
In this section, we first describe how Individual Structs can be added to our schemas. We then extend it to Arrays and Dictionaries of Structs.
Individual Structs¶
Defining And Storing¶
To add a single (i.e., not array) user-defined type to the input file, use the addStruct
method of the Inlet or Container classes to add a Container (collection of Fields and sub-Containers)
that will represent the fields of the struct.
Consider a simple Lua table that contains only primitive types, whose definition might look like:
car = {
make = "BestCompany",
seats = 2,
horsepower = 200,
color = "blue"
}
or in YAML, something like:
car:
make: BestCompany
seats: 2
horsepower: 200
color: blue
Its Inlet schema can be defined as follows:
struct Car
{
std::string make;
std::string color;
int seats;
int horsepower;
// A function can be used to define a schema for a particular struct
// For convenience, it can be implemented as a static method of the struct
static void defineSchema(inlet::Container& car_schema)
{
car_schema.addString("make", "Make of car");
car_schema.addString("color", "Color of car").defaultValue("red");
car_schema.addInt("seats", "Number of seats").range(2, 7);
car_schema.addInt("horsepower", "Amount of horsepower");
}
};
This would be used by creating an inlet::Container
for the Car
instance and then defining the struct
schema on that subcontainer, e.g.:
// Create a container off the global container for the car object
// then define its schema
auto& car_schema = inlet.addStruct("car", "Vehicle description");
Car::defineSchema(car_schema);
Note
The definition of a static defineSchema
member function is not required, and is just used
for convenience. The schema definition for a class or struct could also be implemented as a
free function for third-party types, or even in the same place as the sub-container declaration.
Accessing¶
In order to convert from Inlet’s internal representation to a custom C++ struct
, you must provide
deserialization logic. This is accomplished by a specialization of the FromInlet<T>
functor for your
type T
, which implements a member function with the signature T operator()(const inlet::Container&)
.
This function should return an instance of type T
with its members populated with the corresponding
fields in the input file. For the simple Car
example whose schema is defined above, the specialization
might look like:
template <>
struct FromInlet<Car>
{
Car operator()(const inlet::Container& input_data)
{
Car result;
result.make = input_data["make"];
result.color = input_data["color"];
result.seats = input_data["seats"];
result.horsepower = input_data["horsepower"];
return result;
}
};
In the above snippet, Container::operator[]
is used to extract data from Inlet’s internal representation
which is automatically converted to the correct member variable types when the function’s return value is
constructed. This conversion does not happen automatically for user-defined types.
If a Car
object as defined above is located at the path “car” within the input file, it can be retrieved as follows:
Car car = inlet["car"].get<Car>();
Arrays and Dictionaries of Structs¶
Arrays of user-defined types are also supported in Inlet.
Defining And Storing¶
Consider a collection of cars, described in Lua as:
fleet = {
{
make = "Globex Corp",
seats = 3,
horsepower = 155,
color = "green"
},
{
make = "Initech",
seats = 4,
horsepower = 370
-- uses default value for color
},
{
make = "Cyberdyne",
seats = 1,
horsepower = 101,
color = "silver"
}
}
or in YAML, as:
fleet:
- make: Globex Corp
seats: 3
horsepower: 155
color: green
- make: Initech
seats: 4
horsepower: 370
# uses default value for color
- make: Cyberdyne
seats: 1
horsepower: 101
color: silver
First, use the addStructArray
function to create a subcontainer, then define the schema on that container
using the same Car::defineSchema
used above:
// Create a fleet of cars with the same Car::defineSchema
auto& fleet_schema = inlet.addStructArray("fleet", "A collection of cars");
Car::defineSchema(fleet_schema);
Note
The schema definition logic for a struct is identical between individual instances of structs
and arrays of structs. The distinction is made by Container
on which the struct schema is
defined - specifically, whether it is obtained via addStruct
or addStructArray
.
Associative arrays are also supported, using string keys or a mixture of string and integer keys.
The addStructDictionary
function can be used analogously to the addStructArray
function
for these associative arrays.
Note
Although many of Inlet’s input file languages do not distinguish between a “dictionary” type and a “record” type, Inlet treats them differently for type safety reasons:
Dictionaries use arbitrary strings or integers for their keys, and their values (entries)
can only be retreived as a homogeneous type. In other words, dictionaries must map to
std::unordered_map<Key, Value>
for fixed key and value types.
Structs contain a fixed set of named fields, but these fields can be of any type.
As the name suggests, these map to structs
in C++.
Accessing¶
As with the schema definition, the FromInlet
specialization for a user-defined type will work for both
single instances of the type and arrays of the type.
To retrieve an array of structs as a contiguous array of user-defined type, use std::vector
:
auto fleet = base["fleet"].get<std::vector<Car>>();
Some input file languages support non-contiguous array indexing, so you can also retrieve arrays
as std::unordered_map<int, T>
:
auto fleet = inlet["fleet"].get<std::unordered_map<int, Car>>();
Note
If a non-contiguous array is retrieved as a (contiguous) std::vector
, the elements will be
ordered by increasing index.
String-keyed dictionaries are implemented as std::unordered_map<std::string, T>
and can be retrieved
in the same way as the array above. For dictionaries with a mix of string and integer keys, the
inlet::VariantKey
type can be used, namely, by retrieving a std::unordered_map<inlet::VariantKey, T>
.