D1. Creating a new C++ Module

D1. Creating a new C++ Module

This tutorial contains short instructions on how to set up a new C++ module for Syntalos.

1. Building Syntalos

The easiest way to add a new C++ module is to build it with Syntalos itself, as building it out-of-tree is not very well tested yet.

Follow the instructions to build Syntalos from source in the Syntalos installation instructions. You can also open the Syntalos sources in QtCreator or any C/C++ IDE you prefer.

Quick build instructions for Debian/Ubuntu:

sudo apt install \
    git ca-certificates \
    python3-pip \
    cmake \
    gettext \
    libaravis-dev \
    libavcodec-dev \
    libavformat-dev \
    libavutil-dev \
    libegl-dev \
    libeigen3-dev \
    libglib2.0-dev \
    libgstreamer1.0-dev \
    libgstreamer-plugins-base1.0-dev \
    libkf5archive-dev \
    libkf5texteditor-dev \
    libqtermwidget5-1-dev \
    libvips-dev \
    libopencv-dev \
    libpipewire-0.3-dev \
    libqt5opengl5-dev \
    libiceoryx-introspection-dev \
    libqt5serialport5-dev \
    libqt5svg5-dev \
    libswscale-dev \
    libusb-1.0-0-dev \
    libv4l-dev \
    libxml2-dev \
    libxxhash-dev \
    libsystemd-dev \
    systemd-dev \
    meson \
    ninja-build \
    ocl-icd-opencl-dev \
    pybind11-dev \
    python3-dev \
    python3-numpy \
    qtbase5-dev \
    qtmultimedia5-dev \
    udev \
    uuid-dev
mkdir build && cd build
meson ..
ninja
sudo ninja install

Once you can build Syntalos, you can set up your new C++ module. Do not hesitate to ask questions in case you run into any issues and file an issue or ask in Discussions on GitHub.

2. Copy a Template

The easiest way to start building a new module is to copy a template. A minimal C++ module exists in the form of example-cpp. Copy its directory and rename it to your chosen ID name. A module ID name must be unique and can not be changed once it is chosen, because otherwise existing configurations would break. It uniquely identifies your module, and may only contain alphanumeric characters, dashes and underscores.

3. Adjust Metadata

Rename the C++ files if you want to and adjust any naming in meson.build. Also, ensure your new module directory is included in a subdir() directive in the toplevel modules/meson.build file.

Then, open the examplemodule.cpp file (or what it was renamed to) and scroll to the bottom. You will find these lines, implementing virtual methods of a ModuleInfo class:

QString ExampleCppModuleInfo::id() const
{
    return QStringLiteral("example-cpp");
}

QString ExampleCppModuleInfo::name() const
{
    return QStringLiteral("C++ Module Example");
}

QString ExampleCppModuleInfo::description() const
{
    return QStringLiteral("Most basic module, a starting place to develop a new C++ module.");
}

bool ExampleCppModuleInfo::devel() const
{
    return true;
}

AbstractModule *ExampleCppModuleInfo::createModule(QObject *parent)
{
    return new ExampleCppModule(parent);
}

By removing the ExampleCppModuleInfo::devel() function, you can declare your module as proper module and not just development-aid. Adjust all other values accordingly as well to give your module a nice, human-readable name and description. The module icon is automatically picked up from an SVG file of the same name as your module in the module directory. Alternatively, it can also be generated by C++ code.

The most important method of a ModuleInfo is the createModule(QObject *parent) method. It is invoked when your module is added to a workspace and creates a new instance of your own module class that inherits from ÀbstractModule, in this case ExampleCppModule defined above.

4. Write your code

You will find these lines in the constructor of your new module class:

m_frameIn = registerInputPort<Frame>(QStringLiteral("frames-in"), QStringLiteral("Frames In"));
m_frameOut = registerOutputPort<Frame>(QStringLiteral("frames-out"), QStringLiteral("Frames Out"));

These register the ports that this module supports. The template argument is the type a port accepts/emits, followed by its unique name and human-redable name as parameters. The register(In|Out)putPort will return a refernce to the port that you can use later.

Refer to the AbstractModule API documentation for more information on the methods you can implement to add features to your module. Most usable API for modules can be found in the moduleapi.h header, and is documented there.

The example C++ module also has some inline comments, explaining the purpose of certain functions. If you get stuck while developing a module, do not hesitate to ask for help.

TODO: Add more explanations.

5. Test

Once you recompile Syntalos and run it, your module should automatically show up in the module selection dialog. Once your module addition is ready, we would be happy to merge it into the main Syntalos tree and release it with Syntalos for ease of future use and to make maintenance of new modules easier.