Skip to content

Commit adcb526

Browse files
wjwwoodmikaelarguedas
authored andcommitted
manual port of unique ptr pr (#58)
* manual port of #38 * add static private setter function for unmanaged flag * missing newline * use NULL unless within a "if C++11" block * fixups
1 parent 07a5e8a commit adcb526

File tree

6 files changed

+477
-60
lines changed

6 files changed

+477
-60
lines changed

include/class_loader/class_loader.h

Lines changed: 106 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ std::string systemLibraryFormat(const std::string & library_name);
7575
class ClassLoader
7676
{
7777
public:
78+
#if __cplusplus >= 201103L
79+
template<typename Base>
80+
using DeleterType = std::function<void (Base *)>;
81+
82+
template<typename Base>
83+
using UniquePtr = std::unique_ptr<Base, DeleterType<Base>>;
84+
#endif
85+
7886
/**
7987
* @brief Constructor for ClassLoader
8088
* @param library_path - The path of the runtime library to load
@@ -100,54 +108,66 @@ class ClassLoader
100108
}
101109

102110
/**
103-
* @brief Generates an instance of loadable classes (i.e. class_loader). It is not necessary for the user to call loadLibrary() as it will be invoked automatically if the library is not yet loaded (which typically happens when in "On Demand Load/Unload" mode).
111+
* @brief Generates an instance of loadable classes (i.e. class_loader).
112+
*
113+
* It is not necessary for the user to call loadLibrary() as it will be invoked automatically
114+
* if the library is not yet loaded (which typically happens when in "On Demand Load/Unload" mode).
115+
*
104116
* @param derived_class_name The name of the class we want to create (@see getAvailableClasses())
105117
* @return A std::shared_ptr<Base> to newly created plugin object
106118
*/
107119
template <class Base>
108120
std::shared_ptr<Base> createInstance(const std::string & derived_class_name)
109121
{
110-
if (ClassLoader::hasUnmanagedInstanceBeenCreated() && isOnDemandLoadUnloadEnabled()) {
111-
CONSOLE_BRIDGE_logInform(
112-
"class_loader::ClassLoader: "
113-
"An attempt is being made to create a managed plugin instance (i.e. std::shared_ptr), "
114-
"however an unmanaged instance was created within this process address space. "
115-
"This means libraries for the managed instances will not be shutdown automatically on "
116-
"final plugin destruction if on demand (lazy) loading/unloading mode is used.");
117-
}
118-
119-
if (!isLibraryLoaded()) {
120-
loadLibrary();
121-
}
122-
123-
Base * obj = class_loader::impl::createInstance<Base>(derived_class_name, this);
124-
assert(obj != nullptr); // Unreachable assertion if createInstance() throws on failure
125-
126-
std::lock_guard<std::recursive_mutex> lock(plugin_ref_count_mutex_);
127-
++plugin_ref_count_;
122+
return std::shared_ptr<Base>(
123+
createRawInstance<Base>(derived_class_name, true),
124+
std::bind(&ClassLoader::onPluginDeletion<Base>, this, std::placeholders::_1)
125+
);
126+
}
128127

129-
std::shared_ptr<Base> smart_obj(
130-
obj, std::bind(&class_loader::ClassLoader::onPluginDeletion<Base>, this, std::placeholders::_1));
131-
return smart_obj;
128+
#if __cplusplus >= 201103L
129+
/// Generates an instance of loadable classes (i.e. class_loader).
130+
/**
131+
* It is not necessary for the user to call loadLibrary() as it will be
132+
* invoked automatically if the library is not yet loaded (which typically
133+
* happens when in "On Demand Load/Unload" mode).
134+
*
135+
* If you release the wrapped pointer you must manually call the original
136+
* deleter when you want to destroy the released pointer.
137+
*
138+
* @param derived_class_name
139+
* The name of the class we want to create (@see getAvailableClasses()).
140+
* @return A std::unique_ptr<Base> to newly created plugin object.
141+
*/
142+
template<class Base>
143+
UniquePtr<Base> createUniqueInstance(const std::string& derived_class_name)
144+
{
145+
Base * raw = createRawInstance<Base>(derived_class_name, true);
146+
return std::unique_ptr<Base, DeleterType<Base>>(
147+
raw,
148+
std::bind(&ClassLoader::onPluginDeletion<Base>, this, std::placeholders::_1)
149+
);
132150
}
151+
#endif
133152

153+
/// Generates an instance of loadable classes (i.e. class_loader).
134154
/**
135-
* @brief Generates an instance of loadable classes (i.e. class_loader). It is not necessary for the user to call loadLibrary() as it will be invoked automatically if the library is not yet loaded (which typically happens when in "On Demand Load/Unload" mode).
136-
* @param derived_class_name The name of the class we want to create (@see getAvailableClasses())
155+
* It is not necessary for the user to call loadLibrary() as it will be
156+
* invoked automatically if the library is not yet loaded (which typically
157+
* happens when in "On Demand Load/Unload" mode).
158+
*
159+
* Creating an unmanaged instance disables dynamically unloading libraries
160+
* when managed pointers go out of scope for all class loaders in this
161+
* process.
162+
*
163+
* @param derived_class_name
164+
* The name of the class we want to create (@see getAvailableClasses()).
137165
* @return An unmanaged (i.e. not a shared_ptr) Base* to newly created plugin object.
138166
*/
139167
template <class Base>
140168
Base * createUnmanagedInstance(const std::string & derived_class_name)
141169
{
142-
has_unmananged_instance_been_created_ = true;
143-
if (!isLibraryLoaded()) {
144-
loadLibrary();
145-
}
146-
147-
Base * obj = class_loader::impl::createInstance<Base>(derived_class_name, this);
148-
assert(obj != nullptr); // Unreachable assertion if createInstance() throws on failure
149-
150-
return obj;
170+
return createRawInstance<Base>(derived_class_name, false);
151171
}
152172

153173
/**
@@ -236,12 +256,65 @@ class ClassLoader
236256
}
237257
}
238258

259+
/// Generates an instance of loadable classes (i.e. class_loader).
260+
/**
261+
* It is not necessary for the user to call loadLibrary() as it will be
262+
* invoked automatically if the library is not yet loaded (which typically
263+
* happens when in "On Demand Load/Unload" mode).
264+
*
265+
* @param derived_class_name
266+
* The name of the class we want to create (@see getAvailableClasses()).
267+
* @param managed
268+
* If true, the returned pointer is assumed to be wrapped in a smart
269+
* pointer by the caller.
270+
* @return A Base* to newly created plugin object.
271+
*/
272+
template <class Base>
273+
Base* createRawInstance(const std::string& derived_class_name, bool managed)
274+
{
275+
if (!managed) {
276+
this->setUnmanagedInstanceBeenCreated(true);
277+
}
278+
279+
if (
280+
managed &&
281+
ClassLoader::hasUnmanagedInstanceBeenCreated() &&
282+
isOnDemandLoadUnloadEnabled())
283+
{
284+
CONSOLE_BRIDGE_logInform(
285+
"class_loader::ClassLoader: "
286+
"An attempt is being made to create a managed plugin instance (i.e. boost::shared_ptr), "
287+
"however an unmanaged instance was created within this process address space. "
288+
"This means libraries for the managed instances will not be shutdown automatically on "
289+
"final plugin destruction if on demand (lazy) loading/unloading mode is used."
290+
);
291+
}
292+
293+
if (!isLibraryLoaded()) {
294+
loadLibrary();
295+
}
296+
297+
Base* obj = class_loader::impl::createInstance<Base>(derived_class_name, this);
298+
assert(obj != NULL); // Unreachable assertion if createInstance() throws on failure.
299+
300+
if (managed)
301+
{
302+
std::lock_guard<std::recursive_mutex> lock(plugin_ref_count_mutex_);
303+
++plugin_ref_count_;
304+
}
305+
306+
return obj;
307+
}
308+
239309
/**
240310
* @brief Getter for if an unmanaged (i.e. unsafe) instance has been created flag
241311
*/
242312
CLASS_LOADER_PUBLIC
243313
static bool hasUnmanagedInstanceBeenCreated();
244314

315+
CLASS_LOADER_PUBLIC
316+
static void setUnmanagedInstanceBeenCreated(bool state);
317+
245318
/**
246319
* @brief As the library may be unloaded in "on-demand load/unload" mode, unload maybe called from createInstance(). The problem is that createInstance() locks the plugin_ref_count as does unloadLibrary(). This method is the implementation of unloadLibrary but with a parameter to decide if plugin_ref_mutex_ should be locked
247320
* @param lock_plugin_ref_count - Set to true if plugin_ref_count_mutex_ should be locked, else false

include/class_loader/multi_library_class_loader.h

Lines changed: 91 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,17 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
7272
template <class Base>
7373
std::shared_ptr<Base> createInstance(const std::string & class_name)
7474
{
75-
CONSOLE_BRIDGE_logDebug(
76-
"class_loader::MultiLibraryClassLoader: "
77-
"Attempting to create instance of class type %s.", class_name.c_str());
78-
for (auto & loader : getAllAvailableClassLoaders()) {
79-
if (loader->isClassAvailable<Base>(class_name)) {
80-
return loader->createInstance<Base>(class_name);
81-
}
75+
ClassLoader * loader = getClassLoaderForClass<Base>(class_name);
76+
77+
if (loader == NULL) {
78+
throw class_loader::CreateClassException(
79+
"MultiLibraryClassLoader: Could not create object of class type " +
80+
class_name +
81+
" as no factory exists for it. Make sure that the library exists and "
82+
"was explicitly loaded through MultiLibraryClassLoader::loadLibrary()");
8283
}
8384

84-
throw class_loader::CreateClassException(
85-
"MultiLibraryClassLoader: "
86-
"Could not create object of class type '" + class_name + "' as no factory exists for it. "
87-
"Make sure that the library exists and was explicitly loaded through "
88-
"MultiLibraryClassLoader::loadLibrary()");
85+
return loader->createInstance<Base>(class_name);
8986
}
9087

9188
/**
@@ -100,15 +97,63 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
10097
std::shared_ptr<Base> createInstance(const std::string & class_name, const std::string & library_path)
10198
{
10299
ClassLoader * loader = getClassLoaderForLibrary(library_path);
103-
if (!loader) {
100+
if (loader == NULL) {
104101
throw class_loader::NoClassLoaderExistsException(
105-
"Could not create instance as there is no ClassLoader in MultiLibraryClassLoader bound to "
106-
"library '" + library_path + "'. "
107-
"Ensure you called MultiLibraryClassLoader::loadLibrary()");
102+
"Could not create instance as there is no ClassLoader in "
103+
"MultiLibraryClassLoader bound to library " + library_path +
104+
" Ensure you called MultiLibraryClassLoader::loadLibrary()");
108105
}
109106
return loader->createInstance<Base>(class_name);
110107
}
111108

109+
#if __cplusplus >= 201103L
110+
/// Creates an instance of an object of given class name with ancestor class Base
111+
/**
112+
* This version does not look in a specific library for the factory, but rather the first open library that defines the classs
113+
* @param Base - polymorphic type indicating base class
114+
* @param class_name - the name of the concrete plugin class we want to instantiate
115+
* @return A unique pointer to newly created plugin
116+
*/
117+
template <class Base>
118+
ClassLoader::UniquePtr<Base> createUniqueInstance(const std::string& class_name)
119+
{
120+
CONSOLE_BRIDGE_logDebug(
121+
"class_loader::MultiLibraryClassLoader: Attempting to create instance of class type %s.",
122+
class_name.c_str());
123+
ClassLoader * loader = getClassLoaderForClass<Base>(class_name);
124+
if (loader == nullptr) {
125+
throw class_loader::CreateClassException(
126+
"MultiLibraryClassLoader: Could not create object of class type " + class_name +
127+
" as no factory exists for it. "
128+
"Make sure that the library exists and was explicitly loaded through "
129+
"MultiLibraryClassLoader::loadLibrary()");
130+
}
131+
return loader->createUniqueInstance<Base>(class_name);
132+
}
133+
134+
/// Creates an instance of an object of given class name with ancestor class Base
135+
/**
136+
* This version takes a specific library to make explicit the factory being used
137+
* @param Base - polymorphic type indicating base class
138+
* @param class_name - the name of the concrete plugin class we want to instantiate
139+
* @param library_path - the library from which we want to create the plugin
140+
* @return A unique pointer to newly created plugin
141+
*/
142+
template <class Base>
143+
ClassLoader::UniquePtr<Base>
144+
createUniqueInstance(const std::string& class_name, const std::string& library_path)
145+
{
146+
ClassLoader * loader = getClassLoaderForLibrary(library_path);
147+
if (loader == nullptr) {
148+
throw class_loader::NoClassLoaderExistsException(
149+
"Could not create instance as there is no ClassLoader in "
150+
"MultiLibraryClassLoader bound to library " + library_path +
151+
" Ensure you called MultiLibraryClassLoader::loadLibrary()");
152+
}
153+
return loader->createUniqueInstance<Base>(class_name);
154+
}
155+
#endif
156+
112157
/**
113158
* @brief Creates an instance of an object of given class name with ancestor class Base
114159
* This version does not look in a specific library for the factory, but rather the first open library that defines the classs
@@ -120,14 +165,12 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
120165
template <class Base>
121166
Base* createUnmanagedInstance(const std::string & class_name)
122167
{
123-
for (auto & loader : getAllAvailableClassLoaders()) {
124-
if (loader->isClassAvailable<Base>(class_name)) {
125-
return loader->createUnmanagedInstance<Base>(class_name);
126-
}
168+
ClassLoader * loader = getClassLoaderForClass<Base>(class_name);
169+
if (loader == NULL) {
170+
throw class_loader::CreateClassException(
171+
"MultiLibraryClassLoader: Could not create class of type " + class_name);
127172
}
128-
129-
throw class_loader::CreateClassException(
130-
"MultiLibraryClassLoader: Could not create class of type '" + class_name + "'");
173+
return loader->createUnmanagedInstance<Base>(class_name);
131174
}
132175

133176
/**
@@ -144,9 +187,9 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
144187
ClassLoader * loader = getClassLoaderForLibrary(library_path);
145188
if (!loader) {
146189
throw class_loader::NoClassLoaderExistsException(
147-
"Could not create instance as there is no ClassLoader in MultiLibraryClassLoader bound to "
148-
"library '" + library_path + "'. "
149-
"Ensure you called MultiLibraryClassLoader::loadLibrary()");
190+
"Could not create instance as there is no ClassLoader in "
191+
"MultiLibraryClassLoader bound to library " + library_path +
192+
" Ensure you called MultiLibraryClassLoader::loadLibrary()");
150193
}
151194
return loader->createUnmanagedInstance<Base>(class_name);
152195
}
@@ -231,10 +274,31 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
231274
/**
232275
* @brief Gets a handle to the class loader corresponding to a specific runtime library
233276
* @param library_path - the library from which we want to create the plugin
234-
* @return A pointer to the ClassLoader *, == nullptr if not found
277+
* @return A pointer to the ClassLoader *, == NULL if not found
235278
*/
236279
ClassLoader * getClassLoaderForLibrary(const std::string & library_path);
237280

281+
/// Gets a handle to the class loader corresponding to a specific class.
282+
/**
283+
* @param class_name name of class for which we want to create instance.
284+
* @return A pointer to the ClassLoader, or NULL if not found.
285+
*/
286+
template<typename Base>
287+
ClassLoader * getClassLoaderForClass(const std::string& class_name)
288+
{
289+
ClassLoaderVector loaders = getAllAvailableClassLoaders();
290+
for (ClassLoaderVector::iterator i = loaders.begin(); i != loaders.end(); ++i)
291+
{
292+
if (!(*i)->isLibraryLoaded()) {
293+
(*i)->loadLibrary();
294+
}
295+
if ((*i)->isClassAvailable<Base>(class_name)) {
296+
return *i;
297+
}
298+
}
299+
return NULL;
300+
}
301+
238302
/**
239303
* @brief Gets all class loaders loaded within scope
240304
*/

src/class_loader.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ bool ClassLoader::hasUnmanagedInstanceBeenCreated()
4141
return ClassLoader::has_unmananged_instance_been_created_;
4242
}
4343

44+
void ClassLoader::setUnmanagedInstanceBeenCreated(bool state)
45+
{
46+
ClassLoader::has_unmananged_instance_been_created_ = state;
47+
}
48+
4449
std::string systemLibraryPrefix()
4550
{
4651
#if !defined(WIN32)

test/CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,25 @@ if(TARGET ${PROJECT_NAME}_utest)
2525
add_dependencies(${PROJECT_NAME}_utest ${PROJECT_NAME}_TestPlugins1 ${PROJECT_NAME}_TestPlugins2)
2626
endif()
2727

28+
include(CheckCXXCompilerFlag)
29+
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
30+
if(COMPILER_SUPPORTS_CXX11)
31+
catkin_add_gtest(${PROJECT_NAME}_unique_ptr_test unique_ptr_test.cpp)
32+
if(TARGET ${PROJECT_NAME}_unique_ptr_test)
33+
target_link_libraries(${PROJECT_NAME}_unique_ptr_test
34+
${Boost_LIBRARIES}
35+
${class_loader_LIBRARIES}
36+
)
37+
set_target_properties(${PROJECT_NAME}_unique_ptr_test
38+
PROPERTIES
39+
COMPILE_FLAGS -std=c++11
40+
LINK_FLAGS -std=c++11
41+
)
42+
add_dependencies(${PROJECT_NAME}_unique_ptr_test
43+
${PROJECT_NAME}_TestPlugins1
44+
${PROJECT_NAME}_TestPlugins2
45+
)
46+
endif()
47+
endif()
48+
2849
add_subdirectory(fviz_case_study)

test/base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
class Base
55
{
66
public:
7+
virtual ~Base() {}
78
virtual void saySomething() = 0;
89
};
910

0 commit comments

Comments
 (0)