Skip to content

Commit

Permalink
Refactored example to show factory paradigm with Shape interface
Browse files Browse the repository at this point in the history
  • Loading branch information
marip8 committed Sep 28, 2024
1 parent cb57e21 commit 200fbee
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 65 deletions.
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

0 comments on commit 200fbee

Please sign in to comment.