diff --git a/pluginlib/include/pluginlib/class_loader.hpp b/pluginlib/include/pluginlib/class_loader.hpp index 28eb0f0..2cba481 100644 --- a/pluginlib/include/pluginlib/class_loader.hpp +++ b/pluginlib/include/pluginlib/class_loader.hpp @@ -34,6 +34,7 @@ #include #include +#include "class_loader/interface_traits.hpp" #include "class_loader/multi_library_class_loader.hpp" #include "pluginlib/class_desc.hpp" #include "pluginlib/class_loader_base.hpp" @@ -77,12 +78,16 @@ class ClassLoader : public ClassLoaderBase * Deleting the instance and calling unloadLibraryForClass() is automatically * handled by the shared pointer. * \param lookup_name The name of the class to load + * \param args Arguments passed to the constructor of the plugin. + * Plugin parameters must be declared using class_loader::InterfaceTraits. * \throws pluginlib::LibraryLoadException when the library associated with * the class cannot be loaded * \throws pluginlib::CreateClassException when the class cannot be instantiated * \return An instance of the class */ - std::shared_ptr createSharedInstance(const std::string & lookup_name); + template, bool> = true> + std::shared_ptr createSharedInstance(const std::string & lookup_name, Args && ... args); /// Create an instance of a desired class. /** @@ -95,12 +100,16 @@ class ClassLoader : public ClassLoaderBase * deleter when you want to destroy the released pointer. * * \param lookup_name The name of the class to load. + * \param args Arguments passed to the constructor of the plugin. + * Plugin parameters must be declared using class_loader::InterfaceTraits. * \throws pluginlib::LibraryLoadException when the library associated with * the class cannot be loaded. * \throws pluginlib::CreateClassException when the class cannot be instantiated * \return An instance of the class */ - UniquePtr createUniqueInstance(const std::string & lookup_name); + template, bool> = true> + UniquePtr createUniqueInstance(const std::string & lookup_name, Args && ... args); /// Create an instance of a desired class. /** @@ -111,12 +120,16 @@ class ClassLoader : public ClassLoaderBase * (in order to decrement the associated library counter and unloading it * if it is no more used). * \param lookup_name The name of the class to load + * \param args Arguments passed to the constructor of the plugin. + * Plugin parameters must be declared using class_loader::InterfaceTraits. * \throws pluginlib::LibraryLoadException when the library associated with * the class cannot be loaded * \throws pluginlib::CreateClassException when the class cannot be instantiated * \return An instance of the class */ - T * createUnmanagedInstance(const std::string & lookup_name); + template, bool> = true> + T * createUnmanagedInstance(const std::string & lookup_name, Args && ... args); /// Return a list of all available plugin manifest paths for this ClassLoader's base class type. /** diff --git a/pluginlib/include/pluginlib/class_loader_imp.hpp b/pluginlib/include/pluginlib/class_loader_imp.hpp index e02b678..ff31354 100644 --- a/pluginlib/include/pluginlib/class_loader_imp.hpp +++ b/pluginlib/include/pluginlib/class_loader_imp.hpp @@ -45,6 +45,7 @@ #include "ament_index_cpp/get_package_share_directory.hpp" #include "ament_index_cpp/get_resource.hpp" #include "ament_index_cpp/get_resources.hpp" +#include "class_loader/interface_traits.hpp" #include "class_loader/class_loader.hpp" #include "rcpputils/shared_library.hpp" #include "rcutils/logging_macros.h" @@ -106,14 +107,20 @@ ClassLoader::~ClassLoader() } template -std::shared_ptr ClassLoader::createSharedInstance(const std::string & lookup_name) +template, bool>> +std::shared_ptr ClassLoader::createSharedInstance( + const std::string & lookup_name, + Args &&... args) /***************************************************************************/ { - return createUniqueInstance(lookup_name); + return createUniqueInstance(lookup_name, std::forward(args)...); } template -UniquePtr ClassLoader::createUniqueInstance(const std::string & lookup_name) +template, bool>> +UniquePtr ClassLoader::createUniqueInstance(const std::string & lookup_name, Args &&... args) { RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Attempting to create managed (unique) instance for class %s.", @@ -128,7 +135,8 @@ UniquePtr ClassLoader::createUniqueInstance(const std::string & lookup_nam RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "%s maps to real class type %s", lookup_name.c_str(), class_type.c_str()); - UniquePtr obj = lowlevel_class_loader_.createUniqueInstance(class_type); + UniquePtr obj = lowlevel_class_loader_.createUniqueInstance(class_type, + std::forward(args)...); RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "std::unique_ptr to object of real type %s created.", @@ -145,7 +153,9 @@ UniquePtr ClassLoader::createUniqueInstance(const std::string & lookup_nam } template -T * ClassLoader::createUnmanagedInstance(const std::string & lookup_name) +template, bool>> +T * ClassLoader::createUnmanagedInstance(const std::string & lookup_name, Args &&... args) /***************************************************************************/ { RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", @@ -163,7 +173,8 @@ T * ClassLoader::createUnmanagedInstance(const std::string & lookup_name) std::string class_type = getClassType(lookup_name); RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "%s maps to real class type %s", lookup_name.c_str(), class_type.c_str()); - instance = lowlevel_class_loader_.createUnmanagedInstance(class_type); + instance = lowlevel_class_loader_.createUnmanagedInstance(class_type, + std::forward(args)...); RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Instance of type %s created.", class_type.c_str()); diff --git a/pluginlib/test/include/test_base.hpp b/pluginlib/test/include/test_base.hpp index 5b5978a..267f26c 100644 --- a/pluginlib/test/include/test_base.hpp +++ b/pluginlib/test/include/test_base.hpp @@ -29,7 +29,10 @@ #ifndef TEST_BASE_HPP_ #define TEST_BASE_HPP_ +#include + #include +#include namespace test_base { @@ -43,5 +46,33 @@ class TEST_PLUGINLIB_FIXTURE_PUBLIC Fubar protected: Fubar() {} }; + +class TEST_PLUGINLIB_FIXTURE_PUBLIC FubarWithCtor +{ +public: + virtual double result() = 0; + virtual ~FubarWithCtor() = default; + +protected: + FubarWithCtor() = default; +}; } // namespace test_base + +template<> +struct class_loader::InterfaceTraits +{ + // Using `std::unique_ptr` to test forwarding of move-only types. + using constructor_parameters = class_loader::ConstructorParameters>; +}; + +static_assert( + class_loader::is_interface_constructible_v>, + "BaseWithInterfaceCtor should be interface constructible with the specifed types." +); + +static_assert( + class_loader::is_interface_constructible_v&&>, + "BaseWithInterfaceCtor should be interface constructible with the specifed types." +); + #endif // TEST_BASE_HPP_ diff --git a/pluginlib/test/include/test_plugins.hpp b/pluginlib/test/include/test_plugins.hpp index 5ad2624..023391c 100644 --- a/pluginlib/test/include/test_plugins.hpp +++ b/pluginlib/test/include/test_plugins.hpp @@ -30,6 +30,7 @@ #define TEST_PLUGINS_HPP_ #include +#include #include #include @@ -75,6 +76,21 @@ class TEST_PLUGINLIB_FIXTURE_PUBLIC Foo : public test_base::Fubar return foo_ * foo_; } +private: + double foo_; +}; + +class TEST_PLUGINLIB_FIXTURE_PUBLIC FooWithCtor : public test_base::FubarWithCtor +{ +public: + explicit FooWithCtor(std::unique_ptr foo) + : foo_(*foo) {} + + double result() override + { + return foo_ * foo_; + } + private: double foo_; }; diff --git a/pluginlib/test/test_plugins.cpp b/pluginlib/test/test_plugins.cpp index 1c3708b..5091cf7 100644 --- a/pluginlib/test/test_plugins.cpp +++ b/pluginlib/test/test_plugins.cpp @@ -32,3 +32,4 @@ PLUGINLIB_EXPORT_CLASS(test_plugins::Foo, test_base::Fubar) PLUGINLIB_EXPORT_CLASS(test_plugins::Bar, test_base::Fubar) +PLUGINLIB_EXPORT_CLASS(test_plugins::FooWithCtor, test_base::FubarWithCtor) diff --git a/pluginlib/test/test_plugins.xml b/pluginlib/test/test_plugins.xml index 7327730..f09a966 100644 --- a/pluginlib/test/test_plugins.xml +++ b/pluginlib/test/test_plugins.xml @@ -5,6 +5,9 @@ This is a bar plugin. + + This is a foo plugin with constructor. + This is a broken none plugin. diff --git a/pluginlib/test/unique_ptr_test.cpp b/pluginlib/test/unique_ptr_test.cpp index e59bda1..eb8ec4d 100644 --- a/pluginlib/test/unique_ptr_test.cpp +++ b/pluginlib/test/unique_ptr_test.cpp @@ -67,6 +67,23 @@ TEST(PluginlibUniquePtrTest, workingPlugin) { } } +TEST(PluginlibUniquePtrTest, workingPluginCtor) { + pluginlib::ClassLoader test_loader("test_pluginlib", + "test_base::FubarWithCtor"); + + try { + pluginlib::UniquePtr foo = + test_loader.createUniqueInstance("test_pluginlib/foo_with_ctor", + std::make_unique(10.0)); + EXPECT_DOUBLE_EQ(100.0, foo->result()); + } catch (pluginlib::PluginlibException & ex) { + FAIL() << "Throwing exception: " << ex.what(); + return; + } catch (...) { + FAIL() << "Uncaught exception"; + } +} + TEST(PluginlibUniquePtrTest, createUniqueInstanceAndUnloadLibrary) { RCUTILS_LOG_INFO("Making the ClassLoader..."); pluginlib::ClassLoader pl("test_pluginlib", "test_base::Fubar"); diff --git a/pluginlib/test/utest.cpp b/pluginlib/test/utest.cpp index a47b710..772a858 100644 --- a/pluginlib/test/utest.cpp +++ b/pluginlib/test/utest.cpp @@ -74,6 +74,23 @@ TEST(PluginlibTest, workingPlugin) { } } +TEST(PluginlibTest, workingPluginCtor) { + pluginlib::ClassLoader test_loader("test_pluginlib", + "test_base::FubarWithCtor"); + + try { + std::shared_ptr foo = + test_loader.createSharedInstance("test_pluginlib/foo_with_ctor", + std::make_unique(10.0)); + EXPECT_DOUBLE_EQ(100.0, foo->result()); + } catch (pluginlib::PluginlibException & ex) { + FAIL() << "Throwing exception: " << ex.what(); + return; + } catch (...) { + FAIL() << "Uncaught exception"; + } +} + TEST(PluginlibTest, createUnmanagedInstanceAndUnloadLibrary) { RCUTILS_LOG_INFO("Making the ClassLoader..."); pluginlib::ClassLoader pl("test_pluginlib", "test_base::Fubar"); @@ -99,6 +116,33 @@ TEST(PluginlibTest, createUnmanagedInstanceAndUnloadLibrary) { RCUTILS_LOG_INFO("Done."); } +TEST(PluginlibTest, createUnmanagedInstanceCtorAndUnloadLibrary) { + RCUTILS_LOG_INFO("Making the ClassLoader..."); + pluginlib::ClassLoader pl("test_pluginlib", "test_base::FubarWithCtor"); + + RCUTILS_LOG_INFO("Instantiating plugin..."); + test_base::FubarWithCtor * inst = pl.createUnmanagedInstance("test_pluginlib/foo_with_ctor", + std::make_unique(10.0)); + EXPECT_DOUBLE_EQ(100.0, inst->result()); + + RCUTILS_LOG_INFO("Deleting plugin..."); + delete inst; + + RCUTILS_LOG_INFO("Checking if plugin is loaded with isClassLoaded..."); + if (pl.isClassLoaded("test_pluginlib/foo_with_ctor")) { + RCUTILS_LOG_INFO("Class is loaded"); + } else { + FAIL() << "Library containing class should be loaded but isn't."; + } + RCUTILS_LOG_INFO("Trying to unload class with unloadLibraryForClass..."); + try { + pl.unloadLibraryForClass("test_pluginlib/foo_with_ctor"); + } catch (pluginlib::PluginlibException & e) { + FAIL() << "Could not unload library when I should be able to. " << e.what(); + } + RCUTILS_LOG_INFO("Done."); +} + TEST(PluginlibTest, createManagedInstanceAndUnloadLibrary) { RCUTILS_LOG_INFO("Making the ClassLoader..."); pluginlib::ClassLoader pl("test_pluginlib", "test_base::Fubar");