This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
arcs2:creating_a_component_library [2013/08/20 16:44] didier |
arcs2:creating_a_component_library [2013/08/21 16:08] (current) didier [Embedding components into a library] |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Creating a component library ====== | ||
+ | For this tutorial, we will only work at creating native ARCS components. | ||
+ | We will describe how we design components and store them into libraries. | ||
+ | To begin with, ARCS is heavily relying on Qt, so component description | ||
+ | will share many similarities with the writing of customized | ||
+ | [[http://qt-project.org/doc/qt-5.1/qtcore/qobject.html|QObjects]] | ||
+ | with | ||
+ | specific [[http://qt-project.org/doc/qt-5.1/qtcore/signalsandslots.html|signals and slots]]. | ||
+ | We will document the steps needed to produce a library like the ''sample'' one. | ||
+ | |||
+ | ===== Designing components ===== | ||
+ | We will write two components. The first one will be a **Loop** which take as a parameter an iteration number | ||
+ | and that will execute its iterations. Each iteration will emit a signal indicating the current iteration. | ||
+ | At the end, the component will emit a particular signal in order to indicate the loop is finished. | ||
+ | The second component will receive an integer value and display it in console (component named **DisplayInt**). | ||
+ | The declaration of these components should be as follows: | ||
+ | <file cpp sample.h> | ||
+ | #include <QObject> | ||
+ | |||
+ | class Loop : public QObject // required public inheritance to QObject | ||
+ | { | ||
+ | Q_OBJECT // Mandatory macro to create custom signals and slots | ||
+ | public: | ||
+ | // Constructor with a specific required signature | ||
+ | Loop(QObject* parent=0) : QObject(parent) {} | ||
+ | |||
+ | public slots: // start of slot declarations | ||
+ | void setIterations(int n); // gives the number of iterations to perform | ||
+ | |||
+ | signals: // start of signals declarations | ||
+ | void newIteration(int i); // signal emitted at each iteration | ||
+ | void sendToken(QString s); // signal emitted at the end of the loop | ||
+ | }; | ||
+ | |||
+ | class DisplayInt : public QObject | ||
+ | { | ||
+ | Q_OBJECT | ||
+ | public: | ||
+ | DisplayInt(QObject* parent=0) : QObject(parent) {} | ||
+ | |||
+ | public slots: | ||
+ | void display(int i); // slot that will display the integer passed as a parameter | ||
+ | }; | ||
+ | </file> | ||
+ | The implementation of such components will not be a problem and will unfold quite naturally. | ||
+ | <file cpp sample.cpp> | ||
+ | #include "sample.h" // header declaring components | ||
+ | #include <iostream> | ||
+ | |||
+ | void Loop::setIterations(int n) | ||
+ | { | ||
+ | for (int i=0; i < n; i++) | ||
+ | { | ||
+ | std::cout << "[Loop] Emitting iteration " << i << std::endl; | ||
+ | emit newIteration(i); // emit trigger the corresponding signal | ||
+ | } | ||
+ | emit sendToken("end"); | ||
+ | } | ||
+ | |||
+ | void DisplayInt::display(int i) | ||
+ | { | ||
+ | std::cout << "[DInt] Received integer " << i << std::endl; | ||
+ | } | ||
+ | </file> | ||
+ | |||
+ | ===== Embedding components into a library ===== | ||
+ | Once components are created, the library will be easy to make. | ||
+ | You will have to run once the executable ''arcslibmaker'' | ||
+ | (you may find more information on [[http://arcs.ibisc.univ-evry.fr/doc/arcslibmaker.html|arcslibmaker manpage]]). | ||
+ | If files were in a folder named ''sample'', ''arcslibmaker'' | ||
+ | will create a Qt project file named ''sample.pro'' that includes | ||
+ | source files as well as special directives in order to compile the component library. | ||
+ | Moreover, a file named ''libsample.alx'' will be generated and you will have to | ||
+ | edit it. If necessary, you can also edit the project file (in this case, you may refer to | ||
+ | the complete documentation of the [[http://qt-project.org/doc/qt-5.1/qtdoc/qmake-manual.html|qmake tool]]). | ||
+ | <hidden click to display project file contents ''sample.pro''> | ||
+ | <code bash> | ||
+ | win32-msvc* { | ||
+ | TEMPLATE = vclib | ||
+ | } else { | ||
+ | TEMPLATE = lib | ||
+ | } | ||
+ | INCLUDEPATH += $$(ARCSDIR)/include | ||
+ | LIBS += -L$$(ARCSDIR)/lib -larcs | ||
+ | CONFIG += dll | ||
+ | QT = core | ||
+ | |||
+ | # source files to compile | ||
+ | HEADERS += sample.h | ||
+ | SOURCES += sample.cpp | ||
+ | |||
+ | # ARCS specific rules | ||
+ | ALXFILE = libsample.alx | ||
+ | OTHER_FILES += libsample.alx | ||
+ | arcslibrary.output = alm_${QMAKE_FILE_BASE}.cpp | ||
+ | arcslibrary.input = ALXFILE | ||
+ | arcslibrary.commands = arcslibmaker ${QMAKE_FILE_NAME} | ||
+ | arcslibrary.variable_out = SOURCES | ||
+ | QMAKE_EXTRA_COMPILERS += arcslibrary | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | First, we will have a look at the file ''libsample.alx'' which is a XML formatted document | ||
+ | describing with a few markups component libraries. In this file, two sections will be of | ||
+ | interest: | ||
+ | * the ''headers'' section, where we will put which header files are declaring components; | ||
+ | * the ''components'' section, where we will tell the actual components that the library will store. | ||
+ | Our library contains two components of types **Loop** and **DisplayInt** declared in a header file named | ||
+ | ''sample.h''. Therefore, our file ''libsample.alx'' should be edited as follows: | ||
+ | <file xml libsample.alx> | ||
+ | <library> | ||
+ | <headers> | ||
+ | <header name="sample.h"/> | ||
+ | </headers> | ||
+ | <components> | ||
+ | <component name="Loop"/> | ||
+ | <component name="DisplayInt"/> | ||
+ | </components> | ||
+ | </library> | ||
+ | </file> | ||
+ | |||
+ | Thanks to the rules automatically put in the project file, the utility ''arcslibmaker'' will be able to parse the file | ||
+ | ''libsample.alx'' and generate C++ wrappers for the component library in a file named ''alm_libsample.cpp''. | ||
+ | <hidden Click here to display ''alm_libsample.cpp''> | ||
+ | <code cpp> | ||
+ | #include<arcs/arcslibtoolkit.h> | ||
+ | #include<QMetaType> | ||
+ | #include<sample.h> | ||
+ | |||
+ | extern "C" DLL_EXPORT void arcsLibraryRegister(ARCSComponentMap* cmap, ARCSFamilyMap* , ARCSTypeMap* ) | ||
+ | { | ||
+ | cmap->insert("Loop",new ARCSNativeComponentTemplate<Loop>()); | ||
+ | cmap->insert("DisplayInt",new ARCSNativeComponentTemplate<DisplayInt>()); | ||
+ | } | ||
+ | |||
+ | extern "C" DLL_EXPORT void arcsLibraryUnregister(ARCSComponentMap* cmap, ARCSFamilyMap* , ARCSTypeMap* ) | ||
+ | { | ||
+ | delete cmap->take("Loop"); | ||
+ | delete cmap->take("DisplayInt"); | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | ===== Compiling the library ===== | ||
+ | One call to ''qmake'' will generate the appropriate ''Makefile'' (or MS Visual studio projects). You will then be able | ||
+ | to call ''make'' in order to compile the library. | ||
+ | |||
+ | ===== The complete workflow ===== | ||
+ | To sum it up, the complete workflow is as follows: | ||
+ | - prepare component source files; | ||
+ | - launch ''arcslibmaker''; | ||
+ | - edit XML files that were produced by ''arcslibmaker'' in order to tell what are the components; | ||
+ | - launch ''qmake'' to generate a ''Makefile'' under Unix like systems; | ||
+ | - compile the library using ''make''. | ||
+ | |||