pax_global_header00006660000000000000000000000064125332642040014513gustar00rootroot0000000000000052 comment=61f1262999fb07427685faefa9a65d0fa6c37901 ros-class-loader-0.3.2/000077500000000000000000000000001253326420400146675ustar00rootroot00000000000000ros-class-loader-0.3.2/.gitignore000066400000000000000000000000451253326420400166560ustar00rootroot00000000000000build bin lib CMakeLists.txt.user *~ ros-class-loader-0.3.2/CHANGELOG.rst000066400000000000000000000053151253326420400167140ustar00rootroot00000000000000^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Changelog for package class_loader ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0.3.2 (2015-04-22) ------------------ * Fixed wrong handling of false statement (pkg-config was not installed) * Make catkin optional again * Contributors: Esteve Fernandez, Janosch Machowinski, Matthias Goldhoorn 0.3.1 (2014-12-23) ------------------ * Depend on boost * Use FindPoco.cmake from ros/cmake_modules * Honor BUILD_SHARED_LIBS and do not force building shared libraries. * Contributors: Esteve Fernandez, Gary Servin, Scott K Logan 0.3.0 (2014-06-25) ------------------ * Use system-provided console-bridge * Contributors: Esteve Fernandez 0.2.5 (2014-03-04) ------------------ * Changed format of debug messages so that rosconsole_bridge can correctly parse the prefix * Improved debug output 0.2.4 (2014-02-12) ------------------ * fix race condition with multi threaded library loading (`#16 `_) 0.2.3 (2013-08-21) ------------------ * fix missing class name in logWarn output 0.2.2 (2013-07-14) ------------------ * check for CATKIN_ENABLE_TESTING (`#10 `_) * fix find Poco to return full lib path (`#8 `_) * add missing runtime destination for library under Windows * add Boosst component system 0.2.1 (2013-06-06) ------------------ * improve check for Poco foundation and headers (`#7 `_) 0.2.0 (2013-03-13) ------------------ * use find_package for Poco/dl instead to make it work on other platforms * update Poco cmake file to include libdl on non-windows systems * No longer CATKIN_DEPEND on console_bridge 0.1.27 (2013-01-25) ------------------- * change warning message for managed/unmanaged instance mixture in lazy loading mode 0.1.26 (2013-01-17) ------------------- * fix all instances marked as unmanaged 0.1.25 (2013-01-16) ------------------- * fix redundant destructor definition being pulled into plugin library for metaobjects instead of being contained with libclass_loader.so 0.1.24 (2013-01-14 15:27) ------------------------- * fix syntax error for logInform 0.1.23 (2013-01-14 15:23) ------------------------- * downgrade some warning messages to be info/debug 0.1.22 (2013-01-14 15:01) ------------------------- * add safety checks for mixing of managed/unmanaged mixing as well as pointer equivalency check between graveyard and newly created metaobjects 0.1.21 (2013-01-13) ------------------- * fix compile issue on OSX in dependent packages (`#3 `_) * add more debug information 0.1.20 (2012-12-21 16:04) ------------------------- * first public release for Groovy ros-class-loader-0.3.2/CMakeLists.txt000066400000000000000000000037661253326420400174430ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.3) project(class_loader) find_package(Boost REQUIRED COMPONENTS thread system) find_package(catkin QUIET) find_package(console_bridge REQUIRED) if(${catkin_FOUND}) find_package(catkin REQUIRED COMPONENTS cmake_modules) find_package(Poco REQUIRED COMPONENTS Foundation) catkin_package( INCLUDE_DIRS include LIBRARIES ${PROJECT_NAME} ${Poco_LIBRARIES} DEPENDS Boost Poco console_bridge CFG_EXTRAS class_loader-extras.cmake ) else() message("-- catkin not found") set(Poco_DIR cmake) find_package(Poco REQUIRED COMPONENTS Foundation) set(CATKIN_PACKAGE_LIB_DESTINATION lib) set(CATKIN_GLOBAL_BIN_DESTINATION bin) set(CATKIN_GLOBAL_BIN_DESTINATION bin) set(CATKIN_PACKAGE_INCLUDE_DESTINATION include/class_loader) endif() include_directories(include ${console_bridge_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${Poco_INCLUDE_DIRS}) set(${PROJECT_NAME}_SRCS src/class_loader.cpp src/class_loader_core.cpp src/meta_object.cpp src/multi_library_class_loader.cpp ) add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS}) target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${console_bridge_LIBRARIES} ${Poco_LIBRARIES}) install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}) install(DIRECTORY include/class_loader/ DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}) if(CATKIN_ENABLE_TESTING) add_subdirectory(test) endif() if(NOT ${catkin_FOUND}) set(TARGET_NAME ${PROJECT_NAME}) set(PKGCONFIG_LIBS ${Boost_LIBRARIES} ${console_bridge_LIBRARIES} ${Poco_LIBRARIES} ) # Prepare and install necessary files to support finding of the library # using pkg-config configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/${TARGET_NAME}.pc.in ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION lib/pkgconfig) endif()ros-class-loader-0.3.2/cmake/000077500000000000000000000000001253326420400157475ustar00rootroot00000000000000ros-class-loader-0.3.2/cmake/PocoConfig.cmake000066400000000000000000000157701253326420400210110ustar00rootroot00000000000000# - Find the Poco includes and libraries. # The following variables are set if Poco is found. If Poco is not # found, Poco_FOUND is set to false. # Poco_FOUND - True when the Poco include directory is found. # Poco_INCLUDE_DIRS - the path to where the poco include files are. # Poco_LIBRARY_DIR - The path to where the poco library files are. # Poco_BINARY_DIRS - The path to where the poco dlls are. # Poco_LIBRARIES - list of all libs from requested components. # ---------------------------------------------------------------------------- # If you have installed Poco in a non-standard location. # Then you have three options. # In the following comments, it is assumed that # points to the root directory of the include directory of Poco. e.g # If you have put poco in C:\development\Poco then is # "C:/development/Poco" and in this directory there will be two # directories called "include" and "lib". # 1) After CMake runs, set Poco_INCLUDE_DIR to /poco<-version> # 2) Use CMAKE_INCLUDE_PATH to set a path to /poco<-version>. This will allow FIND_PATH() # to locate Poco_INCLUDE_DIR by utilizing the PATH_SUFFIXES option. e.g. # SET(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "/include") # 3) Set an environment variable called ${POCO_ROOT} that points to the root of where you have # installed Poco, e.g. . It is assumed that there is at least a subdirectory called # Foundation/include/Poco in this path. # # Note: # 1) If you are just using the poco headers, then you do not need to use # Poco_LIBRARY_DIR in your CMakeLists.txt file. # 2) If Poco has not been installed, then when setting Poco_LIBRARY_DIR # the script will look for /lib first and, if this fails, then for /stage/lib. # # Usage: # In your CMakeLists.txt file do something like this: # ... # # Poco # FIND_PACKAGE(Poco COMPONENTS XML Net Data...) # ... # INCLUDE_DIRECTORIES(${Poco_INCLUDE_DIRS}) # LINK_DIRECTORIES(${Poco_LIBRARY_DIR}) # # In Windows, we make the assumption that, if the Poco files are installed, the default directory # will be C:\poco or C:\Program Files\Poco or C:\Programme\Poco. MESSAGE(STATUS "Searching for Poco library...") SET(POCO_INCLUDE_PATH_DESCRIPTION "top-level directory containing the poco include directories. E.g /usr/local/include/ or c:\\poco\\include\\poco-1.3.2") SET(POCO_INCLUDE_DIR_MESSAGE "Set the Poco_INCLUDE_DIR cmake cache entry to the ${POCO_INCLUDE_PATH_DESCRIPTION}") SET(POCO_LIBRARY_PATH_DESCRIPTION "top-level directory containing the poco libraries.") SET(POCO_LIBRARY_DIR_MESSAGE "Set the Poco_LIBRARY_DIR cmake cache entry to the ${POCO_LIBRARY_PATH_DESCRIPTION}") SET(POCO_DIR_SEARCH $ENV{POCO_ROOT}) IF(POCO_DIR_SEARCH) FILE(TO_CMAKE_PATH ${POCO_DIR_SEARCH} POCO_DIR_SEARCH) ENDIF(POCO_DIR_SEARCH) IF(WIN32) SET(POCO_DIR_SEARCH ${POCO_DIR_SEARCH} C:/poco D:/poco "C:/Program Files/poco" "C:/Programme/poco" "D:/Program Files/poco" "D:/Programme/poco" ) ENDIF(WIN32) # Add in some path suffixes. These will have to be updated whenever a new Poco version comes out. SET(SUFFIX_FOR_INCLUDE_PATH poco-1.3.2 poco-1.3.3 poco-1.3.4 poco-1.3.5 poco-1.3.6 ) SET(SUFFIX_FOR_LIBRARY_PATH poco-1.3.2/lib poco-1.3.2/lib/Linux/i686 poco-1.3.2/lib/Linux/x86_64 poco-1.3.3/lib poco-1.3.3/lib/Linux/i686 poco-1.3.3/lib/Linux/x86_64 poco-1.3.4/lib poco-1.3.4/lib/Linux/i686 poco-1.3.4/lib/Linux/x86_64 poco-1.3.5/lib poco-1.3.5/lib/Linux/i686 poco-1.3.5/lib/Linux/x86_64 poco-1.3.6/lib poco-1.3.6/lib/Linux/i686 poco-1.3.6/lib/Linux/x86_64 lib lib/Linux/i686 lib/Linux/x86_64 ) # # Look for an installation. # FIND_PATH(Poco_INCLUDE_DIR NAMES Foundation/include/Poco/SharedLibrary.h PATH_SUFFIXES ${SUFFIX_FOR_INCLUDE_PATH} PATHS # Look in other places. ${POCO_DIR_SEARCH} # Help the user find it if we cannot. DOC "The ${POCO_INCLUDE_PATH_DESCRIPTION}" ) IF(NOT Poco_INCLUDE_DIR) # Look for standard unix include paths FIND_PATH(Poco_INCLUDE_DIR Poco/Poco.h DOC "The ${POCO_INCLUDE_PATH_DESCRIPTION}") ENDIF(NOT Poco_INCLUDE_DIR) # Assume we didn't find it. SET(Poco_FOUND 0) # Now try to get the include and library path. IF(Poco_INCLUDE_DIR) IF(EXISTS "${Poco_INCLUDE_DIR}/Foundation/include/Poco/SharedLibrary.h") SET(Poco_INCLUDE_DIRS ${Poco_INCLUDE_DIR}/CppUnit/include ${Poco_INCLUDE_DIR}/Foundation/include ${Poco_INCLUDE_DIR}/Net/include ${Poco_INCLUDE_DIR}/Util/include ${Poco_INCLUDE_DIR}/XML/include ) SET(Poco_FOUND 1) ELSEIF(EXISTS "${Poco_INCLUDE_DIR}/Poco/Poco.h") SET(Poco_INCLUDE_DIRS ${Poco_INCLUDE_DIR} ) SET(Poco_FOUND 1) ENDIF() IF(NOT Poco_LIBRARY_DIR) FIND_LIBRARY(Poco_FOUNDATION_LIB NAMES PocoFoundation PocoFoundationd PATH_SUFFIXES ${SUFFIX_FOR_LIBRARY_PATH} PATHS # Look in other places. ${Poco_INCLUDE_DIR} ${POCO_DIR_SEARCH} # Help the user find it if we cannot. DOC "The ${POCO_LIBRARY_PATH_DESCRIPTION}" ) SET(Poco_LIBRARY_DIR "" CACHE PATH POCO_LIBARARY_PATH_DESCRIPTION) GET_FILENAME_COMPONENT(Poco_LIBRARY_DIR ${Poco_FOUNDATION_LIB} PATH) SET(Poco_LIBRARIES "") SET(Comp_List "") IF(Poco_LIBRARY_DIR AND Poco_FOUNDATION_LIB) # Look for the poco binary path. SET(Poco_BINARY_DIR ${Poco_INCLUDE_DIR}) IF(Poco_BINARY_DIR AND EXISTS "${Poco_BINARY_DIR}/bin") SET(Poco_BINARY_DIRS ${Poco_BINARY_DIR}/bin) ENDIF(Poco_BINARY_DIR AND EXISTS "${Poco_BINARY_DIR}/bin") ENDIF(Poco_LIBRARY_DIR AND Poco_FOUNDATION_LIB) IF(Poco_FOUNDATION_LIB) IF ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") SET(DBG "d") ELSE ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") SET(DBG "") ENDIF ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") SET(Comp_List "Foundation${DBG}") FOREACH(COMPONENT ${Poco_FIND_COMPONENTS}) FIND_LIBRARY(LIB${COMPONENT} "Poco${COMPONENT}${DBG}" Poco_LIBRARY_DIR) IF (LIB${COMPONENT}) LIST(APPEND Poco_LIBRARIES "${LIB${COMPONENT}}") LIST(APPEND Comp_List "${COMPONENT}${DBG}") ENDIF(LIB${COMPONENT}) ENDFOREACH(COMPONENT) LIST(REMOVE_DUPLICATES Comp_List) ENDIF(Poco_FOUNDATION_LIB) ENDIF(NOT Poco_LIBRARY_DIR) ENDIF(Poco_INCLUDE_DIR) IF(NOT Poco_FOUND) IF(Poco_FIND_QUIETLY) MESSAGE(STATUS "Poco was not found. ${POCO_INCLUDE_DIR_MESSAGE}") ELSE(Poco_FIND_QUIETLY) IF(Poco_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Poco was not found. ${POCO_INCLUDE_DIR_MESSAGE}") ENDIF(Poco_FIND_REQUIRED) ENDIF(Poco_FIND_QUIETLY) ELSE(NOT Poco_FOUND) MESSAGE(STATUS " Found Poco!") SET(COMPONENT_STR "components found:") FOREACH(comp ${Comp_List}) SET(COMPONENT_STR "${COMPONENT_STR}, ${comp}") ENDFOREACH(comp ${Comp_List}) STRING(REPLACE ":," ":" COMPONENT_LSTR ${COMPONENT_STR}) MESSAGE(STATUS "${COMPONENT_LSTR}.") ENDIF(NOT Poco_FOUND) #I added this in to add "libdl" on non-Windows systems. Technically dl is only neded if the "Foundation" component is used, #but i doesn't hurt to add it in anyway - mas if(Poco_FOUND AND NOT WIN32) LIST(APPEND Poco_LIBRARIES "dl") endif(Poco_FOUND AND NOT WIN32) ros-class-loader-0.3.2/cmake/class_loader-extras.cmake000066400000000000000000000011331253326420400227060ustar00rootroot00000000000000# hides all symbols of a library function(class_loader_hide_library_symbols target) set(version_script "${CMAKE_CURRENT_BINARY_DIR}/class_loader_hide_library_symbols__${target}.script") file(WRITE "${version_script}" " { local: *; };" ) # checks if the linker supports version script include(TestCXXAcceptsFlag) check_cxx_accepts_flag("-Wl,--version-script,\"${version_script}\"" LD_ACCEPTS_VERSION_SCRIPT) if(LD_ACCEPTS_VERSION_SCRIPT) set_target_properties(${target} PROPERTIES LINK_FLAGS "-Wl,-version-script=\"${version_script}\"") endif() endfunction() ros-class-loader-0.3.2/doc/000077500000000000000000000000001253326420400154345ustar00rootroot00000000000000ros-class-loader-0.3.2/doc/class_loader_classdiagram.dia000066400000000000000000000065551253326420400232730ustar00rootroot00000000000000][sH~ϯHncOœV%yHjPz#$Vv؇m] FH*_Vy`]禫aԵ3~boWgEG<ớ<W^4p=bjR$޾tg`р% dC;o3]9V7Nu}}{I6;{Igl1-?kEF:Y/vt}.^|$|șݾȂyob%n*l`AweЈ— ]ST798xzGy/r6N,5VJm1UK=T x3ޙǭÓw'EN. +}-)ͲJϝdHoo8r!2!b nIˢVO+0G)Dtͼ$-TwN4NNYiT맏6&nݖJM$^S-x2fFdh뿯=L&=Q|lօz=M!zҘ9&B{UDzdZ2F5l kl S2TzRVӎ4 vżh}dQUcn8d7!9D<+&kZ!Dc7_E̦%D=YJauRsAxr]ʯM$ދ9BݨӾR/8fc9}jdt6۵[퐨 UQPӑ]*g {nv${ +g{p_ҩ0`^t]yy?aB "\ܹyb֝K^/\o9Z%JQa"c"(>mSM,SL]avqM*e$JliSQIޙ;Vǹ,qp4G/f%Fiٔ"&åZd-k,-o;Ȏ6Y☳Z$了e*e,KjΜy<ΔlۈZ3ab1j{ ]-' gIR닽gGxIyH,\ټWoE e.5{˗;V\]V(, ƨXlA+ WN秌P`Hߟ{j0`v2TTp/Ǐz;,>Wm":95^^5Idq'0}v_3f+p89:cm:Ybn?:P l51kV+\***iJ)hh屖ZkdLlhu~#sY@G#l>:8mA@@@*I,jp---"M7B\p'yjbiӀ"by% 3}wcQ5[:1C]m5sq}[BÖg1|eN*Y{nH %+;K>8_XVK6ޠ煿ep tCn솉n(fsTGD4ɤsTTb\k 5*Ĭ)P-"갸0-U y6Z^*  n5s=A{v&*] Luf4j [{" MgN+!( \adM뱌gYeYu6UT֔ i.T d/!F uBFt+Vv@%} EVyNOO ~Zӂ?-i;?-iӞ3?mа]Z(k}D@" ! NP\UiJ`%X V"až{ pÀp8G# x70FAz`j%Sppp~UV9bZZZ30adˌiF 7 _"\0WS3-piP#P#åEЛFM24w0%c\}BGĔ装Hߍ#?mw"an>fG@=+x>>>%اX>;Cma@9#q rpY_1FqDp ݫI$`4gWYw!p8 z@ v/T.A\[ ccNH^=ܭ9k2,# 0p1zr6f'NVHEgz? <3 83 83 83 83 83 8Q3l7ɞdž.=^5=ۅw1-h9y.cu4FU#L&L6&S%vynA.ѦL = G=S7aJ 7YXܮ7|)ldž~ldž~ldž~l 2|M$/D\pW/~ yy+/;hTՉ*/ {J #*]"ht;۴f*1x}RYxeᕅW^YxeᕅW^YxeᕅW^YxeᕅWԎ+{"^Y0w]6_zLM|æ>F O*T꟡WD3N!N&kܔjЊRA ʐ$ix P@GPL)U8[0*TΣ6.wH-$Y7XN?zF''3Z.?s%WL2 WƮN5cS>>L+`㕳6ا+Cӫ.M ޗ Qon(q.6՞Aׁ^dh\ܓ¬ ?]02҅Ժt„ sg.;mfֲlF*e +U{w&~#~f]ܾ?3|ros-class-loader-0.3.2/include/000077500000000000000000000000001253326420400163125ustar00rootroot00000000000000ros-class-loader-0.3.2/include/class_loader/000077500000000000000000000000001253326420400207455ustar00rootroot00000000000000ros-class-loader-0.3.2/include/class_loader/class_loader.h000066400000000000000000000260031253326420400235520ustar00rootroot00000000000000/* * 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. */ #ifndef CLASS_LOADER_CLASS_LOADER_H_DEFINED #define CLASS_LOADER_CLASS_LOADER_H_DEFINED #include #include #include #include #include #include #include "class_loader/class_loader_register_macro.h" #include "class_loader/class_loader_core.h" namespace class_loader { /** * returns runtime library extension for native os */ std::string systemLibrarySuffix(); /** * @class ClassLoader * @brief This class allows loading and unloading of dynamically linked libraries which contain class definitions from which objects can be created/destroyed during runtime (i.e. class_loader). Libraries loaded by a ClassLoader are only accessible within scope of that ClassLoader object. */ class ClassLoader { public: /** * @brief Constructor for ClassLoader * @param library_path - The path of the runtime library to load * @param ondemand_load_unload - Indicates if on-demand (lazy) unloading/loading of libraries occurs as plugins are created/destroyed */ ClassLoader(const std::string& library_path, bool ondemand_load_unload = false); /** * @brief Destructor for ClassLoader. All libraries opened by this ClassLoader are unloaded automatically. */ virtual ~ClassLoader(); /** * @brief Indicates which classes (i.e. class_loader) that can be loaded by this object * @return vector of strings indicating names of instantiable classes derived from */ template std::vector getAvailableClasses() { return(class_loader::class_loader_private::getAvailableClasses(this)); } /** * @brief Gets the full-qualified path and name of the library associated with this class loader */ std::string getLibraryPath(){return(library_path_);} /** * @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). * @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 */ template boost::shared_ptr createInstance(const std::string& derived_class_name) { if(ClassLoader::hasUnmanagedInstanceBeenCreated() && isOnDemandLoadUnloadEnabled()) logInform("class_loader::ClassLoader: An attempt is being made to create a managed plugin instance (i.e. boost::shared_ptr), however an unmanaged instance was created within this process address space. This means libraries for the managed instances will not be shutdown automatically on final plugin destruction if on demand (lazy) loading/unloading mode is used."); if(!isLibraryLoaded()) loadLibrary(); Base* obj = class_loader::class_loader_private::createInstance(derived_class_name, this); assert(obj != NULL); //Unreachable assertion if createInstance() throws on failure boost::recursive_mutex::scoped_lock lock(plugin_ref_count_mutex_); plugin_ref_count_ = plugin_ref_count_ + 1; boost::shared_ptr smart_obj(obj, boost::bind(&class_loader::ClassLoader::onPluginDeletion, this, _1)); return(smart_obj); } /** * @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). * @param derived_class_name The name of the class we want to create (@see getAvailableClasses()) * @return An unmanaged (i.e. not a shared_ptr) Base* to newly created plugin object. */ template Base* createUnmanagedInstance(const std::string& derived_class_name) { has_unmananged_instance_been_created_ = true; if(!isLibraryLoaded()) loadLibrary(); Base* obj = class_loader::class_loader_private::createInstance(derived_class_name, this); assert(obj != NULL); //Unreachable assertion if createInstance() throws on failure return(obj); } /** * @brief Indicates if a plugin class is available * @param Base - polymorphic type indicating base class * @param class_name - the name of the plugin class * @return true if yes it is available, false otherwise */ template bool isClassAvailable(const std::string& class_name) { std::vector available_classes = getAvailableClasses(); return(std::find(available_classes.begin(), available_classes.end(), class_name) != available_classes.end()); } /** * @brief Indicates if a library is loaded within the scope of this ClassLoader. Note that the library may already be loaded internally through another ClassLoader, but until loadLibrary() method is called, the ClassLoader cannot create objects from said library. If we want to see if the library has been opened by somebody else, @see isLibraryLoadedByAnyClassloader() * @param library_path The path to the library to load * @return true if library is loaded within this ClassLoader object's scope, otherwise false */ bool isLibraryLoaded(); /** * @brief Indicates if a library is loaded by some entity in the plugin system (another ClassLoader), but not necessarily loaded by this ClassLoader * @return true if library is loaded within the scope of the plugin system, otherwise false */ bool isLibraryLoadedByAnyClassloader(); /** * @brief Indicates if the library is to be loaded/unloaded on demand...meaning that only to load a lib when the first plugin is created and automatically shut it down when last active plugin is destroyed. */ bool isOnDemandLoadUnloadEnabled(){return(ondemand_load_unload_);} /** * @brief Attempts to load a library on behalf of the ClassLoader. If the library is already opened, this method has no effect. If the library has been already opened by some other entity (i.e. another ClassLoader or global interface), this object is given permissions to access any plugin classes loaded by that other entity. This is * @param library_path The path to the library to load */ void loadLibrary(); /** * @brief Attempts to unload a library loaded within scope of the ClassLoader. If the library is not opened, this method has no effect. If the library is opened by other another ClassLoader, the library will NOT be unloaded internally -- however this ClassLoader will no longer be able to instantiate class_loader bound to that library. If there are plugin objects that exist in memory created by this classloader, a warning message will appear and the library will not be unloaded. If loadLibrary() was called multiple times (e.g. in the case of multiple threads or purposefully in a single thread), the user is responsible for calling unloadLibrary() the same number of times. The library will not be unloaded within the context of this classloader until the number of unload calls matches the number of loads. * @return The number of times more unloadLibrary() has to be called for it to be unbound from this ClassLoader */ int unloadLibrary(); private: /** * @brief Callback method when a plugin created by this class loader is destroyed * @param obj - A pointer to the deleted object */ template void onPluginDeletion(Base* obj) { logDebug("class_loader::ClassLoader: Calling onPluginDeletion() for obj ptr = %p.\n", obj); if(obj) { boost::recursive_mutex::scoped_lock lock(plugin_ref_count_mutex_); delete(obj); plugin_ref_count_ = plugin_ref_count_ - 1; assert(plugin_ref_count_ >= 0); if(plugin_ref_count_ == 0 && isOnDemandLoadUnloadEnabled()) { if(!ClassLoader::hasUnmanagedInstanceBeenCreated()) unloadLibraryInternal(false); else logWarn("class_loader::ClassLoader: Cannot unload library %s even though last shared pointer went out of scope. This is because createUnmanagedInstance was used within the scope of this process, perhaps by a different ClassLoader. Library will NOT be closed.", getLibraryPath().c_str()); } } } /** * @brief Getter for if an unmanaged (i.e. unsafe) instance has been created flag */ static bool hasUnmanagedInstanceBeenCreated(); /** * @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 * @param lock_plugin_ref_count - Set to true if plugin_ref_count_mutex_ should be locked, else false * @return The number of times unloadLibraryInternal has to be called again for it to be unbound from this ClassLoader */ int unloadLibraryInternal(bool lock_plugin_ref_count); private: bool ondemand_load_unload_; std::string library_path_; int load_ref_count_; boost::recursive_mutex load_ref_count_mutex_; int plugin_ref_count_; boost::recursive_mutex plugin_ref_count_mutex_; static bool has_unmananged_instance_been_created_; }; } #endif ros-class-loader-0.3.2/include/class_loader/class_loader_core.h000066400000000000000000000361331253326420400245670ustar00rootroot00000000000000/* * 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. */ #ifndef class_loader_private_H_DEFINED #define class_loader_private_H_DEFINED #include #include //#include #include #include #include #include "class_loader/meta_object.h" #include "class_loader/class_loader_exceptions.h" #include /** * @note This header file is the internal implementation of the plugin system which is exposed via the ClassLoader class */ namespace class_loader { class ClassLoader; //Forward declaration namespace class_loader_private { //Typedefs /*****************************************************************************/ typedef std::string LibraryPath; typedef std::string ClassName; typedef std::string BaseClassName; typedef std::map FactoryMap; typedef std::map BaseToFactoryMapMap; typedef std::pair LibraryPair; typedef std::vector LibraryVector; typedef std::vector MetaObjectVector; //Debug /*****************************************************************************/ void printDebugInfoToScreen(); //Global storage /*****************************************************************************/ /** * @brief Gets a handle to a global data structure that holds a map of base class names (Base class describes plugin interface) to a FactoryMap which holds the factories for the various different concrete classes that can be instantiated. Note that the Base class is NOT THE LITERAL CLASSNAME, but rather the result of typeid(Base).name() which sometimes is the literal class name (as on Windows) but is often in mangled form (as on Linux). * @return A reference to the global base to factory map */ BaseToFactoryMapMap& getGlobalPluginBaseToFactoryMapMap(); /** * @brief Gets a handle to a list of open libraries in the form of LibraryPairs which encode the library path+name and the handle to the underlying Poco::SharedLibrary * @return A reference to the global vector that tracks loaded libraries */ LibraryVector& getLoadedLibraryVector(); /** * @brief When a library is being loaded, in order for factories to know which library they are being associated with, they use this function to query which library is being loaded. * @return The currently set loading library name as a string */ std::string getCurrentlyLoadingLibraryName(); /** * @brief When a library is being loaded, in order for factories to know which library they are being associated with, this function is called to set the name of the library currently being loaded. * @param library_name - The name of library that is being loaded currently */ void setCurrentlyLoadingLibraryName(const std::string& library_name); /** * @brief Gets the ClassLoader currently in scope which used when a library is being loaded. * @return A pointer to the currently active ClassLoader. */ ClassLoader* getCurrentlyActiveClassLoader(); /** * @brief Sets the ClassLoader currently in scope which used when a library is being loaded. * @param loader - pointer to the currently active ClassLoader. */ void setCurrentlyActiveClassLoader(ClassLoader* loader); /** * @brief This function extracts a reference to the FactoryMap for appropriate base class out of the global plugin base to factory map. This function should be used by functions in this namespace that need to access the various factories so as to make sure the right key is generated to index into the global map. * @return A reference to the FactoryMap contained within the global Base-to-FactoryMap map. */ FactoryMap& getFactoryMapForBaseClass(const std::string& typeid_base_class_name); /** * @brief Same as above but uses a type parameter instead of string for more safety if info is available. * @return A reference to the FactoryMap contained within the global Base-to-FactoryMap map. */ template FactoryMap& getFactoryMapForBaseClass() { return(getFactoryMapForBaseClass(typeid(Base).name())); } /** * @brief To provide thread safety, all exposed plugin functions can only be run serially by multiple threads. This is implemented by using critical sections enforced by a single mutex which is locked and released with the following two functions * @return A reference to the global mutex */ boost::recursive_mutex& getLoadedLibraryVectorMutex(); boost::recursive_mutex& getPluginBaseToFactoryMapMapMutex(); /** * @brief Indicates if a library containing more than just plugins has been opened by the running process * @return True if a non-pure plugin library has been opened, otherwise false */ bool hasANonPurePluginLibraryBeenOpened(); /** * @brief Sets a flag indicating if a library containing more than just plugins has been opened by the running process * @param hasIt - The flag */ void hasANonPurePluginLibraryBeenOpened(bool hasIt); //Plugin Functions /*****************************************************************************/ /** * @brief This function is called by the CLASS_LOADER_REGISTER_CLASS macro in plugin_register_macro.h to register factories. * Classes that use that macro will cause this function to be invoked when the library is loaded. The function will create a MetaObject (i.e. factory) for the corresponding Derived class and insert it into the appropriate FactoryMap in the global Base-to-FactoryMap map. Note that the passed class_name is the literal class name and not the mangled version. * @param Derived - parameteric type indicating concrete type of plugin * @param Base - parameteric type indicating base type of plugin * @param class_name - the literal name of the class being registered (NOT MANGLED) */ template 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. logDebug("class_loader.class_loader_private: Registering plugin factory for class = %s, ClassLoader* = %p and library name %s.", class_name.c_str(), getCurrentlyActiveClassLoader(), getCurrentlyLoadingLibraryName().c_str()); if(getCurrentlyActiveClassLoader() == NULL) { logDebug("class_loader.class_loader_private: 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 class_loader_private::AbstractMetaObject* new_factory = new class_loader_private::MetaObject(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(); if(factoryMap.find(class_name) != factoryMap.end()) logWarn("class_loader.class_loader_private: SEVERE WARNING!!! A namespace collision has occured 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(); logDebug("class_loader.class_loader_private: Registration of %s complete (Metaobject Address = %p)", class_name.c_str(), new_factory); } /** * @brief This function creates an instance of a plugin class given the derived name of the class and returns a pointer of the Base class type. * @param derived_class_name - The name of the derived class (unmangled) * @param loader - The ClassLoader whose scope we are within * @return A pointer to newly created plugin, note caller is responsible for object destruction */ template Base* createInstance(const std::string& derived_class_name, ClassLoader* loader) { AbstractMetaObject* factory = NULL; getPluginBaseToFactoryMapMapMutex().lock(); FactoryMap& factoryMap = getFactoryMapForBaseClass(); if(factoryMap.find(derived_class_name) != factoryMap.end()) factory = dynamic_cast*>(factoryMap[derived_class_name]); else { logError("class_loader.class_loader_private: No metaobject exists for class type %s.", derived_class_name.c_str()); } getPluginBaseToFactoryMapMapMutex().unlock(); Base* obj = NULL; if(factory != NULL && factory->isOwnedBy(loader)) obj = factory->create(); if(obj == NULL) //Was never created { if(factory && factory->isOwnedBy(NULL)) { logDebug("class_loader.class_loader_private: 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)); } logDebug("class_loader.class_loader_private: Created instance of type %s and object pointer = %p", (typeid(obj).name()), obj); return(obj); } /** * @brief This function returns all the available class_loader in the plugin system that are derived from Base and within scope of the passed ClassLoader. * @param loader - The pointer to the ClassLoader whose scope we are within, * @return A vector of strings where each string is a plugin we can create */ template std::vector getAvailableClasses(ClassLoader* loader) { boost::recursive_mutex::scoped_lock lock(getPluginBaseToFactoryMapMapMutex()); FactoryMap& factory_map = getFactoryMapForBaseClass(); std::vector classes; std::vector classes_with_no_owner; for(FactoryMap::const_iterator itr = factory_map.begin(); itr != factory_map.end(); ++itr) { AbstractMetaObjectBase* factory = itr->second; if(factory->isOwnedBy(loader)) classes.push_back(itr->first); else if(factory->isOwnedBy(NULL)) classes_with_no_owner.push_back(itr->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); } /** * @brief This function returns the names of all libraries in use by a given class loader. * @param loader - The ClassLoader whose scope we are within * @return A vector of strings where each string is the path+name of each library that are within a ClassLoader's visible scope */ std::vector getAllLibrariesUsedByClassLoader(const ClassLoader* loader); /** * @brief Indicates if passed library loaded within scope of a ClassLoader. The library maybe loaded in memory, but to the class loader it may not be. * @param library_path - The name of the library we wish to check is open * @param loader - The pointer to the ClassLoader whose scope we are within * @return true if the library is loaded within loader's scope, else false */ bool isLibraryLoaded(const std::string& library_path, ClassLoader* loader); /** * @brief Indicates if passed library has been loaded by ANY ClassLoader * @param library_path - The name of the library we wish to check is open * @return true if the library is loaded in memory, otherwise false */ bool isLibraryLoadedByAnybody(const std::string& library_path); /** * @brief Loads a library into memory if it has not already been done so. Attempting to load an already loaded library has no effect. * @param library_path - The name of the library to open * @param loader - The pointer to the ClassLoader whose scope we are within */ void loadLibrary(const std::string& library_path, ClassLoader* loader); /** * @brief Unloads a library if it loaded in memory and cleans up its corresponding class factories. If it is not loaded, the function has no effect * @param library_path - The name of the library to open * @param loader - The pointer to the ClassLoader whose scope we are within */ void unloadLibrary(const std::string& library_path, ClassLoader* loader); } //End namespace class_loader_private } //End namespace class_loader #endif ros-class-loader-0.3.2/include/class_loader/class_loader_exceptions.h000066400000000000000000000061551253326420400260210ustar00rootroot00000000000000/* * 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 namespace class_loader { /** * @class ClassLoader sException * @brief A base class for all class_loader exceptions that inherits from std::runtime_exception */ class ClassLoaderException: public std::runtime_error { public: ClassLoaderException(const std::string error_desc) : std::runtime_error(error_desc) {} }; /** * @class LibraryLoadException * @brief An exception class thrown when class_loader is unable to load a runtime library */ class LibraryLoadException: public ClassLoaderException { public: LibraryLoadException(const std::string error_desc) : ClassLoaderException(error_desc) {} }; /** * @class LibraryUnloadException * @brief An exception class thrown when class_loader is unable to unload a runtime library */ class LibraryUnloadException: public ClassLoaderException { public: LibraryUnloadException(const std::string error_desc) : ClassLoaderException(error_desc) {} }; /** * @class CreateClassException * @brief An exception class thrown when class_loader is unable to create a plugin */ class CreateClassException: public ClassLoaderException { public: CreateClassException(const std::string error_desc) : ClassLoaderException(error_desc) {} }; /** * @class NoClassLoaderExistsException * @brief An exception class thrown when a multilibrary class loader does not have a ClassLoader bound to it */ class NoClassLoaderExistsException: public ClassLoaderException { public: NoClassLoaderExistsException(const std::string error_desc) : ClassLoaderException(error_desc) {} }; } ros-class-loader-0.3.2/include/class_loader/class_loader_register_macro.h000066400000000000000000000065621253326420400266470ustar00rootroot00000000000000/* * 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. */ #ifndef CLASS_LOADER_REGISTER_MACRO_H_DEFINED #define CLASS_LOADER_REGISTER_MACRO_H_DEFINED #include "class_loader_core.h" #include #define CLASS_LOADER_REGISTER_CLASS_INTERNAL_WITH_MESSAGE(Derived, Base, UniqueID, Message) \ namespace \ {\ struct ProxyExec##UniqueID \ {\ typedef Derived _derived; \ typedef Base _base; \ ProxyExec##UniqueID() \ { \ if(std::string(Message)!="")\ logInform("%s", Message);\ class_loader::class_loader_private::registerPlugin<_derived, _base>(#Derived, #Base); \ }\ };\ static ProxyExec##UniqueID g_register_plugin_##UniqueID;\ } #define CLASS_LOADER_REGISTER_CLASS_INTERNAL_HOP1_WITH_MESSAGE(Derived, Base, UniqueID, Message) CLASS_LOADER_REGISTER_CLASS_INTERNAL_WITH_MESSAGE(Derived, Base, UniqueID, Message) /** * @macro This macro is same as CLASS_LOADER_REGISTER_CLASS, but will spit out a message when the plugin is registered * at library load time */ #define CLASS_LOADER_REGISTER_CLASS_WITH_MESSAGE(Derived, Base, Message) CLASS_LOADER_REGISTER_CLASS_INTERNAL_HOP1_WITH_MESSAGE(Derived, Base, __COUNTER__, Message) /** * @macro This is the macro which must be declared within the source (.cpp) file for each class that is to be exported as plugin. * The macro utilizes a trick where a new struct is generated along with a declaration of static global variable of same type after it. The struct's constructor invokes a registration function with the plugin system. When the plugin system loads a library with registered classes in it, the initialization of static variables forces the invocation of the struct constructors, and all exported classes are automatically registerd. */ #define CLASS_LOADER_REGISTER_CLASS(Derived, Base) CLASS_LOADER_REGISTER_CLASS_WITH_MESSAGE(Derived, Base, "") #endif ros-class-loader-0.3.2/include/class_loader/meta_object.h000066400000000000000000000147561253326420400234070ustar00rootroot00000000000000/* * 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. */ //Note: This header defines a simplication of Poco::MetaObject that allows us to tag MetaObjects with an associated library name. #ifndef PLUGINS_PRIVATE_META_OBJECT_DEFINED #define PLUGINS_PRIVATE_META_OBJECT_DEFINED #include #include #include namespace class_loader { class ClassLoader; //Forward declaration namespace class_loader_private { typedef std::vector ClassLoaderVector; /** * @class AbstractMetaObjectBase * @brief A base class for MetaObjects that excludes a polymorphic type parameter. Subclasses are class templates though. */ class AbstractMetaObjectBase { public: /** * @brief Constructor for the class */ AbstractMetaObjectBase(const std::string& class_name, const std::string& base_class_name); /** * @brief Destructor for the class. THIS MUST NOT BE VIRTUAL AND OVERRIDDEN BY * TEMPLATE SUBCLASSES, OTHERWISE THEY WILL PULL IN A REDUNDANT METAOBJECT * DESTRUCTOR OUTSIDE OF libclass_loader WITHIN THE PLUGIN LIBRARY! T */ ~AbstractMetaObjectBase(); /** * @brief Gets the literal name of the class. * @return The literal name of the class as a C-string. */ std::string className() const; /** * @brief gets the base class for the class this factory represents */ std::string baseClassName() const; /** * @brief Gets the name of the class as typeid(BASE_CLASS).name() would return it */ std::string typeidBaseClassName() const; /** * @brief Gets the path to the library associated with this factory * @return Library path as a std::string */ std::string getAssociatedLibraryPath(); /** * @brief Sets the path to the library associated with this factory */ void setAssociatedLibraryPath(std::string library_path); /** * @brief Associates a ClassLoader owner with this factory, * @param loader Handle to the owning ClassLoader. */ void addOwningClassLoader(ClassLoader* loader); /** * @brief Removes a ClassLoader that is an owner of this factory * @param loader Handle to the owning ClassLoader. */ void removeOwningClassLoader(const ClassLoader* loader); /** * @brief Indicates if the factory is within the usable scope of a ClassLoader * @param loader Handle to the owning ClassLoader. */ bool isOwnedBy(const ClassLoader* loader); /** * @brief Indicates if the factory is within the usable scope of any ClassLoader */ bool isOwnedByAnybody(); /** * A vector of class loaders that own this metaobject */ ClassLoaderVector getAssociatedClassLoaders(); protected: /** * This is needed to make base class polymorphic (i.e. have a vtable) */ virtual void dummyMethod(){} protected: ClassLoaderVector associated_class_loaders_; std::string associated_library_path_; std::string base_class_name_; std::string class_name_; std::string typeid_base_class_name_; }; /** * @class AbstractMetaObject * @brief Abstract base class for factories where polymorphic type variable indicates base class for plugin interface. * @parm B The base class interface for the plugin */ template class AbstractMetaObject : public AbstractMetaObjectBase { public: /** * @brief A constructor for this class * @param name The literal name of the class. */ AbstractMetaObject(const std::string& class_name, const std::string& base_class_name) : AbstractMetaObjectBase(class_name, base_class_name) { AbstractMetaObjectBase::typeid_base_class_name_ = std::string(typeid(B).name()); } /** * @brief Defines the factory interface that the MetaObject must implement. * @return A pointer of parametric type B to a newly created object. */ virtual B* create() const = 0; /// Create a new instance of a class. /// Cannot be used for singletons. private: AbstractMetaObject(); AbstractMetaObject(const AbstractMetaObject&); AbstractMetaObject& operator = (const AbstractMetaObject&); }; /** * @class MetaObject * @brief The actual factory. * @parm C The derived class (the actual plugin) * @parm B The base class interface for the plugin */ template class MetaObject: public AbstractMetaObject { public: /** * @brief Constructor for the class */ MetaObject(const std::string& class_name, const std::string& base_class_name) : AbstractMetaObject(class_name, base_class_name) { } /** * @brief The factory interface to generate an object. The object has type C in reality, though a pointer of the base class type is returned. * @return A pointer to a newly created plugin with the base class type (type parameter B) */ B* create() const { return new C; } }; } // End namespace class_loader_private } // End namespace class_loader #endif ros-class-loader-0.3.2/include/class_loader/multi_library_class_loader.h000066400000000000000000000252631253326420400265170ustar00rootroot00000000000000/* * 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. */ #ifndef CLASS_LOADER_MULTI_LIBRARY_CLASS_LOADER_H_DEFINED #define CLASS_LOADER_MULTI_LIBRARY_CLASS_LOADER_H_DEFINED #include "class_loader.h" #include namespace class_loader { typedef std::string LibraryPath; typedef std::map LibraryToClassLoaderMap; typedef std::vector ClassLoaderVector; /** * @class MultiLibraryClassLoader * @brief A ClassLoader that can bind more than one runtime library */ class MultiLibraryClassLoader { public: /** * @brief Constructor for the class * @param enable_ondemand_loadunload - Flag indicates if classes are to be loaded/unloaded automatically as class_loader are created and destroyed */ MultiLibraryClassLoader(bool enable_ondemand_loadunload); /** * @brief Virtual destructor for class */ virtual ~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 boost::shared_ptr to newly created plugin */ template boost::shared_ptr createInstance(const std::string& class_name) { logDebug("class_loader::MultiLibraryClassLoader: Attempting to create instance of class type %s.", class_name.c_str()); ClassLoaderVector active_loaders = getAllAvailableClassLoaders(); for(unsigned int c = 0; c < active_loaders.size(); c++) { ClassLoader* current = active_loaders.at(c); if(current->isClassAvailable(class_name)) return(current->createInstance(class_name)); } 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()")); } /** * @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 boost::shared_ptr to newly created plugin */ template boost::shared_ptr createInstance(const std::string& class_name, const std::string& library_path) { ClassLoader* loader = getClassLoaderForLibrary(library_path); if(loader) return(loader->createInstance(class_name)); else 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()"); } /** * @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 * This version should not be used as the plugin system cannot do automated safe loading/unloadings * @param Base - polymorphic type indicating base class * @param class_name - the name of the concrete plugin class we want to instantiate * @return An unmanaged Base* to newly created plugin */ template Base* createUnmanagedInstance(const std::string& class_name) { ClassLoaderVector active_loaders = getAllAvailableClassLoaders(); for(unsigned int c = 0; c < active_loaders.size(); c++) { ClassLoader* current = active_loaders.at(c); if(current->isClassAvailable(class_name)) return(current->createUnmanagedInstance(class_name)); } throw(class_loader::CreateClassException("MultiLibraryClassLoader: Could not create class of type " + class_name)); } /** * @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 * This version should not be used as the plugin system cannot do automated safe loading/unloadings * @param Base - polymorphic type indicating Base class * @param class_name - name of class for which we want to create instance * @param library_path - the fully qualified path to the runtime library */ template Base* createUnmanagedInstance(const std::string& class_name, const std::string& library_path) { ClassLoader* loader = getClassLoaderForLibrary(library_path); if(loader) return(loader->createUnmanagedInstance(class_name)); else 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()"); } /** * @brief Indicates if a class has been loaded and can be instantiated * @param Base - polymorphic type indicating Base class * @param class_name - name of class that is be inquired about * @return true if loaded, false otherwise */ template bool isClassAvailable(const std::string& class_name) { std::vector available_classes = getAvailableClasses(); return(available_classes.end() != std::find(available_classes.begin(), available_classes.end(), class_name)); } /** * @brief Indicates if a library has been loaded into memory * @param library_path - The full qualified path to the runtime library * @return true if library is loaded, false otherwise */ bool isLibraryAvailable(const std::string& library_path); /** * @brief Gets a list of all classes that are loaded by the class loader * @param Base - polymorphic type indicating Base class * @return A vector of the available classes */ template std::vector getAvailableClasses() { std::vector available_classes; ClassLoaderVector loaders = getAllAvailableClassLoaders(); for(unsigned int c = 0; c < loaders.size(); c++) { ClassLoader* current = loaders.at(c); std::vector loader_classes = current->getAvailableClasses(); available_classes.insert(available_classes.end(), loader_classes.begin(), loader_classes.end()); } return(available_classes); } /** * @brief Gets a list of all classes loaded for a particular library * @param Base - polymorphic type indicating Base class * @return A vector of the available classes in the passed library */ template std::vector getAvailableClassesForLibrary(const std::string& library_path) { ClassLoader* loader = getClassLoaderForLibrary(library_path); std::vector available_classes; if(loader) { available_classes = loader->getAvailableClasses(); return(available_classes); } else throw class_loader::NoClassLoaderExistsException("There is no ClassLoader in MultiLibraryClassLoader bound to library " + library_path + " Ensure you called MultiLibraryClassLoader::loadLibrary()"); } /** * @brief Gets a list of all libraries opened by this class loader @ @return A list of libraries opened by this class loader */ std::vector getRegisteredLibraries(); /** * @brief Loads a library into memory for this class loader * @param library_path - the fully qualified path to the runtime library */ void loadLibrary(const std::string& library_path); /** * @brief Unloads a library for this class loader * @param library_path - the fully qualified path to the runtime library */ int unloadLibrary(const std::string& library_path); private: /** * @brief Indicates if on-demand (lazy) load/unload is enabled so libraries are loaded/unloaded automatically as needed */ bool isOnDemandLoadUnloadEnabled(){return(enable_ondemand_loadunload_);} /** * @brief Gets a handle to the class loader corresponding to a specific runtime library * @param library_path - the library from which we want to create the plugin * @return A pointer to the ClassLoader*, == NULL if not found */ ClassLoader* getClassLoaderForLibrary(const std::string& library_path); /** * @brief Gets all class loaders loaded within scope */ ClassLoaderVector getAllAvailableClassLoaders(); /** * @brief Destroys all ClassLoaders */ void shutdownAllClassLoaders(); private: bool enable_ondemand_loadunload_; LibraryToClassLoaderMap active_class_loaders_; boost::mutex loader_mutex_; }; } #endif ros-class-loader-0.3.2/package.xml000066400000000000000000000022001253326420400167760ustar00rootroot00000000000000 class_loader 0.3.2 The class_loader package is a ROS-independent package for loading plugins during runtime and the foundation of the higher level ROS "pluginlib" library. class_loader utilizes the host operating system's runtime loader to open runtime libraries (e.g. .so/.dll files), introspect the library for exported plugin classes, and allows users to instantiate objects of said exported classes without the explicit declaration (i.e. header file) for those classes. Esteve Fernandez Mirza Shah BSD http://ros.org/wiki/class_loader catkin boost libconsole-bridge-dev libpoco-dev cmake_modules boost libconsole-bridge-dev libpoco-dev ros-class-loader-0.3.2/src/000077500000000000000000000000001253326420400154565ustar00rootroot00000000000000ros-class-loader-0.3.2/src/class_loader.cpp000066400000000000000000000076071253326420400206270ustar00rootroot00000000000000/* * 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 "class_loader/class_loader.h" namespace class_loader { bool ClassLoader::has_unmananged_instance_been_created_ = false; bool ClassLoader::hasUnmanagedInstanceBeenCreated() { return ClassLoader::has_unmananged_instance_been_created_; } std::string systemLibrarySuffix() { return(Poco::SharedLibrary::suffix()); } ClassLoader::ClassLoader(const std::string& library_path, bool ondemand_load_unload) : ondemand_load_unload_(ondemand_load_unload), library_path_(library_path), load_ref_count_(0), plugin_ref_count_(0) { logDebug("class_loader.ClassLoader: Constructing new ClassLoader (%p) bound to library %s.", this, library_path.c_str()); if(!isOnDemandLoadUnloadEnabled()) loadLibrary(); } ClassLoader::~ClassLoader() { logDebug("class_loader.ClassLoader: Destroying class loader, unloading associated library...\n"); unloadLibrary(); //TODO: while(unloadLibrary() > 0){} ?? } bool ClassLoader::isLibraryLoaded() { return(class_loader::class_loader_private::isLibraryLoaded(getLibraryPath(), this)); } bool ClassLoader::isLibraryLoadedByAnyClassloader() { return(class_loader::class_loader_private::isLibraryLoadedByAnybody(getLibraryPath())); } void ClassLoader::loadLibrary() { boost::recursive_mutex::scoped_lock lock(load_ref_count_mutex_); load_ref_count_ = load_ref_count_ + 1; class_loader::class_loader_private::loadLibrary(getLibraryPath(), this); } int ClassLoader::unloadLibrary() { return(unloadLibraryInternal(true)); } int ClassLoader::unloadLibraryInternal(bool lock_plugin_ref_count) { boost::recursive_mutex::scoped_lock load_ref_lock(load_ref_count_mutex_); boost::recursive_mutex::scoped_lock plugin_ref_lock; if(lock_plugin_ref_count) plugin_ref_lock = boost::recursive_mutex::scoped_lock(plugin_ref_count_mutex_); if(plugin_ref_count_ > 0) logWarn("class_loader.ClassLoader: SEVERE WARNING!!! Attempting to unload library while objects created by this loader exist in the heap! You should delete your objects before attempting to unload the library or destroying the ClassLoader. The library will NOT be unloaded."); else { load_ref_count_ = load_ref_count_ - 1; if(load_ref_count_ == 0) class_loader::class_loader_private::unloadLibrary(getLibraryPath(), this); else if(load_ref_count_ < 0) load_ref_count_ = 0; } return(load_ref_count_); } } ros-class-loader-0.3.2/src/class_loader.pc.in000066400000000000000000000005041253326420400210410ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=${prefix}/lib includedir=${prefix}/include Name: @TARGET_NAME@ Description: @PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ Requires: @PKGCONFIG_REQUIRES@ Libs: -L${libdir} -l@TARGET_NAME@ @PKGCONFIG_LIBS@ Cflags: -I${includedir} @PKGCONFIG_CFLAGS@ ros-class-loader-0.3.2/src/class_loader_core.cpp000066400000000000000000000615651253326420400216420ustar00rootroot00000000000000/* * 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 "class_loader/class_loader_core.h" #include "class_loader/class_loader.h" #include namespace class_loader { namespace class_loader_private { //Global data /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ boost::recursive_mutex& getLoadedLibraryVectorMutex() /*****************************************************************************/ { static boost::recursive_mutex m; return m; } boost::recursive_mutex& getPluginBaseToFactoryMapMapMutex() /*****************************************************************************/ { static boost::recursive_mutex m; return m; } BaseToFactoryMapMap& getGlobalPluginBaseToFactoryMapMap() /*****************************************************************************/ { static BaseToFactoryMapMap instance; return instance; } FactoryMap& getFactoryMapForBaseClass(const std::string& typeid_base_class_name) /*****************************************************************************/ { BaseToFactoryMapMap& factoryMapMap = getGlobalPluginBaseToFactoryMapMap(); std::string base_class_name = typeid_base_class_name; if(factoryMapMap.find(base_class_name) == factoryMapMap.end()) factoryMapMap[base_class_name] = FactoryMap(); return(factoryMapMap[base_class_name]); } MetaObjectVector& getMetaObjectGraveyard() /*****************************************************************************/ { static MetaObjectVector instance; return instance; } LibraryVector& getLoadedLibraryVector() /*****************************************************************************/ { static LibraryVector instance; return instance; } std::string& getCurrentlyLoadingLibraryNameReference() /*****************************************************************************/ { static std::string library_name; return library_name; } std::string getCurrentlyLoadingLibraryName() /*****************************************************************************/ { return(getCurrentlyLoadingLibraryNameReference()); } void setCurrentlyLoadingLibraryName(const std::string& library_name) /*****************************************************************************/ { std::string& library_name_ref = getCurrentlyLoadingLibraryNameReference(); library_name_ref = library_name; } ClassLoader*& getCurrentlyActiveClassLoaderReference() /*****************************************************************************/ { static ClassLoader* loader = NULL; return(loader); } ClassLoader* getCurrentlyActiveClassLoader() /*****************************************************************************/ { return(getCurrentlyActiveClassLoaderReference()); } void setCurrentlyActiveClassLoader(ClassLoader* loader) /*****************************************************************************/ { ClassLoader*& loader_ref = getCurrentlyActiveClassLoaderReference(); loader_ref = loader; } bool& hasANonPurePluginLibraryBeenOpenedReference() /*****************************************************************************/ { static bool hasANonPurePluginLibraryBeenOpenedReference = false; return hasANonPurePluginLibraryBeenOpenedReference; } bool hasANonPurePluginLibraryBeenOpened() /*****************************************************************************/ { return(hasANonPurePluginLibraryBeenOpenedReference()); } void hasANonPurePluginLibraryBeenOpened(bool hasIt) /*****************************************************************************/ { hasANonPurePluginLibraryBeenOpenedReference() = hasIt; } //MetaObject search/insert/removal/query /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ MetaObjectVector allMetaObjects(const FactoryMap& factories) /*****************************************************************************/ { MetaObjectVector all_meta_objs; for(FactoryMap::const_iterator factoryItr = factories.begin(); factoryItr != factories.end(); factoryItr++) all_meta_objs.push_back(factoryItr->second); return(all_meta_objs); } MetaObjectVector allMetaObjects() /*****************************************************************************/ { boost::recursive_mutex::scoped_lock lock(getPluginBaseToFactoryMapMapMutex()); MetaObjectVector all_meta_objs; BaseToFactoryMapMap& factory_map_map = getGlobalPluginBaseToFactoryMapMap(); BaseToFactoryMapMap::iterator itr; for(itr = factory_map_map.begin(); itr != factory_map_map.end(); itr++) { MetaObjectVector objs = allMetaObjects(itr->second); all_meta_objs.insert(all_meta_objs.end(), objs.begin(), objs.end()); } return(all_meta_objs); } MetaObjectVector filterAllMetaObjectsOwnedBy(const MetaObjectVector& to_filter, const ClassLoader* owner) /*****************************************************************************/ { MetaObjectVector filtered_objs; for(unsigned int c = 0; c < to_filter.size(); c++) if(to_filter.at(c)->isOwnedBy(owner)) filtered_objs.push_back(to_filter.at(c)); return(filtered_objs); } MetaObjectVector filterAllMetaObjectsAssociatedWithLibrary(const MetaObjectVector& to_filter, const std::string& library_path) /*****************************************************************************/ { MetaObjectVector filtered_objs; for(unsigned int c = 0; c < to_filter.size(); c++) if(to_filter.at(c)->getAssociatedLibraryPath()==library_path) filtered_objs.push_back(to_filter.at(c)); return(filtered_objs); } MetaObjectVector allMetaObjectsForClassLoader(const ClassLoader* owner) /*****************************************************************************/ { return(filterAllMetaObjectsOwnedBy(allMetaObjects(), owner)); } MetaObjectVector allMetaObjectsForLibrary(const std::string& library_path) /*****************************************************************************/ { return(filterAllMetaObjectsAssociatedWithLibrary(allMetaObjects(), library_path)); } MetaObjectVector allMetaObjectsForLibraryOwnedBy(const std::string& library_path, const ClassLoader* owner) /*****************************************************************************/ { return(filterAllMetaObjectsOwnedBy(allMetaObjectsForLibrary(library_path), owner)); } void insertMetaObjectIntoGraveyard(AbstractMetaObjectBase* meta_obj) /*****************************************************************************/ { logDebug("class_loader.class_loader_private: Inserting MetaObject (class = %s, base_class = %s, ptr = %p) into graveyard", meta_obj->className().c_str(), meta_obj->baseClassName().c_str(), meta_obj); getMetaObjectGraveyard().push_back(meta_obj); } void destroyMetaObjectsForLibrary(const std::string& library_path, FactoryMap& factories, const ClassLoader* loader) /*****************************************************************************/ { FactoryMap::iterator factory_itr = factories.begin(); while(factory_itr != factories.end()) { AbstractMetaObjectBase* meta_obj = factory_itr->second; if(meta_obj->getAssociatedLibraryPath() == library_path && meta_obj->isOwnedBy(loader)) { meta_obj->removeOwningClassLoader(loader); if(!meta_obj->isOwnedByAnybody()) { FactoryMap::iterator factory_itr_copy = factory_itr; factory_itr++; factories.erase(factory_itr_copy); //Note: map::erase does not return iterator like vector::erase does. Hence the ugliness of this code and a need for copy. Should be fixed in next C++ revision //Insert into graveyard //We remove the metaobject from its factory map, but we don't destroy it...instead it saved to //a "graveyard" to the side. This is due to our static global variable initialization problem //that causes factories to not be registered when a library is closed and then reopened. This is //because it's truly not closed due to the use of global symbol binding i.e. calling dlopen //with RTLD_GLOBAL instead of RTLD_LOCAL. We require using the former as the which is required to support //RTTI insertMetaObjectIntoGraveyard(meta_obj); } else factory_itr++; } else factory_itr++; } } void destroyMetaObjectsForLibrary(const std::string& library_path, const ClassLoader* loader) /*****************************************************************************/ { boost::recursive_mutex::scoped_lock lock(getPluginBaseToFactoryMapMapMutex()); logDebug("class_loader.class_loader_private: Removing MetaObjects associated with library %s and class loader %p from global plugin-to-factorymap map.\n", library_path.c_str(), loader); //We have to walk through all FactoryMaps to be sure BaseToFactoryMapMap& factory_map_map = getGlobalPluginBaseToFactoryMapMap(); BaseToFactoryMapMap::iterator itr; for(itr = factory_map_map.begin(); itr != factory_map_map.end(); itr++) destroyMetaObjectsForLibrary(library_path, itr->second, loader); logDebug("class_loader.class_loader_private: Metaobjects removed."); } bool areThereAnyExistingMetaObjectsForLibrary(const std::string& library_path) /*****************************************************************************/ { return(allMetaObjectsForLibrary(library_path).size() > 0); } //Loaded Library Vector manipulation /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ LibraryVector::iterator findLoadedLibrary(const std::string& library_path) /*****************************************************************************/ { LibraryVector& open_libraries = getLoadedLibraryVector(); LibraryVector::iterator itr; for(itr = open_libraries.begin(); itr != open_libraries.end(); itr++ ) { if(itr->first == library_path) break; } return(itr); } bool isLibraryLoadedByAnybody(const std::string& library_path) /*****************************************************************************/ { boost::recursive_mutex::scoped_lock lock(getLoadedLibraryVectorMutex()); LibraryVector& open_libraries = getLoadedLibraryVector(); LibraryVector::iterator itr = findLoadedLibrary(library_path); if(itr != open_libraries.end()) { assert(itr->second->isLoaded() == true); //Ensure Poco actually thinks the library is loaded return(true); } else return(false); }; bool isLibraryLoaded(const std::string& library_path, ClassLoader* loader) /*****************************************************************************/ { bool is_lib_loaded_by_anyone = isLibraryLoadedByAnybody(library_path); int num_meta_objs_for_lib = allMetaObjectsForLibrary(library_path).size(); int num_meta_objs_for_lib_bound_to_loader = allMetaObjectsForLibraryOwnedBy(library_path,loader).size(); bool are_meta_objs_bound_to_loader = (num_meta_objs_for_lib == 0) ? true : (num_meta_objs_for_lib_bound_to_loader <= num_meta_objs_for_lib); return(is_lib_loaded_by_anyone && are_meta_objs_bound_to_loader); } std::vector getAllLibrariesUsedByClassLoader(const ClassLoader* loader) /*****************************************************************************/ { MetaObjectVector all_loader_meta_objs = allMetaObjectsForClassLoader(loader); std::vector all_libs; for(unsigned int c = 0; c < all_loader_meta_objs.size(); c++) { std::string lib_path = all_loader_meta_objs.at(c)->getAssociatedLibraryPath(); if(std::find(all_libs.begin(), all_libs.end(), lib_path) == all_libs.end()) all_libs.push_back(lib_path); } return(all_libs); } //Implementation of Remaining Core plugin_private Functions /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void addClassLoaderOwnerForAllExistingMetaObjectsForLibrary(const std::string& library_path, ClassLoader* loader) /*****************************************************************************/ { MetaObjectVector all_meta_objs = allMetaObjectsForLibrary(library_path); for(unsigned int c = 0; c < all_meta_objs.size(); c++) { AbstractMetaObjectBase* meta_obj = all_meta_objs.at(c); logDebug("class_loader.class_loader_private: Tagging existing MetaObject %p (base = %s, derived = %s) with class loader %p (library path = %s).", meta_obj, meta_obj->baseClassName().c_str(), meta_obj->className().c_str(), loader, loader ? loader->getLibraryPath().c_str() : "NULL"); all_meta_objs.at(c)->addOwningClassLoader(loader); } } void revivePreviouslyCreateMetaobjectsFromGraveyard(const std::string& library_path, ClassLoader* loader) /*****************************************************************************/ { boost::recursive_mutex::scoped_lock b2fmm_lock(getPluginBaseToFactoryMapMapMutex()); MetaObjectVector& graveyard = getMetaObjectGraveyard(); for(MetaObjectVector::iterator itr = graveyard.begin(); itr != graveyard.end(); itr++) { AbstractMetaObjectBase* obj = *itr; if(obj->getAssociatedLibraryPath() == library_path) { logDebug("class_loader.class_loader_private: Resurrected factory metaobject from graveyard, class = %s, base_class = %s ptr = %p...bound to ClassLoader %p (library path = %s)", obj->className().c_str(), obj->baseClassName().c_str(), obj, loader, loader ? loader->getLibraryPath().c_str() : "NULL"); obj->addOwningClassLoader(loader); assert(obj->typeidBaseClassName() != "UNSET"); FactoryMap& factory = getFactoryMapForBaseClass(obj->typeidBaseClassName()); factory[obj->className()] = obj; } } } void purgeGraveyardOfMetaobjects(const std::string& library_path, ClassLoader* loader, bool delete_objs) /*****************************************************************************/ { MetaObjectVector all_meta_objs = allMetaObjects(); boost::recursive_mutex::scoped_lock b2fmm_lock(getPluginBaseToFactoryMapMapMutex()); //Note: Lock must happen after call to allMetaObjects as that will lock MetaObjectVector& graveyard = getMetaObjectGraveyard(); MetaObjectVector::iterator itr = graveyard.begin(); while(itr != graveyard.end()) { AbstractMetaObjectBase* obj = *itr; if(obj->getAssociatedLibraryPath() == library_path) { logDebug("class_loader.class_loader_private: Purging factory metaobject from graveyard, class = %s, base_class = %s ptr = %p...bound to ClassLoader %p (library path = %s)", obj->className().c_str(), obj->baseClassName().c_str(), obj, loader, loader ? loader->getLibraryPath().c_str() : "NULL"); bool is_address_in_graveyard_same_as_global_factory_map = std::find(all_meta_objs.begin(), all_meta_objs.end(), *itr) != all_meta_objs.end(); itr = graveyard.erase(itr); if(delete_objs) { if(is_address_in_graveyard_same_as_global_factory_map) logDebug("class_loader.class_loader_private: Newly created metaobject factory in global factory map map has same address as one in graveyard -- metaobject has been purged from graveyard but not deleted."); else { assert(hasANonPurePluginLibraryBeenOpened() == false); logDebug("class_loader.class_loader_private: Also destroying metaobject %p (class = %s, base_class = %s, library_path = %s) in addition to purging it from graveyard.", obj, obj->className().c_str(), obj->baseClassName().c_str(), obj->getAssociatedLibraryPath().c_str()); delete(obj); //Note: This is the only place where metaobjects can be destroyed } } } else itr++; } } void loadLibrary(const std::string& library_path, ClassLoader* loader) /*****************************************************************************/ { logDebug("class_loader.class_loader_private: Attempting to load library %s on behalf of ClassLoader handle %p...\n", library_path.c_str(), loader); //If it's already open, just update existing metaobjects to have an additional owner. if(isLibraryLoadedByAnybody(library_path)) { logDebug("class_loader.class_loader_private: Library already in memory, but binding existing MetaObjects to loader if necesesary.\n"); addClassLoaderOwnerForAllExistingMetaObjectsForLibrary(library_path, loader); return; } Poco::SharedLibrary* library_handle = NULL; static boost::recursive_mutex loader_mutex; { boost::recursive_mutex::scoped_lock loader_lock(loader_mutex); try { setCurrentlyActiveClassLoader(loader); setCurrentlyLoadingLibraryName(library_path); library_handle = new Poco::SharedLibrary(library_path); } catch(const Poco::LibraryLoadException& e) { setCurrentlyLoadingLibraryName(""); setCurrentlyActiveClassLoader(NULL); throw(class_loader::LibraryLoadException("Could not load library (Poco exception = " + std::string(e.message()) + ")")); } catch(const Poco::LibraryAlreadyLoadedException& e) { setCurrentlyLoadingLibraryName(""); setCurrentlyActiveClassLoader(NULL); throw(class_loader::LibraryLoadException("Library already loaded (Poco exception = " + std::string(e.message()) + ")")); } catch(const Poco::NotFoundException& e) { setCurrentlyLoadingLibraryName(""); setCurrentlyActiveClassLoader(NULL); throw(class_loader::LibraryLoadException("Library not found (Poco exception = " + std::string(e.message()) + ")")); } setCurrentlyLoadingLibraryName(""); setCurrentlyActiveClassLoader(NULL); } assert(library_handle != NULL); logDebug("class_loader.class_loader_private: Successfully loaded library %s into memory (Poco::SharedLibrary handle = %p).", library_path.c_str(), library_handle); //Graveyard scenario unsigned int num_lib_objs = allMetaObjectsForLibrary(library_path).size(); if(num_lib_objs == 0) { logDebug("class_loader.class_loader_private: Though the library %s was just loaded, it seems no factory metaobjects were registered. Checking factory graveyard for previously loaded metaobjects...", library_path.c_str()); revivePreviouslyCreateMetaobjectsFromGraveyard(library_path, loader); purgeGraveyardOfMetaobjects(library_path, loader, false); //Note: The 'false' indicates we don't want to invoke delete on the metaobject } else { logDebug("class_loader.class_loader_private: Library %s generated new factory metaobjects on load. Destroying graveyarded objects from previous loads...", library_path.c_str()); purgeGraveyardOfMetaobjects(library_path, loader, true); } //Insert library into global loaded library vectory boost::recursive_mutex::scoped_lock llv_lock(getLoadedLibraryVectorMutex()); LibraryVector& open_libraries = getLoadedLibraryVector(); open_libraries.push_back(LibraryPair(library_path, library_handle)); //Note: Poco::SharedLibrary automatically calls load() when library passed to constructor } void unloadLibrary(const std::string& library_path, ClassLoader* loader) /*****************************************************************************/ { if(hasANonPurePluginLibraryBeenOpened()) { logDebug("class_loader.class_loader_private: Cannot unload %s or ANY other library as a non-pure plugin library was opened. As class_loader has no idea which libraries class factories were exported from, it can safely close any library without potentially unlinking symbols that are still actively being used. You must refactor your plugin libraries to be made exclusively of plugins in order for this error to stop happening.", library_path.c_str()); } else { logDebug("class_loader.class_loader_private: Unloading library %s on behalf of ClassLoader %p...", library_path.c_str(), loader); boost::recursive_mutex::scoped_lock lock(getLoadedLibraryVectorMutex()); LibraryVector& open_libraries = getLoadedLibraryVector(); LibraryVector::iterator itr = findLoadedLibrary(library_path); if(itr != open_libraries.end()) { Poco::SharedLibrary* library = itr->second; std::string library_path = itr->first; try { destroyMetaObjectsForLibrary(library_path, loader); //Remove from loaded library list as well if no more factories associated with said library if(!areThereAnyExistingMetaObjectsForLibrary(library_path)) { logDebug("class_loader.class_loader_private: There are no more MetaObjects left for %s so unloading library and removing from loaded library vector.\n", library_path.c_str()); library->unload(); assert(library->isLoaded() == false); delete(library); itr = open_libraries.erase(itr); } else logDebug("class_loader.class_loader_private: MetaObjects still remain in memory meaning other ClassLoaders are still using library, keeping library %s open.", library_path.c_str()); return; } catch(const Poco::RuntimeException& e) { delete(library); throw(class_loader::LibraryUnloadException("Could not unload library (Poco exception = " + std::string(e.message()) + ")")); } } throw(class_loader::LibraryUnloadException("Attempt to unload library that class_loader is unaware of.")); } } //Other /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void printDebugInfoToScreen() /*****************************************************************************/ { printf("*******************************************************************************\n"); printf("***** class_loader_private DEBUG INFORMATION *****\n"); printf("*******************************************************************************\n"); printf("OPEN LIBRARIES IN MEMORY:\n"); printf("--------------------------------------------------------------------------------\n"); boost::recursive_mutex::scoped_lock lock(getLoadedLibraryVectorMutex()); LibraryVector libs = getLoadedLibraryVector(); for(unsigned int c = 0; c < libs.size(); c++) printf("Open library %i = %s (Poco SharedLibrary handle = %p)\n", c, (libs.at(c)).first.c_str(), (libs.at(c)).second); printf("METAOBJECTS (i.e. FACTORIES) IN MEMORY:\n"); printf("--------------------------------------------------------------------------------\n"); MetaObjectVector meta_objs = allMetaObjects(); for(unsigned int c = 0; c < meta_objs.size(); c++) { AbstractMetaObjectBase* obj = meta_objs.at(c); printf("Metaobject %i (ptr = %p):\n TypeId = %s\n Associated Library = %s\n", c, obj, (typeid(*obj).name()), obj->getAssociatedLibraryPath().c_str()); ClassLoaderVector loaders = obj->getAssociatedClassLoaders(); for(unsigned int i = 0; i < loaders.size(); i++) printf(" Associated Loader %i = %p\n", i, loaders.at(i)); printf("--------------------------------------------------------------------------------\n"); } printf("********************************** END DEBUG **********************************\n"); printf("*******************************************************************************\n\n"); } } //End namespace class_loader_private } //End namespace class_loader ros-class-loader-0.3.2/src/meta_object.cpp000066400000000000000000000114331253326420400204400ustar00rootroot00000000000000/* * 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 "class_loader/meta_object.h" #include "class_loader/class_loader.h" namespace class_loader { namespace class_loader_private { AbstractMetaObjectBase::AbstractMetaObjectBase(const std::string& class_name, const std::string& base_class_name) : associated_library_path_("Unknown"), base_class_name_(base_class_name), class_name_(class_name), typeid_base_class_name_("UNSET") /*****************************************************************************/ { logDebug("class_loader.class_loader_private.AbstractMetaObjectBase: Creating MetaObject %p (base = %s, derived = %s, library path = %s)", this, baseClassName().c_str(), className().c_str(), getAssociatedLibraryPath().c_str()); } AbstractMetaObjectBase::~AbstractMetaObjectBase() /*****************************************************************************/ { logDebug("class_loader.class_loader_private.AbstractMetaObjectBase: Destroying MetaObject %p (base = %s, derived = %s, library path = %s)", this, baseClassName().c_str(), className().c_str(), getAssociatedLibraryPath().c_str()); } std::string AbstractMetaObjectBase::className() const /*****************************************************************************/ { return class_name_; } std::string AbstractMetaObjectBase::baseClassName() const /*****************************************************************************/ { return base_class_name_; } std::string AbstractMetaObjectBase::typeidBaseClassName() const /*****************************************************************************/ { return typeid_base_class_name_; } std::string AbstractMetaObjectBase::getAssociatedLibraryPath() /*****************************************************************************/ { return(associated_library_path_); } void AbstractMetaObjectBase::setAssociatedLibraryPath(std::string library_path) /*****************************************************************************/ { associated_library_path_ = library_path; } void AbstractMetaObjectBase::addOwningClassLoader(ClassLoader* loader) /*****************************************************************************/ { ClassLoaderVector& v = associated_class_loaders_; if(std::find(v.begin(), v.end(), loader) == v.end()) v.push_back(loader); } void AbstractMetaObjectBase::removeOwningClassLoader(const ClassLoader* loader) /*****************************************************************************/ { ClassLoaderVector& v = associated_class_loaders_; ClassLoaderVector::iterator itr = std::find(v.begin(), v.end(), loader); if(itr != v.end()) v.erase(itr); } bool AbstractMetaObjectBase::isOwnedBy(const ClassLoader* loader) /*****************************************************************************/ { ClassLoaderVector& v = associated_class_loaders_; ClassLoaderVector::iterator itr = std::find(v.begin(), v.end(), loader); return(itr != v.end()); } bool AbstractMetaObjectBase::isOwnedByAnybody() /*****************************************************************************/ { return(associated_class_loaders_.size() > 0); } ClassLoaderVector AbstractMetaObjectBase::getAssociatedClassLoaders() /*****************************************************************************/ { return(associated_class_loaders_); } } } ros-class-loader-0.3.2/src/multi_library_class_loader.cpp000066400000000000000000000074111253326420400235560ustar00rootroot00000000000000/* * 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 "class_loader/multi_library_class_loader.h" namespace class_loader { MultiLibraryClassLoader::MultiLibraryClassLoader(bool enable_ondemand_loadunload) : enable_ondemand_loadunload_(enable_ondemand_loadunload) { } MultiLibraryClassLoader::~MultiLibraryClassLoader() { shutdownAllClassLoaders(); } std::vector MultiLibraryClassLoader::getRegisteredLibraries() { std::vector libraries; for(LibraryToClassLoaderMap::iterator itr = active_class_loaders_.begin(); itr != active_class_loaders_.end(); itr++) { if(itr->second != NULL) libraries.push_back(itr->first); } return(libraries); } ClassLoader* MultiLibraryClassLoader::getClassLoaderForLibrary(const std::string& library_path) { return(active_class_loaders_[library_path]); } ClassLoaderVector MultiLibraryClassLoader::getAllAvailableClassLoaders() { ClassLoaderVector loaders; for(LibraryToClassLoaderMap::iterator itr = active_class_loaders_.begin(); itr != active_class_loaders_.end(); itr++) loaders.push_back(itr->second); return(loaders); } bool MultiLibraryClassLoader::isLibraryAvailable(const std::string& library_name) { std::vector available_libraries = getRegisteredLibraries(); return(available_libraries.end() != std::find(available_libraries.begin(), available_libraries.end(), library_name)); } void MultiLibraryClassLoader::loadLibrary(const std::string& library_path) { if(!isLibraryAvailable(library_path)) active_class_loaders_[library_path] = new class_loader::ClassLoader(library_path, isOnDemandLoadUnloadEnabled()); } void MultiLibraryClassLoader::shutdownAllClassLoaders() { std::vector available_libraries = getRegisteredLibraries(); for(unsigned int c = 0; c < available_libraries.size(); c++) unloadLibrary(available_libraries.at(c)); } int MultiLibraryClassLoader::unloadLibrary(const std::string& library_path) { int remaining_unloads = 0; if(isLibraryAvailable(library_path)) { ClassLoader* loader = getClassLoaderForLibrary(library_path); if((remaining_unloads = loader->unloadLibrary()) == 0) { active_class_loaders_[library_path] = NULL; delete(loader); } } return(remaining_unloads); } } ros-class-loader-0.3.2/test/000077500000000000000000000000001253326420400156465ustar00rootroot00000000000000ros-class-loader-0.3.2/test/CMakeLists.txt000066400000000000000000000015541253326420400204130ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) find_package(Boost REQUIRED COMPONENTS thread) find_package(catkin REQUIRED COMPONENTS class_loader) include_directories(${catkin_INCLUDE_DIRS}) add_library(${PROJECT_NAME}_TestPlugins1 EXCLUDE_FROM_ALL plugins1.cpp) target_link_libraries(${PROJECT_NAME}_TestPlugins1 ${PROJECT_NAME}) class_loader_hide_library_symbols(${PROJECT_NAME}_TestPlugins1) add_library(${PROJECT_NAME}_TestPlugins2 EXCLUDE_FROM_ALL plugins2.cpp) target_link_libraries(${PROJECT_NAME}_TestPlugins2 ${PROJECT_NAME}) class_loader_hide_library_symbols(${PROJECT_NAME}_TestPlugins2) catkin_add_gtest(${PROJECT_NAME}_utest utest.cpp) if(TARGET ${PROJECT_NAME}_utest) target_link_libraries(${PROJECT_NAME}_utest ${Boost_LIBRARIES} ${class_loader_LIBRARIES}) add_dependencies(${PROJECT_NAME}_utest ${PROJECT_NAME}_TestPlugins1 ${PROJECT_NAME}_TestPlugins2) endif() ros-class-loader-0.3.2/test/base.h000066400000000000000000000001461253326420400167320ustar00rootroot00000000000000#ifndef BASE_H #define BASE_H class Base { public: virtual void saySomething() = 0; }; #endif ros-class-loader-0.3.2/test/plugins1.cpp000066400000000000000000000014661253326420400201230ustar00rootroot00000000000000#include "base.h" #include #include class Dog : public Base { public: virtual void saySomething(){std::cout << "Bark" << std::endl;} }; class Cat : public Base { public: virtual void saySomething(){std::cout << "Meow" << std::endl;} }; class Duck : public Base { public: virtual void saySomething(){std::cout << "Quack" << std::endl;} }; class Cow : public Base { public: virtual void saySomething(){std::cout << "Moooo" << std::endl;} }; class Sheep : public Base { public: virtual void saySomething(){std::cout << "Baaah" << std::endl;} }; CLASS_LOADER_REGISTER_CLASS(Dog, Base); CLASS_LOADER_REGISTER_CLASS(Cat, Base); CLASS_LOADER_REGISTER_CLASS(Duck, Base); CLASS_LOADER_REGISTER_CLASS(Cow, Base); CLASS_LOADER_REGISTER_CLASS(Sheep, Base); ros-class-loader-0.3.2/test/plugins2.cpp000066400000000000000000000013101253326420400201100ustar00rootroot00000000000000#include "base.h" #include #include class Robot : public Base { public: virtual void saySomething(){std::cout << "Beep boop" << std::endl;} }; class Alien : public Base { public: virtual void saySomething(){std::cout << "Znornoff!!!" << std::endl;} }; class Monster : public Base { public: virtual void saySomething(){std::cout << "BEAAAHHHH" << std::endl;} }; class Zombie : public Base { public: virtual void saySomething(){std::cout << "Brains!!!" << std::endl;} }; CLASS_LOADER_REGISTER_CLASS(Robot, Base); CLASS_LOADER_REGISTER_CLASS(Alien, Base); CLASS_LOADER_REGISTER_CLASS(Monster, Base); CLASS_LOADER_REGISTER_CLASS(Zombie, Base); ros-class-loader-0.3.2/test/utest.cpp000066400000000000000000000173021253326420400175210ustar00rootroot00000000000000#include #include #include #include #include "base.h" #include const std::string LIBRARY_1 = "libclass_loader_TestPlugins1.so"; const std::string LIBRARY_2 = "libclass_loader_TestPlugins2.so"; /*****************************************************************************/ TEST(ClassLoaderTest, basicLoad) { try { class_loader::ClassLoader loader1(LIBRARY_1, false); loader1.createInstance("Cat")->saySomething(); //See if lazy load works } catch(class_loader::ClassLoaderException& e) { FAIL() << "ClassLoaderException: " << e.what() << "\n"; } SUCCEED(); } /*****************************************************************************/ TEST(ClassLoaderTest, correctNonLazyLoadUnload) { try { ASSERT_FALSE(class_loader::class_loader_private::isLibraryLoadedByAnybody(LIBRARY_1)); class_loader::ClassLoader loader1(LIBRARY_1, false); ASSERT_TRUE(class_loader::class_loader_private::isLibraryLoadedByAnybody(LIBRARY_1)); ASSERT_TRUE(loader1.isLibraryLoaded()); loader1.unloadLibrary(); ASSERT_FALSE(class_loader::class_loader_private::isLibraryLoadedByAnybody(LIBRARY_1)); ASSERT_FALSE(loader1.isLibraryLoaded()); return; } catch(class_loader::ClassLoaderException& e) { FAIL() << "ClassLoaderException: " << e.what() << "\n"; } catch(...) { FAIL() << "Unhandled exception"; } } /*****************************************************************************/ TEST(ClassLoaderTest, correctLazyLoadUnload) { try { ASSERT_FALSE(class_loader::class_loader_private::isLibraryLoadedByAnybody(LIBRARY_1)); class_loader::ClassLoader loader1(LIBRARY_1, true); ASSERT_FALSE(class_loader::class_loader_private::isLibraryLoadedByAnybody(LIBRARY_1)); ASSERT_FALSE(loader1.isLibraryLoaded()); { boost::shared_ptr obj = loader1.createInstance("Cat"); ASSERT_TRUE(class_loader::class_loader_private::isLibraryLoadedByAnybody(LIBRARY_1)); ASSERT_TRUE(loader1.isLibraryLoaded()); } //The library will unload automatically when the only plugin object left is destroyed ASSERT_FALSE(class_loader::class_loader_private::isLibraryLoadedByAnybody(LIBRARY_1)); return; } catch(class_loader::ClassLoaderException& e) { FAIL() << "ClassLoaderException: " << e.what() << "\n"; } catch(...) { FAIL() << "Unhandled exception"; } } /*****************************************************************************/ TEST(ClassLoaderTest, nonExistentPlugin) { class_loader::ClassLoader loader1(LIBRARY_1, false); try { boost::shared_ptr obj = loader1.createInstance("Bear"); if(obj == NULL) FAIL() << "Null object being returned instead of exception thrown."; obj->saySomething(); } catch(const class_loader::CreateClassException& e) { SUCCEED(); return; } catch(...) { FAIL() << "Unknown exception caught.\n"; } FAIL() << "Did not throw exception as expected.\n"; } /*****************************************************************************/ TEST(ClassLoaderTest, nonExistentLibrary) { try { class_loader::ClassLoader loader1("libDoesNotExist.so", false); } catch(const class_loader::LibraryLoadException& e) { SUCCEED(); return; } catch(...) { FAIL() << "Unknown exception caught.\n"; } FAIL() << "Did not throw exception as expected.\n"; } /*****************************************************************************/ class InvalidBase { }; TEST(ClassLoaderTest, 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& e) { FAIL() << "Unexpected exception"; } catch(...) { FAIL() << "Unexpected and unknown exception caught.\n"; } } /*****************************************************************************/ void wait(int seconds) { boost::this_thread::sleep(boost::posix_time::seconds(seconds)); } void run(class_loader::ClassLoader* loader) { std::vector classes = loader->getAvailableClasses(); for(unsigned int c = 0; c < classes.size(); c++) { loader->createInstance(classes.at(c))->saySomething(); } } TEST(ClassLoaderTest, 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(unsigned int c = 0; c < 1000; c++) client_threads.push_back(new boost::thread(boost::bind(&run, &loader1))); for(unsigned int c = 0; c < client_threads.size(); c++) client_threads.at(c)->join(); for(unsigned int c = 0; c < client_threads.size(); c++) delete(client_threads.at(c)); loader1.unloadLibrary(); ASSERT_FALSE(loader1.isLibraryLoaded()); } catch(const class_loader::ClassLoaderException& ex) { FAIL() << "Unexpected ClassLoaderException."; } catch(...) { FAIL() << "Unknown exception."; } } /*****************************************************************************/ TEST(ClassLoaderTest, 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& e) { FAIL() << "Unexpected exception.\n"; } catch(...) { FAIL() << "Unknown exception caught.\n"; } FAIL() << "Did not throw exception as expected.\n"; } /*****************************************************************************/ TEST(ClassLoaderTest, loadRefCountingLazy) { try { class_loader::ClassLoader loader1(LIBRARY_1, true); ASSERT_FALSE(loader1.isLibraryLoaded()); { boost::shared_ptr obj = loader1.createInstance("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& e) { FAIL() << "Unexpected exception.\n"; } catch(...) { FAIL() << "Unknown exception caught.\n"; } FAIL() << "Did not throw exception as expected.\n"; } /*****************************************************************************/ // Run all the tests that were declared with TEST() int main(int argc, char **argv){ testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }