diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a0f8407..d72bf69 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -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}) diff --git a/examples/example.cpp b/examples/example.cpp index 9dc9369..f12f4eb 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -16,20 +16,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "plugin.h" #include + +// Include the plugin APIs +#include "printer/printer.h" +#include "shape/shape.h" + +#include #include #include 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 printer_plugins = loader.getAvailablePlugins(); assert(printer_plugins.size() == 2); @@ -40,16 +44,50 @@ int main(int /*argc*/, char** /*argv*/) // NOLINT Printer::Ptr plugin = loader.createInstance(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 shape_plugins = loader.getAvailablePlugins(); + std::vector shape_plugins = loader.getAvailablePlugins(); 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(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("Square"); + auto triangle_factory = loader.createInstance("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{ 2.0, 3.0 }); + Shape::Ptr triangle_2 = triangle_factory->create(std::tuple{ 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); } diff --git a/examples/plugin.cpp b/examples/plugin.cpp index 7a0be87..1c47bf6 100644 --- a/examples/plugin.cpp +++ b/examples/plugin.cpp @@ -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 namespace boost_plugin_loader @@ -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 diff --git a/examples/printer/console_printer.cpp b/examples/printer/console_printer.cpp new file mode 100644 index 0000000..4c380d1 --- /dev/null +++ b/examples/printer/console_printer.cpp @@ -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 + +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) diff --git a/examples/plugin_impl.cpp b/examples/printer/hello_world_printer.cpp similarity index 61% rename from examples/plugin_impl.cpp rename to examples/printer/hello_world_printer.cpp index b6a89f9..64b918d 100644 --- a/examples/plugin_impl.cpp +++ b/examples/printer/hello_world_printer.cpp @@ -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 namespace boost_plugin_loader { -struct ConsolePrinter : public Printer -{ -public: - void operator()() const override - { - std::cout << "IMPL: ConsolePrinter" << std::endl; - } -}; - struct HelloWorldPrinter : public Printer { public: @@ -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) diff --git a/examples/plugin.h b/examples/printer/printer.h similarity index 76% rename from examples/plugin.h rename to examples/printer/printer.h index a611d86..f5ef8a4 100644 --- a/examples/plugin.h +++ b/examples/printer/printer.h @@ -42,23 +42,8 @@ class Printer static std::string getSection(); }; -/** @brief Plugin interface implementation for testing */ -class Shape -{ -public: - using Ptr = std::shared_ptr; - virtual void operator()() const = 0; - -private: - friend class PluginLoader; - friend struct has_getSection; - static std::string getSection(); -}; - } // namespace boost_plugin_loader #include // 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) diff --git a/examples/shape/shape.h b/examples/shape/shape.h new file mode 100644 index 0000000..741c3fa --- /dev/null +++ b/examples/shape/shape.h @@ -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 +#include +#include + +namespace boost_plugin_loader +{ +/** + * @brief Interface class for representing shapes that can compute area + */ +class Shape +{ +public: + using Ptr = std::shared_ptr; + virtual double area() const = 0; +}; + +// Forward declare PluginLoader and has_getSection classes +class PluginLoader; + +template +struct has_getSection; + +/** + * @brief Plugin interface for creating shape objects + */ +class ShapeFactory +{ +public: + using Ptr = std::shared_ptr; + virtual std::shared_ptr create(const std::any& params) const = 0; + +private: + friend class PluginLoader; + friend struct has_getSection; + static std::string getSection(); +}; + +} // namespace boost_plugin_loader + +#include +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +#define EXPORT_SHAPE_PLUGIN(DERIVED_CLASS, ALIAS) EXPORT_CLASS_SECTIONED(DERIVED_CLASS, ALIAS, shape) diff --git a/examples/shape/square.cpp b/examples/shape/square.cpp new file mode 100644 index 0000000..5e2313a --- /dev/null +++ b/examples/shape/square.cpp @@ -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 create(const std::any& params) const override + { + double width = std::any_cast(params); + return std::make_shared(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) diff --git a/examples/shape/triangle.cpp b/examples/shape/triangle.cpp new file mode 100644 index 0000000..4e0db58 --- /dev/null +++ b/examples/shape/triangle.cpp @@ -0,0 +1,59 @@ +/** + * + * @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 Triangle shape implementation + */ +class Triangle : public Shape +{ +public: + Triangle(double b, double h) : base(b), height(h) + { + } + + double area() const override + { + return base * height / 2.0; + } + +protected: + double base; + double height; +}; + +/** + * @brief Triangle plugin factory + */ +class TriangleFactory : public ShapeFactory +{ + std::shared_ptr create(const std::any& params) const override + { + double base, height; + std::tie(base, height) = std::any_cast>(params); + return std::make_shared(base, height); + } +}; + +} // namespace boost_plugin_loader + +// Export the factory as the plugin to allow for multiple differently configured instances of the Triangle shape +EXPORT_SHAPE_PLUGIN(boost_plugin_loader::TriangleFactory, Triangle)