Declaring the Model
When building a Flip compatible application, the first task is defining your model. The Model
is a set of declarations that will be used together in a concrete Document
. In this sense, the Model can be seen as a blueprint or template of the actual Documents you will make with.
To help you understand the Flip principles, this guide considers the following application :
- Your company is called
acme
and is making the applicationproduct
- The application is a simple audio application
- The application manipulates songs
- A song has a global tempo value, representing the number of beats per minutes
- A song has an ordered list of tracks
- Each track has a set of clips, which are implicitely ordered through their position
- Each clip has a position and a duration, expressed in beats
- They are two kind of clips : audio clips which contain an audio sample and "MIDI" clips which contain musical events
Declaring the Model
Every class managed by flip needs to be part of a model. Therefore you first begin by declaring this model in your header files, and defines its revision string. The revision string represents a particular revision of the model itself, and is used when converting documents from one version to another.
#include "flip/DataModel.h" |
class Model : public flip::DataModel <Model> {}; |
void declare () |
{ |
Model::version ("1.0"); |
} |
Declaring a Class
Every class managed by flip will need to inherit from flip::Object
, so the first thing is to inherit from it.
We will also need to declare the class before we can use it, so that we will add a static
declare
member.
Every model has exactly one root class. We could say that the root class, which represents the top level document class, is the song, and modelize this like the following example.
#include "flip/Object.h" |
class Song : public flip::Object |
{ |
public: |
static void declare (); |
}; |
void Song::declare () |
{ |
Model::declare <Song> ().name ("acme.product.Song"); |
} |
Finally the function declare
in the section above would now look like :
void declare () |
{ |
Model::version ("1.0"); |
Song::declare (); |
} |
Adding the first Flip Member
In our example, the tempo is a global property of the song. To modelize that, we can simply make the tempo a floating point member of the song and write this like in the following example.
#include "flip/Float.h" |
#include "flip/Object.h" |
class Song : public flip::Object |
{ |
public: |
static void declare (); |
flip::Float tempo; |
}; |
void Song::declare () |
{ |
Model::declare <Song> () |
.name ("acme.product.Song") |
.member <flip::Float, &Song::tempo> ("tempo"); (1) |
} |
- Add the typed
Float
,tempo
Song
member, and give it the name"tempo"
Important: Names are used for document writing, and are also used in conversions. As such, carefull naming of members is important.
Adding the Flip Containers
Flip provides different kinds of containers.
- One is a strictly linearly ordered sequence container, called
Array
which is a template class. It is similar to the C++ standard library containerstd::list
orstd::vector
- Another one is an unordered container, called
Collection
which is a template class. It is similar to the C++ standard library containerstd::set
- Another is a one element polymorphic container, called
Variant
which is a template class. It is similar to the boost template classboost::variant
- Another is a zero or one element polymorphic container, called
Optional
which is a template class. It is similar to the boost template classboost::optional
- Another is a key/value container, called
Map
which is a template class.
container.
The clips are implictely specified as implicitely ordered by their position. In this case, an Array
or Collection
could be used, but generally implicitely ordered elements are better modelized using a Collection
.
The tracks contains a list of parameters, and those parameters name are known at runtime. So we will use a Map
container.
We then will modelize the track using a Track
class, and the clip using a Clip
class. The song contains tracks, and a track contains clips. This can be modelized as in the following example.
#include "flip/Array.h" |
#include "flip/Collection.h" |
#include "flip/Float.h" |
#include "flip/Object.h" |
class Clip : public flip::Object |
{ |
public: |
static void declare (); |
flip::Float position; |
flip::Float duration; |
}; |
class Parameter : public flip::Object |
{ |
public: |
static void declare (); |
flip::Float value; |
}; |
class Track : public flip::Object |
{ |
public: |
static void declare (); |
flip::Collection <Clip> |
clips; |
flip::Map <Parameter> |
parameters; |
}; |
class Song : public flip::Object |
{ |
public: |
static void declare (); |
flip::Float tempo; |
flip::Array <Track> |
tracks; |
}; |
void Clip::declare () |
{ |
Model::declare <Clip> () |
.name ("acme.product.Clip") |
.member <flip::Float, &Clip::position> ("position") |
.member <flip::Float, &Clip::duration> ("duration"); |
} |
void Parameter::declare () |
{ |
Model::declare <Parameter> () |
.name ("acme.product.Parameter") |
.member <flip::Float, &Parameter::value> ("value"); |
} |
void Track::declare () |
{ |
Model::declare <Track> () |
.name ("acme.product.Track") |
.member <flip::Collection <Clip>, &Track::clips> ("clips") |
.member <flip::Map <Parameter>, &Track::parameters> ("parameters"); |
} |
void Song::declare () |
{ |
Model::declare <Song> () |
.name ("acme.product.Song") |
.member <flip::Float, &Song::tempo> ("tempo") |
.member <flip::Array <Track>, &Song::tracks> ("tracks"); |
} |
Finally the whole model declaration, the function declare
in the section above, would now look like :
void declare () |
{ |
Model::version ("1.0"); |
Clip::declare (); |
Parameter::declare (); |
Track::declare (); |
Song::declare (); |
} |
Inheritance
Flip provides inheritance features. It allows you to declare a Flip class as inherited from another Flip class. Inheritance provides parent class members importing as well as C++ polymorphism.
In our example, the clip is specified as being one of two kind : either a clip containing audio or a clip containing musical events.
Both type of clips share the same main clip properties position and duration. In the case of the example, we can introduce ClipAudio
and ClipMidi
which allows to support those two behaviors.
class ClipAudio : public Clip (1) |
{ |
public: |
static void declare (); |
flip::String sample_url; |
}; |
void ClipAudio::declare () |
{ |
Model::declare <MySubClass> () |
.name ("acme.product.MySubClass") |
.inherit <MyClass> () (2) |
.member <flip::String, &ClipAudio::sample_url> ("sample_url"); |
} |
Then declaring the flip class will look like the following example.
void declare () |
{ |
Model::version ("1.0"); |
Clip::declare (); |
ClipAudio::declare (); |
ClipMidi::declare (); |
... code skipped ... |
} |
Important: Make sure that the base class is declared before.
Flip Types
Every class managed by flip may contain an unlimited number of either Flip basic types or your own custom Flip classes.
Flip offers categories of basic types :
- Value types that hold a value
- Container types
- Reference type
- Signaling type
Value Types
flip::Bool
holds a boolean valueflip::Int
holds a 64-bit integer valueflip::Float
holds a 64-bit floating point valueflip::Enum
holds anenum
valueflip::Blob
holds a dynamic-sized opaque blob of dataflip::String
holds a string similar tostd::string
Container Types
flip::Array
is a strictly linear ordered sequence container similar tostd::list
orstd::vector
flip::Collection
is an unordered container similar tostd::set
flip::Variant
is an one element container similar toboost::variant
flip::Optional
is a zero or one element container similar toboost::optional
flip::Vector
is a strictly linear ordered sequence container, oriented for performance to the detriment of concurrency, similar tostd::list
orstd::vector
flip::Map
is a key/value container, when the keys could be theorically be known in advance, but is only available at runtime, similar tostd::map
Reference Type
flip::ObjectRef
holds a reference to another object in the model tree. It is similar to a pointer
Signaling Type
flip::Message
allows to signal an object with arbitrary non-persistent data
The next chapter, Organizing your Code will guide you through the task of organizing the model code.