QFace IDL syntax

This page explains the basic usage of the QFace IDL. A more detailed description of the library can be found on its page

QFace (Qt interface language) is an Interface Description Languge (IDL). While it is primarily designed to define an interface between Qt, QML and C++, it is intended to be flexible enough also to be used in other contexts.

The IDL

The IDL uses common API concepts such as modules, interfaces, properties, structs and enums/flags. Additionally it knows about lists and models. A list is an array of primitive or complex types. A model is an indicator for large data sets which are typical used via a defined API (e.g. pagination).

module org.example 1.0

interface Echo {
    string message;
    void echo(string message);
    signal broadcast(string message);
    Status status;
}

enum Status {
    Null, Loading, Ready, Error
}

The data types provided by QFace can be divided into primitive and complex types:

Primitive Types

  • bool
  • int
  • real
  • string
  • var

Complex Types

  • Interface
  • Struct
  • Enum
  • Flag
  • Array
  • Model

The language as such does not provide any support for maps or dictionaries. The reason for not providing a map container type is that keys in dictionaries requires a hash which can not always be guaranteed to be available in complex types.

Grammar

The grammar of QFace is well defined and is based on the concepts of modules as a larger collection of information.

A module can have several interfaces, structs and/or enums/flags.

module <module> <version>
import <module> <version>

interface <Identifier> {
    [readonly] <type> <identifier>
    <type> <operation>(<parameter>*)
    signal <signal>(<parameter>*)
}

struct <Identifier> {
    <type> <identifier>;
}

enum <Identifier> {
    <name> = <value>,
}

flag <Identifier> {
    <name> = <value>,
}

A QFace document always describes one module. Each document can contain one or more interfaces, structs, flags or enums. Each document can import other modules using the import statement.

Module

A module is identified by its name. The name should normally be a URI where all parts are lowercase (e.g. entertainment.tuner). A module may import other modules with the primary purpose being to ensure that dependencies are declared inside the QFace file.

// org.example.qface
module org.example 1.0

import org.common 1.0

Interface

An interface is a collection of properties, operation and signals. Properties carry data, whereas the operations normally modify the data. Signals are used to notify the user of changes.

interface WeatherStation {
    real temperature;
    void reset();
    signal error(string message);
}

Struct

The struct is supposed to serve as a container to transport structured data. It supports neither properties nor operations.

Property

Interfaces and structures data are carried by properties: syntax elements allowing to describe some attributes of the entity. A property can be of any type, known to IDL. It can be marked as readonly, in which case this attribute of the interface is not supposed to be written to from the outside code. It's up to the generator to enforce this constraint.

Enum/Flag

Enums and flags are the concepts known from many popular programming languages (C++,Java,etc). They differ only in what values they can take: enums are allowed to take only a single value, whereas flags can be an OR-ed combination of multiple values.

Types

Types are either local and can be referenced simply by their name, or they are from an external module in which case they need to be referenced with the fully qualified name (module + '.' + name). A type can be an interface, struct, enum or flag.

A module consists of either one or more interfaces, structs and enums/flags. They can come in any number or combination. The interface is the only type which can contain operations and signals. A struct is merely a container to transport structured data. Enum and flags allows the user to encode information used inside the struct or interface as datatype.

The QFace library does not allow to extend interfaces. It is by design kept simple.

Below is an example of a QFace file.

module entertainment.tuner 1.0;

import common 1.0

interface Tuner {
    // property currentStation
    readonly Station currentStation;
    // operation nextStation
    void nextStation();
    // operation previousStation
    void previousStation();
    // operation updateCurrentStation
    void updateCurrentStation(int stationId);

    list<int> primitiveList;
    list<Station> complexList;
    model<int> primitiveModel;
    model<Station> complexModel;
}

Annotations

Annotations is a way to add meta information to your interface definition. It is available to each symbol in the interface.

Annotations allows an interface author to extend the existing interface with additional meta information, called tags, aka annotations. One or several annotations can precede a module, interface, struct or enum. They are also allowed before an operation, property or signal. Everywhere where a documentation comment is allowed you can also add annotations.

An annotation looks like this:

@service: {port: 12345}
interface Tuner {
}

An in code annotation precedes a symbol and it starts with an @ sign. A symbol can have more than one one annotation line. Each line should be one individual annotation. The content is YAML content. All @ signs preceding a symbol are collected and then evaluated using a YAML parser.

For larger annotations one can use the external annotation document feature.

@singleton: yes
@data: [1,2,3]
@config: { values: [LEFT, RIGHT, TOP] }

This will be result into a YAML content of

singleton: yes
data: [1,2,3]
config: { values: [LEFT, RIGHT, TOP] }

And the result as Python object would be

{
  "data": [ 1, 2, 3 ],
  "singleton": true,
  "config": {
    "values": [ "LEFT", "RIGHT", "TOP" ]
  }
}

Annotation Documents

QFace allows also to specify these annotations in external documents using the YAML syntax. For this you need to create a document with the same name as the QFace document but with the extension .yaml. It should have roughly the following format

com.pelagicore.ivi.Tuner:
    service:
      port: 12345

On the root level should be a fully qualified name of a symbol. The symbol will be looked up and the following annotation information merged with the existing annotations from the QFace document.

Merging Annotations

The external annotations will be merged on top of the embedded annotations on per symbol base. Dictionaries will be merged. If a merge can not be done then the external document based annotations will override the embedded annotations.

The annotation are available later when navigating the domain model.

{% if "service" in interface.tags %}
interface {{interface}} is served on port: {{interface.tags.service.port}}
{% else %}
interface {{interface}} is not served
{% endif %}

Note: QFace does not specify specific annotations, but defines just the annotation format. The set of annotations supported must be defined and documented by the generator.

Domain Model

As a result of parsing the IDL document, a domain model object must be created. The domain model resembles the structure of our system as objects. It is build by the parser and serves as the input into the generator.

The IDL is converted into an in memory domain model (see qface/idl/domain.py)

- System
    - Module
        - Import
        - Interface
              - Property
              - Operation
              - Event
        - Enum
        - Flag
        - Struct
              - Property

The domain model is the base for the code generation. You traverse the domain tree and trigger your own code generation.

Detailed description of QFace library API is found on the library page

© 2020 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.