Making component libraries

This tutorial is for ARCS1. A knowledge of Qt development is needed in order to follow this tutorial.

The theory

In order to make a component library, you will have to follow a few steps :

  1. develop component's source code ;
  2. create a specific source file for the library ;
  3. create a project file (to be processed by qmake) ;
  4. compile the whole thing.

Creating component source file

ARCS1 components are nothing less than objects instantiated from classes inheriting from QObject of the Qt API. Therefore, their declaration is similar to classes inheriting from QObject that would implement signals and slots. Those last ones would form the public interface of the component.

Here is how a component's header file should look like :

#include <qobject.h>
 class MyComponent : public QObject
 {
    Q_OBJECT
    public :
       MyComponent(QObject* parent=0, const char* name=0) : QObject(parent, name) { ... }
    public slots :
       void gogogo();
    signals :
       void gone();
 }

Here the QOBJECT macro is necessary in order to enable signal and slot precompiling. Slots (component inputs) are declared using the keywords public slots. Signals (component outputs) are declared in a section called signals.

Please also notice that the constructor should conform to the shown signature.

Creating component library source file

Once components have been developed, they must be stored in a shared library in order to instantiate them at runtime. The body of this library can be put in a separate C++ source file.

Its structure would be :

#include <metalibrary/metalibrarytoolkit.h>
// .... other headers
METALIB_BEGIN
METALIB_OBJECT(ComponentName)
// .... other components
METALIB_END

The header metalibrary/metalibrarytoolkit.h declares several required macros that are useful to create the library. Then the files that declares components will be listed.

The next code block is introducing the actual library body. It starts with the macro METALIB_BEGIN. The next following lines should contain the macro METALIB_OBJECT followed by the names of the components that would be made available through the library. This list should end with the METALIB_END macro signaling the end of the library's body.

Creating a project

When using Qt, a project file is a textual file with the extension .pro. Some important lines should be given in order to properly configure the project.

unix:  TEMPLATE = lib
win32: TEMPLATE = vclib

TARGET = library_name

CONFIG = qt thread debug
win32: CONFIG+= dll extensions

INCLUDEPATH += path_to_metalibrarytoolkit_header

The first two lines will enable the build of a library, with a Makefile under linux and a project for Visual Studio under Microsoft Windows.
The next line gives the library name.
The next two line are adjusting configuration parameters, including specifics for Windows.
The last line is mandatory in order to compile the project: it helps to find the headers that are necessary to compile the project and make a component library.

Compiling the library

When using Linux, you just have to execute the following lines in the library directory:

qmake
make

When using Windows, you will have to run qmake in order to generate a Visual Studio project. Then you can open it and run its compilation.

A practical example

This example is also distributed with the source code of ARCS1. It is located in the sample directory. It follows the steps described above and is completed by an example of an application that should test and run these components.

The components

We will start by a functional description of the components and then disclose their source code.

Functional description

The Boucle component

It is actually a component wrapping a loop. This component, once initialized with a given number of iterations, will iterate over a loop for the given number of iterations (slot setIterations()). Each iteration in this loop will :

  1. display a message giving the current iteration number ;
  2. emit a signal that gives the current iteration number (signal newIteration()).

Once the loop has ended, another signal is emitted (signal sendToken()).

The DisplayInt component

This component contains a slot that takes an integer as an input (slot display()). This number is then displayed on standard output.

Source code

The source code for the two components is found in two different files: one header and one body.

The header file ''boucle.h''

This one will contain the declaration for the two components as follows :

boucle.h
#include <qobject.h>
#include <qstring.h>
 
class Boucle: public QObject
{
    Q_OBJECT
    public:
        Boucle(QObject* parent = 0, const char* name = 0);
    public slots:
        void setIterations(int n);
    signals:
        void newIteration(int i);
        void sendToken(QString s);
};
 
class DisplayInt: public QObject
{
    Q_OBJECT
    public:
        DisplayInt(QObject* parent = 0, const char* name = 0);
    public slots:
        void display(int i);
};
The body file ''boucle.cpp''

It will contain the actual implementation of components. That is to say, constructors and slots instructions (signals do not have any actual implementation).

boucle.cpp
#include "boucle.h"
#include <iostream>
 
Boucle::Boucle(QObject* obj, const char* name) : QObject(obj, name)
{ /*Constructeur, ici vide*/ }
 
void Boucle::setIterations(int n)
{
    for (int i = 0; i < n ; i++)
    {
        std::cout << "Emitting iteration " << i << std::endl;
        emit newIteration(i); // Emission d'un signal
    }
    emit sendToken("end");
}
 
DisplayInt::DisplayInt(QObject* obj, const char* name) : QObject(obj, name)
{}
 
void DisplayInt::display(int i)
{
    std::cout << "Recieved integer " << i << std::endl;
}

Making the library

In order to make the library, we will need another source file to describe the library and a project file.

Additional library source code ''lib.cpp''

lib.cpp
#include <metalibrary/metalibrarytoolkit.h>
#include "boucle.h"
 
METALIB_BEGIN
METALIB_OBJECT(Boucle)
METALIB_OBJECT(DisplayInt)
METALIB_END

Project file ''boucle.pro''

This one will be parsed by qmake in order to generate a Makefile.

boucle.pro
unix:  TEMPLATE  = lib
win32: TEMPLATE = vclib
 
TARGET = boucle
 
HEADERS = boucle.h
SOURCES = boucle.cpp
SOURCES+= lib.cpp
 
CONFIG = qt thread release 
win32: CONFIG += dll exceptions

Test and run the components

In order to test and run the components, we will have to create an application description file. It will be parsed bu the ARCS1 engine in order to load components from libraries, instantiate and configure them.

Application description (''boucle.xml'')

Here is the xml description of an application. Two components are declared: B which is of the Boucle type and D which is of the DisplayInt type. Then signal newIteration() from B is connected to slot display() of D. The whole application is started by setting in postconnection the slot setIterations() to a given value.

boucle.xml
<?xml version="1.0"?>
<!DOCTYPE application SYSTEM "../xml/application.dtd">
<application>
        <libraries>
                <library name="./boucle"/>
        </libraries>
        <objects>
                <object classname="Boucle" id="B"/>
                <object classname="DisplayInt" id="D"/>
        </objects>
        <sheets>
                <sheet id="S">
                        <tokensender object="B"/>
                        <connection>
                                <wire objsource="B" signal="newIteration(int)" objdest="D" slot="display(int)"/>
                        </connection>
                        <postconnection>
                                <init object="B" slot="setIterations(int)" type="int" value="10"/> 
                        </postconnection>
                </sheet>
                <sheet id="E"/>
        </sheets>
        <statemachine terminal="E">
                <transition stepA="S" stepB="E" token="end"/>
        </statemachine>
</application>

The final test

Once the xml file written, all that is needed is to pass its path as a parameter of the ARCS1 engine which is called metaruntime.

bash-3.1$ metaruntime boucle.xml 
[META] document has not the right number of <defines> markup
Waiting 1s before launching runtime ...
Launching Runtime, go go go !
==========================================================
Emitting iteration 0
Recieved integer 0
Emitting iteration 1
Recieved integer 1
Emitting iteration 2
Recieved integer 2
Emitting iteration 3
Recieved integer 3
Emitting iteration 4
Recieved integer 4
Emitting iteration 5
Recieved integer 5
Emitting iteration 6
Recieved integer 6
Emitting iteration 7
Recieved integer 7
Emitting iteration 8
Recieved integer 8
Emitting iteration 9
Recieved integer 9
Emitting iteration 0
Recieved integer 0

Each line started with Emitting is written by B and each line starting with Recieved is written by D.