Skip to content

Commit

Permalink
Windows compat and style fixups (ros#90)
Browse files Browse the repository at this point in the history
* add visibility macros to public functions

* rename private namespace 'class_loader_private' to 'impl' to match ros2 branch
  • Loading branch information
mikaelarguedas authored Apr 26, 2018
1 parent 89b062d commit 0a778b9
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 69 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ set(${PROJECT_NAME}_HDRS
)
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS} ${${PROJECT_NAME}_HDRS})
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${console_bridge_LIBRARIES} ${Poco_LIBRARIES})
if(WIN32)
# Causes the visibility macros to use dllexport rather than dllimport
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME} PRIVATE "CLASS_LOADER_BUILDING_DLL")
endif()

install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
Expand Down
21 changes: 17 additions & 4 deletions include/class_loader/class_loader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,21 @@

#include "class_loader/class_loader_core.hpp"
#include "class_loader/register_macro.hpp"
#include "class_loader/visibility_control.hpp"

namespace class_loader
{

/**
* @brief Returns the default library prefix for the native os
*/
CLASS_LOADER_PUBLIC
std::string systemLibraryPrefix();

/**
* @brief Returns runtime library extension for native os
*/
CLASS_LOADER_PUBLIC
std::string systemLibrarySuffix();

/**
Expand All @@ -65,6 +68,7 @@ std::string systemLibrarySuffix();
* On *nix platforms the library name is prefixed with `lib`.
* On all platforms the output of class_loader::systemLibrarySuffix() is appended.
*/
CLASS_LOADER_PUBLIC
std::string systemLibraryFormat(const std::string & library_name);

/**
Expand All @@ -85,11 +89,13 @@ class 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
*/
CLASS_LOADER_PUBLIC
explicit ClassLoader(const std::string & library_path, bool ondemand_load_unload = false);

/**
* @brief Destructor for ClassLoader. All libraries opened by this ClassLoader are unloaded automatically.
*/
CLASS_LOADER_PUBLIC
virtual ~ClassLoader();

/**
Expand All @@ -99,12 +105,13 @@ class ClassLoader
template<class Base>
std::vector<std::string> getAvailableClasses()
{
return class_loader::class_loader_private::getAvailableClasses<Base>(this);
return class_loader::impl::getAvailableClasses<Base>(this);
}

/**
* @brief Gets the full-qualified path and name of the library associated with this class loader
*/
CLASS_LOADER_PUBLIC
std::string getLibraryPath() {return library_path_;}

/**
Expand Down Expand Up @@ -182,29 +189,34 @@ class ClassLoader
* @param library_path The path to the library to load
* @return true if library is loaded within this ClassLoader object's scope, otherwise false
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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.
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
int unloadLibrary();

private:
Expand Down Expand Up @@ -273,13 +285,12 @@ class ClassLoader
loadLibrary();
}

Base * obj =
class_loader::class_loader_private::createInstance<Base>(derived_class_name, this);
Base * obj = class_loader::impl::createInstance<Base>(derived_class_name, this);
assert(obj != nullptr); // Unreachable assertion if createInstance() throws on failure

if (managed) {
boost::recursive_mutex::scoped_lock lock(plugin_ref_count_mutex_);
plugin_ref_count_ = plugin_ref_count_ + 1;
++plugin_ref_count_;
}

return obj;
Expand All @@ -288,13 +299,15 @@ class ClassLoader
/**
* @brief Getter for if an unmanaged (i.e. unsafe) instance has been created flag
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
int unloadLibraryInternal(bool lock_plugin_ref_count);

private:
Expand Down
42 changes: 28 additions & 14 deletions include/class_loader/class_loader_core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@

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

/**
* @note This header file is the internal implementation of the plugin system which is exposed via the ClassLoader class
Expand All @@ -55,20 +56,21 @@ namespace class_loader

class ClassLoader; // Forward declaration

namespace class_loader_private
namespace impl
{

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

// Debug
CLASS_LOADER_PUBLIC
void printDebugInfoToScreen();

// Global storage
Expand All @@ -77,44 +79,51 @@ void printDebugInfoToScreen();
* @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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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.
*/
CLASS_LOADER_PUBLIC
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.
*/
CLASS_LOADER_PUBLIC
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.
*/
CLASS_LOADER_PUBLIC
FactoryMap & getFactoryMapForBaseClass(const std::string & typeid_base_class_name);

/**
Expand All @@ -131,19 +140,23 @@ FactoryMap & getFactoryMapForBaseClass()
* @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
*/
CLASS_LOADER_PUBLIC
boost::recursive_mutex & getLoadedLibraryVectorMutex();
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
void hasANonPurePluginLibraryBeenOpened(bool hasIt);

// Plugin Functions
Expand All @@ -162,7 +175,7 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
// opens a library. Normally it will happen within the scope of loadLibrary(),
// but that may not be guaranteed.
CONSOLE_BRIDGE_logDebug(
"class_loader.class_loader_private: "
"class_loader.impl: "
"Registering plugin factory for class = %s, ClassLoader* = %p and library name %s.",
class_name.c_str(), getCurrentlyActiveClassLoader(),
getCurrentlyLoadingLibraryName().c_str());
Expand All @@ -188,8 +201,8 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
}

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

Expand All @@ -212,7 +225,7 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
getPluginBaseToFactoryMapMapMutex().unlock();

CONSOLE_BRIDGE_logDebug(
"class_loader.class_loader_private: "
"class_loader.impl: "
"Registration of %s complete (Metaobject Address = %p)",
class_name.c_str(), reinterpret_cast<void *>(new_factory));
}
Expand All @@ -231,12 +244,10 @@ Base * createInstance(const std::string & derived_class_name, ClassLoader * load
getPluginBaseToFactoryMapMapMutex().lock();
FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
if (factoryMap.find(derived_class_name) != factoryMap.end()) {
factory = dynamic_cast<class_loader_private::AbstractMetaObject<Base> *>(
factoryMap[derived_class_name]);
factory = dynamic_cast<impl::AbstractMetaObject<Base> *>(factoryMap[derived_class_name]);
} else {
CONSOLE_BRIDGE_logError(
"class_loader.class_loader_private: No metaobject exists for class type %s.",
derived_class_name.c_str());
"class_loader.impl: No metaobject exists for class type %s.", derived_class_name.c_str());
}
getPluginBaseToFactoryMapMapMutex().unlock();

Expand Down Expand Up @@ -266,8 +277,7 @@ Base * createInstance(const std::string & derived_class_name, ClassLoader * load
}

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

return obj;
Expand Down Expand Up @@ -307,6 +317,7 @@ std::vector<std::string> getAvailableClasses(ClassLoader * 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
*/
CLASS_LOADER_PUBLIC
std::vector<std::string> getAllLibrariesUsedByClassLoader(const ClassLoader * loader);

/**
Expand All @@ -315,31 +326,34 @@ std::vector<std::string> getAllLibrariesUsedByClassLoader(const ClassLoader * lo
* @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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
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
*/
CLASS_LOADER_PUBLIC
void unloadLibrary(const std::string & library_path, ClassLoader * loader);


} // namespace class_loader_private
} // namespace impl
} // namespace class_loader

#endif // CLASS_LOADER__CLASS_LOADER_CORE_HPP_
15 changes: 10 additions & 5 deletions include/class_loader/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class ClassLoaderException : public std::runtime_error
{
public:
explicit inline ClassLoaderException(const std::string & error_desc)
: std::runtime_error(error_desc) {}
: std::runtime_error(error_desc)
{}
};

/**
Expand All @@ -57,7 +58,8 @@ class LibraryLoadException : public ClassLoaderException
{
public:
explicit inline LibraryLoadException(const std::string & error_desc)
: ClassLoaderException(error_desc) {}
: ClassLoaderException(error_desc)
{}
};

/**
Expand All @@ -68,7 +70,8 @@ class LibraryUnloadException : public ClassLoaderException
{
public:
explicit inline LibraryUnloadException(const std::string & error_desc)
: ClassLoaderException(error_desc) {}
: ClassLoaderException(error_desc)
{}
};

/**
Expand All @@ -79,7 +82,8 @@ class CreateClassException : public ClassLoaderException
{
public:
explicit inline CreateClassException(const std::string & error_desc)
: ClassLoaderException(error_desc) {}
: ClassLoaderException(error_desc)
{}
};

/**
Expand All @@ -90,7 +94,8 @@ class NoClassLoaderExistsException : public ClassLoaderException
{
public:
explicit inline NoClassLoaderExistsException(const std::string & error_desc)
: ClassLoaderException(error_desc) {}
: ClassLoaderException(error_desc)
{}
};

} // namespace class_loader
Expand Down
Loading

0 comments on commit 0a778b9

Please sign in to comment.