Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example update #19

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,32 @@ See the [test plugin base class definition](examples/plugin.h) for an example.
## Declaring plugin implementations
Creating an implementation of a plugin is as simple as inheriting from the plugin base class, and calling the `EXPORT_CLASS_SECTIONED` macro with the correct section
(or calling a custom export macro defined for the plugin base class, described above). See the [test plugin implementations](examples/plugin_impl.cpp) for an example.

## Usage Notes

### Multiple instances of the same plugin with varying configuration

Objects loaded with `PluginLoader<T>::createInstance` are effectively singleton objects.
Multiple calls to `PluginLoader<T>::createInstance` with the same arguments will create a pointer to the same object.
If you need to load multiple instances of the same type of plugin but configured differently, consider making your plugin base class a factory that is itself capable of creating and configuring objects.
See the [`ShapeFactory` plugin for an example implementation](examples/shape/shape.h).

## Keep plugins in scope during use

Once the plugin object goes out of scope, the library providing it will be unloaded, resulting in undefined behavior and potential segfaults.
Thus, the plugin object must be kept in scope for as long as it (and objects created by it) are being used.
Here is an example of what **not** to do:

```c++
boost_plugin_loader::PluginLoader<ShapeFactory> plugin_loader;

Shape::Ptr shape;
{
ShapeFactory::Ptr square_factory = plugin_loader.createInstance("Square");
shape = square_factory.create(2.0);

// Library providing "Square" plugin is unloaded here when `square_factory` goes out of scope
}

std::cout << "Square area: " << shape->area() << std::endl; // <-- segfault because the library providing plugin factory (and the object generated by it) was unloaded
```
7 changes: 6 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ target_cxx_version(${PROJECT_NAME}_example_plugin PUBLIC VERSION 17)
target_clang_tidy(${PROJECT_NAME}_example_plugin ENABLE ${ENABLE_CLANG_TIDY})

# Example Implementation Library
add_library(${PROJECT_NAME}_example_plugin_impl plugin_impl.cpp)
add_library(
${PROJECT_NAME}_example_plugin_impl
printer/console_printer.cpp
printer/hello_world_printer.cpp
shape/square.cpp
shape/triangle.cpp)
target_link_libraries(${PROJECT_NAME}_example_plugin_impl PUBLIC ${PROJECT_NAME}_example_plugin)
target_cxx_version(${PROJECT_NAME}_example_plugin_impl PUBLIC VERSION 17)
target_clang_tidy(${PROJECT_NAME}_example_plugin_impl ENABLE ${ENABLE_CLANG_TIDY})
Expand Down
64 changes: 51 additions & 13 deletions examples/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "plugin.h"
#include <boost_plugin_loader/plugin_loader.h>

// Include the plugin APIs
#include "printer/printer.h"
#include "shape/shape.h"

#include <iomanip>
#include <iostream>
#include <cassert>

using boost_plugin_loader::PluginLoader;
using boost_plugin_loader::Printer;
using boost_plugin_loader::Shape;
using boost_plugin_loader::ShapeFactory;

int main(int /*argc*/, char** /*argv*/) // NOLINT
void demoPrinterPlugins(const PluginLoader& loader)
{
PluginLoader loader;
loader.search_paths.insert(PLUGIN_DIR);
loader.search_libraries.insert(PLUGINS);
std::cout << "Loading printer plugins" << std::endl;

std::vector<std::string> printer_plugins = loader.getAvailablePlugins<Printer>();
assert(printer_plugins.size() == 2);
Expand All @@ -40,16 +44,50 @@ int main(int /*argc*/, char** /*argv*/) // NOLINT
Printer::Ptr plugin = loader.createInstance<Printer>(plugin_name);
assert(plugin != nullptr);
plugin->operator()();

// Note: `plugin` goes out of scope here, and the library providing its definition will be unloaded
}
}

void demoShapePlugins(const PluginLoader& loader)
{
std::cout << "Loading shape plugins" << std::endl;

std::vector<std::string> shape_plugins = loader.getAvailablePlugins<Shape>();
std::vector<std::string> shape_plugins = loader.getAvailablePlugins<ShapeFactory>();
assert(shape_plugins.size() == 2);

for (const std::string& plugin_name : shape_plugins)
{
std::cout << "Loading plugin '" << plugin_name << "'" << std::endl;
Printer::Ptr plugin = loader.createInstance<Printer>(plugin_name);
assert(plugin != nullptr);
plugin->operator()();
}
// Create the square and triangle factory plugins
// Note: these factories must stay in scope as long as they and any object they create are being used. Otherwise, the
// library providing them will be unloaded, resulting in undefined behavior and potential segfaults
auto square_factory = loader.createInstance<ShapeFactory>("Square");
auto triangle_factory = loader.createInstance<ShapeFactory>("Triangle");

// Create two different types of squares
Shape::Ptr square_1 = square_factory->create(2.0);
Shape::Ptr square_2 = square_factory->create(4.0);

// Create two different types of triangles
Shape::Ptr triangle_1 = triangle_factory->create(std::tuple<double, double>{ 2.0, 3.0 });
Shape::Ptr triangle_2 = triangle_factory->create(std::tuple<double, double>{ 8.0, 20.0 });

// Compute the areas of all of the different shapes
std::cout << std::setprecision(2) << "Square 1 area: " << square_1->area() << std::endl;
std::cout << std::setprecision(2) << "Square 2 area: " << square_2->area() << std::endl;
std::cout << std::setprecision(2) << "Triangle 1 area: " << triangle_1->area() << std::endl;
std::cout << std::setprecision(2) << "Triangle 2 area: " << triangle_2->area() << std::endl;
}

int main(int /*argc*/, char** /*argv*/) // NOLINT
{
// Create and configure the plugin loader to be able to find libraries in which plugins exist
PluginLoader loader;
loader.search_paths.insert(PLUGIN_DIR);
loader.search_libraries.insert(PLUGINS);

// Printer plugins
demoPrinterPlugins(loader);

// Shape plugins
std::cout << std::endl;
demoShapePlugins(loader);
}
9 changes: 5 additions & 4 deletions examples/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "plugin.h"
#include "printer/printer.h"
#include "shape/shape.h"
#include <boost_plugin_loader/plugin_loader.hpp>

namespace boost_plugin_loader
Expand All @@ -29,11 +30,11 @@ std::string Printer::getSection()
}
INSTANTIATE_PLUGIN_LOADER(Printer)

// Define the section name for loading plugins of base class `Shape`
// Define the section name for loading plugins of base class `ShapeFactory`
// This should match the section name specified in the plugin export macro for this class
std::string Shape::getSection()
std::string ShapeFactory::getSection()
{
return "shape";
}
INSTANTIATE_PLUGIN_LOADER(Shape)
INSTANTIATE_PLUGIN_LOADER(ShapeFactory)
} // namespace boost_plugin_loader
35 changes: 35 additions & 0 deletions examples/printer/console_printer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
*
* @copyright Copyright (c) 2021, Southwest Research Institute
*
* @par License
* Software License Agreement (Apache License)
* @par
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* @par
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "printer.h"
#include <iostream>

namespace boost_plugin_loader
{
struct ConsolePrinter : public Printer
{
public:
void operator()() const override
{
std::cout << "IMPL: ConsolePrinter" << std::endl;
}
};

} // namespace boost_plugin_loader

EXPORT_PRINTER_PLUGIN(boost_plugin_loader::ConsolePrinter, ConsolePrinter)
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "plugin.h"

#include "printer.h"
#include <iostream>

namespace boost_plugin_loader
{
struct ConsolePrinter : public Printer
{
public:
void operator()() const override
{
std::cout << "IMPL: ConsolePrinter" << std::endl;
}
};

struct HelloWorldPrinter : public Printer
{
public:
Expand All @@ -40,27 +30,6 @@ struct HelloWorldPrinter : public Printer
}
};

struct Square : public Shape
{
public:
void operator()() const override
{
std::cout << "IMPL: Square" << std::endl;
}
};

struct Triangle : public Shape
{
public:
void operator()() const override
{
std::cout << "IMPL: Triangle" << std::endl;
}
};

} // namespace boost_plugin_loader

EXPORT_PRINTER_PLUGIN(boost_plugin_loader::ConsolePrinter, ConsolePrinter)
EXPORT_PRINTER_PLUGIN(boost_plugin_loader::HelloWorldPrinter, HelloWorldPrinter)
EXPORT_SHAPE_PLUGIN(boost_plugin_loader::Square, Square)
EXPORT_SHAPE_PLUGIN(boost_plugin_loader::Triangle, Triangle)
15 changes: 0 additions & 15 deletions examples/plugin.h → examples/printer/printer.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,8 @@ class Printer
static std::string getSection();
};

/** @brief Plugin interface implementation for testing */
class Shape
{
public:
using Ptr = std::shared_ptr<Shape>;
virtual void operator()() const = 0;

private:
friend class PluginLoader;
friend struct has_getSection<Shape>;
static std::string getSection();
};

} // namespace boost_plugin_loader

#include <boost_plugin_loader/macros.h>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
#define EXPORT_PRINTER_PLUGIN(DERIVED_CLASS, ALIAS) EXPORT_CLASS_SECTIONED(DERIVED_CLASS, ALIAS, printer)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
#define EXPORT_SHAPE_PLUGIN(DERIVED_CLASS, ALIAS) EXPORT_CLASS_SECTIONED(DERIVED_CLASS, ALIAS, shape)
62 changes: 62 additions & 0 deletions examples/shape/shape.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
*
* @copyright Copyright (c) 2021, Southwest Research Institute
*
* @par License
* Software License Agreement (Apache License)
* @par
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* @par
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <memory>
#include <string>
#include <any>

namespace boost_plugin_loader
{
/**
* @brief Interface class for representing shapes that can compute area
*/
class Shape
{
public:
using Ptr = std::shared_ptr<Shape>;
virtual double area() const = 0;
};

// Forward declare PluginLoader and has_getSection classes
class PluginLoader;

template <typename T>
struct has_getSection;

/**
* @brief Plugin interface for creating shape objects
*/
class ShapeFactory
{
public:
using Ptr = std::shared_ptr<ShapeFactory>;
virtual std::shared_ptr<Shape> create(const std::any& params) const = 0;

private:
friend class PluginLoader;
friend struct has_getSection<ShapeFactory>;
static std::string getSection();
};

} // namespace boost_plugin_loader

#include <boost_plugin_loader/macros.h>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
#define EXPORT_SHAPE_PLUGIN(DERIVED_CLASS, ALIAS) EXPORT_CLASS_SECTIONED(DERIVED_CLASS, ALIAS, shape)
57 changes: 57 additions & 0 deletions examples/shape/square.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
*
* @copyright Copyright (c) 2021, Southwest Research Institute
*
* @par License
* Software License Agreement (Apache License)
* @par
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* @par
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "shape.h"

namespace boost_plugin_loader
{
/**
* @brief Square shape implementation
*/
class Square : public Shape
{
public:
Square(double w) : width(w)
{
}

double area() const override
{
return width * width;
}

protected:
double width;
};

/**
* @brief Square factory plugin implementation
*/
class SquareFactory : public ShapeFactory
{
std::shared_ptr<Shape> create(const std::any& params) const override
{
double width = std::any_cast<double>(params);
return std::make_shared<Square>(width);
}
};

} // namespace boost_plugin_loader

// Export the factory as the plugin to allow for multiple differently configured instances of the Square shape
EXPORT_SHAPE_PLUGIN(boost_plugin_loader::SquareFactory, Square)
Loading
Loading