Skip to content

Commit 45ce9ed

Browse files
authored
Example update (#19)
1 parent 89db350 commit 45ce9ed

File tree

10 files changed

+321
-63
lines changed

10 files changed

+321
-63
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,32 @@ See the [test plugin base class definition](examples/plugin.h) for an example.
4141
## Declaring plugin implementations
4242
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
4343
(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.
44+
45+
## Usage Notes
46+
47+
### Multiple instances of the same plugin with varying configuration
48+
49+
Objects loaded with `PluginLoader<T>::createInstance` are effectively singleton objects.
50+
Multiple calls to `PluginLoader<T>::createInstance` with the same arguments will create a pointer to the same object.
51+
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.
52+
See the [`ShapeFactory` plugin for an example implementation](examples/shape/shape.h).
53+
54+
## Keep plugins in scope during use
55+
56+
Once the plugin object goes out of scope, the library providing it will be unloaded, resulting in undefined behavior and potential segfaults.
57+
Thus, the plugin object must be kept in scope for as long as it (and objects created by it) are being used.
58+
Here is an example of what **not** to do:
59+
60+
```c++
61+
boost_plugin_loader::PluginLoader<ShapeFactory> plugin_loader;
62+
63+
Shape::Ptr shape;
64+
{
65+
ShapeFactory::Ptr square_factory = plugin_loader.createInstance("Square");
66+
shape = square_factory.create(2.0);
67+
68+
// Library providing "Square" plugin is unloaded here when `square_factory` goes out of scope
69+
}
70+
71+
std::cout << "Square area: " << shape->area() << std::endl; // <-- segfault because the library providing plugin factory (and the object generated by it) was unloaded
72+
```

examples/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ target_cxx_version(${PROJECT_NAME}_example_plugin PUBLIC VERSION 17)
55
target_clang_tidy(${PROJECT_NAME}_example_plugin ENABLE ${ENABLE_CLANG_TIDY})
66

77
# Example Implementation Library
8-
add_library(${PROJECT_NAME}_example_plugin_impl plugin_impl.cpp)
8+
add_library(
9+
${PROJECT_NAME}_example_plugin_impl
10+
printer/console_printer.cpp
11+
printer/hello_world_printer.cpp
12+
shape/square.cpp
13+
shape/triangle.cpp)
914
target_link_libraries(${PROJECT_NAME}_example_plugin_impl PUBLIC ${PROJECT_NAME}_example_plugin)
1015
target_cxx_version(${PROJECT_NAME}_example_plugin_impl PUBLIC VERSION 17)
1116
target_clang_tidy(${PROJECT_NAME}_example_plugin_impl ENABLE ${ENABLE_CLANG_TIDY})

examples/example.cpp

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
* See the License for the specific language governing permissions and
1717
* limitations under the License.
1818
*/
19-
#include "plugin.h"
19+
// Include the plugin APIs
20+
#include "printer/printer.h"
21+
#include "shape/shape.h"
2022

2123
// STD
2224
#include <string>
2325
#include <vector>
26+
#include <iomanip>
2427
#include <iostream>
2528
#include <cassert>
2629

@@ -30,12 +33,11 @@
3033
using boost_plugin_loader::PluginLoader;
3134
using boost_plugin_loader::Printer;
3235
using boost_plugin_loader::Shape;
36+
using boost_plugin_loader::ShapeFactory;
3337

34-
int main(int /*argc*/, char** /*argv*/) // NOLINT
38+
void demoPrinterPlugins(const PluginLoader& loader)
3539
{
36-
PluginLoader loader;
37-
loader.search_paths.insert(PLUGIN_DIR);
38-
loader.search_libraries.insert(PLUGINS);
40+
std::cout << "Loading printer plugins" << std::endl;
3941

4042
const std::vector<std::string> printer_plugins = loader.getAvailablePlugins<Printer>();
4143
assert(printer_plugins.size() == 2);
@@ -46,16 +48,49 @@ int main(int /*argc*/, char** /*argv*/) // NOLINT
4648
const Printer::Ptr plugin = loader.createInstance<Printer>(plugin_name);
4749
assert(plugin != nullptr);
4850
plugin->operator()();
51+
// Note: `plugin` goes out of scope here, and the library providing its definition will be unloaded
4952
}
53+
}
54+
55+
void demoShapePlugins(const PluginLoader& loader)
56+
{
57+
std::cout << "Loading shape plugins" << std::endl;
5058

51-
const std::vector<std::string> shape_plugins = loader.getAvailablePlugins<Shape>();
59+
std::vector<std::string> shape_plugins = loader.getAvailablePlugins<ShapeFactory>();
5260
assert(shape_plugins.size() == 2);
5361

54-
for (const std::string& plugin_name : shape_plugins)
55-
{
56-
std::cout << "Loading plugin '" << plugin_name << "'\n";
57-
const Printer::Ptr plugin = loader.createInstance<Printer>(plugin_name);
58-
assert(plugin != nullptr);
59-
plugin->operator()();
60-
}
62+
// Create the square and triangle factory plugins
63+
// Note: these factories must stay in scope as long as they and any object they create are being used. Otherwise, the
64+
// library providing them will be unloaded, resulting in undefined behavior and potential segfaults
65+
auto square_factory = loader.createInstance<ShapeFactory>("Square");
66+
auto triangle_factory = loader.createInstance<ShapeFactory>("Triangle");
67+
68+
// Create two different types of squares
69+
Shape::Ptr square_1 = square_factory->create(2.0);
70+
Shape::Ptr square_2 = square_factory->create(4.0);
71+
72+
// Create two different types of triangles
73+
Shape::Ptr triangle_1 = triangle_factory->create(std::tuple<double, double>{ 2.0, 3.0 });
74+
Shape::Ptr triangle_2 = triangle_factory->create(std::tuple<double, double>{ 8.0, 20.0 });
75+
76+
// Compute the areas of all of the different shapes
77+
std::cout << std::setprecision(2) << "Square 1 area: " << square_1->area() << std::endl;
78+
std::cout << std::setprecision(2) << "Square 2 area: " << square_2->area() << std::endl;
79+
std::cout << std::setprecision(2) << "Triangle 1 area: " << triangle_1->area() << std::endl;
80+
std::cout << std::setprecision(2) << "Triangle 2 area: " << triangle_2->area() << std::endl;
81+
}
82+
83+
int main(int /*argc*/, char** /*argv*/) // NOLINT
84+
{
85+
// Create and configure the plugin loader to be able to find libraries in which plugins exist
86+
PluginLoader loader;
87+
loader.search_paths.insert(PLUGIN_DIR);
88+
loader.search_libraries.insert(PLUGINS);
89+
90+
// Printer plugins
91+
demoPrinterPlugins(loader);
92+
93+
// Shape plugins
94+
std::cout << std::endl;
95+
demoShapePlugins(loader);
6196
}

examples/plugin.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
* See the License for the specific language governing permissions and
1717
* limitations under the License.
1818
*/
19-
#include "plugin.h"
19+
#include "printer/printer.h"
20+
#include "shape/shape.h"
2021

2122
// STD
2223
#include <string>
@@ -35,11 +36,12 @@ std::string Printer::getSection()
3536
}
3637
INSTANTIATE_PLUGIN_LOADER(Printer)
3738

38-
// Define the section name for loading plugins of base class `Shape`
39+
// Define the section name for loading plugins of base class `ShapeFactory`
3940
// This should match the section name specified in the plugin export macro for this class
40-
std::string Shape::getSection()
41+
std::string ShapeFactory::getSection()
4142
{
4243
return "shape";
4344
}
44-
INSTANTIATE_PLUGIN_LOADER(Shape)
45+
INSTANTIATE_PLUGIN_LOADER(ShapeFactory)
46+
4547
} // namespace boost_plugin_loader

examples/printer/console_printer.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
*
3+
* @copyright Copyright (c) 2021, Southwest Research Institute
4+
*
5+
* @par License
6+
* Software License Agreement (Apache License)
7+
* @par
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
* @par
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
#include "printer.h"
20+
#include <iostream>
21+
22+
namespace boost_plugin_loader
23+
{
24+
struct ConsolePrinter : public Printer
25+
{
26+
public:
27+
void operator()() const override
28+
{
29+
std::cout << "IMPL: ConsolePrinter" << std::endl;
30+
}
31+
};
32+
33+
} // namespace boost_plugin_loader
34+
35+
EXPORT_PRINTER_PLUGIN(boost_plugin_loader::ConsolePrinter, ConsolePrinter)

examples/plugin_impl.cpp renamed to examples/printer/hello_world_printer.cpp

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,11 @@
1616
* See the License for the specific language governing permissions and
1717
* limitations under the License.
1818
*/
19-
#include "plugin.h"
20-
19+
#include "printer.h"
2120
#include <iostream>
2221

2322
namespace boost_plugin_loader
2423
{
25-
struct ConsolePrinter : public Printer
26-
{
27-
public:
28-
void operator()() const override
29-
{
30-
std::cout << "IMPL: ConsolePrinter\n";
31-
}
32-
};
33-
3424
struct HelloWorldPrinter : public Printer
3525
{
3626
public:
@@ -40,27 +30,6 @@ struct HelloWorldPrinter : public Printer
4030
}
4131
};
4232

43-
struct Square : public Shape
44-
{
45-
public:
46-
void operator()() const override
47-
{
48-
std::cout << "IMPL: Square\n";
49-
}
50-
};
51-
52-
struct Triangle : public Shape
53-
{
54-
public:
55-
void operator()() const override
56-
{
57-
std::cout << "IMPL: Triangle\n";
58-
}
59-
};
60-
6133
} // namespace boost_plugin_loader
6234

63-
EXPORT_PRINTER_PLUGIN(boost_plugin_loader::ConsolePrinter, ConsolePrinter)
6435
EXPORT_PRINTER_PLUGIN(boost_plugin_loader::HelloWorldPrinter, HelloWorldPrinter)
65-
EXPORT_SHAPE_PLUGIN(boost_plugin_loader::Square, Square)
66-
EXPORT_SHAPE_PLUGIN(boost_plugin_loader::Triangle, Triangle)

examples/printer/printer.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
*
3+
* @copyright Copyright (c) 2021, Southwest Research Institute
4+
*
5+
* @par License
6+
* Software License Agreement (Apache License)
7+
* @par
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
* @par
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
#pragma once
20+
21+
#include <memory>
22+
#include <string>
23+
24+
namespace boost_plugin_loader
25+
{
26+
// Forward declare PluginLoader and has_getSection classes
27+
class PluginLoader;
28+
29+
template <typename T>
30+
struct has_getSection;
31+
32+
/** @brief Plugin interface implementation for testing */
33+
class Printer
34+
{
35+
public:
36+
using Ptr = std::shared_ptr<Printer>;
37+
virtual ~Printer() = default;
38+
virtual void operator()() const = 0;
39+
40+
private:
41+
friend class PluginLoader;
42+
friend struct has_getSection<Printer>;
43+
static std::string getSection();
44+
};
45+
46+
} // namespace boost_plugin_loader
47+
48+
#include <boost_plugin_loader/macros.h>
49+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
50+
#define EXPORT_PRINTER_PLUGIN(DERIVED_CLASS, ALIAS) EXPORT_CLASS_SECTIONED(DERIVED_CLASS, ALIAS, printer)

examples/plugin.h renamed to examples/shape/shape.h

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <memory>
2222
#include <string>
23+
#include <any>
2324

2425
namespace boost_plugin_loader
2526
{
@@ -29,38 +30,42 @@ class PluginLoader;
2930
template <typename T>
3031
struct has_getSection;
3132

32-
/** @brief Plugin interface implementation for testing */
33-
class Printer
33+
/**
34+
* @brief Interface class for representing shapes that can compute area
35+
*/
36+
class Shape
3437
{
3538
public:
36-
using Ptr = std::shared_ptr<Printer>;
37-
virtual ~Printer() = default;
39+
using Ptr = std::shared_ptr<Shape>;
40+
virtual ~Shape() = default;
41+
3842
virtual void operator()() const = 0;
43+
virtual double area() const = 0;
3944

4045
private:
4146
friend class PluginLoader;
42-
friend struct has_getSection<Printer>;
47+
friend struct has_getSection<Shape>;
4348
static std::string getSection();
4449
};
4550

46-
/** @brief Plugin interface implementation for testing */
47-
class Shape
51+
/**
52+
* @brief Plugin interface for creating shape objects
53+
*/
54+
class ShapeFactory
4855
{
4956
public:
50-
using Ptr = std::shared_ptr<Shape>;
51-
virtual ~Shape() = default;
52-
virtual void operator()() const = 0;
57+
using Ptr = std::shared_ptr<ShapeFactory>;
58+
virtual ~ShapeFactory() = default;
59+
virtual std::shared_ptr<Shape> create(const std::any& params) const = 0;
5360

5461
private:
5562
friend class PluginLoader;
56-
friend struct has_getSection<Shape>;
63+
friend struct has_getSection<ShapeFactory>;
5764
static std::string getSection();
5865
};
5966

6067
} // namespace boost_plugin_loader
6168

6269
#include <boost_plugin_loader/macros.h>
6370
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
64-
#define EXPORT_PRINTER_PLUGIN(DERIVED_CLASS, ALIAS) EXPORT_CLASS_SECTIONED(DERIVED_CLASS, ALIAS, printer)
65-
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
6671
#define EXPORT_SHAPE_PLUGIN(DERIVED_CLASS, ALIAS) EXPORT_CLASS_SECTIONED(DERIVED_CLASS, ALIAS, shape)

0 commit comments

Comments
 (0)