diff --git a/include/class_loader/class_loader.hpp b/include/class_loader/class_loader.hpp
index 7615b687..de79c633 100644
--- a/include/class_loader/class_loader.hpp
+++ b/include/class_loader/class_loader.hpp
@@ -121,7 +121,20 @@ class ClassLoader
* if the library is not yet loaded (which typically happens when in "On Demand Load/Unload" mode).
*
* @param derived_class_name The name of the class we want to create (@see getAvailableClasses())
- * @return A boost::shared_ptr to newly created plugin object
+ * @return A std::shared_ptr to newly created plugin object
+ */
+ template
+ std::shared_ptr createSharedInstance(const std::string & derived_class_name)
+ {
+ return std::shared_ptr(
+ createRawInstance(derived_class_name, true),
+ boost::bind(&ClassLoader::onPluginDeletion, this, _1));
+ }
+
+ /**
+ * @brief Generates an instance of loadable classes (i.e. class_loader).
+ *
+ * Same as createSharedInstance() except it returns a boost::shared_ptr.
*/
template
boost::shared_ptr createInstance(const std::string & derived_class_name)
diff --git a/include/class_loader/multi_library_class_loader.hpp b/include/class_loader/multi_library_class_loader.hpp
index e07254c3..41bf2337 100644
--- a/include/class_loader/multi_library_class_loader.hpp
+++ b/include/class_loader/multi_library_class_loader.hpp
@@ -72,10 +72,10 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
* This version does not look in a specific library for the factory, but rather the first open library that defines the classs
* @param Base - polymorphic type indicating base class
* @param class_name - the name of the concrete plugin class we want to instantiate
- * @return A boost::shared_ptr to newly created plugin
+ * @return A std::shared_ptr to newly created plugin
*/
template
- boost::shared_ptr createInstance(const std::string & class_name)
+ std::shared_ptr createSharedInstance(const std::string & class_name)
{
CONSOLE_BRIDGE_logDebug(
"class_loader::MultiLibraryClassLoader: "
@@ -90,7 +90,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
"was explicitly loaded through MultiLibraryClassLoader::loadLibrary()");
}
- return loader->createInstance(class_name);
+ return loader->createSharedInstance(class_name);
}
/**
@@ -99,7 +99,48 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
* @param Base - polymorphic type indicating base class
* @param class_name - the name of the concrete plugin class we want to instantiate
* @param library_path - the library from which we want to create the plugin
- * @return A boost::shared_ptr to newly created plugin
+ * @return A std::shared_ptr to newly created plugin
+ */
+ template
+ std::shared_ptr
+ createSharedInstance(const std::string & class_name, const std::string & library_path)
+ {
+ ClassLoader * loader = getClassLoaderForLibrary(library_path);
+ if (nullptr == loader) {
+ throw class_loader::NoClassLoaderExistsException(
+ "Could not create instance as there is no ClassLoader in "
+ "MultiLibraryClassLoader bound to library " + library_path +
+ " Ensure you called MultiLibraryClassLoader::loadLibrary()");
+ }
+ return loader->createSharedInstance(class_name);
+ }
+
+ /**
+ * @brief Creates an instance of an object of given class name with ancestor class Base
+ * Same as createSharedInstance() except it returns a boost::shared_ptr.
+ */
+ template
+ boost::shared_ptr createInstance(const std::string & class_name)
+ {
+ CONSOLE_BRIDGE_logDebug(
+ "class_loader::MultiLibraryClassLoader: "
+ "Attempting to create instance of class type %s.",
+ class_name.c_str());
+ ClassLoader * loader = getClassLoaderForClass(class_name);
+ if (nullptr == loader) {
+ throw class_loader::CreateClassException(
+ "MultiLibraryClassLoader: Could not create object of class type " +
+ class_name +
+ " as no factory exists for it. Make sure that the library exists and "
+ "was explicitly loaded through MultiLibraryClassLoader::loadLibrary()");
+ }
+
+ return loader->createInstance(class_name);
+ }
+
+ /**
+ * @brief Creates an instance of an object of given class name with ancestor class Base
+ * Same as createSharedInstance() except it returns a boost::shared_ptr.
*/
template
boost::shared_ptr
@@ -117,10 +158,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
/**
* @brief Creates an instance of an object of given class name with ancestor class Base
- * This version does not look in a specific library for the factory, but rather the first open library that defines the classs
- * @param Base - polymorphic type indicating base class
- * @param class_name - the name of the concrete plugin class we want to instantiate
- * @return A unique pointer to newly created plugin
+ * Same as createSharedInstance() except it returns a std::unique_ptr.
*/
template
ClassLoader::UniquePtr createUniqueInstance(const std::string & class_name)
@@ -141,11 +179,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
/**
* @brief Creates an instance of an object of given class name with ancestor class Base
- * This version takes a specific library to make explicit the factory being used
- * @param Base - polymorphic type indicating base class
- * @param class_name - the name of the concrete plugin class we want to instantiate
- * @param library_path - the library from which we want to create the plugin
- * @return A unique pointer to newly created plugin
+ * Same as createSharedInstance() except it returns a std::unique_ptr.
*/
template
ClassLoader::UniquePtr
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 46e1b5f1..15bbb334 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -18,6 +18,12 @@ if(TARGET ${PROJECT_NAME}_utest)
add_dependencies(${PROJECT_NAME}_utest ${PROJECT_NAME}_TestPlugins1 ${PROJECT_NAME}_TestPlugins2)
endif()
+catkin_add_gtest(${PROJECT_NAME}_shared_ptr_test shared_ptr_test.cpp)
+if(TARGET ${PROJECT_NAME}_shared_ptr_test)
+ target_link_libraries(${PROJECT_NAME}_shared_ptr_test ${Boost_LIBRARIES} ${class_loader_LIBRARIES})
+ add_dependencies(${PROJECT_NAME}_shared_ptr_test ${PROJECT_NAME}_TestPlugins1 ${PROJECT_NAME}_TestPlugins2)
+endif()
+
catkin_add_gtest(${PROJECT_NAME}_unique_ptr_test unique_ptr_test.cpp)
if(TARGET ${PROJECT_NAME}_unique_ptr_test)
target_link_libraries(${PROJECT_NAME}_unique_ptr_test ${Boost_LIBRARIES} ${class_loader_LIBRARIES})
diff --git a/test/shared_ptr_test.cpp b/test/shared_ptr_test.cpp
new file mode 100644
index 00000000..cb398f49
--- /dev/null
+++ b/test/shared_ptr_test.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2012, Willow Garage, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the Willow Garage, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "class_loader/class_loader.hpp"
+#include "class_loader/multi_library_class_loader.hpp"
+
+#include "gtest/gtest.h"
+
+#include "./base.hpp"
+
+const std::string LIBRARY_1 = class_loader::systemLibraryFormat("class_loader_TestPlugins1"); // NOLINT
+const std::string LIBRARY_2 = class_loader::systemLibraryFormat("class_loader_TestPlugins2"); // NOLINT
+
+TEST(ClassLoaderSharedPtrTest, basicLoad) {
+ try {
+ class_loader::ClassLoader loader1(LIBRARY_1, false);
+ loader1.createSharedInstance("Cat")->saySomething(); // See if lazy load works
+ } catch (class_loader::ClassLoaderException & e) {
+ FAIL() << "ClassLoaderException: " << e.what() << "\n";
+ }
+
+ SUCCEED();
+}
+
+TEST(ClassLoaderSharedPtrTest, correctNonLazyLoadUnload) {
+ try {
+ ASSERT_FALSE(class_loader::impl::isLibraryLoadedByAnybody(LIBRARY_1));
+ class_loader::ClassLoader loader1(LIBRARY_1, false);
+ ASSERT_TRUE(class_loader::impl::isLibraryLoadedByAnybody(LIBRARY_1));
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+ loader1.unloadLibrary();
+ ASSERT_FALSE(class_loader::impl::isLibraryLoadedByAnybody(LIBRARY_1));
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+ return;
+ } catch (class_loader::ClassLoaderException & e) {
+ FAIL() << "ClassLoaderException: " << e.what() << "\n";
+ } catch (...) {
+ FAIL() << "Unhandled exception";
+ }
+}
+
+TEST(ClassLoaderSharedPtrTest, correctLazyLoadUnload) {
+ try {
+ ASSERT_FALSE(class_loader::impl::isLibraryLoadedByAnybody(LIBRARY_1));
+ class_loader::ClassLoader loader1(LIBRARY_1, true);
+ ASSERT_FALSE(class_loader::impl::isLibraryLoadedByAnybody(LIBRARY_1));
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+
+ {
+ std::shared_ptr obj = loader1.createSharedInstance("Cat");
+ ASSERT_TRUE(class_loader::impl::isLibraryLoadedByAnybody(LIBRARY_1));
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+ }
+
+ // The library will unload automatically when the only plugin object left is destroyed
+ ASSERT_FALSE(class_loader::impl::isLibraryLoadedByAnybody(LIBRARY_1));
+ return;
+ } catch (class_loader::ClassLoaderException & e) {
+ FAIL() << "ClassLoaderException: " << e.what() << "\n";
+ } catch (...) {
+ FAIL() << "Unhandled exception";
+ }
+}
+
+TEST(ClassLoaderSharedPtrTest, nonExistentPlugin) {
+ class_loader::ClassLoader loader1(LIBRARY_1, false);
+
+ try {
+ std::shared_ptr obj = loader1.createSharedInstance("Bear");
+ if (nullptr == obj) {
+ FAIL() << "Null object being returned instead of exception thrown.";
+ }
+
+ obj->saySomething();
+ } catch (const class_loader::CreateClassException &) {
+ SUCCEED();
+ return;
+ } catch (...) {
+ FAIL() << "Unknown exception caught.\n";
+ }
+
+ FAIL() << "Did not throw exception as expected.\n";
+}
+
+TEST(ClassLoaderSharedPtrTest, nonExistentLibrary) {
+ try {
+ class_loader::ClassLoader loader1("libDoesNotExist.so", false);
+ } catch (const class_loader::LibraryLoadException &) {
+ SUCCEED();
+ return;
+ } catch (...) {
+ FAIL() << "Unknown exception caught.\n";
+ }
+
+ FAIL() << "Did not throw exception as expected.\n";
+}
+
+class InvalidBase
+{
+};
+
+TEST(ClassLoaderSharedPtrTest, invalidBase) {
+ try {
+ class_loader::ClassLoader loader1(LIBRARY_1, false);
+ if (loader1.isClassAvailable("Cat")) {
+ FAIL() << "Cat should not be available for InvalidBase";
+ } else if (loader1.isClassAvailable("Cat")) {
+ SUCCEED();
+ return;
+ } else {
+ FAIL() << "Class not available for correct base class.";
+ }
+ } catch (const class_loader::LibraryLoadException &) {
+ FAIL() << "Unexpected exception";
+ } catch (...) {
+ FAIL() << "Unexpected and unknown exception caught.\n";
+ }
+}
+
+void wait(int seconds)
+{
+ std::this_thread::sleep_for(std::chrono::seconds(seconds));
+}
+
+void run(class_loader::ClassLoader * loader)
+{
+ std::vector classes = loader->getAvailableClasses();
+ for (auto & class_ : classes) {
+ loader->createSharedInstance(class_)->saySomething();
+ }
+}
+
+TEST(ClassLoaderSharedPtrTest, threadSafety) {
+ class_loader::ClassLoader loader1(LIBRARY_1);
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ // Note: Hard to test thread safety to make sure memory isn't corrupted.
+ // The hope is this test is hard enough that once in a while it'll segfault
+ // or something if there's some implementation error.
+ try {
+ std::vector client_threads;
+
+ for (size_t c = 0; c < 1000; c++) {
+ client_threads.push_back(new std::thread(std::bind(&run, &loader1)));
+ }
+
+ for (auto & client_thread : client_threads) {
+ client_thread->join();
+ }
+
+ for (auto & client_thread : client_threads) {
+ delete (client_thread);
+ }
+
+ loader1.unloadLibrary();
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+ } catch (const class_loader::ClassLoaderException &) {
+ FAIL() << "Unexpected ClassLoaderException.";
+ } catch (...) {
+ FAIL() << "Unknown exception.";
+ }
+}
+
+TEST(ClassLoaderSharedPtrTest, loadRefCountingNonLazy) {
+ try {
+ class_loader::ClassLoader loader1(LIBRARY_1, false);
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ loader1.loadLibrary();
+ loader1.loadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ loader1.unloadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ loader1.unloadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ loader1.unloadLibrary();
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+
+ loader1.unloadLibrary();
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+
+ loader1.loadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ return;
+ } catch (const class_loader::ClassLoaderException &) {
+ FAIL() << "Unexpected exception.\n";
+ } catch (...) {
+ FAIL() << "Unknown exception caught.\n";
+ }
+
+ FAIL() << "Did not throw exception as expected.\n";
+}
+
+TEST(ClassLoaderSharedPtrTest, loadRefCountingLazy) {
+ try {
+ class_loader::ClassLoader loader1(LIBRARY_1, true);
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+
+ {
+ std::shared_ptr obj = loader1.createSharedInstance("Dog");
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+ }
+
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+
+ loader1.loadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ loader1.loadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ loader1.unloadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ loader1.unloadLibrary();
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+
+ loader1.unloadLibrary();
+ ASSERT_FALSE(loader1.isLibraryLoaded());
+
+ loader1.loadLibrary();
+ ASSERT_TRUE(loader1.isLibraryLoaded());
+
+ return;
+ } catch (const class_loader::ClassLoaderException &) {
+ FAIL() << "Unexpected exception.\n";
+ } catch (...) {
+ FAIL() << "Unknown exception caught.\n";
+ }
+
+ FAIL() << "Did not throw exception as expected.\n";
+}
+
+void testMultiClassLoader(bool lazy)
+{
+ try {
+ class_loader::MultiLibraryClassLoader loader(lazy);
+ loader.loadLibrary(LIBRARY_1);
+ loader.loadLibrary(LIBRARY_2);
+ for (int i = 0; i < 2; ++i) {
+ loader.createSharedInstance("Cat")->saySomething();
+ loader.createSharedInstance("Dog")->saySomething();
+ loader.createSharedInstance("Robot")->saySomething();
+ }
+ } catch (class_loader::ClassLoaderException & e) {
+ FAIL() << "ClassLoaderException: " << e.what() << "\n";
+ }
+
+ SUCCEED();
+}
+
+TEST(MultiClassLoaderTest, lazyLoad) {
+ testMultiClassLoader(true);
+}
+TEST(MultiClassLoaderTest, lazyLoadSecondTime) {
+ testMultiClassLoader(true);
+}
+TEST(MultiClassLoaderTest, nonLazyLoad) {
+ testMultiClassLoader(false);
+}
+TEST(MultiClassLoaderTest, noWarningOnLazyLoad) {
+ try {
+ std::shared_ptr cat, dog, rob;
+ {
+ class_loader::MultiLibraryClassLoader loader(true);
+ loader.loadLibrary(LIBRARY_1);
+ loader.loadLibrary(LIBRARY_2);
+
+ cat = loader.createSharedInstance("Cat");
+ dog = loader.createSharedInstance("Dog");
+ rob = loader.createSharedInstance("Robot");
+ }
+ cat->saySomething();
+ dog->saySomething();
+ rob->saySomething();
+ } catch (class_loader::ClassLoaderException & e) {
+ FAIL() << "ClassLoaderException: " << e.what() << "\n";
+ }
+
+ SUCCEED();
+}
+
+// Run all the tests that were declared with TEST()
+int main(int argc, char ** argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/unique_ptr_test.cpp b/test/unique_ptr_test.cpp
index 2d03b3a8..e0fccf95 100644
--- a/test/unique_ptr_test.cpp
+++ b/test/unique_ptr_test.cpp
@@ -32,12 +32,13 @@
#include
#include
-#include
+#include
#include
#include
#include
#include
+#include
#include
#include "./base.hpp"
@@ -102,7 +103,7 @@ TEST(ClassLoaderUniquePtrTest, nonExistentPlugin) {
void wait(int seconds)
{
- boost::this_thread::sleep(boost::posix_time::seconds(seconds));
+ std::this_thread::sleep_for(std::chrono::seconds(seconds));
}
void run(ClassLoader * loader)
@@ -121,7 +122,7 @@ TEST(ClassLoaderUniquePtrTest, threadSafety) {
// The hope is this test is hard enough that once in a while it'll segfault
// or something if there's some implementation error.
try {
- std::vector client_threads;
+ std::vector client_threads;
for (size_t c = 0; c < 1000; c++) {
client_threads.emplace_back(std::bind(&run, &loader1));
diff --git a/test/utest.cpp b/test/utest.cpp
index e41a0708..6bf9b23b 100644
--- a/test/utest.cpp
+++ b/test/utest.cpp
@@ -27,11 +27,12 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include
-#include
+#include
#include
+#include
#include
#include
+#include
#include
#include "class_loader/class_loader.hpp"
@@ -152,7 +153,7 @@ TEST(ClassLoaderTest, invalidBase) {
void wait(int seconds)
{
- boost::this_thread::sleep(boost::posix_time::seconds(seconds));
+ std::this_thread::sleep_for(std::chrono::seconds(seconds));
}
void run(class_loader::ClassLoader * loader)
@@ -171,10 +172,10 @@ TEST(ClassLoaderTest, threadSafety) {
// The hope is this test is hard enough that once in a while it'll segfault
// or something if there's some implementation error.
try {
- std::vector client_threads;
+ std::vector client_threads;
for (size_t c = 0; c < 1000; c++) {
- client_threads.push_back(new boost::thread(boost::bind(&run, &loader1)));
+ client_threads.push_back(new std::thread(std::bind(&run, &loader1)));
}
for (auto & client_thread : client_threads) {