Skip to content

Creating an Empty Plugin

SJulianS edited this page Jun 3, 2024 · 8 revisions

To create an empty plugin, you first need to create a new empty folder within the hal/plugins folder and then set up the initial file structure. For illustration, we want to create a plugin called example. Hence, we create the following files and folders following the unwritten HAL plugin naming conventions:

plugins
└──example
   ├── CMakeLists.txt
   ├── include
   │   └── example
   │       └── plugin_example.h
   └── src
       └── plugin_example.cpp

Before we start implementing the plugin's intended functionality, we need to prepare the CMakeLists.txt, plugin_example.h, and plugin_example.cpp files so that they implement to the HAL plugin API and thereby allow our plugin to be treated as such by HAL.

Let's start with the CMakeLists.txt. It registers the plugin with HAL whenever cmake is called and makes sure that it is build alongside HAL (if the respective cmake options are enabled; more on this later). The contents of the file should look like this:

option(PL_EXAMPLE "PL_EXAMPLE" ON)

if(PL_EXAMPLE OR BUILD_ALL_PLUGINS)
    file(GLOB_RECURSE EXAMPLE_INC ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
    file(GLOB_RECURSE EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
    hal_add_plugin(example
                   SHARED
                   HEADER ${EXAMPLE_INC}
                   SOURCES ${EXAMPLE_SRC}
    )
endif()

The ON in the first line will enable the plugin by default. If you do not expect many users will need this plugin, set this to OFF so that your plugin is only build if the user explicitly asks for it to be build. See Building HAL for how to enable/disable individual plugins during the build process. Disabling plugins when building HAL helps keep the build times low. The lines with hal_add_plugin actually register the plugin with the HAL build system, which relies on cmake to coordinate the build process.

Now that we have this figured out, let's take care of plugin_example.h. This file is the header that actually implements the HAL plugin API and makes sure that HAL can interact with the plugin. As a bare minimum, a name and a version must be provided for the plugin. We also strongly encourage to give a description and explicitly define dependencies on other plugins, even if none exist. The way this is done is by creating a class, e.g., ExamplePlugin, that inherits from BasePluginInterface and overwrites the respective methods as in the example below. All of this needs to be done within the hal namespace and should be properly documented.

/**
 * @file plugin_example.h 
 * @brief This file contains all functions related to the HAL plugin API.
 */

#pragma once

#include "hal_core/plugin_system/plugin_interface_base.h"

namespace hal
{
    /**
     * @class ExamplePlugin
     * @brief Plugin interface for the example plugin.
     * 
     * This class provides an interface to integrate the example plugin as a plugin within the HAL framework.
     */
    class PLUGIN_API ExamplePlugin : public BasePluginInterface
    {
    public:
        /** 
         * @brief Default constructor for `ExamplePlugin`.
         */
        ExamplePlugin() = default;

        /** 
         * @brief Default destructor for `ExamplePlugin`.
         */
        ~ExamplePlugin() = default;

        /**
         * @brief Get the name of the plugin.
         *
         * @returns The name of the plugin.
         */
        std::string get_name() const override;

        /**
         * @brief Get the version of the plugin.
         *
         * @returns The version of the plugin.
         */
        std::string get_version() const override;

        /**
         * @brief Get a short description of the plugin.
         *
         * @returns The short description of the plugin.
         */
        std::string get_description() const override;

        /**
         * @brief Get the plugin dependencies.
         * 
         * @returns A set of plugin names that this plugin depends on.
         */
        std::set<std::string> get_dependencies() const override;
    };
}    // namespace hal

To actually implement these functions, let's head over to plugin_example.cpp. Before we implement the functions from the header file, we need to implement the create_plugin_instance function defined in include/hal_core/plugin_system/plugin_interface_base.h. This way we ensure that our plugin can actually be instantiated at runtime using the function plugin_manager::get_plugin_instance (or PluginManager.get_plugin_instance via Python). While it may not always be necessary to get the plugin instance at runtime, our plugin should still support it to comply with the HAL plugin API. Afterwards, we simply implement the functions from the header and return the respective strings for name, version, and description as well as an empty set for dependencies since our plugins does not (yet) have any.

#include "example/plugin_example.h"

namespace hal
{
    extern std::unique_ptr<BasePluginInterface> create_plugin_instance()
    {
        return std::make_unique<ExamplePlugin>();
    }

    std::string ExamplePlugin::get_name() const
    {
        return std::string("example");
    }

    std::string ExamplePlugin::get_version() const
    {
        return std::string("0.1");
    }

    std::string ExamplePlugin::get_description() const
    {
        return "An example plugin that does nothing but demonstrate how to write plugins for HAL.";
    }

    std::set<std::string> ExamplePlugin::get_dependencies() const
    {
        return {};
    }
}    // namespace hal

This completes our empty and so far pretty useless example plugin. Check out Adding Functionality to a Plugin to find out how to implement some actual functionality.

Clone this wiki locally