Workspace 7.0.2
Including existing data types


The following are suggested pre-reading for this tutorial:


Tutorial contents


Files used in this tutorial


Tutorial goals

In the previous tutorial, you learned how to create your own custom data type. However, you may have an existing class and want to include it in Workspace as a custom data type. Or you may want to use a C++ STL class or a built-in data type from an external third-party library. These data types do not need to extend CSIRO::DataExecution::ObjectGroup like the previous example. This tutorial shows how to import exisitng data types.


Importing a C++ STL class

In the following example we will learn how to import a class from the C++ STL. We will import std::deque from the C++ STL. For most data types, we really only have to add two lines of code to import the class into Workspace: the DECLARE and DEFINE macros.

The best way to understand this is to see it in action, so let's have a look at this new header file:

#ifndef CSIRO_IMPORTDATATYPEPLUGIN_STLTYPE_H
#define CSIRO_IMPORTDATATYPEPLUGIN_STLTYPE_H
#include <queue>
#include "importdatatypeplugin.h"

The macro:

DECLARE_WORKSPACE_DATA_FACTORY(std::deque<double>, CSIRO_IMPORTDATATYPEPLUGIN_API)
#define DECLARE_WORKSPACE_DATA_FACTORY(T, WORKSPACE_EXPORT_SYMBOL)
Definition: datafactorytraits.h:782

tells us that std::deque will be made available to Workspace.

DEFINE_WORKSPACE_DATA_FACTORY(std::deque<double>, CSIRO::ImportDataTypePlugin::getInstance())
#define DEFINE_WORKSPACE_DATA_FACTORY(T, P)
Definition: typeddatafactory.h:1426

The DEFINE_WORKSPACE_DATA_FACTORY or DEFINE_WORKSPACE_DATA_FACTORY_NO_QMETATYPE macro should just about always be at the end of the file and it must sit outside any namespaces. Most importantly, it must never appear in a header file. The macro associates your custom class with your plugin and adds a data factory for it to Workspace. It even takes care of handling whether or not your class supports serialization. It should be used as follows:

  • The first parameter should be the name of the class and will be used to identify the class for which a Workspace data type is being defined. Do not try to use any namespace "using" directives to shorten this name, as it must be unique among all data types that Workspace will see from all plugins. This name is also used as the name of the class when shown to the user in the case of any such clashes.
  • The second parameter for the DEFINE macro is the plugin instance the data type belongs to. This second parameter will just be the getInstance() function of your plugin.

Importing QSerialPort

In the following example we will learn how to import a class from a third-party library. We will import QSerialPort from the Qt library. As demonstrated above in Importing a C++ STL class, for most data types, we really only have to add two lines of code to import the class into Workspace: the DECLARE and DEFINE macros.

Let's have a look at this header file:

#ifndef CSIRO_IMPORTDATATYPEPLUGIN_SERIALPORT_H
#define CSIRO_IMPORTDATATYPEPLUGIN_SERIALPORT_H
#include <QSerialPort>
#include "importdatatypeplugin.h"
namespace CSIRO
{
namespace DataExecution
{
template<>
struct CloneDataType<QSerialPort>
{
enum
{
};
};
template<>
struct AssignDataType<QSerialPort>
{
enum
{
};
};
} // namespace DataExecution
} // namespace CSIRO
DECLARE_WORKSPACE_DATA_FACTORY_NO_QMETATYPE(QSerialPort, CSIRO_IMPORTDATATYPEPLUGIN_API)
#endif
#define DECLARE_WORKSPACE_DATA_FACTORY_NO_QMETATYPE(T, WORKSPACE_EXPORT_SYMBOL)
Definition: datafactorytraits.h:798
Top level namespace for all Workspace code.
Definition: applicationsupportplugin.cpp:32
@ Supported
Definition: datafactorytraits.h:563
@ Supported
Definition: datafactorytraits.h:470

The QSerialPort disables the use of copy constructors and assignment operators. Hence we need to explicitly add:

struct CloneDataType<QSerialPort>
{
enum
{
Supported = 0
};
};
template<>
struct AssignDataType<QSerialPort>
{
enum
{
Supported = 0
};
};

The macro:

DECLARE_WORKSPACE_DATA_FACTORY_NO_QMETATYPE(QSerialPort, CSIRO_IMPORTDATATYPEPLUGIN_API)

tells us that QSerialPort will be made available to Workspace except that no QMetaType support is added for the type T (QSerialPort in this case). This macro must be used if T refers to a type for which Qt already provides QMetaType support (built in C++ types and Qt classes). It must also be used if T does not support cloning, since the QMetaType system requires types to be clonable. In this example we can tell that QSerialPort does not support cloning by the presence of the macro Q_DISABLE_COPY(QSerialPort). Q_DISABLE_COPY disables the use of copy constructors and assignment operators for the given class. Another way to recognize this is to check the code for the copy constructor and assignment operator. If the copy constructor and assignment operator are declared in the private section, the class is not clonable and cannot be assigned.

class MyClass : public QObject
{
private:
MyClass(const MyClass &);
MyClass &operator=(const MyClass &);
};

The CPP file, serialport.cpp contains the DEFINE macro:

DEFINE_WORKSPACE_DATA_FACTORY_NO_QMETATYPE(QSerialPort, CSIRO::ImportDataTypePlugin::getInstance())
#define DEFINE_WORKSPACE_DATA_FACTORY_NO_QMETATYPE(T, P)
Definition: typeddatafactory.h:1440

Updating the plugin.cpp and CMakeLists files

We also need to modify the plugin CPP file (importdatatypeplugin.cpp) and add the headers and source files to the CMakeLists.txt files.

#include "serialport.h"
#include "stltype.h"

Include the header files for the newly imported data types.

bool ImportDataTypePlugin::setup()
{
// Add your data factories like this:
//addFactory( CSIRO::DataExecution::DataFactoryTraits<MyDataType>::getInstance() );
addFactory(CSIRO::DataExecution::DataFactoryTraits<std::deque<double>>::getInstance());
// Add your operation factories like this:
//addFactory( CSIRO::DataExecution::OperationFactoryTraits<MyOperation>::getInstance() );
// Add your widget factories like this:
//addFactory( MyNamespace::MyWidgetFactory::getInstance() );
// Add your adaptor factories like this:
//addFactory( CSIRO::DataExecution::SimpleAdaptorFactory<MyDataType, OtherDataType>::getInstance());
return true;
}
Traits class for data objects of type T.
Definition: datafactorytraits.h:143

Add the data factories for the new data types to this plugin.

find_package(Qt5SerialPort)
set(QT_LIBRARIES Qt5::Core;Qt5::Widgets;Qt5::SerialPort)
set(CMAKE_AUTOMOC ON)
set(HEADERS
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/importdatatypeplugin_api.h
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/importdatatypeplugin.h
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/serialport.h
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/stltype.h
)
set(INSTALL_HEADERS
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/importdatatypeplugin_api.h
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/importdatatypeplugin.h
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/serialport.h
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/stltype.h
)
set(MOC_HEADERS
)
set(SOURCES
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/importdatatypeplugin.cpp
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/serialport.cpp
${IMPORTDATATYPEPLUGIN_SOURCE_DIR}/stltype.cpp
)
set(UI_SOURCES

In this case we need a new Qt package - Qt5SerialPort. Add this to the find_package() directive in CMakeLists.txt and the QT_LIBRARIES. Also add the new .h and .cpp files for the new data types.

Build the plugin again as per the instruction in Writing a Simple Workspace Plugin.

The data types can now be used.

std::deque as a Workspace data type
QSerialPort as a Workspace data type

Summary

This tutorial has introduced you to the essentials of including an existing class in Workspace as a custom data type.


Next steps

The following is suggested for further reading: