Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always build multi component libs and deprecate ORO_CREATE_COMPONENT_TYPE #308

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 56 additions & 100 deletions rtt/Component.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,54 +53,7 @@
#include "rtt-fwd.hpp"
#include "rtt-config.h"

namespace RTT
{
/**
* This signature defines how a component can be instantiated.
*/
typedef TaskContext* (*ComponentLoaderSignature)(std::string instance_name);
typedef std::map<std::string,ComponentLoaderSignature> FactoryMap;

/**
* A global variable storing all component factories added with
* \a ORO_LIST_COMPONENT_TYPE.
* This factory needs to be present in both static and dynamic library
* deployments.
*/
class ComponentFactories
{
/**
* When static linking is used, the Component library loaders can be
* found in this map.
*/
RTT_HIDE static FactoryMap* Factories;
public:
RTT_HIDE static FactoryMap& Instance() {
if ( Factories == 0)
Factories = new FactoryMap();
return *Factories;
}
};

/**
* A helper class storing a single component factory
* in case of static library deployments.
*/
template<class C>
class ComponentFactoryLoader
{
public:
ComponentFactoryLoader(std::string type_name)
{
ComponentFactories::Instance()[type_name] = &ComponentFactoryLoader<C>::createComponent;
}

static TaskContext* createComponent(std::string instance_name)
{
return new C(instance_name);
}
};
}
#include <rtt/deployment/ComponentLoader.hpp>

// Helper macros.
#define ORO_CONCAT_LINE2(x,y) x##y
Expand All @@ -110,6 +63,35 @@ namespace RTT
#define ORO_LIST_COMPONENT_TYPE_str(s) ORO_LIST_COMPONENT_TYPE__str(s)
#define ORO_LIST_COMPONENT_TYPE__str(s) #s

#if defined(__GNUC__)
#define ORO_PP_PRAGMA(x) _Pragma(#x)
#define ORO_PP_WARNING(text) ORO_PP_PRAGMA(GCC warning text)
#define ORO_PP_ERROR(text) ORO_PP_PRAGMA(GCC error text)
#else
#define ORO_PP_WARNING(text)
#define ORO_PP_ERROR(text)
#endif

/**
* Use this macro to register multiple components in a shared library (plug-in).
* For each component, add this line in the .cpp file. Use this macro in combination with
* ORO_CREATE_COMPONENT_LIBRARY.
*
* The advantage of this approach is that one library can create different component
* \a types and that you may link multiple component libraries with each other.
*
* This macro can be used for both shared and static libraries. In case of a shared library,
* the component factory will be registered to the shared library's local FactoryMap. In case
* of a static library, the component factory will be registered in the static library's global
* FactoryMap. In both cases, the DeploymentComponent can access these factories and
* create the registered component types.
*
* @param CLASS_NAME the class name of the component you are adding to the library.
*/

#define ORO_LIST_COMPONENT_TYPE(CLASS_NAME) namespace { namespace ORO_CONCAT_LINE(LOADER_) { RTT::ComponentFactoryLoader<CLASS_NAME> m_cloader(ORO_LIST_COMPONENT_TYPE_str(CLASS_NAME)); } }


// ORO_CREATE_COMPONENT and ORO_CREATE_COMPONENT_LIBRARY are only used in shared libraries.
#if defined(OCL_DLL_EXPORT) || defined (RTT_COMPONENT)

Expand All @@ -121,36 +103,6 @@ namespace RTT
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
#endif

/**
* Use this macro to register a single component in a shared library (plug-in).
* You can only use this macro \b once in a .cpp file for the whole shared library \b and
* you may \b not link with another component library when using this macro. Use
* ORO_CREATE_COMPONENT_LIBRARY if you are in that situation.
*
* It adds a function 'createComponent', which will return a new instance of
* the library's component type and a function 'getComponentType', which returns
* the type (namespace::class) name of the component.
*
* The advantage of this approach is that the user does not need to know the
* class name of the component, he just needs to locate the shared library itself.
* The disadvantage is that only one component \a type per shared library can be created.
*
* @param CNAME the class name of the component you are adding to the library.
*/
#define ORO_CREATE_COMPONENT(CNAME) \
extern "C" { \
RTT_EXPORT RTT::TaskContext* createComponent(std::string instance_name); \
RTT::TaskContext* createComponent(std::string instance_name) \
{ \
return new CNAME(instance_name); \
} \
RTT_EXPORT std::string getComponentType(); \
std::string getComponentType() \
{ \
return ORO_LIST_COMPONENT_TYPE_str(CNAME); \
} \
} /* extern "C" */

/**
* Use this macro to create a component library which contains all components listed with
* ORO_LIST_COMPONENT_TYPE.
Expand Down Expand Up @@ -179,45 +131,49 @@ extern "C" { \
RTT_EXPORT RTT::FactoryMap* getComponentFactoryMap() { return &RTT::ComponentFactories::Instance(); } \
} /* extern "C" */


/**
* Use this macro to register a single component in a shared library (plug-in).
* You can only use this macro \b once in a .cpp file for the whole shared library \b and
* you may \b not link with another component library when using this macro. Use
* ORO_CREATE_COMPONENT_LIBRARY if you are in that situation.
*
* It adds a function 'createComponent', which will return a new instance of
* the library's component type and a function 'getComponentType', which returns
* the type (namespace::class) name of the component.
*
* The advantage of this approach is that the user does not need to know the
* class name of the component, he just needs to locate the shared library itself.
* The disadvantage is that only one component \a type per shared library can be created.
*
* @param CNAME the class name of the component you are adding to the library.
*/
#define ORO_CREATE_COMPONENT(CNAME) \
ORO_LIST_COMPONENT_TYPE(CNAME) \
ORO_CREATE_COMPONENT_LIBRARY()

#else

#if !defined(OCL_STATIC) && !defined(RTT_STATIC) && !defined(RTT_DLL_EXPORT)
#warning "You're compiling with static library settings. The resulting component library \
#error "You're compiling with static library settings. The resulting component library \
will not be loadable at runtime with the deployer.\
Compile with -DRTT_COMPONENT to enable dynamic loadable components, \
or use -DRTT_STATIC to suppress this warning."
#endif

// Static OCL library:
// Identical to ORO_LIST_COMPONENT_TYPE:
#define ORO_CREATE_COMPONENT(CLASS_NAME) namespace { namespace ORO_CONCAT_LINE(LOADER_) { RTT::ComponentFactoryLoader<CLASS_NAME> m_cloader(ORO_LIST_COMPONENT_TYPE_str(CLASS_NAME)); } }
#define ORO_CREATE_COMPONENT(CLASS_NAME) ORO_LIST_COMPONENT_TYPE(CLASS_NAME)
#define ORO_CREATE_COMPONENT_LIBRARY() __attribute__((weak)) RTT::FactoryMap* RTT::ComponentFactories::Factories = 0;

#endif

/**
* Use this macro to register multiple components in a shared library (plug-in).
* For each component, add this line in the .cpp file. Use this macro in combination with
* ORO_CREATE_COMPONENT_LIBRARY.
*
* The advantage of this approach is that one library can create different component
* \a types and that you may link multiple component libraries with each other.
*
* This macro can be used for both shared and static libraries. In case of a shared library,
* the component factory will be registered to the shared library's local FactoryMap. In case
* of a static library, the component factory will be registered in the static library's global
* FactoryMap. In both cases, the DeploymentComponent can access these factories and
* create the registered component types.
*
* @param CLASS_NAME the class name of the component you are adding to the library.
*/

#define ORO_LIST_COMPONENT_TYPE(CLASS_NAME) namespace { namespace ORO_CONCAT_LINE(LOADER_) { RTT::ComponentFactoryLoader<CLASS_NAME> m_cloader(ORO_LIST_COMPONENT_TYPE_str(CLASS_NAME)); } }

/**
* Backwards compatibility macro which is now replaced by ORO_CREATE_COMPONENT_LIBRARY( )
*/
#define ORO_CREATE_COMPONENT_TYPE( ) ORO_CREATE_COMPONENT_LIBRARY( )
#define ORO_CREATE_COMPONENT_TYPE( ) \
ORO_PP_WARNING("ORO_CREATE_COMPONENT_TYPE() is deprecated. Please use ORO_CREATE_COMPONENT_LIBRARY() instead.") \
ORO_CREATE_COMPONENT_LIBRARY( )

#endif

Expand Down
19 changes: 15 additions & 4 deletions rtt/deployment/ComponentLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ bool ComponentLoader::loadInProcess(string file, string libname, bool log_error)
return false;
}

handle = dlopen ( p.string().c_str(), RTLD_NOW);
handle = dlopen ( p.string().c_str(), RTLD_NOW | RTLD_GLOBAL );

if (!handle) {
if ( log_error ) {
Expand All @@ -652,8 +652,15 @@ bool ComponentLoader::loadInProcess(string file, string libname, bool log_error)
if ((error = dlerror()) == NULL) {
// symbol found, register factories...
fmap = (*getfactory)();
ComponentFactories::Instance().insert( fmap->begin(), fmap->end() );
log(Info) << "Loaded multi component library '"<< file <<"'"<<endlog();
for( FactoryMap::const_iterator it = fmap->begin(); it != fmap->end(); ++it ) {
if ( ComponentFactories::Instance().count(it->first) == 1 ) {
log(Warning) << "Component type name " << it->first << " already used: overriding." << endlog();
}
ComponentFactories::Instance().insert(*it);
log(Info) << "Loaded component type '" << it->first << "'" << endlog();
loading_lib.components_type.push_back( it->first );
}
log(Info) << "Loaded component library '"<< file <<"'"<<endlog();
getcomponenttypes = (vector<string>(*)(void))(dlsym(handle, "getComponentTypeNames"));
if ((error = dlerror()) == NULL) {
log(Debug) << "Components:";
Expand All @@ -666,7 +673,9 @@ bool ComponentLoader::loadInProcess(string file, string libname, bool log_error)
success = true;
}

// Lookup createComponent (single component case):
if (success) return true;

// Lookup deprecated createComponent (single component case):
dlerror(); /* Clear any existing error */

RTT::TaskContext* (*factory)(std::string) = 0;
Expand All @@ -680,6 +689,8 @@ bool ComponentLoader::loadInProcess(string file, string libname, bool log_error)
error = dlerror();
if (error) gettype_error = error;
if ( factory && tname ) {
log(Warning) << "Component library '" << libname << "' is a deprecated single component library. "
"Consider to rebuild it with the latest version of RTT or it might break in the future." << endlog();
std::string cname = (*tname)();
if ( ComponentFactories::Instance().count(cname) == 1 ) {
log(Warning) << "Component type name "<<cname<<" already used: overriding."<<endlog();
Expand Down
49 changes: 48 additions & 1 deletion rtt/deployment/ComponentLoader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,56 @@
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <rtt/Component.hpp>
#include <rtt/TaskContext.hpp>

namespace RTT {

/**
* This signature defines how a component can be instantiated.
*/
typedef TaskContext* (*ComponentLoaderSignature)(std::string instance_name);
typedef std::map<std::string,ComponentLoaderSignature> FactoryMap;

/**
* A global variable storing all component factories added with
* \a ORO_LIST_COMPONENT_TYPE.
* This factory needs to be present in both static and dynamic library
* deployments.
*/
class ComponentFactories
{
/**
* When static linking is used, the Component library loaders can be
* found in this map.
*/
RTT_HIDE static FactoryMap* Factories;
public:
RTT_HIDE static FactoryMap& Instance() {
if ( Factories == 0)
Factories = new FactoryMap();
return *Factories;
}
};

/**
* A helper class storing a single component factory
* in case of static library deployments.
*/
template<class C>
class ComponentFactoryLoader
{
public:
ComponentFactoryLoader(std::string type_name)
{
ComponentFactories::Instance()[type_name] = &ComponentFactoryLoader<C>::createComponent;
}

static TaskContext* createComponent(std::string instance_name)
{
return new C(instance_name);
}
};

/**
* Locates Component libraries found on the filesystem and keeps track of loaded Components.
* In case a no components of a component library are running, the library can be unloaded.
Expand Down