Skip to content

4D-STAR/libplugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libplugin

libplugin is a simple, dynamic library based, plugin framework developed for SERiF, a 4D-STAR project. libplugin has been developed with the primary goal of being easy and type safe for plugin developers to use. It includes support for plugin bundles, which allow packaging multiple plugins together with metadata and resources into a single distributable file.

Funding

libplugin is a part of the 4D-STAR collaboration. 4D-STAR is funded by European Research Council (ERC) under the Horizon Europe programme (Synergy Grant agreement No. 101071505: 4D-STAR) Work for this project is funded by the European Union. Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Council.

Installation

The primary installation channel for libplugin is directly from source. Note that you will need a C++ compiler supporting C++23.

git clone https://github.com/4D-STAR/libplugin
cd libplugin
meson setup build
meson compile -C build
meson test -C build
meson install -C build

Examples

There are robust examples in the examples/ directory. Users are strongly encoraged to make use of these examples as learning tools.

Usage

The primary usage flow for libplugin is

  1. Define some common and pure virtual plugin interface as a decendent of fourdst::plugin::PluginBase. Generally this will be defined by whatever program expects the plugins to be made for it. That is to say that if I was the maintainer of the GridFire project and I wanted users to be able to make plugins for GridFire::solver::DirectNetworkSolver::RHSManager::observe() I would need to make some header file which defined the plugin class interface.
  2. The plugin author must then make a specific implimentation of that interface. So if GridFire presents an interface called RHSManagerObservePlugin then the plugin author must make a concrete implimentation of that pure virtual interface.
  3. The plugin author compiles their plugin as a dynamic library (*.dylib on macOS, *.so on linux). They must tell the compiler to include the libplugin headers (and libplugin library) of course, but also the common interface header.
  4. The "Host" program (in the above examples this would be GridFire) must then be somehow informed of the location of the dynamic library. The method to do this is up to the Host program to impliment (this could be something like command line arguments or a config file)
  5. Within the Host program the fourdst::plugin::manager::PluginManager::load("path.dylib") must then be called for that path. Again it is on the Host program to impliment the details of how this path is recived (though presumably it should be a runtime argument with safe handling for if no path is recived.)

Plugin Bundles

libplugin supports plugin bundles, which are single-file packages containing one or more plugins along with their metadata, resources, and signatures. Bundles are distributed as .fourdst files and provide several benefits:

  • Single-file distribution - Easy to share and deploy
  • Code signing - Verify the authenticity of plugins
  • Dependency management - Specify required system libraries and versions
  • Metadata - Include author, version, and description information
  • Resource packaging - Bundle additional files needed by the plugins

Creating a Bundle

Use the fourdst-cli bundle create command to create a new bundle:

First create a plugin (or multiple plugins)

fourdst-cli plugin init test_1_plugin -H host_provided_interface.h
fourdst-cli plugin init test_2_plugin -H host_provided_interface.h

Now edit those to do what you want them to do.

Next create a bundle from multiple plugins

fourdst-cli bundle create -o example.fbundle --name TestPluginBundle --ver 0.1.0 --author "Emily M. Boudreaux" --target-macos-version 12.0  test_1_plugin test_2_plugin

Now fill the bundle with precompiled dynamic libraries for various systems (mac binaries can only be produced on macos, linux binaries can be produced on any system using docker) In order to use docker the docker daemon must be running.

fourdst-cli bundle fill example.fbundle

Then create a key (or use an existing one to sign)

# Creating a key if you dont already have one
fourdst-cli keys generate --name example
fourdst-cli keys add example.pub.pem # This adds your personal key to the fourdst keyring
fourdst-cli bundle sign example.fbundle --key example.pub.pem

Finally, you can inspect and or verify the bundle

fourdst-cli bundle inspect example.fbundle
fourdst-cli bundle verify example.fbundle

Loading a Bundle

In your application, load a bundle using the PluginManager:

#include <fourdst/plugin/manager/plugin_manager.h>
#include <fourdst/plugin/bundle/plugin_bundle.h>

fourdst::plugin::manager::PluginManager& manager = fourdst::plugin::manager::PluginManager::getInstance();
fourdst::plugin::bundle::PluginBundle bundle("path/to/bundle.fbundle");

// Access plugins from the bundle
auto* plugin = manager.get<MyPluginInterface>("plugin_name");

Examples

A very simple example follows

Host Program

We start with what the host program needs. Remember this includes

  • Some common and pure virtual interface for what kind of plugin it will expect
  • A way to get the path to a dynamic library from a plugin author

Host Program Entry Point

// File main.cpp
// This is intended to be the entry point for the host

#include <fourdst/plugin/plugin.h>
#include "host_interface.h"

int main() {
  fourdst::plugin::manager::PluginManager& manager = fourdst::plugin::manager::PluginManager::getInstance();
  manager.load("libsimple_plugin.dylib"); // For now I will just assume that the plugin is at this fixed path
  auto* plugin = manager.get<IHostDefinedPlugin>("plugin_main");
  plugin -> say_hello();
}

Host Program Interface

// File: host_interface.h
// This is a file the host program must provide so that both it and the plugin author can know about the plugin interface

#pragma once
#include <fourdst/plugin/plugin.h>

class IHostDefinedPlugin : public fourdst::plugin::PluginBase {
public:
  ~HostDefinedPlugin() override = default;
  virtual void say_hello() const = 0;
}

Plugin Program

The plugin author then only needs to write one file

// File: plugin.cpp
// This is the file a plugin author would write
#include <fourdst/plugin/plugin.h>
#include "host_interface.h" // Note that it is generally up to the host to sort out how to inform the plugin author where this file is...

#include <iostream>

class CustomPlugin : public IHostDefinedPlugin {
  void say_hello() const override {
    std::cout << "Hello, World!\n";
  }
}

// The first argument is the name of the plugin authors implimentation
// The second argument is the name of the plugin that the host will call. The names the host expects to call should be documented *by the host*
// The last argument is the version number
FOURDST_REGISTER_PLUGIN(CustomPlugin, "plugin_main", "1.0.0")

Compiling a plugin

You very well could call the compiler directly; however, we strongly recommend you use a build system to compile your plugins. We use meson

# File: meson.build
project('simple_plugin, 'cpp', version: '1.0.0', default_options: ['cpp_std=c++23'])

# Find the fourdst plugin library
fourdst_plugin_dep = dependency('fourdst_plugin', required: true)

# We need some way of informing the plugin about the header from the host program
# if your host program has been installed in a find-able manner this could perhapse done like this
# alternitivley you could use meson's shared_library's include_directories directive and point it
# at whatever directory that header file is in
host_program_dep = dependency('host_program', required: true)

simple_plugin = shared_library(
    'simple_plugin',
    'plugin.cpp',
    dependencies: [fourdst_plugin_dep, host_program_dep],
    install: false,  # Set to true if you want to install the plugin
    cpp_args: ['-fPIC']  # Ensure position-independent code
)

then you would simply run the standard set of meson commands

meson setup buildPlugin
meson compile -C buildPlugin

now you will have a file in build named libsimple_plugin.cpp

Templates

We include a simple FunctorPlugin_T<T> template plugin allowing Host authors to impliment functor style plugins. This plugin expects that operator() will be overloaded with the signature

T operator()(T arg);

An example usage might be

Host

Host interface file

// File: TimestepFunctorInterface.h
#include <fourdst/plugin/plugin.h>

struct TimestepContext {
  double time;
  double dt;
  double value;
  size_t step;
}

class ITimestepFunctor : public fourdst::plugin::templates::FunctorPlugin_T<TimestepContext> {};

Host usage of plugin

#include <fourdst/plugin/plugin.h>

#include "TimestepFunctorInterface.h"

void foo() {
  fourdst::plugin::manager::PluginManager manager;
  TimestepContext context {54.3, 0.1, 10.892745, 7}
  manager.load("path.dylib");
  auto* plugin = manager.get<ITimestepFunctor>("plugin_A")
  TimestepContext result = (*plugin)(context);
}

Plugin author usage

#include <fourdst/plugin/plugin.h>

#include "TimestepFunctorInterface.h"

class TimestepFunctor : public ITimestepFunctor {
  TimestepContext operator()(TimestepContext ctx) {
    std::cout << "Time: " << ctx.time << ", value: " << ctx.value << "\n";
    return ctx;
  }
}

fourdst-cli

The fourdst library contains cli tool named fourdst-cli. One function of this tool is to make the lives of plugin authors much easier. See here for more details but the basics are covered below.

Once fourdst-cli is installed it is invoked using a git like syntax from

fourdst-cli plugin init PluginName --header <path/to/interface/defined/by/host>

This will let setup all the required files so that the author can just go in and impliment the functions they want to then build with meson.

Future

We hope to add support for python plugins being called from C++ in the near future.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published