Skip to content

Commit ee18deb

Browse files
authored
Add support for passing arguments to constructors (#291)
- updated `create*Instance` methods to forward arguments to plugin constructors using class_loader interface traits Signed-off-by: pum1k <55055380+pum1k@users.noreply.github.com>
1 parent e33d236 commit ee18deb

8 files changed

Lines changed: 145 additions & 9 deletions

File tree

pluginlib/include/pluginlib/class_loader.hpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <string>
3535
#include <vector>
3636

37+
#include "class_loader/interface_traits.hpp"
3738
#include "class_loader/multi_library_class_loader.hpp"
3839
#include "pluginlib/class_desc.hpp"
3940
#include "pluginlib/class_loader_base.hpp"
@@ -77,12 +78,16 @@ class ClassLoader : public ClassLoaderBase
7778
* Deleting the instance and calling unloadLibraryForClass() is automatically
7879
* handled by the shared pointer.
7980
* \param lookup_name The name of the class to load
81+
* \param args Arguments passed to the constructor of the plugin.
82+
* Plugin parameters must be declared using class_loader::InterfaceTraits.
8083
* \throws pluginlib::LibraryLoadException when the library associated with
8184
* the class cannot be loaded
8285
* \throws pluginlib::CreateClassException when the class cannot be instantiated
8386
* \return An instance of the class
8487
*/
85-
std::shared_ptr<T> createSharedInstance(const std::string & lookup_name);
88+
template<typename ... Args,
89+
std::enable_if_t<class_loader::is_interface_constructible_v<T, Args...>, bool> = true>
90+
std::shared_ptr<T> createSharedInstance(const std::string & lookup_name, Args && ... args);
8691

8792
/// Create an instance of a desired class.
8893
/**
@@ -95,12 +100,16 @@ class ClassLoader : public ClassLoaderBase
95100
* deleter when you want to destroy the released pointer.
96101
*
97102
* \param lookup_name The name of the class to load.
103+
* \param args Arguments passed to the constructor of the plugin.
104+
* Plugin parameters must be declared using class_loader::InterfaceTraits.
98105
* \throws pluginlib::LibraryLoadException when the library associated with
99106
* the class cannot be loaded.
100107
* \throws pluginlib::CreateClassException when the class cannot be instantiated
101108
* \return An instance of the class
102109
*/
103-
UniquePtr<T> createUniqueInstance(const std::string & lookup_name);
110+
template<typename ... Args,
111+
std::enable_if_t<class_loader::is_interface_constructible_v<T, Args...>, bool> = true>
112+
UniquePtr<T> createUniqueInstance(const std::string & lookup_name, Args && ... args);
104113

105114
/// Create an instance of a desired class.
106115
/**
@@ -111,12 +120,16 @@ class ClassLoader : public ClassLoaderBase
111120
* (in order to decrement the associated library counter and unloading it
112121
* if it is no more used).
113122
* \param lookup_name The name of the class to load
123+
* \param args Arguments passed to the constructor of the plugin.
124+
* Plugin parameters must be declared using class_loader::InterfaceTraits.
114125
* \throws pluginlib::LibraryLoadException when the library associated with
115126
* the class cannot be loaded
116127
* \throws pluginlib::CreateClassException when the class cannot be instantiated
117128
* \return An instance of the class
118129
*/
119-
T * createUnmanagedInstance(const std::string & lookup_name);
130+
template<typename ... Args,
131+
std::enable_if_t<class_loader::is_interface_constructible_v<T, Args...>, bool> = true>
132+
T * createUnmanagedInstance(const std::string & lookup_name, Args && ... args);
120133

121134
/// Return a list of all available plugin manifest paths for this ClassLoader's base class type.
122135
/**

pluginlib/include/pluginlib/class_loader_imp.hpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "ament_index_cpp/get_package_share_directory.hpp"
4646
#include "ament_index_cpp/get_resource.hpp"
4747
#include "ament_index_cpp/get_resources.hpp"
48+
#include "class_loader/interface_traits.hpp"
4849
#include "class_loader/class_loader.hpp"
4950
#include "rcpputils/shared_library.hpp"
5051
#include "rcutils/logging_macros.h"
@@ -106,14 +107,20 @@ ClassLoader<T>::~ClassLoader()
106107
}
107108

108109
template<class T>
109-
std::shared_ptr<T> ClassLoader<T>::createSharedInstance(const std::string & lookup_name)
110+
template<typename ... Args,
111+
std::enable_if_t<class_loader::is_interface_constructible_v<T, Args...>, bool>>
112+
std::shared_ptr<T> ClassLoader<T>::createSharedInstance(
113+
const std::string & lookup_name,
114+
Args &&... args)
110115
/***************************************************************************/
111116
{
112-
return createUniqueInstance(lookup_name);
117+
return createUniqueInstance(lookup_name, std::forward<Args>(args)...);
113118
}
114119

115120
template<class T>
116-
UniquePtr<T> ClassLoader<T>::createUniqueInstance(const std::string & lookup_name)
121+
template<typename ... Args,
122+
std::enable_if_t<class_loader::is_interface_constructible_v<T, Args...>, bool>>
123+
UniquePtr<T> ClassLoader<T>::createUniqueInstance(const std::string & lookup_name, Args &&... args)
117124
{
118125
RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader",
119126
"Attempting to create managed (unique) instance for class %s.",
@@ -128,7 +135,8 @@ UniquePtr<T> ClassLoader<T>::createUniqueInstance(const std::string & lookup_nam
128135
RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "%s maps to real class type %s",
129136
lookup_name.c_str(), class_type.c_str());
130137

131-
UniquePtr<T> obj = lowlevel_class_loader_.createUniqueInstance<T>(class_type);
138+
UniquePtr<T> obj = lowlevel_class_loader_.createUniqueInstance<T>(class_type,
139+
std::forward<Args>(args)...);
132140

133141
RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader",
134142
"std::unique_ptr to object of real type %s created.",
@@ -145,7 +153,9 @@ UniquePtr<T> ClassLoader<T>::createUniqueInstance(const std::string & lookup_nam
145153
}
146154

147155
template<class T>
148-
T * ClassLoader<T>::createUnmanagedInstance(const std::string & lookup_name)
156+
template<typename ... Args,
157+
std::enable_if_t<class_loader::is_interface_constructible_v<T, Args...>, bool>>
158+
T * ClassLoader<T>::createUnmanagedInstance(const std::string & lookup_name, Args &&... args)
149159
/***************************************************************************/
150160
{
151161
RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader",
@@ -163,7 +173,8 @@ T * ClassLoader<T>::createUnmanagedInstance(const std::string & lookup_name)
163173
std::string class_type = getClassType(lookup_name);
164174
RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "%s maps to real class type %s",
165175
lookup_name.c_str(), class_type.c_str());
166-
instance = lowlevel_class_loader_.createUnmanagedInstance<T>(class_type);
176+
instance = lowlevel_class_loader_.createUnmanagedInstance<T>(class_type,
177+
std::forward<Args>(args)...);
167178
RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader",
168179
"Instance of type %s created.",
169180
class_type.c_str());

pluginlib/test/include/test_base.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
#ifndef TEST_BASE_HPP_
3030
#define TEST_BASE_HPP_
3131

32+
#include <memory>
33+
3234
#include <visibility_control.hpp>
35+
#include <class_loader/interface_traits.hpp>
3336

3437
namespace test_base
3538
{
@@ -43,5 +46,33 @@ class TEST_PLUGINLIB_FIXTURE_PUBLIC Fubar
4346
protected:
4447
Fubar() {}
4548
};
49+
50+
class TEST_PLUGINLIB_FIXTURE_PUBLIC FubarWithCtor
51+
{
52+
public:
53+
virtual double result() = 0;
54+
virtual ~FubarWithCtor() = default;
55+
56+
protected:
57+
FubarWithCtor() = default;
58+
};
4659
} // namespace test_base
60+
61+
template<>
62+
struct class_loader::InterfaceTraits<test_base::FubarWithCtor>
63+
{
64+
// Using `std::unique_ptr<double>` to test forwarding of move-only types.
65+
using constructor_parameters = class_loader::ConstructorParameters<std::unique_ptr<double>>;
66+
};
67+
68+
static_assert(
69+
class_loader::is_interface_constructible_v<test_base::FubarWithCtor, std::unique_ptr<double>>,
70+
"BaseWithInterfaceCtor should be interface constructible with the specifed types."
71+
);
72+
73+
static_assert(
74+
class_loader::is_interface_constructible_v<test_base::FubarWithCtor, std::unique_ptr<double>&&>,
75+
"BaseWithInterfaceCtor should be interface constructible with the specifed types."
76+
);
77+
4778
#endif // TEST_BASE_HPP_

pluginlib/test/include/test_plugins.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#define TEST_PLUGINS_HPP_
3131

3232
#include <cmath>
33+
#include <memory>
3334

3435
#include <test_base.hpp>
3536
#include <visibility_control.hpp>
@@ -75,6 +76,21 @@ class TEST_PLUGINLIB_FIXTURE_PUBLIC Foo : public test_base::Fubar
7576
return foo_ * foo_;
7677
}
7778

79+
private:
80+
double foo_;
81+
};
82+
83+
class TEST_PLUGINLIB_FIXTURE_PUBLIC FooWithCtor : public test_base::FubarWithCtor
84+
{
85+
public:
86+
explicit FooWithCtor(std::unique_ptr<double> foo)
87+
: foo_(*foo) {}
88+
89+
double result() override
90+
{
91+
return foo_ * foo_;
92+
}
93+
7894
private:
7995
double foo_;
8096
};

pluginlib/test/test_plugins.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@
3232

3333
PLUGINLIB_EXPORT_CLASS(test_plugins::Foo, test_base::Fubar)
3434
PLUGINLIB_EXPORT_CLASS(test_plugins::Bar, test_base::Fubar)
35+
PLUGINLIB_EXPORT_CLASS(test_plugins::FooWithCtor, test_base::FubarWithCtor)

pluginlib/test/test_plugins.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
<class name="test_pluginlib/bar" type="test_plugins::Bar" base_class_type="test_base::Fubar">
66
<description>This is a bar plugin.</description>
77
</class>
8+
<class name="test_pluginlib/foo_with_ctor" type="test_plugins::FooWithCtor" base_class_type="test_base::FubarWithCtor">
9+
<description>This is a foo plugin with constructor.</description>
10+
</class>
811
<class name="test_pluginlib/none" type="test_plugins::None" base_class_type="test_base::Fubar">
912
<description>This is a broken none plugin.</description>
1013
</class>

pluginlib/test/unique_ptr_test.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,23 @@ TEST(PluginlibUniquePtrTest, workingPlugin) {
6767
}
6868
}
6969

70+
TEST(PluginlibUniquePtrTest, workingPluginCtor) {
71+
pluginlib::ClassLoader<test_base::FubarWithCtor> test_loader("test_pluginlib",
72+
"test_base::FubarWithCtor");
73+
74+
try {
75+
pluginlib::UniquePtr<test_base::FubarWithCtor> foo =
76+
test_loader.createUniqueInstance("test_pluginlib/foo_with_ctor",
77+
std::make_unique<double>(10.0));
78+
EXPECT_DOUBLE_EQ(100.0, foo->result());
79+
} catch (pluginlib::PluginlibException & ex) {
80+
FAIL() << "Throwing exception: " << ex.what();
81+
return;
82+
} catch (...) {
83+
FAIL() << "Uncaught exception";
84+
}
85+
}
86+
7087
TEST(PluginlibUniquePtrTest, createUniqueInstanceAndUnloadLibrary) {
7188
RCUTILS_LOG_INFO("Making the ClassLoader...");
7289
pluginlib::ClassLoader<test_base::Fubar> pl("test_pluginlib", "test_base::Fubar");

pluginlib/test/utest.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,23 @@ TEST(PluginlibTest, workingPlugin) {
7474
}
7575
}
7676

77+
TEST(PluginlibTest, workingPluginCtor) {
78+
pluginlib::ClassLoader<test_base::FubarWithCtor> test_loader("test_pluginlib",
79+
"test_base::FubarWithCtor");
80+
81+
try {
82+
std::shared_ptr<test_base::FubarWithCtor> foo =
83+
test_loader.createSharedInstance("test_pluginlib/foo_with_ctor",
84+
std::make_unique<double>(10.0));
85+
EXPECT_DOUBLE_EQ(100.0, foo->result());
86+
} catch (pluginlib::PluginlibException & ex) {
87+
FAIL() << "Throwing exception: " << ex.what();
88+
return;
89+
} catch (...) {
90+
FAIL() << "Uncaught exception";
91+
}
92+
}
93+
7794
TEST(PluginlibTest, createUnmanagedInstanceAndUnloadLibrary) {
7895
RCUTILS_LOG_INFO("Making the ClassLoader...");
7996
pluginlib::ClassLoader<test_base::Fubar> pl("test_pluginlib", "test_base::Fubar");
@@ -99,6 +116,33 @@ TEST(PluginlibTest, createUnmanagedInstanceAndUnloadLibrary) {
99116
RCUTILS_LOG_INFO("Done.");
100117
}
101118

119+
TEST(PluginlibTest, createUnmanagedInstanceCtorAndUnloadLibrary) {
120+
RCUTILS_LOG_INFO("Making the ClassLoader...");
121+
pluginlib::ClassLoader<test_base::FubarWithCtor> pl("test_pluginlib", "test_base::FubarWithCtor");
122+
123+
RCUTILS_LOG_INFO("Instantiating plugin...");
124+
test_base::FubarWithCtor * inst = pl.createUnmanagedInstance("test_pluginlib/foo_with_ctor",
125+
std::make_unique<double>(10.0));
126+
EXPECT_DOUBLE_EQ(100.0, inst->result());
127+
128+
RCUTILS_LOG_INFO("Deleting plugin...");
129+
delete inst;
130+
131+
RCUTILS_LOG_INFO("Checking if plugin is loaded with isClassLoaded...");
132+
if (pl.isClassLoaded("test_pluginlib/foo_with_ctor")) {
133+
RCUTILS_LOG_INFO("Class is loaded");
134+
} else {
135+
FAIL() << "Library containing class should be loaded but isn't.";
136+
}
137+
RCUTILS_LOG_INFO("Trying to unload class with unloadLibraryForClass...");
138+
try {
139+
pl.unloadLibraryForClass("test_pluginlib/foo_with_ctor");
140+
} catch (pluginlib::PluginlibException & e) {
141+
FAIL() << "Could not unload library when I should be able to. " << e.what();
142+
}
143+
RCUTILS_LOG_INFO("Done.");
144+
}
145+
102146
TEST(PluginlibTest, createManagedInstanceAndUnloadLibrary) {
103147
RCUTILS_LOG_INFO("Making the ClassLoader...");
104148
pluginlib::ClassLoader<test_base::Fubar> pl("test_pluginlib", "test_base::Fubar");

0 commit comments

Comments
 (0)