Program Listing for File class_loader_core.hpp

Return to documentation for file (include/class_loader/class_loader_core.hpp)

/*
 * Software License Agreement (BSD License)
 *
 * 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 copyright holders 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.
 */

#ifndef CLASS_LOADER__CLASS_LOADER_CORE_HPP_
#define CLASS_LOADER__CLASS_LOADER_CORE_HPP_

#include <cstddef>
#include <cstdio>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <typeinfo>
#include <utility>
#include <vector>

// TODO(mikaelarguedas) remove this once console_bridge complies with this
// see https://github.com/ros/console_bridge/issues/55
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
#include "console_bridge/console.h"
#ifdef __clang__
# pragma clang diagnostic pop
#endif

#include "class_loader/exceptions.hpp"
#include "class_loader/meta_object.hpp"
#include "class_loader/visibility_control.hpp"

#include "rcpputils/shared_library.hpp"

namespace class_loader
{

class ClassLoader;  // Forward declaration

namespace impl
{

// Typedefs
typedef std::string LibraryPath;
typedef std::string ClassName;
typedef std::string BaseClassName;
typedef std::map<ClassName, impl::AbstractMetaObjectBase *> FactoryMap;
typedef std::map<BaseClassName, FactoryMap> BaseToFactoryMapMap;
typedef std::pair<LibraryPath, std::shared_ptr<rcpputils::SharedLibrary>> LibraryPair;
typedef std::vector<LibraryPair> LibraryVector;
typedef std::vector<AbstractMetaObjectBase *> MetaObjectVector;

CLASS_LOADER_PUBLIC
void printDebugInfoToScreen();

// Global storage

CLASS_LOADER_PUBLIC
BaseToFactoryMapMap & getGlobalPluginBaseToFactoryMapMap();

CLASS_LOADER_PUBLIC
LibraryVector & getLoadedLibraryVector();

CLASS_LOADER_PUBLIC
std::string getCurrentlyLoadingLibraryName();

CLASS_LOADER_PUBLIC
void setCurrentlyLoadingLibraryName(const std::string & library_name);


CLASS_LOADER_PUBLIC
ClassLoader * getCurrentlyActiveClassLoader();

CLASS_LOADER_PUBLIC
void setCurrentlyActiveClassLoader(ClassLoader * loader);


CLASS_LOADER_PUBLIC
FactoryMap & getFactoryMapForBaseClass(const std::string & typeid_base_class_name);

template<typename Base>
FactoryMap & getFactoryMapForBaseClass()
{
  return getFactoryMapForBaseClass(typeid(Base).name());
}

CLASS_LOADER_PUBLIC
std::recursive_mutex & getLoadedLibraryVectorMutex();
CLASS_LOADER_PUBLIC
std::recursive_mutex & getPluginBaseToFactoryMapMapMutex();

CLASS_LOADER_PUBLIC
bool hasANonPurePluginLibraryBeenOpened();

CLASS_LOADER_PUBLIC
void hasANonPurePluginLibraryBeenOpened(bool hasIt);

// Plugin Functions

template<typename Derived, typename Base>
void registerPlugin(const std::string & class_name, const std::string & base_class_name)
{
  // Note: This function will be automatically invoked when a dlopen() call
  // opens a library. Normally it will happen within the scope of loadLibrary(),
  // but that may not be guaranteed.
  CONSOLE_BRIDGE_logDebug(
    "class_loader.impl: "
    "Registering plugin factory for class = %s, ClassLoader* = %p and library name %s.",
    class_name.c_str(), getCurrentlyActiveClassLoader(),
    getCurrentlyLoadingLibraryName().c_str());

  if (nullptr == getCurrentlyActiveClassLoader()) {
    CONSOLE_BRIDGE_logDebug(
      "%s",
      "class_loader.impl: ALERT!!! "
      "A library containing plugins has been opened through a means other than through the "
      "class_loader or pluginlib package. "
      "This can happen if you build plugin libraries that contain more than just plugins "
      "(i.e. normal code your app links against). "
      "This inherently will trigger a dlopen() prior to main() and cause problems as class_loader "
      "is not aware of plugin factories that autoregister under the hood. "
      "The class_loader package can compensate, but you may run into namespace collision problems "
      "(e.g. if you have the same plugin class in two different libraries and you load them both "
      "at the same time). "
      "The biggest problem is that library can now no longer be safely unloaded as the "
      "ClassLoader does not know when non-plugin code is still in use. "
      "In fact, no ClassLoader instance in your application will be unable to unload any library "
      "once a non-pure one has been opened. "
      "Please refactor your code to isolate plugins into their own libraries.");
    hasANonPurePluginLibraryBeenOpened(true);
  }

  // Create factory
  impl::AbstractMetaObject<Base> * new_factory =
    new impl::MetaObject<Derived, Base>(class_name, base_class_name);
  new_factory->addOwningClassLoader(getCurrentlyActiveClassLoader());
  new_factory->setAssociatedLibraryPath(getCurrentlyLoadingLibraryName());


  // Add it to global factory map map
  getPluginBaseToFactoryMapMapMutex().lock();
  FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
  if (factoryMap.find(class_name) != factoryMap.end()) {
    CONSOLE_BRIDGE_logWarn(
      "class_loader.impl: SEVERE WARNING!!! "
      "A namespace collision has occurred with plugin factory for class %s. "
      "New factory will OVERWRITE existing one. "
      "This situation occurs when libraries containing plugins are directly linked against an "
      "executable (the one running right now generating this message). "
      "Please separate plugins out into their own library or just don't link against the library "
      "and use either class_loader::ClassLoader/MultiLibraryClassLoader to open.",
      class_name.c_str());
  }
  factoryMap[class_name] = new_factory;
  getPluginBaseToFactoryMapMapMutex().unlock();

  CONSOLE_BRIDGE_logDebug(
    "class_loader.impl: "
    "Registration of %s complete (Metaobject Address = %p)",
    class_name.c_str(), reinterpret_cast<void *>(new_factory));
}

template<typename Base>
Base * createInstance(const std::string & derived_class_name, ClassLoader * loader)
{
  AbstractMetaObject<Base> * factory = nullptr;

  getPluginBaseToFactoryMapMapMutex().lock();
  FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
  if (factoryMap.find(derived_class_name) != factoryMap.end()) {
    factory = dynamic_cast<impl::AbstractMetaObject<Base> *>(factoryMap[derived_class_name]);
  } else {
    CONSOLE_BRIDGE_logError(
      "class_loader.impl: No metaobject exists for class type %s.", derived_class_name.c_str());
  }
  getPluginBaseToFactoryMapMapMutex().unlock();

  Base * obj = nullptr;
  if (factory != nullptr && factory->isOwnedBy(loader)) {
    obj = factory->create();
  }

  if (nullptr == obj) {  // Was never created
    if (factory && factory->isOwnedBy(nullptr)) {
      CONSOLE_BRIDGE_logDebug(
        "%s",
        "class_loader.impl: ALERT!!! "
        "A metaobject (i.e. factory) exists for desired class, but has no owner. "
        "This implies that the library containing the class was dlopen()ed by means other than "
        "through the class_loader interface. "
        "This can happen if you build plugin libraries that contain more than just plugins "
        "(i.e. normal code your app links against) -- that intrinsically will trigger a dlopen() "
        "prior to main(). "
        "You should isolate your plugins into their own library, otherwise it will not be "
        "possible to shutdown the library!");

      obj = factory->create();
    } else {
      throw class_loader::CreateClassException(
              "Could not create instance of type " + derived_class_name);
    }
  }

  CONSOLE_BRIDGE_logDebug(
    "class_loader.impl: Created instance of type %s and object pointer = %p",
    (typeid(obj).name()), obj);

  return obj;
}

template<typename Base>
std::vector<std::string> getAvailableClasses(const ClassLoader * loader)
{
  std::lock_guard<std::recursive_mutex> lock(getPluginBaseToFactoryMapMapMutex());

  FactoryMap & factory_map = getFactoryMapForBaseClass<Base>();
  std::vector<std::string> classes;
  std::vector<std::string> classes_with_no_owner;

  for (auto & it : factory_map) {
    AbstractMetaObjectBase * factory = it.second;
    if (factory->isOwnedBy(loader)) {
      classes.push_back(it.first);
    } else if (factory->isOwnedBy(nullptr)) {
      classes_with_no_owner.push_back(it.first);
    }
  }

  // Added classes not associated with a class loader (Which can happen through
  // an unexpected dlopen() to the library)
  classes.insert(classes.end(), classes_with_no_owner.begin(), classes_with_no_owner.end());
  return classes;
}

CLASS_LOADER_PUBLIC
std::vector<std::string> getAllLibrariesUsedByClassLoader(const ClassLoader * loader);

CLASS_LOADER_PUBLIC
bool isLibraryLoaded(const std::string & library_path, const ClassLoader * loader);

CLASS_LOADER_PUBLIC
bool isLibraryLoadedByAnybody(const std::string & library_path);

CLASS_LOADER_PUBLIC
void loadLibrary(const std::string & library_path, ClassLoader * loader);

CLASS_LOADER_PUBLIC
void unloadLibrary(const std::string & library_path, ClassLoader * loader);

}  // namespace impl
}  // namespace class_loader

#endif  // CLASS_LOADER__CLASS_LOADER_CORE_HPP_