class_loader  master
The class_loader package is a ROS-independent package for loading plugins during runtime.
class_loader_core.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  * * Neither the name of the copyright holders nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #ifndef CLASS_LOADER__CLASS_LOADER_CORE_HPP_
33 #define CLASS_LOADER__CLASS_LOADER_CORE_HPP_
34 
35 #include <cstddef>
36 #include <cstdio>
37 #include <map>
38 #include <memory>
39 #include <mutex>
40 #include <string>
41 #include <typeinfo>
42 #include <utility>
43 #include <vector>
44 
45 // TODO(mikaelarguedas) remove this once console_bridge complies with this
46 // see https://github.com/ros/console_bridge/issues/55
47 #ifdef __clang__
48 # pragma clang diagnostic push
49 # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
50 #endif
51 #include "console_bridge/console.h"
52 #ifdef __clang__
53 # pragma clang diagnostic pop
54 #endif
55 
59 
61 
67 namespace class_loader
68 {
69 
70 class ClassLoader; // Forward declaration
71 
72 namespace impl
73 {
74 
75 // Typedefs
84 
87 
88 // Global storage
89 
101 
110 
119 
126 void setCurrentlyLoadingLibraryName(const std::string & library_name);
127 
128 
135 
143 
144 
154 FactoryMap & getFactoryMapForBaseClass(const std::string & typeid_base_class_name);
155 
161 template<typename Base>
163 {
164  return getFactoryMapForBaseClass(typeid(Base).name());
165 }
166 
179 
187 
194 void hasANonPurePluginLibraryBeenOpened(bool hasIt);
195 
196 // Plugin Functions
197 
209 template<typename Derived, typename Base>
210 void registerPlugin(const std::string & class_name, const std::string & base_class_name)
211 {
212  // Note: This function will be automatically invoked when a dlopen() call
213  // opens a library. Normally it will happen within the scope of loadLibrary(),
214  // but that may not be guaranteed.
215  CONSOLE_BRIDGE_logDebug(
216  "class_loader.impl: "
217  "Registering plugin factory for class = %s, ClassLoader* = %p and library name %s.",
218  class_name.c_str(), getCurrentlyActiveClassLoader(),
220 
221  if (nullptr == getCurrentlyActiveClassLoader()) {
222  CONSOLE_BRIDGE_logDebug(
223  "%s",
224  "class_loader.impl: ALERT!!! "
225  "A library containing plugins has been opened through a means other than through the "
226  "class_loader or pluginlib package. "
227  "This can happen if you build plugin libraries that contain more than just plugins "
228  "(i.e. normal code your app links against). "
229  "This inherently will trigger a dlopen() prior to main() and cause problems as class_loader "
230  "is not aware of plugin factories that autoregister under the hood. "
231  "The class_loader package can compensate, but you may run into namespace collision problems "
232  "(e.g. if you have the same plugin class in two different libraries and you load them both "
233  "at the same time). "
234  "The biggest problem is that library can now no longer be safely unloaded as the "
235  "ClassLoader does not know when non-plugin code is still in use. "
236  "In fact, no ClassLoader instance in your application will be unable to unload any library "
237  "once a non-pure one has been opened. "
238  "Please refactor your code to isolate plugins into their own libraries.");
240  }
241 
242  // Create factory
243  impl::AbstractMetaObject<Base> * new_factory =
244  new impl::MetaObject<Derived, Base>(class_name, base_class_name);
247 
248 
249  // Add it to global factory map map
251  FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
252  if (factoryMap.find(class_name) != factoryMap.end()) {
253  CONSOLE_BRIDGE_logWarn(
254  "class_loader.impl: SEVERE WARNING!!! "
255  "A namespace collision has occured with plugin factory for class %s. "
256  "New factory will OVERWRITE existing one. "
257  "This situation occurs when libraries containing plugins are directly linked against an "
258  "executable (the one running right now generating this message). "
259  "Please separate plugins out into their own library or just don't link against the library "
260  "and use either class_loader::ClassLoader/MultiLibraryClassLoader to open.",
261  class_name.c_str());
262  }
263  factoryMap[class_name] = new_factory;
265 
266  CONSOLE_BRIDGE_logDebug(
267  "class_loader.impl: "
268  "Registration of %s complete (Metaobject Address = %p)",
269  class_name.c_str(), reinterpret_cast<void *>(new_factory));
270 }
271 
280 template<typename Base>
281 Base * createInstance(const std::string & derived_class_name, ClassLoader * loader)
282 {
283  AbstractMetaObject<Base> * factory = nullptr;
284 
286  FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
287  if (factoryMap.find(derived_class_name) != factoryMap.end()) {
288  factory = dynamic_cast<impl::AbstractMetaObject<Base> *>(factoryMap[derived_class_name]);
289  } else {
290  CONSOLE_BRIDGE_logError(
291  "class_loader.impl: No metaobject exists for class type %s.", derived_class_name.c_str());
292  }
294 
295  Base * obj = nullptr;
296  if (factory != nullptr && factory->isOwnedBy(loader)) {
297  obj = factory->create();
298  }
299 
300  if (nullptr == obj) { // Was never created
301  if (factory && factory->isOwnedBy(nullptr)) {
302  CONSOLE_BRIDGE_logDebug(
303  "%s",
304  "class_loader.impl: ALERT!!! "
305  "A metaobject (i.e. factory) exists for desired class, but has no owner. "
306  "This implies that the library containing the class was dlopen()ed by means other than "
307  "through the class_loader interface. "
308  "This can happen if you build plugin libraries that contain more than just plugins "
309  "(i.e. normal code your app links against) -- that intrinsically will trigger a dlopen() "
310  "prior to main(). "
311  "You should isolate your plugins into their own library, otherwise it will not be "
312  "possible to shutdown the library!");
313 
314  obj = factory->create();
315  } else {
317  "Could not create instance of type " + derived_class_name);
318  }
319  }
320 
321  CONSOLE_BRIDGE_logDebug(
322  "class_loader.impl: Created instance of type %s and object pointer = %p",
323  (typeid(obj).name()), obj);
324 
325  return obj;
326 }
327 
335 template<typename Base>
337 {
339 
340  FactoryMap & factory_map = getFactoryMapForBaseClass<Base>();
341  std::vector<std::string> classes;
342  std::vector<std::string> classes_with_no_owner;
343 
344  for (auto & it : factory_map) {
345  AbstractMetaObjectBase * factory = it.second;
346  if (factory->isOwnedBy(loader)) {
347  classes.push_back(it.first);
348  } else if (factory->isOwnedBy(nullptr)) {
349  classes_with_no_owner.push_back(it.first);
350  }
351  }
352 
353  // Added classes not associated with a class loader (Which can happen through
354  // an unexpected dlopen() to the library)
355  classes.insert(classes.end(), classes_with_no_owner.begin(), classes_with_no_owner.end());
356  return classes;
357 }
358 
368 
378 bool isLibraryLoaded(const std::string & library_path, const ClassLoader * loader);
379 
387 bool isLibraryLoadedByAnybody(const std::string & library_path);
388 
397 void loadLibrary(const std::string & library_path, ClassLoader * loader);
398 
407 void unloadLibrary(const std::string & library_path, ClassLoader * loader);
408 
409 } // namespace impl
410 } // namespace class_loader
411 
412 #endif // CLASS_LOADER__CLASS_LOADER_CORE_HPP_
class_loader::impl::getPluginBaseToFactoryMapMapMutex
std::recursive_mutex & getPluginBaseToFactoryMapMapMutex()
class_loader::impl::loadLibrary
void loadLibrary(const std::string &library_path, ClassLoader *loader)
Loads a library into memory if it has not already been done so. Attempting to load an already loaded ...
class_loader::impl::isLibraryLoaded
bool isLibraryLoaded(const std::string &library_path, const ClassLoader *loader)
Indicates if passed library loaded within scope of a ClassLoader. The library maybe loaded in memory,...
std::recursive_mutex::lock
T lock(T... args)
exceptions.hpp
class_loader::impl::AbstractMetaObject::create
virtual B * create() const =0
Defines the factory interface that the MetaObject must implement.
std::string
class_loader::impl::registerPlugin
void registerPlugin(const std::string &class_name, const std::string &base_class_name)
This function is called by the CLASS_LOADER_REGISTER_CLASS macro in plugin_register_macro....
Definition: class_loader_core.hpp:210
class_loader::impl::getLoadedLibraryVector
LibraryVector & getLoadedLibraryVector()
Gets a handle to a list of open libraries in the form of LibraryPairs which encode the library path+n...
class_loader
Definition: class_loader.hpp:59
std::pair
class_loader::impl::AbstractMetaObjectBase
A base class for MetaObjects that excludes a polymorphic type parameter. Subclasses are class templat...
Definition: meta_object.hpp:57
std::vector
std::map::find
T find(T... args)
class_loader::impl::AbstractMetaObjectBase::addOwningClassLoader
void addOwningClassLoader(ClassLoader *loader)
Associates a ClassLoader owner with this factory,.
std::recursive_mutex
std::lock_guard
class_loader::impl::LibraryVector
std::vector< LibraryPair > LibraryVector
Definition: class_loader_core.hpp:82
class_loader::impl::createInstance
Base * createInstance(const std::string &derived_class_name, ClassLoader *loader)
This function creates an instance of a plugin class given the derived name of the class and returns a...
Definition: class_loader_core.hpp:281
class_loader::impl::getAllLibrariesUsedByClassLoader
std::vector< std::string > getAllLibrariesUsedByClassLoader(const ClassLoader *loader)
This function returns the names of all libraries in use by a given class loader.
class_loader::impl::getCurrentlyLoadingLibraryName
std::string getCurrentlyLoadingLibraryName()
When a library is being loaded, in order for factories to know which library they are being associate...
class_loader::impl::MetaObjectVector
std::vector< AbstractMetaObjectBase * > MetaObjectVector
Definition: class_loader_core.hpp:83
class_loader::impl::AbstractMetaObject
Abstract base class for factories where polymorphic type variable indicates base class for plugin int...
Definition: meta_object.hpp:163
std::recursive_mutex::unlock
T unlock(T... args)
std::vector::push_back
T push_back(T... args)
class_loader::impl::getAvailableClasses
std::vector< std::string > getAvailableClasses(const ClassLoader *loader)
This function returns all the available class_loader in the plugin system that are derived from Base ...
Definition: class_loader_core.hpp:336
class_loader::impl::hasANonPurePluginLibraryBeenOpened
bool hasANonPurePluginLibraryBeenOpened()
Indicates if a library containing more than just plugins has been opened by the running process.
class_loader::impl::AbstractMetaObjectBase::isOwnedBy
bool isOwnedBy(const ClassLoader *loader) const
Indicates if the factory is within the usable scope of a ClassLoader.
class_loader::impl::isLibraryLoadedByAnybody
bool isLibraryLoadedByAnybody(const std::string &library_path)
Indicates if passed library has been loaded by ANY ClassLoader.
std::string::c_str
T c_str(T... args)
shared_library.hpp
class_loader::impl::getFactoryMapForBaseClass
FactoryMap & getFactoryMapForBaseClass(const std::string &typeid_base_class_name)
This function extracts a reference to the FactoryMap for appropriate base class out of the global plu...
class_loader::impl::BaseClassName
std::string BaseClassName
Definition: class_loader_core.hpp:78
std::map
class_loader::impl::getCurrentlyActiveClassLoader
ClassLoader * getCurrentlyActiveClassLoader()
Gets the ClassLoader currently in scope which used when a library is being loaded.
class_loader::impl::LibraryPath
std::string LibraryPath
Definition: class_loader_core.hpp:76
class_loader::ClassLoader
This class allows loading and unloading of dynamically linked libraries which contain class definitio...
Definition: class_loader.hpp:79
class_loader::impl::setCurrentlyLoadingLibraryName
void setCurrentlyLoadingLibraryName(const std::string &library_name)
When a library is being loaded, in order for factories to know which library they are being associate...
class_loader::impl::getLoadedLibraryVectorMutex
std::recursive_mutex & getLoadedLibraryVectorMutex()
To provide thread safety, all exposed plugin functions can only be run serially by multiple threads.
class_loader::impl::ClassName
std::string ClassName
Definition: class_loader_core.hpp:77
CLASS_LOADER_PUBLIC
#define CLASS_LOADER_PUBLIC
Definition: visibility_control.hpp:58
class_loader::impl::MetaObject
The actual factory.
Definition: meta_object.hpp:199
class_loader::impl::LibraryPair
std::pair< LibraryPath, std::shared_ptr< rcpputils::SharedLibrary > > LibraryPair
Definition: class_loader_core.hpp:81
std::vector::begin
T begin(T... args)
visibility_control.hpp
std::vector::insert
T insert(T... args)
std::map::end
T end(T... args)
class_loader::impl::BaseToFactoryMapMap
std::map< BaseClassName, FactoryMap > BaseToFactoryMapMap
Definition: class_loader_core.hpp:80
class_loader::impl::FactoryMap
std::map< ClassName, impl::AbstractMetaObjectBase * > FactoryMap
Definition: class_loader_core.hpp:79
class_loader::impl::unloadLibrary
void unloadLibrary(const std::string &library_path, ClassLoader *loader)
Unloads a library if it loaded in memory and cleans up its corresponding class factories....
meta_object.hpp
class_loader::CreateClassException
An exception class thrown when class_loader is unable to create a plugin.
Definition: exceptions.hpp:83
class_loader::impl::printDebugInfoToScreen
void printDebugInfoToScreen()
class_loader::impl::getGlobalPluginBaseToFactoryMapMap
BaseToFactoryMapMap & getGlobalPluginBaseToFactoryMapMap()
Gets a handle to a global data structure that holds a map of base class names (Base class describes p...
class_loader::impl::AbstractMetaObjectBase::setAssociatedLibraryPath
void setAssociatedLibraryPath(const std::string &library_path)
Sets the path to the library associated with this factory.
class_loader::impl::setCurrentlyActiveClassLoader
void setCurrentlyActiveClassLoader(ClassLoader *loader)
Sets the ClassLoader currently in scope which used when a library is being loaded.