From ef9bde309ce3191004d89432c170f0ead7f3630c Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Wed, 20 Jun 2018 11:59:58 +0200 Subject: [PATCH 01/13] Remove obsolete __REFLEX__ references --- GaudiKernel/GaudiKernel/AlgTool.h | 2 -- GaudiKernel/GaudiKernel/Algorithm.h | 3 +-- GaudiKernel/GaudiKernel/Auditor.h | 2 -- GaudiKernel/GaudiKernel/Converter.h | 2 -- GaudiKernel/GaudiKernel/Service.h | 3 +-- GaudiPluginService/Gaudi/Details/PluginServiceDetails.h | 2 -- GaudiPluginService/Gaudi/PluginService.h | 3 --- GaudiUtils/GaudiUtils/IFileCatalog.h | 2 -- 8 files changed, 2 insertions(+), 17 deletions(-) diff --git a/GaudiKernel/GaudiKernel/AlgTool.h b/GaudiKernel/GaudiKernel/AlgTool.h index 4d2b01d09b..6147c64059 100644 --- a/GaudiKernel/GaudiKernel/AlgTool.h +++ b/GaudiKernel/GaudiKernel/AlgTool.h @@ -49,9 +49,7 @@ class GAUDI_API AlgTool PropertyHolder>>> { public: -#ifndef __REFLEX__ typedef Gaudi::PluginService::Factory Factory; -#endif /// Query for a given interface StatusCode queryInterface( const InterfaceID& riid, void** ppvUnknown ) override; diff --git a/GaudiKernel/GaudiKernel/Algorithm.h b/GaudiKernel/GaudiKernel/Algorithm.h index 31c15f16ae..3889e5ede0 100644 --- a/GaudiKernel/GaudiKernel/Algorithm.h +++ b/GaudiKernel/GaudiKernel/Algorithm.h @@ -80,9 +80,8 @@ class GAUDI_API Algorithm PropertyHolder>>> { public: -#ifndef __REFLEX__ typedef Gaudi::PluginService::Factory Factory; -#endif + friend AlgorithmManager; /** Constructor diff --git a/GaudiKernel/GaudiKernel/Auditor.h b/GaudiKernel/GaudiKernel/Auditor.h index 0d692500b4..cad3301e37 100644 --- a/GaudiKernel/GaudiKernel/Auditor.h +++ b/GaudiKernel/GaudiKernel/Auditor.h @@ -35,9 +35,7 @@ class Algorithm; class GAUDI_API Auditor : public PropertyHolder>> { public: -#ifndef __REFLEX__ typedef Gaudi::PluginService::Factory Factory; -#endif /** Constructor @param name The algorithm object's name diff --git a/GaudiKernel/GaudiKernel/Converter.h b/GaudiKernel/GaudiKernel/Converter.h index 7d0da10466..6f2aa8478d 100644 --- a/GaudiKernel/GaudiKernel/Converter.h +++ b/GaudiKernel/GaudiKernel/Converter.h @@ -24,9 +24,7 @@ class IRegistry; class GAUDI_API Converter : public implements { public: -#ifndef __REFLEX__ typedef Gaudi::PluginService::Factory Factory; -#endif /// Initialize the converter StatusCode initialize() override; diff --git a/GaudiKernel/GaudiKernel/Service.h b/GaudiKernel/GaudiKernel/Service.h index e822a98462..b0611afc01 100644 --- a/GaudiKernel/GaudiKernel/Service.h +++ b/GaudiKernel/GaudiKernel/Service.h @@ -36,9 +36,8 @@ class ServiceManager; class GAUDI_API Service : public PropertyHolder>> { public: -#ifndef __REFLEX__ typedef Gaudi::PluginService::Factory Factory; -#endif + friend class ServiceManager; /** Retrieve name of the service */ diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h b/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h index b95fe7ed6d..80b0163fa0 100644 --- a/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h +++ b/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h @@ -57,13 +57,11 @@ namespace Gaudi class Factory { public: -#if !defined( __REFLEX__ ) || defined( ATLAS ) template static typename S::ReturnType create( Args&&... args ) { return new T( std::forward( args )... ); } -#endif }; /// Function used to load a specific factory function. diff --git a/GaudiPluginService/Gaudi/PluginService.h b/GaudiPluginService/Gaudi/PluginService.h index c4d42c3006..ee6fceed9d 100644 --- a/GaudiPluginService/Gaudi/PluginService.h +++ b/GaudiPluginService/Gaudi/PluginService.h @@ -38,8 +38,6 @@ namespace Gaudi { namespace PluginService { - -#if !defined( __REFLEX__ ) || defined( ATLAS ) /// Class wrapping the signature for a factory with any number of arguments. template class Factory @@ -62,7 +60,6 @@ namespace Gaudi return create( o.str(), std::forward( args )... ); } }; -#endif class GAUDIPS_EXPORT Exception : public std::exception { diff --git a/GaudiUtils/GaudiUtils/IFileCatalog.h b/GaudiUtils/GaudiUtils/IFileCatalog.h index e39daa7be9..9d9309d402 100644 --- a/GaudiUtils/GaudiUtils/IFileCatalog.h +++ b/GaudiUtils/GaudiUtils/IFileCatalog.h @@ -31,9 +31,7 @@ namespace Gaudi /// InterfaceID DeclareInterfaceID( IFileCatalog, 2, 0 ); -#ifndef __REFLEX__ typedef Gaudi::PluginService::Factory Factory; -#endif /// Public type definitions typedef std::pair NamedItem; -- GitLab From 6e4c5a466674db790720688d76926f39361266fb Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Tue, 26 Jun 2018 09:28:00 +0200 Subject: [PATCH 02/13] Move PluginService in the inline namespace "v1" --- .../Gaudi/Details/PluginServiceDetails.h | 349 +++++++-------- GaudiPluginService/Gaudi/PluginService.h | 64 +-- GaudiPluginService/src/PluginService.cpp | 415 +++++++++--------- GaudiPluginService/src/capi_pluginservice.cpp | 2 +- GaudiPluginService/src/listcomponents.cpp | 5 +- GaudiPluginService/tests/src/UseCases.cpp | 36 ++ 6 files changed, 459 insertions(+), 412 deletions(-) diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h b/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h index 80b0163fa0..c37c4a5705 100644 --- a/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h +++ b/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h @@ -46,204 +46,206 @@ namespace Gaudi { namespace PluginService { - - namespace Details + inline namespace v1 { - /// Class providing default factory functions. - /// - /// The template argument T is the class to be created, while the methods - /// template argument S is the specific factory signature. - template - class Factory + namespace Details { - public: - template - static typename S::ReturnType create( Args&&... args ) + /// Class providing default factory functions. + /// + /// The template argument T is the class to be created, while the methods + /// template argument S is the specific factory signature. + template + class Factory + { + public: + template + static typename S::ReturnType create( Args&&... args ) + { + return new T( std::forward( args )... ); + } + }; + + /// Function used to load a specific factory function. + /// @return the pointer to the factory function. + GAUDIPS_API + void* getCreator( const std::string& id, const std::string& type ); + + /// Convoluted implementation of getCreator with an embedded + /// reinterpret_cast, used to avoid the warning + ///
+        /// warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object
+        /// 
+ /// It is an ugly trick but works.
+ /// See: + ///
    + ///
  • http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
  • + ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#573
  • + ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
  • + ///
+ template + inline F getCreator( const std::string& id ) { - return new T( std::forward( args )... ); + union { + void* src; + F dst; + } p2p; + p2p.src = getCreator( id, typeid( F ).name() ); + return p2p.dst; } - }; - - /// Function used to load a specific factory function. - /// @return the pointer to the factory function. - GAUDIPS_API - void* getCreator( const std::string& id, const std::string& type ); - - /// Convoluted implementation of getCreator with an embedded - /// reinterpret_cast, used to avoid the warning - ///
-      /// warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object
-      /// 
- /// It is an ugly trick but works.
- /// See: - ///
    - ///
  • http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
  • - ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#573
  • - ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
  • - ///
- template - inline F getCreator( const std::string& id ) - { - union { - void* src; - F dst; - } p2p; - p2p.src = getCreator( id, typeid( F ).name() ); - return p2p.dst; - } - /// Return a canonical name for type_info object (implementation borrowed - /// from GaudiKernel/System). - GAUDIPS_API - std::string demangle( const std::type_info& id ); + /// Return a canonical name for type_info object (implementation borrowed + /// from GaudiKernel/System). + GAUDIPS_API + std::string demangle( const std::type_info& id ); - /// Return a canonical name for the template argument. - template - inline std::string demangle() - { - return demangle( typeid( T ) ); - } + /// Return a canonical name for the template argument. + template + inline std::string demangle() + { + return demangle( typeid( T ) ); + } - /// In-memory database of the loaded factories. - class GAUDIPS_API Registry - { - public: - typedef std::string KeyType; - - /// Type used for the properties implementation. - typedef std::map Properties; - - struct FactoryInfo { - FactoryInfo( std::string lib, void* p = nullptr, std::string t = "", std::string rt = "", std::string cn = "", - Properties props = Properties() ) - : library( std::move( lib ) ) - , ptr( p ) - , type( std::move( t ) ) - , rtype( std::move( rt ) ) - , className( std::move( cn ) ) - , properties( std::move( props ) ) + /// In-memory database of the loaded factories. + class GAUDIPS_API Registry + { + public: + typedef std::string KeyType; + + /// Type used for the properties implementation. + typedef std::map Properties; + + struct FactoryInfo { + FactoryInfo( std::string lib, void* p = nullptr, std::string t = "", std::string rt = "", + std::string cn = "", Properties props = Properties() ) + : library( std::move( lib ) ) + , ptr( p ) + , type( std::move( t ) ) + , rtype( std::move( rt ) ) + , className( std::move( cn ) ) + , properties( std::move( props ) ) + { + } + + std::string library; + void* ptr; + std::string type; + std::string rtype; + std::string className; + Properties properties; + + FactoryInfo& addProperty( const KeyType& k, std::string v ) + { + properties[k] = std::move( v ); + return *this; + } + }; + + /// Type used for the database implementation. + typedef std::map FactoryMap; + + /// Retrieve the singleton instance of Registry. + static Registry& instance(); + + /// Add a factory to the database. + template + inline FactoryInfo& add( const I& id, typename F::FuncType ptr ) { + union { + typename F::FuncType src; + void* dst; + } p2p; + p2p.src = ptr; + std::ostringstream o; + o << id; + return add( o.str(), p2p.dst, typeid( typename F::FuncType ).name(), + typeid( typename F::ReturnType ).name(), demangle() ); } - std::string library; - void* ptr; - std::string type; - std::string rtype; - std::string className; - Properties properties; + /// Retrieve the factory for the given id. + void* get( const std::string& id, const std::string& type ) const; - FactoryInfo& addProperty( const KeyType& k, std::string v ) - { - properties[k] = std::move( v ); - return *this; - } - }; + /// Retrieve the FactoryInfo object for an id. + const FactoryInfo& getInfo( const std::string& id ) const; - /// Type used for the database implementation. - typedef std::map FactoryMap; + /// Add a property to an already existing FactoryInfo object (via its id.) + Registry& addProperty( const std::string& id, const std::string& k, const std::string& v ); - /// Retrieve the singleton instance of Registry. - static Registry& instance(); + /// Return a list of all the known and loaded factories + std::set loadedFactoryNames() const; - /// Add a factory to the database. - template - inline FactoryInfo& add( const I& id, typename F::FuncType ptr ) - { - union { - typename F::FuncType src; - void* dst; - } p2p; - p2p.src = ptr; - std::ostringstream o; - o << id; - return add( o.str(), p2p.dst, typeid( typename F::FuncType ).name(), typeid( typename F::ReturnType ).name(), - demangle() ); - } + /// Return the known factories (loading the list if not yet done). + inline const FactoryMap& factories() const + { + if ( !m_initialized ) const_cast( this )->initialize(); + return m_factories; + } - /// Retrieve the factory for the given id. - void* get( const std::string& id, const std::string& type ) const; + private: + /// Private constructor for the singleton pattern. + /// At construction time, the internal database of known factories is + /// filled with the name of the libraries containing them, using the + /// ".components" files in the LD_LIBRARY_PATH. + Registry(); - /// Retrieve the FactoryInfo object for an id. - const FactoryInfo& getInfo( const std::string& id ) const; + /// Private copy constructor for the singleton pattern. + Registry( const Registry& ) : m_initialized( false ) {} - /// Add a property to an already existing FactoryInfo object (via its id.) - Registry& addProperty( const std::string& id, const std::string& k, const std::string& v ); + /// Add a factory to the database. + FactoryInfo& add( const std::string& id, void* factory, const std::string& type, const std::string& rtype, + const std::string& className, const Properties& props = Properties() ); - /// Return a list of all the known and loaded factories - std::set loadedFactoryNames() const; + /// Return the known factories (loading the list if not yet done). + inline FactoryMap& factories() + { + if ( !m_initialized ) initialize(); + return m_factories; + } - /// Return the known factories (loading the list if not yet done). - inline const FactoryMap& factories() const - { - if ( !m_initialized ) const_cast( this )->initialize(); - return m_factories; - } + /// Initialize the registry loading the list of factories from the + /// .component files in the library search path. + void initialize(); - private: - /// Private constructor for the singleton pattern. - /// At construction time, the internal database of known factories is - /// filled with the name of the libraries containing them, using the - /// ".components" files in the LD_LIBRARY_PATH. - Registry(); + /// Flag recording if the registry has been initialized or not. + bool m_initialized; - /// Private copy constructor for the singleton pattern. - Registry( const Registry& ) : m_initialized( false ) {} + /// Internal storage for factories. + FactoryMap m_factories; - /// Add a factory to the database. - FactoryInfo& add( const std::string& id, void* factory, const std::string& type, const std::string& rtype, - const std::string& className, const Properties& props = Properties() ); + /// Mutex used to control concurrent access to the internal data. + mutable std::recursive_mutex m_mutex; + }; - /// Return the known factories (loading the list if not yet done). - inline FactoryMap& factories() + /// Simple logging class, just to provide a default implementation. + class GAUDIPS_API Logger { - if ( !m_initialized ) initialize(); - return m_factories; - } - - /// Initialize the registry loading the list of factories from the - /// .component files in the library search path. - void initialize(); - - /// Flag recording if the registry has been initialized or not. - bool m_initialized; - - /// Internal storage for factories. - FactoryMap m_factories; + public: + enum Level { Debug = 0, Info = 1, Warning = 2, Error = 3 }; + Logger( Level level = Warning ) : m_level( level ) {} + virtual ~Logger() {} + inline Level level() const { return m_level; } + inline void setLevel( Level level ) { m_level = level; } + inline void info( const std::string& msg ) { report( Info, msg ); } + inline void debug( const std::string& msg ) { report( Debug, msg ); } + inline void warning( const std::string& msg ) { report( Warning, msg ); } + inline void error( const std::string& msg ) { report( Error, msg ); } + + private: + virtual void report( Level lvl, const std::string& msg ); + Level m_level; + }; - /// Mutex used to control concurrent access to the internal data. - mutable std::recursive_mutex m_mutex; - }; + /// Return the current logger instance. + GAUDIPS_API Logger& logger(); + /// Set the logger instance to use. + /// It must be a new instance and the ownership is passed to the function. + GAUDIPS_API void setLogger( Logger* logger ); + } - /// Simple logging class, just to provide a default implementation. - class GAUDIPS_API Logger - { - public: - enum Level { Debug = 0, Info = 1, Warning = 2, Error = 3 }; - Logger( Level level = Warning ) : m_level( level ) {} - virtual ~Logger() {} - inline Level level() const { return m_level; } - inline void setLevel( Level level ) { m_level = level; } - inline void info( const std::string& msg ) { report( Info, msg ); } - inline void debug( const std::string& msg ) { report( Debug, msg ); } - inline void warning( const std::string& msg ) { report( Warning, msg ); } - inline void error( const std::string& msg ) { report( Error, msg ); } - - private: - virtual void report( Level lvl, const std::string& msg ); - Level m_level; - }; - - /// Return the current logger instance. - GAUDIPS_API Logger& logger(); - /// Set the logger instance to use. - /// It must be a new instance and the ownership is passed to the function. - GAUDIPS_API void setLogger( Logger* logger ); + /// Backward compatibility with Reflex. + GAUDIPS_API void SetDebug( int debugLevel ); + /// Backward compatibility with Reflex. + GAUDIPS_API int Debug(); } - - /// Backward compatibility with Reflex. - GAUDIPS_API void SetDebug( int debugLevel ); - /// Backward compatibility with Reflex. - GAUDIPS_API int Debug(); } } @@ -260,13 +262,14 @@ namespace Gaudi static s_t::FuncType creator() { return &f_t::create; } \ _INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ { \ - using ::Gaudi::PluginService::Details::Registry; \ + using ::Gaudi::PluginService::v1::Details::Registry; \ Registry::instance().add( id, creator() ); \ } \ } _INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ } #define _INTERNAL_DECLARE_FACTORY( type, id, factory, serial ) \ - _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, ::Gaudi::PluginService::Details::Factory, id, factory, serial ) + _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, ::Gaudi::PluginService::v1::Details::Factory, id, factory, \ + serial ) #endif //_GAUDI_PLUGIN_SERVICE_DETAILS_H_ diff --git a/GaudiPluginService/Gaudi/PluginService.h b/GaudiPluginService/Gaudi/PluginService.h index ee6fceed9d..7a31125a37 100644 --- a/GaudiPluginService/Gaudi/PluginService.h +++ b/GaudiPluginService/Gaudi/PluginService.h @@ -22,13 +22,14 @@ #define DECLARE_FACTORY_WITH_ID( type, id, factory ) _INTERNAL_DECLARE_FACTORY( type, id, factory, __LINE__ ) #define DECLARE_FACTORY( type, factory ) \ - DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::Details::demangle(), factory ) + DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::v1::Details::demangle(), factory ) #define DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, __LINE__ ) #define DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ - DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, ::Gaudi::PluginService::Details::demangle(), factory ) + DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, ::Gaudi::PluginService::v1::Details::demangle(), \ + factory ) #define DECLARE_COMPONENT( type ) DECLARE_FACTORY( type, type::Factory ) @@ -38,39 +39,42 @@ namespace Gaudi { namespace PluginService { - /// Class wrapping the signature for a factory with any number of arguments. - template - class Factory + inline namespace v1 { - public: - typedef R ReturnType; - typedef R ( *FuncType )( Args&&... ); - - static ReturnType create( const std::string& id, Args... args ) + /// Class wrapping the signature for a factory with any number of arguments. + template + class Factory { - const FuncType c = Details::getCreator( id ); - return c ? ( *c )( std::forward( args )... ) : 0; - } + public: + typedef R ReturnType; + typedef R ( *FuncType )( Args&&... ); - template - static ReturnType create( const T& id, Args... args ) - { - std::ostringstream o; - o << id; - return create( o.str(), std::forward( args )... ); - } - }; + static ReturnType create( const std::string& id, Args... args ) + { + const FuncType c = Details::getCreator( id ); + return c ? ( *c )( std::forward( args )... ) : 0; + } - class GAUDIPS_EXPORT Exception : public std::exception - { - public: - Exception( std::string msg ); - ~Exception() throw() override; - const char* what() const throw() override; + template + static ReturnType create( const T& id, Args... args ) + { + std::ostringstream o; + o << id; + return create( o.str(), std::forward( args )... ); + } + }; + + class GAUDIPS_EXPORT Exception : public std::exception + { + public: + Exception( std::string msg ); + ~Exception() throw() override; + const char* what() const throw() override; - private: - std::string m_msg; - }; + private: + std::string m_msg; + }; + } } } diff --git a/GaudiPluginService/src/PluginService.cpp b/GaudiPluginService/src/PluginService.cpp index a6e266de00..c8fcd21a7d 100644 --- a/GaudiPluginService/src/PluginService.cpp +++ b/GaudiPluginService/src/PluginService.cpp @@ -116,253 +116,256 @@ namespace Gaudi { namespace PluginService { - - Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {} - Exception::~Exception() throw() {} - const char* Exception::what() const throw() { return m_msg.c_str(); } - - namespace Details + namespace v1 { - void* getCreator( const std::string& id, const std::string& type ) - { - return Registry::instance().get( id, type ); - } + Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {} + Exception::~Exception() throw() {} + const char* Exception::what() const throw() { return m_msg.c_str(); } - std::string demangle( const std::string& id ) + namespace Details { - int status; - auto realname = std::unique_ptr( - abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free ); - if ( !realname ) return id; + void* getCreator( const std::string& id, const std::string& type ) + { + return Registry::instance().get( id, type ); + } + + std::string demangle( const std::string& id ) + { + int status; + auto realname = std::unique_ptr( + abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free ); + if ( !realname ) return id; #if _GLIBCXX_USE_CXX11_ABI - return std::regex_replace( - realname.get(), - std::regex{"std::__cxx11::basic_string, std::allocator >( (?=>))?"}, - "std::string" ); + return std::regex_replace( + realname.get(), + std::regex{"std::__cxx11::basic_string, std::allocator >( (?=>))?"}, + "std::string" ); #else - return std::string{realname.get()}; + return std::string{realname.get()}; #endif - } - std::string demangle( const std::type_info& id ) { return demangle( id.name() ); } + } + std::string demangle( const std::type_info& id ) { return demangle( id.name() ); } - Registry& Registry::instance() - { - SINGLETON_LOCK - static Registry r; - return r; - } + Registry& Registry::instance() + { + SINGLETON_LOCK + static Registry r; + return r; + } - Registry::Registry() : m_initialized( false ) {} + Registry::Registry() : m_initialized( false ) {} - void Registry::initialize() - { - REG_SCOPE_LOCK - if ( m_initialized ) return; - m_initialized = true; + void Registry::initialize() + { + REG_SCOPE_LOCK + if ( m_initialized ) return; + m_initialized = true; #if defined( _WIN32 ) - const char* envVar = "PATH"; - const char sep = ';'; + const char* envVar = "PATH"; + const char sep = ';'; #elif defined( __APPLE__ ) - const char* envVar = "DYLD_LIBRARY_PATH"; - const char sep = ':'; + const char* envVar = "DYLD_LIBRARY_PATH"; + const char sep = ':'; #else - const char* envVar = "LD_LIBRARY_PATH"; - const char sep = ':'; + const char* envVar = "LD_LIBRARY_PATH"; + const char sep = ':'; #endif - char* search_path = ::getenv( envVar ); - if ( search_path ) { - logger().debug( std::string( "searching factories in " ) + envVar ); - std::string path( search_path ); - std::string::size_type pos = 0; - std::string::size_type newpos = 0; - while ( pos != std::string::npos ) { - std::string dirName; - // get the next entry in the path - newpos = path.find( sep, pos ); - if ( newpos != std::string::npos ) { - dirName = path.substr( pos, newpos - pos ); - pos = newpos + 1; - } else { - dirName = path.substr( pos ); - pos = newpos; - } - logger().debug( std::string( " looking into " ) + dirName ); - // look for files called "*.components" in the directory - DIR* dir = opendir( dirName.c_str() ); - if ( dir ) { - struct dirent* entry; - while ( ( entry = readdir( dir ) ) ) { - std::string name( entry->d_name ); - // check if the file name ends with ".components" - std::string::size_type extpos = name.find( ".components" ); - if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) { - std::string fullPath = ( dirName + '/' + name ); - { // check if it is a regular file - struct stat buf; - stat( fullPath.c_str(), &buf ); - if ( !S_ISREG( buf.st_mode ) ) continue; - } - // read the file - logger().debug( std::string( " reading " ) + name ); - std::ifstream factories{fullPath}; - std::string line; - int factoriesCount = 0; - int lineCount = 0; - while ( !factories.eof() ) { - ++lineCount; - std::getline( factories, line ); - trim( line ); - // skip empty lines and lines starting with '#' - if ( line.empty() || line[0] == '#' ) continue; - // look for the separator - auto pos = line.find( ':' ); - if ( pos == std::string::npos ) { - logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) ); - continue; + char* search_path = ::getenv( envVar ); + if ( search_path ) { + logger().debug( std::string( "searching factories in " ) + envVar ); + std::string path( search_path ); + std::string::size_type pos = 0; + std::string::size_type newpos = 0; + while ( pos != std::string::npos ) { + std::string dirName; + // get the next entry in the path + newpos = path.find( sep, pos ); + if ( newpos != std::string::npos ) { + dirName = path.substr( pos, newpos - pos ); + pos = newpos + 1; + } else { + dirName = path.substr( pos ); + pos = newpos; + } + logger().debug( std::string( " looking into " ) + dirName ); + // look for files called "*.components" in the directory + DIR* dir = opendir( dirName.c_str() ); + if ( dir ) { + struct dirent* entry; + while ( ( entry = readdir( dir ) ) ) { + std::string name( entry->d_name ); + // check if the file name ends with ".components" + std::string::size_type extpos = name.find( ".components" ); + if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) { + std::string fullPath = ( dirName + '/' + name ); + { // check if it is a regular file + struct stat buf; + stat( fullPath.c_str(), &buf ); + if ( !S_ISREG( buf.st_mode ) ) continue; } - const std::string lib( line, 0, pos ); - const std::string fact( line, pos + 1 ); - m_factories.emplace( fact, FactoryInfo( lib ) ); + // read the file + logger().debug( std::string( " reading " ) + name ); + std::ifstream factories{fullPath}; + std::string line; + int factoriesCount = 0; + int lineCount = 0; + while ( !factories.eof() ) { + ++lineCount; + std::getline( factories, line ); + trim( line ); + // skip empty lines and lines starting with '#' + if ( line.empty() || line[0] == '#' ) continue; + // look for the separator + auto pos = line.find( ':' ); + if ( pos == std::string::npos ) { + logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) ); + continue; + } + const std::string lib( line, 0, pos ); + const std::string fact( line, pos + 1 ); + m_factories.emplace( fact, FactoryInfo( lib ) ); #ifdef GAUDI_REFLEX_COMPONENT_ALIASES - // add an alias for the factory using the Reflex convention - std::string old_name = old_style_name( fact ); - if ( fact != old_name ) { - FactoryInfo old_info( lib ); - old_info.properties["ReflexName"] = "true"; - m_factories.emplace( old_name, old_info ); - } + // add an alias for the factory using the Reflex convention + std::string old_name = old_style_name( fact ); + if ( fact != old_name ) { + FactoryInfo old_info( lib ); + old_info.properties["ReflexName"] = "true"; + m_factories.emplace( old_name, old_info ); + } #endif - ++factoriesCount; - } - if ( logger().level() <= Logger::Debug ) { - logger().debug( " found " + std::to_string( factoriesCount ) + " factories" ); + ++factoriesCount; + } + if ( logger().level() <= Logger::Debug ) { + logger().debug( " found " + std::to_string( factoriesCount ) + " factories" ); + } } } + closedir( dir ); } - closedir( dir ); } } } - } - Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type, - const std::string& rtype, const std::string& className, - const Properties& props ) - { - REG_SCOPE_LOCK - FactoryMap& facts = factories(); - auto entry = facts.find( id ); - if ( entry == facts.end() ) { - // this factory was not known yet - entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first; - } else { - // do not replace an existing factory with a new one - if ( !entry->second.ptr ) entry->second.ptr = factory; - factoryInfoSetHelper( entry->second.type, type, "type", id ); - factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id ); - factoryInfoSetHelper( entry->second.className, className, "class", id ); - } + Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type, + const std::string& rtype, const std::string& className, + const Properties& props ) + { + REG_SCOPE_LOCK + FactoryMap& facts = factories(); + auto entry = facts.find( id ); + if ( entry == facts.end() ) { + // this factory was not known yet + entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first; + } else { + // do not replace an existing factory with a new one + if ( !entry->second.ptr ) entry->second.ptr = factory; + factoryInfoSetHelper( entry->second.type, type, "type", id ); + factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id ); + factoryInfoSetHelper( entry->second.className, className, "class", id ); + } #ifdef GAUDI_REFLEX_COMPONENT_ALIASES - // add an alias for the factory using the Reflex convention - std::string old_name = old_style_name( id ); - if ( id != old_name ) add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true"; + // add an alias for the factory using the Reflex convention + std::string old_name = old_style_name( id ); + if ( id != old_name ) + add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true"; #endif - return entry->second; - } + return entry->second; + } - void* Registry::get( const std::string& id, const std::string& type ) const - { - REG_SCOPE_LOCK - const FactoryMap& facts = factories(); - auto f = facts.find( id ); - if ( f != facts.end() ) { + void* Registry::get( const std::string& id, const std::string& type ) const + { + REG_SCOPE_LOCK + const FactoryMap& facts = factories(); + auto f = facts.find( id ); + if ( f != facts.end() ) { #ifdef GAUDI_REFLEX_COMPONENT_ALIASES - const Properties& props = f->second.properties; - if ( props.find( "ReflexName" ) != props.end() ) - logger().warning( "requesting factory via old name '" + id + "'" - "use '" + - f->second.className + "' instead" ); + const Properties& props = f->second.properties; + if ( props.find( "ReflexName" ) != props.end() ) + logger().warning( "requesting factory via old name '" + id + "'" + "use '" + + f->second.className + "' instead" ); #endif - if ( !f->second.ptr ) { - if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) { - logger().warning( "cannot load " + f->second.library + " for factory " + id ); - char* dlmsg = dlerror(); - if ( dlmsg ) logger().warning( dlmsg ); - return nullptr; + if ( !f->second.ptr ) { + if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) { + logger().warning( "cannot load " + f->second.library + " for factory " + id ); + char* dlmsg = dlerror(); + if ( dlmsg ) logger().warning( dlmsg ); + return nullptr; + } + f = facts.find( id ); // ensure that the iterator is valid } - f = facts.find( id ); // ensure that the iterator is valid + if ( f->second.type == type ) return f->second.ptr; + logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) + + " instead of " + demangle( type ) ); } - if ( f->second.type == type ) return f->second.ptr; - logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) + - " instead of " + demangle( type ) ); + return nullptr; // factory not found } - return nullptr; // factory not found - } - const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const - { - REG_SCOPE_LOCK - static const FactoryInfo unknown( "unknown" ); - const FactoryMap& facts = factories(); - auto f = facts.find( id ); - return ( f != facts.end() ) ? f->second : unknown; - } + const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const + { + REG_SCOPE_LOCK + static const FactoryInfo unknown( "unknown" ); + const FactoryMap& facts = factories(); + auto f = facts.find( id ); + return ( f != facts.end() ) ? f->second : unknown; + } - Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) - { - REG_SCOPE_LOCK - FactoryMap& facts = factories(); - auto f = facts.find( id ); - if ( f != facts.end() ) f->second.properties[k] = v; - return *this; - } + Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) + { + REG_SCOPE_LOCK + FactoryMap& facts = factories(); + auto f = facts.find( id ); + if ( f != facts.end() ) f->second.properties[k] = v; + return *this; + } - std::set Registry::loadedFactoryNames() const - { - REG_SCOPE_LOCK - std::set l; - for ( const auto& f : factories() ) { - if ( f.second.ptr ) l.insert( f.first ); + std::set Registry::loadedFactoryNames() const + { + REG_SCOPE_LOCK + std::set l; + for ( const auto& f : factories() ) { + if ( f.second.ptr ) l.insert( f.first ); + } + return l; } - return l; - } - void Logger::report( Level lvl, const std::string& msg ) - { - static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "}; - if ( lvl >= level() ) { - std::cerr << levels[lvl] << msg << std::endl; + void Logger::report( Level lvl, const std::string& msg ) + { + static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "}; + if ( lvl >= level() ) { + std::cerr << levels[lvl] << msg << std::endl; + } } - } - static auto s_logger = std::make_unique(); - Logger& logger() { return *s_logger; } - void setLogger( Logger* logger ) { s_logger.reset( logger ); } + static auto s_logger = std::make_unique(); + Logger& logger() { return *s_logger; } + void setLogger( Logger* logger ) { s_logger.reset( logger ); } - } // namespace Details + } // namespace Details - void SetDebug( int debugLevel ) - { - using namespace Details; - Logger& l = logger(); - if ( debugLevel > 1 ) - l.setLevel( Logger::Debug ); - else if ( debugLevel > 0 ) - l.setLevel( Logger::Info ); - else - l.setLevel( Logger::Warning ); - } + void SetDebug( int debugLevel ) + { + using namespace Details; + Logger& l = logger(); + if ( debugLevel > 1 ) + l.setLevel( Logger::Debug ); + else if ( debugLevel > 0 ) + l.setLevel( Logger::Info ); + else + l.setLevel( Logger::Warning ); + } - int Debug() - { - using namespace Details; - switch ( logger().level() ) { - case Logger::Debug: - return 2; - case Logger::Info: - return 1; - default: - return 0; + int Debug() + { + using namespace Details; + switch ( logger().level() ) { + case Logger::Debug: + return 2; + case Logger::Info: + return 1; + default: + return 0; + } } } } diff --git a/GaudiPluginService/src/capi_pluginservice.cpp b/GaudiPluginService/src/capi_pluginservice.cpp index e35c0d09c9..80701d983d 100644 --- a/GaudiPluginService/src/capi_pluginservice.cpp +++ b/GaudiPluginService/src/capi_pluginservice.cpp @@ -4,7 +4,7 @@ #include #include -using namespace Gaudi::PluginService::Details; +using namespace Gaudi::PluginService::v1::Details; cgaudi_pluginsvc_t cgaudi_pluginsvc_instance() { diff --git a/GaudiPluginService/src/listcomponents.cpp b/GaudiPluginService/src/listcomponents.cpp index e4f049e485..bd67373c8a 100644 --- a/GaudiPluginService/src/listcomponents.cpp +++ b/GaudiPluginService/src/listcomponents.cpp @@ -46,8 +46,9 @@ void usage( std::string argv0 ) int main( int argc, char* argv[] ) { - Gaudi::PluginService::Details::Registry& reg = Gaudi::PluginService::Details::Registry::instance(); - typedef Gaudi::PluginService::Details::Registry::KeyType key_type; + Gaudi::PluginService::Details::Registry& reg = Gaudi::PluginService::v1::Details::Registry::instance(); + + typedef Gaudi::PluginService::v1::Details::Registry::KeyType key_type; // cache to keep track of the loaded factories std::map loaded; diff --git a/GaudiPluginService/tests/src/UseCases.cpp b/GaudiPluginService/tests/src/UseCases.cpp index cdfa5154e1..3c16e8ed5c 100644 --- a/GaudiPluginService/tests/src/UseCases.cpp +++ b/GaudiPluginService/tests/src/UseCases.cpp @@ -108,6 +108,31 @@ DECLARE_COMPONENT_WITH_ID( Test::ComponentB, "B" ) // explicit factory DECLARE_FACTORY_WITH_ID( Test::ComponentA, "A", Base::Factory ) +// custom factory example +namespace +{ + bool _custom_factory_called = false; + + struct CompWithCustomFactory : Base { + }; + + class _register__CompWithCustomFactory + { + public: + typedef Base::Factory s_t; + static Base* creator() + { + _custom_factory_called = true; + return new CompWithCustomFactory{}; + } + _register__CompWithCustomFactory() + { + using ::Gaudi::PluginService::Details::Registry; + Registry::instance().add( "CompWithCustomFactory", creator ); + } + } _register__CompWithCustomFactory; +} + // Tests #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN @@ -148,3 +173,14 @@ BOOST_AUTO_TEST_CASE( properties ) BOOST_CHECK( props["name"] == "Component1" ); } + +BOOST_AUTO_TEST_CASE( custom_factory ) +{ + { + _custom_factory_called = false; + + auto c = Base::Factory::create( "CompWithCustomFactory" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( _custom_factory_called == true ); + } +} -- GitLab From 96edccbf8d97a044c139726acd2b39448811b9e8 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Tue, 26 Jun 2018 09:55:55 +0200 Subject: [PATCH 03/13] Added PluginService v2 identical to v1, just to prepare for changes --- GaudiPluginService/CMakeLists.txt | 9 +- ...viceDetails.h => PluginServiceDetailsV1.h} | 30 +- .../Gaudi/Details/PluginServiceDetailsV2.h | 258 ++++++++++++ GaudiPluginService/Gaudi/PluginService.h | 108 ++--- GaudiPluginService/Gaudi/PluginServiceV1.h | 85 ++++ GaudiPluginService/Gaudi/PluginServiceV2.h | 84 ++++ ...{PluginService.cpp => PluginServiceV1.cpp} | 3 +- GaudiPluginService/src/PluginServiceV2.cpp | 373 ++++++++++++++++++ GaudiPluginService/src/capi_pluginservice.cpp | 6 +- GaudiPluginService/src/listcomponents.cpp | 31 +- .../tests/src/LegacyUseCases.cpp | 188 +++++++++ GaudiPluginService/tests/src/UseCases.cpp | 2 + 12 files changed, 1072 insertions(+), 105 deletions(-) rename GaudiPluginService/Gaudi/Details/{PluginServiceDetails.h => PluginServiceDetailsV1.h} (94%) create mode 100644 GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h create mode 100644 GaudiPluginService/Gaudi/PluginServiceV1.h create mode 100644 GaudiPluginService/Gaudi/PluginServiceV2.h rename GaudiPluginService/src/{PluginService.cpp => PluginServiceV1.cpp} (99%) create mode 100644 GaudiPluginService/src/PluginServiceV2.cpp create mode 100644 GaudiPluginService/tests/src/LegacyUseCases.cpp diff --git a/GaudiPluginService/CMakeLists.txt b/GaudiPluginService/CMakeLists.txt index e2de69ba2e..39cc5c567b 100644 --- a/GaudiPluginService/CMakeLists.txt +++ b/GaudiPluginService/CMakeLists.txt @@ -12,8 +12,9 @@ if(GAUDI_REFLEX_COMPONENT_ALIASES) endif() # Library -gaudi_add_library(GaudiPluginService src/PluginService.cpp src/capi_pluginservice.cpp - LINK_LIBRARIES ${CMAKE_DL_LIBS} +gaudi_add_library(GaudiPluginService src/PluginServiceV2.cpp src/PluginServiceV1.cpp src/capi_pluginservice.cpp + ${includes} + LINK_LIBRARIES ${CMAKE_DL_LIBS} -lstdc++fs ${libs} PUBLIC_HEADERS Gaudi) # Disable generation of ConfUserDB (must be done before gaudi_install_python_modules) @@ -33,6 +34,10 @@ gaudi_add_unit_test(Test_GaudiPluginService_UseCases tests/src/UseCases.cpp LINK_LIBRARIES GaudiPluginService TYPE Boost) +gaudi_add_unit_test(Test_GaudiPluginService_LegacyUseCases tests/src/LegacyUseCases.cpp + LINK_LIBRARIES GaudiPluginService + TYPE Boost) + gaudi_add_test(listcomponents.usage COMMAND $ PASSREGEX "Usage:") diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV1.h similarity index 94% rename from GaudiPluginService/Gaudi/Details/PluginServiceDetails.h rename to GaudiPluginService/Gaudi/Details/PluginServiceDetailsV1.h index c37c4a5705..0835d62064 100644 --- a/GaudiPluginService/Gaudi/Details/PluginServiceDetails.h +++ b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV1.h @@ -1,5 +1,5 @@ -#ifndef _GAUDI_PLUGIN_SERVICE_DETAILS_H_ -#define _GAUDI_PLUGIN_SERVICE_DETAILS_H_ +#ifndef _GAUDI_PLUGIN_SERVICE_DETAILS_V1_H_ +#define _GAUDI_PLUGIN_SERVICE_DETAILS_V1_H_ /*****************************************************************************\ * (c) Copyright 2013 CERN * * * @@ -22,31 +22,11 @@ #include -#if __GNUC__ >= 4 -#define GAUDIPS_HASCLASSVISIBILITY -#endif - -#if defined( GAUDIPS_HASCLASSVISIBILITY ) -#define GAUDIPS_IMPORT __attribute__( ( visibility( "default" ) ) ) -#define GAUDIPS_EXPORT __attribute__( ( visibility( "default" ) ) ) -#define GAUDIPS_LOCAL __attribute__( ( visibility( "hidden" ) ) ) -#else -#define GAUDIPS_IMPORT -#define GAUDIPS_EXPORT -#define GAUDIPS_LOCAL -#endif - -#ifdef GaudiPluginService_EXPORTS -#define GAUDIPS_API GAUDIPS_EXPORT -#else -#define GAUDIPS_API GAUDIPS_IMPORT -#endif - namespace Gaudi { namespace PluginService { - inline namespace v1 + GAUDI_PLUGIN_SERVICE_V1_INLINE namespace v1 { namespace Details { @@ -249,6 +229,8 @@ namespace Gaudi } } +#if !GAUDI_PLUGIN_SERVICE_USE_V2 + #define _INTERNAL_FACTORY_REGISTER_CNAME( name, serial ) _register_##_##serial #define _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, serial ) \ @@ -272,4 +254,6 @@ namespace Gaudi _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, ::Gaudi::PluginService::v1::Details::Factory, id, factory, \ serial ) +#endif + #endif //_GAUDI_PLUGIN_SERVICE_DETAILS_H_ diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h new file mode 100644 index 0000000000..c5ab0f2868 --- /dev/null +++ b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h @@ -0,0 +1,258 @@ +#ifndef _GAUDI_PLUGIN_SERVICE_DETAILS_V2_H_ +#define _GAUDI_PLUGIN_SERVICE_DETAILS_V2_H_ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ + +/// @author Marco Clemencic + +#include +#include +#include +#include +#include +#include + +#include + +namespace Gaudi +{ + namespace PluginService + { + GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 + { + namespace Details + { + /// Class providing default factory functions. + /// + /// The template argument T is the class to be created, while the methods + /// template argument S is the specific factory signature. + template + class Factory + { + public: + template + static typename S::ReturnType create( Args&&... args ) + { + return new T( std::forward( args )... ); + } + }; + + /// Function used to load a specific factory function. + /// @return the pointer to the factory function. + GAUDIPS_API + void* getCreator( const std::string& id, const std::string& type ); + + /// Convoluted implementation of getCreator with an embedded + /// reinterpret_cast, used to avoid the warning + ///
+        /// warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object
+        /// 
+ /// It is an ugly trick but works.
+ /// See: + ///
    + ///
  • http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
  • + ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#573
  • + ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
  • + ///
+ template + inline F getCreator( const std::string& id ) + { + union { + void* src; + F dst; + } p2p; + p2p.src = getCreator( id, typeid( F ).name() ); + return p2p.dst; + } + + /// Return a canonical name for type_info object (implementation borrowed + /// from GaudiKernel/System). + GAUDIPS_API + std::string demangle( const std::type_info& id ); + + /// Return a canonical name for the template argument. + template + inline std::string demangle() + { + return demangle( typeid( T ) ); + } + + /// In-memory database of the loaded factories. + class GAUDIPS_API Registry + { + public: + typedef std::string KeyType; + + /// Type used for the properties implementation. + typedef std::map Properties; + + struct FactoryInfo { + FactoryInfo( std::string lib, void* p = nullptr, std::string t = "", std::string rt = "", + std::string cn = "", Properties props = Properties() ) + : library( std::move( lib ) ) + , ptr( p ) + , type( std::move( t ) ) + , rtype( std::move( rt ) ) + , className( std::move( cn ) ) + , properties( std::move( props ) ) + { + } + + std::string library; + void* ptr; + std::string type; + std::string rtype; + std::string className; + Properties properties; + + FactoryInfo& addProperty( const KeyType& k, std::string v ) + { + properties[k] = std::move( v ); + return *this; + } + }; + + /// Type used for the database implementation. + typedef std::map FactoryMap; + + /// Retrieve the singleton instance of Registry. + static Registry& instance(); + + /// Add a factory to the database. + template + inline FactoryInfo& add( const I& id, typename F::FuncType ptr ) + { + union { + typename F::FuncType src; + void* dst; + } p2p; + p2p.src = ptr; + std::ostringstream o; + o << id; + return add( o.str(), p2p.dst, typeid( typename F::FuncType ).name(), + typeid( typename F::ReturnType ).name(), demangle() ); + } + + /// Retrieve the factory for the given id. + void* get( const std::string& id, const std::string& type ) const; + + /// Retrieve the FactoryInfo object for an id. + const FactoryInfo& getInfo( const std::string& id ) const; + + /// Add a property to an already existing FactoryInfo object (via its id.) + Registry& addProperty( const std::string& id, const std::string& k, const std::string& v ); + + /// Return a list of all the known and loaded factories + std::set loadedFactoryNames() const; + + /// Return the known factories (loading the list if not yet done). + inline const FactoryMap& factories() const + { + if ( !m_initialized ) const_cast( this )->initialize(); + return m_factories; + } + + private: + /// Private constructor for the singleton pattern. + /// At construction time, the internal database of known factories is + /// filled with the name of the libraries containing them, using the + /// ".components" files in the LD_LIBRARY_PATH. + Registry(); + + /// Private copy constructor for the singleton pattern. + Registry( const Registry& ) : m_initialized( false ) {} + + /// Add a factory to the database. + FactoryInfo& add( const std::string& id, void* factory, const std::string& type, const std::string& rtype, + const std::string& className, const Properties& props = Properties() ); + + /// Return the known factories (loading the list if not yet done). + inline FactoryMap& factories() + { + if ( !m_initialized ) initialize(); + return m_factories; + } + + /// Initialize the registry loading the list of factories from the + /// .component files in the library search path. + void initialize(); + + /// Flag recording if the registry has been initialized or not. + bool m_initialized; + + /// Internal storage for factories. + FactoryMap m_factories; + + /// Mutex used to control concurrent access to the internal data. + mutable std::recursive_mutex m_mutex; + }; + + /// Simple logging class, just to provide a default implementation. + class GAUDIPS_API Logger + { + public: + enum Level { Debug = 0, Info = 1, Warning = 2, Error = 3 }; + Logger( Level level = Warning ) : m_level( level ) {} + virtual ~Logger() {} + inline Level level() const { return m_level; } + inline void setLevel( Level level ) { m_level = level; } + inline void info( const std::string& msg ) { report( Info, msg ); } + inline void debug( const std::string& msg ) { report( Debug, msg ); } + inline void warning( const std::string& msg ) { report( Warning, msg ); } + inline void error( const std::string& msg ) { report( Error, msg ); } + + private: + virtual void report( Level lvl, const std::string& msg ); + Level m_level; + }; + + /// Return the current logger instance. + GAUDIPS_API Logger& logger(); + /// Set the logger instance to use. + /// It must be a new instance and the ownership is passed to the function. + GAUDIPS_API void setLogger( Logger* logger ); + } + + /// Backward compatibility with Reflex. + GAUDIPS_API void SetDebug( int debugLevel ); + /// Backward compatibility with Reflex. + GAUDIPS_API int Debug(); + } + } +} + +#if GAUDI_PLUGIN_SERVICE_USE_V2 + +#define _INTERNAL_FACTORY_REGISTER_CNAME( name, serial ) _register_##_##serial + +#define _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, serial ) \ + namespace \ + { \ + class _INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ + { \ + public: \ + typedef factory s_t; \ + typedef typecreator f_t; \ + static s_t::FuncType creator() { return &f_t::create; } \ + _INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ + { \ + using ::Gaudi::PluginService::v2::Details::Registry; \ + Registry::instance().add( id, creator() ); \ + } \ + } _INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ + } + +#define _INTERNAL_DECLARE_FACTORY( type, id, factory, serial ) \ + _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, ::Gaudi::PluginService::v2::Details::Factory, id, factory, serial ) + +#endif + +#endif //_GAUDI_PLUGIN_SERVICE_DETAILS_H_ diff --git a/GaudiPluginService/Gaudi/PluginService.h b/GaudiPluginService/Gaudi/PluginService.h index 7a31125a37..5e5f58498c 100644 --- a/GaudiPluginService/Gaudi/PluginService.h +++ b/GaudiPluginService/Gaudi/PluginService.h @@ -12,70 +12,44 @@ \*****************************************************************************/ /// @author Marco Clemencic -/// @see @ref GaudiPluginService-readme - -#include -#include -#include -#include - -#define DECLARE_FACTORY_WITH_ID( type, id, factory ) _INTERNAL_DECLARE_FACTORY( type, id, factory, __LINE__ ) - -#define DECLARE_FACTORY( type, factory ) \ - DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::v1::Details::demangle(), factory ) - -#define DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ - _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, __LINE__ ) - -#define DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ - DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, ::Gaudi::PluginService::v1::Details::demangle(), \ - factory ) - -#define DECLARE_COMPONENT( type ) DECLARE_FACTORY( type, type::Factory ) - -#define DECLARE_COMPONENT_WITH_ID( type, id ) DECLARE_FACTORY_WITH_ID( type, id, type::Factory ) - -namespace Gaudi -{ - namespace PluginService - { - inline namespace v1 - { - /// Class wrapping the signature for a factory with any number of arguments. - template - class Factory - { - public: - typedef R ReturnType; - typedef R ( *FuncType )( Args&&... ); - - static ReturnType create( const std::string& id, Args... args ) - { - const FuncType c = Details::getCreator( id ); - return c ? ( *c )( std::forward( args )... ) : 0; - } - - template - static ReturnType create( const T& id, Args... args ) - { - std::ostringstream o; - o << id; - return create( o.str(), std::forward( args )... ); - } - }; - - class GAUDIPS_EXPORT Exception : public std::exception - { - public: - Exception( std::string msg ); - ~Exception() throw() override; - const char* what() const throw() override; - - private: - std::string m_msg; - }; - } - } -} - -#endif //_GAUDI_PLUGIN_SERVICE_H_ +/// See @ref GaudiPluginService-readme + +#ifndef GAUDI_PLUGIN_SERVICE_USE_V2 +#if defined( GAUDI_PLUGIN_SERVICE_V2 ) || !defined( GAUDI_PLUGIN_SERVICE_V1 ) +#define GAUDI_PLUGIN_SERVICE_V2_INLINE inline +#define GAUDI_PLUGIN_SERVICE_V1_INLINE +#define GAUDI_PLUGIN_SERVICE_USE_V2 1 +#else +#define GAUDI_PLUGIN_SERVICE_V2_INLINE +#define GAUDI_PLUGIN_SERVICE_V1_INLINE inline +#define GAUDI_PLUGIN_SERVICE_USE_V2 0 +#endif +#endif + +#if __GNUC__ >= 4 +#define GAUDIPS_HASCLASSVISIBILITY +#endif + +#if defined( GAUDIPS_HASCLASSVISIBILITY ) +#define GAUDIPS_IMPORT __attribute__( ( visibility( "default" ) ) ) +#define GAUDIPS_EXPORT __attribute__( ( visibility( "default" ) ) ) +#define GAUDIPS_LOCAL __attribute__( ( visibility( "hidden" ) ) ) +#else +#define GAUDIPS_IMPORT +#define GAUDIPS_EXPORT +#define GAUDIPS_LOCAL +#endif + +#ifdef GaudiPluginService_EXPORTS +#define GAUDIPS_API GAUDIPS_EXPORT +#else +#define GAUDIPS_API GAUDIPS_IMPORT +#endif + +#if GAUDI_PLUGIN_SERVICE_USE_V2 +#include "Gaudi/PluginServiceV2.h" +#else +#include "Gaudi/PluginServiceV1.h" +#endif + +#endif diff --git a/GaudiPluginService/Gaudi/PluginServiceV1.h b/GaudiPluginService/Gaudi/PluginServiceV1.h new file mode 100644 index 0000000000..3a6c207010 --- /dev/null +++ b/GaudiPluginService/Gaudi/PluginServiceV1.h @@ -0,0 +1,85 @@ +#ifndef _GAUDI_PLUGIN_SERVICE_V1_H_ +#define _GAUDI_PLUGIN_SERVICE_V1_H_ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ + +/// @author Marco Clemencic +/// @see @ref GaudiPluginService-readme + +#include +#include +#include +#include + +#if !GAUDI_PLUGIN_SERVICE_USE_V2 + +#define DECLARE_FACTORY_WITH_ID( type, id, factory ) _INTERNAL_DECLARE_FACTORY( type, id, factory, __LINE__ ) + +#define DECLARE_FACTORY( type, factory ) \ + DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::v1::Details::demangle(), factory ) + +#define DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ + _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, __LINE__ ) + +#define DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ + DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, ::Gaudi::PluginService::v1::Details::demangle(), \ + factory ) + +#define DECLARE_COMPONENT( type ) DECLARE_FACTORY( type, type::Factory ) + +#define DECLARE_COMPONENT_WITH_ID( type, id ) DECLARE_FACTORY_WITH_ID( type, id, type::Factory ) + +#endif + +namespace Gaudi +{ + namespace PluginService + { + GAUDI_PLUGIN_SERVICE_V1_INLINE namespace v1 + { + /// Class wrapping the signature for a factory with any number of arguments. + template + class Factory + { + public: + typedef R ReturnType; + typedef R ( *FuncType )( Args&&... ); + + static ReturnType create( const std::string& id, Args... args ) + { + const FuncType c = Details::getCreator( id ); + return c ? ( *c )( std::forward( args )... ) : 0; + } + + template + static ReturnType create( const T& id, Args... args ) + { + std::ostringstream o; + o << id; + return create( o.str(), std::forward( args )... ); + } + }; + + class GAUDIPS_EXPORT Exception : public std::exception + { + public: + Exception( std::string msg ); + ~Exception() throw() override; + const char* what() const throw() override; + + private: + std::string m_msg; + }; + } + } +} + +#endif //_GAUDI_PLUGIN_SERVICE_H_ diff --git a/GaudiPluginService/Gaudi/PluginServiceV2.h b/GaudiPluginService/Gaudi/PluginServiceV2.h new file mode 100644 index 0000000000..ad2751f8c6 --- /dev/null +++ b/GaudiPluginService/Gaudi/PluginServiceV2.h @@ -0,0 +1,84 @@ +#ifndef _GAUDI_PLUGIN_SERVICE_V2_H_ +#define _GAUDI_PLUGIN_SERVICE_V2_H_ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ + +/// @author Marco Clemencic +/// @see @ref GaudiPluginService-readme + +#include +#include +#include +#include + +#if GAUDI_PLUGIN_SERVICE_USE_V2 + +#define DECLARE_FACTORY_WITH_ID( type, id, factory ) _INTERNAL_DECLARE_FACTORY( type, id, factory, __LINE__ ) + +#define DECLARE_FACTORY( type, factory ) \ + DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::v2::Details::demangle(), factory ) + +#define DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ + _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, __LINE__ ) + +#define DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ + DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, ::Gaudi::PluginService::v2::Details::demangle(), factory ) + +#define DECLARE_COMPONENT( type ) DECLARE_FACTORY( type, type::Factory ) + +#define DECLARE_COMPONENT_WITH_ID( type, id ) DECLARE_FACTORY_WITH_ID( type, id, type::Factory ) + +#endif + +namespace Gaudi +{ + namespace PluginService + { + GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 + { + /// Class wrapping the signature for a factory with any number of arguments. + template + class Factory + { + public: + typedef R ReturnType; + typedef R ( *FuncType )( Args&&... ); + + static ReturnType create( const std::string& id, Args... args ) + { + const FuncType c = Details::getCreator( id ); + return c ? ( *c )( std::forward( args )... ) : 0; + } + + template + static ReturnType create( const T& id, Args... args ) + { + std::ostringstream o; + o << id; + return create( o.str(), std::forward( args )... ); + } + }; + + class GAUDIPS_EXPORT Exception : public std::exception + { + public: + Exception( std::string msg ); + ~Exception() throw() override; + const char* what() const throw() override; + + private: + std::string m_msg; + }; + } + } +} + +#endif //_GAUDI_PLUGIN_SERVICE_H_ diff --git a/GaudiPluginService/src/PluginService.cpp b/GaudiPluginService/src/PluginServiceV1.cpp similarity index 99% rename from GaudiPluginService/src/PluginService.cpp rename to GaudiPluginService/src/PluginServiceV1.cpp index c8fcd21a7d..e1c52feca8 100644 --- a/GaudiPluginService/src/PluginService.cpp +++ b/GaudiPluginService/src/PluginServiceV1.cpp @@ -11,6 +11,7 @@ /// @author Marco Clemencic +#define GAUDI_PLUGIN_SERVICE_V1 #include #include @@ -116,7 +117,7 @@ namespace Gaudi { namespace PluginService { - namespace v1 + GAUDI_PLUGIN_SERVICE_V1_INLINE namespace v1 { Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {} Exception::~Exception() throw() {} diff --git a/GaudiPluginService/src/PluginServiceV2.cpp b/GaudiPluginService/src/PluginServiceV2.cpp new file mode 100644 index 0000000000..0806f257ef --- /dev/null +++ b/GaudiPluginService/src/PluginServiceV2.cpp @@ -0,0 +1,373 @@ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ + +/// @author Marco Clemencic + +#define GAUDI_PLUGIN_SERVICE_V2 +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define REG_SCOPE_LOCK std::lock_guard _guard( m_mutex ); + +namespace +{ + std::mutex registrySingletonMutex; +} +#define SINGLETON_LOCK std::lock_guard _guard(::registrySingletonMutex ); + +#include + +namespace +{ + // string trimming functions taken from + // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring + + constexpr struct is_space_t { + bool operator()( int i ) const { return std::isspace( i ); } + } is_space{}; + + // trim from start + static inline std::string& ltrim( std::string& s ) + { + s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), is_space ) ); + return s; + } + + // trim from end + static inline std::string& rtrim( std::string& s ) + { + s.erase( std::find_if_not( s.rbegin(), s.rend(), is_space ).base(), s.end() ); + return s; + } + // trim from both ends + static inline std::string& trim( std::string& s ) { return ltrim( rtrim( s ) ); } +} + +namespace +{ + /// Helper function used to set values in FactoryInfo data members only + /// if the original value is empty and reporting warnings in case of + /// inconsistencies. + inline void factoryInfoSetHelper( std::string& dest, const std::string value, const std::string& desc, + const std::string& id ) + { + if ( dest.empty() ) { + dest = value; + } else if ( dest != value ) { + Gaudi::PluginService::Details::logger().warning( "new factory loaded for '" + id + "' with different " + desc + + ": " + dest + " != " + value ); + } + } + + struct OldStyleCnv { + std::string name; + void operator()( const char c ) + { + switch ( c ) { + case '<': + case '>': + case ',': + case '(': + case ')': + case ':': + case '.': + name.push_back( '_' ); + break; + case '&': + name.push_back( 'r' ); + break; + case '*': + name.push_back( 'p' ); + break; + case ' ': + break; + default: + name.push_back( c ); + break; + } + } + }; + /// Convert a class name in the string used with the Reflex plugin service + std::string old_style_name( const std::string& name ) + { + return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name; + } +} + +namespace Gaudi +{ + namespace PluginService + { + GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 + { + Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {} + Exception::~Exception() throw() {} + const char* Exception::what() const throw() { return m_msg.c_str(); } + + namespace Details + { + void* getCreator( const std::string& id, const std::string& type ) + { + return Registry::instance().get( id, type ); + } + + std::string demangle( const std::string& id ) + { + int status; + auto realname = std::unique_ptr( + abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free ); + if ( !realname ) return id; +#if _GLIBCXX_USE_CXX11_ABI + return std::regex_replace( + realname.get(), + std::regex{"std::__cxx11::basic_string, std::allocator >( (?=>))?"}, + "std::string" ); +#else + return std::string{realname.get()}; +#endif + } + std::string demangle( const std::type_info& id ) { return demangle( id.name() ); } + + Registry& Registry::instance() + { + SINGLETON_LOCK + static Registry r; + return r; + } + + Registry::Registry() : m_initialized( false ) {} + + void Registry::initialize() + { + REG_SCOPE_LOCK + if ( m_initialized ) return; + m_initialized = true; +#if defined( _WIN32 ) + const char* envVar = "PATH"; + const char sep = ';'; +#elif defined( __APPLE__ ) + const char* envVar = "DYLD_LIBRARY_PATH"; + const char sep = ':'; +#else + const char* envVar = "LD_LIBRARY_PATH"; + const char sep = ':'; +#endif + char* search_path = ::getenv( envVar ); + if ( search_path ) { + logger().debug( std::string( "searching factories in " ) + envVar ); + std::string path( search_path ); + std::string::size_type pos = 0; + std::string::size_type newpos = 0; + while ( pos != std::string::npos ) { + std::string dirName; + // get the next entry in the path + newpos = path.find( sep, pos ); + if ( newpos != std::string::npos ) { + dirName = path.substr( pos, newpos - pos ); + pos = newpos + 1; + } else { + dirName = path.substr( pos ); + pos = newpos; + } + logger().debug( std::string( " looking into " ) + dirName ); + // look for files called "*.components" in the directory + DIR* dir = opendir( dirName.c_str() ); + if ( dir ) { + struct dirent* entry; + while ( ( entry = readdir( dir ) ) ) { + std::string name( entry->d_name ); + // check if the file name ends with ".components" + std::string::size_type extpos = name.find( ".components" ); + if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) { + std::string fullPath = ( dirName + '/' + name ); + { // check if it is a regular file + struct stat buf; + stat( fullPath.c_str(), &buf ); + if ( !S_ISREG( buf.st_mode ) ) continue; + } + // read the file + logger().debug( std::string( " reading " ) + name ); + std::ifstream factories{fullPath}; + std::string line; + int factoriesCount = 0; + int lineCount = 0; + while ( !factories.eof() ) { + ++lineCount; + std::getline( factories, line ); + trim( line ); + // skip empty lines and lines starting with '#' + if ( line.empty() || line[0] == '#' ) continue; + // look for the separator + auto pos = line.find( ':' ); + if ( pos == std::string::npos ) { + logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) ); + continue; + } + const std::string lib( line, 0, pos ); + const std::string fact( line, pos + 1 ); + m_factories.emplace( fact, FactoryInfo( lib ) ); +#ifdef GAUDI_REFLEX_COMPONENT_ALIASES + // add an alias for the factory using the Reflex convention + std::string old_name = old_style_name( fact ); + if ( fact != old_name ) { + FactoryInfo old_info( lib ); + old_info.properties["ReflexName"] = "true"; + m_factories.emplace( old_name, old_info ); + } +#endif + ++factoriesCount; + } + if ( logger().level() <= Logger::Debug ) { + logger().debug( " found " + std::to_string( factoriesCount ) + " factories" ); + } + } + } + closedir( dir ); + } + } + } + } + + Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type, + const std::string& rtype, const std::string& className, + const Properties& props ) + { + REG_SCOPE_LOCK + FactoryMap& facts = factories(); + auto entry = facts.find( id ); + if ( entry == facts.end() ) { + // this factory was not known yet + entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first; + } else { + // do not replace an existing factory with a new one + if ( !entry->second.ptr ) entry->second.ptr = factory; + factoryInfoSetHelper( entry->second.type, type, "type", id ); + factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id ); + factoryInfoSetHelper( entry->second.className, className, "class", id ); + } +#ifdef GAUDI_REFLEX_COMPONENT_ALIASES + // add an alias for the factory using the Reflex convention + std::string old_name = old_style_name( id ); + if ( id != old_name ) + add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true"; +#endif + return entry->second; + } + + void* Registry::get( const std::string& id, const std::string& type ) const + { + REG_SCOPE_LOCK + const FactoryMap& facts = factories(); + auto f = facts.find( id ); + if ( f != facts.end() ) { +#ifdef GAUDI_REFLEX_COMPONENT_ALIASES + const Properties& props = f->second.properties; + if ( props.find( "ReflexName" ) != props.end() ) + logger().warning( "requesting factory via old name '" + id + "'" + "use '" + + f->second.className + "' instead" ); +#endif + if ( !f->second.ptr ) { + if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) { + logger().warning( "cannot load " + f->second.library + " for factory " + id ); + char* dlmsg = dlerror(); + if ( dlmsg ) logger().warning( dlmsg ); + return nullptr; + } + f = facts.find( id ); // ensure that the iterator is valid + } + if ( f->second.type == type ) return f->second.ptr; + logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) + + " instead of " + demangle( type ) ); + } + return nullptr; // factory not found + } + + const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const + { + REG_SCOPE_LOCK + static const FactoryInfo unknown( "unknown" ); + const FactoryMap& facts = factories(); + auto f = facts.find( id ); + return ( f != facts.end() ) ? f->second : unknown; + } + + Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) + { + REG_SCOPE_LOCK + FactoryMap& facts = factories(); + auto f = facts.find( id ); + if ( f != facts.end() ) f->second.properties[k] = v; + return *this; + } + + std::set Registry::loadedFactoryNames() const + { + REG_SCOPE_LOCK + std::set l; + for ( const auto& f : factories() ) { + if ( f.second.ptr ) l.insert( f.first ); + } + return l; + } + + void Logger::report( Level lvl, const std::string& msg ) + { + static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "}; + if ( lvl >= level() ) { + std::cerr << levels[lvl] << msg << std::endl; + } + } + + static auto s_logger = std::make_unique(); + Logger& logger() { return *s_logger; } + void setLogger( Logger* logger ) { s_logger.reset( logger ); } + + } // namespace Details + + void SetDebug( int debugLevel ) + { + using namespace Details; + Logger& l = logger(); + if ( debugLevel > 1 ) + l.setLevel( Logger::Debug ); + else if ( debugLevel > 0 ) + l.setLevel( Logger::Info ); + else + l.setLevel( Logger::Warning ); + } + + int Debug() + { + using namespace Details; + switch ( logger().level() ) { + case Logger::Debug: + return 2; + case Logger::Info: + return 1; + default: + return 0; + } + } + } + } +} // namespace Gaudi::PluginService diff --git a/GaudiPluginService/src/capi_pluginservice.cpp b/GaudiPluginService/src/capi_pluginservice.cpp index 80701d983d..f27f6fb824 100644 --- a/GaudiPluginService/src/capi_pluginservice.cpp +++ b/GaudiPluginService/src/capi_pluginservice.cpp @@ -1,10 +1,14 @@ #include "capi_pluginservice.h" + +#define GAUDI_PLUGIN_SERVICE_V2 #include + #include #include #include -using namespace Gaudi::PluginService::v1::Details; + +using namespace Gaudi::PluginService::v2::Details; cgaudi_pluginsvc_t cgaudi_pluginsvc_instance() { diff --git a/GaudiPluginService/src/listcomponents.cpp b/GaudiPluginService/src/listcomponents.cpp index bd67373c8a..5da03f02f8 100644 --- a/GaudiPluginService/src/listcomponents.cpp +++ b/GaudiPluginService/src/listcomponents.cpp @@ -22,7 +22,9 @@ #include #include +#define GAUDI_PLUGIN_SERVICE_V2 #include +#include void help( std::string argv0 ) { @@ -46,14 +48,16 @@ void usage( std::string argv0 ) int main( int argc, char* argv[] ) { - Gaudi::PluginService::Details::Registry& reg = Gaudi::PluginService::v1::Details::Registry::instance(); + auto& reg2 = Gaudi::PluginService::v2::Details::Registry::instance(); + auto& reg1 = Gaudi::PluginService::v1::Details::Registry::instance(); - typedef Gaudi::PluginService::v1::Details::Registry::KeyType key_type; + using key_type = Gaudi::PluginService::v2::Details::Registry::KeyType; // cache to keep track of the loaded factories std::map loaded; // initialize the local cache - for ( const auto& name : reg.loadedFactoryNames() ) loaded.emplace( name, "" ); + for ( const auto& name : reg2.loadedFactoryNames() ) loaded.emplace( name, "" ); + for ( const auto& name : reg1.loadedFactoryNames() ) loaded.emplace( name, "" ); // Parse command line std::list libs; @@ -97,17 +101,22 @@ int main( int argc, char* argv[] ) } std::ostream& output = ( output_file ? *output_file : std::cout ); + auto dump_from = [&output, &loaded]( auto& reg, char* lib ) { + for ( const auto& factoryName : reg.loadedFactoryNames() ) { + auto f = loaded.find( factoryName ); + if ( f == loaded.end() ) { + output << lib << ":" << factoryName << std::endl; + loaded.emplace( factoryName, lib ); + } else + std::cerr << "WARNING: factory '" << factoryName << "' already found in " << f->second << std::endl; + } + }; + // loop over the list of libraries passed on the command line for ( char* lib : libs ) { if ( dlopen( lib, RTLD_LAZY | RTLD_LOCAL ) ) { - for ( const auto& factoryName : reg.loadedFactoryNames() ) { - auto f = loaded.find( factoryName ); - if ( f == loaded.end() ) { - output << lib << ":" << factoryName << std::endl; - loaded.emplace( factoryName, lib ); - } else - std::cerr << "WARNING: factory '" << factoryName << "' already found in " << f->second << std::endl; - } + dump_from( reg2, lib ); + dump_from( reg1, lib ); } else { std::cerr << "ERROR: failed to load " << lib << ": " << dlerror() << std::endl; return EXIT_FAILURE; diff --git a/GaudiPluginService/tests/src/LegacyUseCases.cpp b/GaudiPluginService/tests/src/LegacyUseCases.cpp new file mode 100644 index 0000000000..688471f03c --- /dev/null +++ b/GaudiPluginService/tests/src/LegacyUseCases.cpp @@ -0,0 +1,188 @@ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ +/** + * Compile-time test for all known PluginService use-cases + * + * @author Marco Clemencic + */ + +#define GAUDI_PLUGIN_SERVICE_V1 + +#include + +// standard use, 0 arguments +class Base +{ +public: + typedef Gaudi::PluginService::Factory Factory; + virtual ~Base() {} +}; +class Component0 : public Base +{ +}; +DECLARE_COMPONENT( Component0 ) + +class Component1 : public Base +{ +}; +#define DECLARE_COMPONENT_WITH_PROPS( type ) DECLARE_FACTORY_WITH_PROPS( type, type::Factory ) +#define DECLARE_FACTORY_WITH_PROPS( type, factory ) \ + DECLARE_FACTORY_WITH_ID_AND_PROPS( type, ::Gaudi::PluginService::Details::demangle(), factory ) +#define DECLARE_FACTORY_WITH_ID_AND_PROPS( type, id, factory ) \ + _INTERNAL_DECLARE_FACTORY_WITH_PROPS( type, id, factory, __LINE__ ) +#define _INTERNAL_DECLARE_FACTORY_WITH_PROPS( type, id, factory, serial ) \ + _INTERNAL_DECLARE_FACTORY_WITH_CREATOR_AND_PROPS( type, ::Gaudi::PluginService::Details::Factory, id, factory, \ + serial ) +#define _INTERNAL_DECLARE_FACTORY_WITH_CREATOR_AND_PROPS( type, typecreator, id, factory, serial ) \ + namespace \ + { \ + class _INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ + { \ + public: \ + typedef factory s_t; \ + typedef typecreator f_t; \ + static s_t::FuncType creator() { return &f_t::create; } \ + _INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ + { \ + using ::Gaudi::PluginService::Details::Registry; \ + Registry::instance().add( id, creator() ).addProperty( "name", #type ); \ + } \ + } _INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ + } + +DECLARE_COMPONENT_WITH_PROPS( Component1 ) + +// standard use, 2 arguments +class Base2 +{ +public: + typedef Gaudi::PluginService::Factory Factory; + virtual ~Base2() {} +}; +class Component2 : public Base2 +{ +public: + Component2( std::string _s, int _i ) : i( _i ), s( std::move( _s ) ) {} + int i; + std::string s; +}; +DECLARE_COMPONENT( Component2 ) + +// namespaces +namespace Test +{ + class ComponentA : public Base + { + }; + class ComponentB : public Base + { + }; + class ComponentC : public Base + { + }; +} + +namespace +{ + using Test::ComponentA; + DECLARE_COMPONENT( ComponentA ) +} + +DECLARE_COMPONENT( Test::ComponentB ) + +namespace Test +{ + DECLARE_COMPONENT( ComponentC ) +} + +// using ids +DECLARE_COMPONENT_WITH_ID( Component2, "Id2" ) +DECLARE_COMPONENT_WITH_ID( Test::ComponentB, "B" ) + +// explicit factory +DECLARE_FACTORY_WITH_ID( Test::ComponentA, "A", Base::Factory ) + +// custom factory example +namespace +{ + bool _custom_factory_called = false; + + struct CompWithCustomFactory : Base { + }; + + class _register__CompWithCustomFactory + { + public: + typedef Base::Factory s_t; + static Base* creator() + { + _custom_factory_called = true; + return new CompWithCustomFactory{}; + } + _register__CompWithCustomFactory() + { + using ::Gaudi::PluginService::Details::Registry; + Registry::instance().add( "CompWithCustomFactory", creator ); + } + } _register__CompWithCustomFactory; +} + +// Tests +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN +#include + +BOOST_AUTO_TEST_CASE( basic ) { BOOST_CHECK( Base::Factory::create( "Component0" ) != nullptr ); } + +BOOST_AUTO_TEST_CASE( basic_with_args ) +{ + Base2* instance = Base2::Factory::create( "Component2", "hello", 2 ); + BOOST_CHECK( instance != nullptr ); + + Component2* c2 = dynamic_cast( instance ); + BOOST_REQUIRE( c2 != nullptr ); + BOOST_CHECK( c2->i == 2 ); + BOOST_CHECK( c2->s == "hello" ); +} + +BOOST_AUTO_TEST_CASE( namespaces ) +{ + BOOST_CHECK( Base::Factory::create( "Test::ComponentA" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentB" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentC" ) != nullptr ); +} + +BOOST_AUTO_TEST_CASE( ids ) +{ + BOOST_CHECK( Base2::Factory::create( "Id2", "id", -2 ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "A" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "B" ) != nullptr ); +} + +BOOST_AUTO_TEST_CASE( properties ) +{ + using Gaudi::PluginService::Details::Registry; + Registry& reg = Registry::instance(); + Registry::Properties props = reg.getInfo( "Component1" ).properties; + + BOOST_CHECK( props["name"] == "Component1" ); +} + +BOOST_AUTO_TEST_CASE( custom_factory ) +{ + { + _custom_factory_called = false; + + auto c = Base::Factory::create( "CompWithCustomFactory" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( _custom_factory_called == true ); + } +} diff --git a/GaudiPluginService/tests/src/UseCases.cpp b/GaudiPluginService/tests/src/UseCases.cpp index 3c16e8ed5c..66932aed1c 100644 --- a/GaudiPluginService/tests/src/UseCases.cpp +++ b/GaudiPluginService/tests/src/UseCases.cpp @@ -14,6 +14,8 @@ * @author Marco Clemencic */ +#define GAUDI_PLUGIN_SERVICE_V2 + #include // standard use, 0 arguments -- GitLab From cd333af56af4fa91e6deeabad0d1b1ac07b1ec42 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Tue, 26 Jun 2018 12:08:51 +0200 Subject: [PATCH 04/13] Rewrite of PluginService in modern C++ - use std::any and std::function to wrap factories (instead of void*) - optionally use boost::any for C++14 compatibility - use std::(experimental::)filsystem for filesystem operations - use std::(experimental::)string_view for string manipulation - improved/fixed/cleaned up documentation - Doxygen and README.md --- Gaudi/python/Gaudi/Main.py | 5 +- GaudiCommonSvc/src/AuditorSvc.cpp | 2 +- .../src/ApplicationMgr/AlgorithmManager.cpp | 2 +- .../src/ApplicationMgr/ServiceManager.cpp | 2 +- GaudiCoreSvc/src/ApplicationMgr/ToolSvc.cpp | 2 +- .../EventSelector/EventCollectionSelector.cpp | 2 +- GaudiExamples/src/AlgErrAud/MyAudTool.h | 4 - .../PluginService/SpecialCustomFactory.cpp | 32 +- GaudiKernel/GaudiKernel/AlgTool.h | 2 +- GaudiKernel/GaudiKernel/Algorithm.h | 2 +- GaudiKernel/GaudiKernel/Auditor.h | 2 +- GaudiKernel/GaudiKernel/Converter.h | 2 +- GaudiKernel/GaudiKernel/ObjectFactory.h | 7 +- GaudiKernel/GaudiKernel/Service.h | 2 +- GaudiKernel/src/Lib/Bootstrap.cpp | 12 +- GaudiKernel/src/Lib/ConversionSvc.cpp | 4 +- GaudiKernel/src/Lib/DataStreamTool.cpp | 2 +- GaudiKernel/src/Util/DsoUtils.h | 100 ------ GaudiKernel/src/Util/genconf.cpp | 97 +++--- GaudiKernel/tests/src/custom_factory.cpp | 2 +- GaudiPluginService/CMakeLists.txt | 6 + .../Gaudi/Details/PluginServiceCommon.h | 47 +++ .../Gaudi/Details/PluginServiceDetailsV1.h | 22 +- .../Gaudi/Details/PluginServiceDetailsV2.h | 286 ++++++++---------- GaudiPluginService/Gaudi/PluginService.h | 32 +- GaudiPluginService/Gaudi/PluginServiceV1.h | 32 +- GaudiPluginService/Gaudi/PluginServiceV2.h | 150 ++++++--- GaudiPluginService/doc/README.md | 188 ++++++++---- .../python/GaudiPluginService/cpluginsvc.py | 16 +- GaudiPluginService/src/PluginServiceV2.cpp | 273 +++++++++-------- GaudiPluginService/src/capi_pluginservice.cpp | 18 +- GaudiPluginService/src/capi_pluginservice.h | 3 - .../tests/src/LegacyUseCases.cpp | 6 +- GaudiPluginService/tests/src/UseCases.cpp | 143 +++++---- GaudiSvc/src/NTupleSvc/CollectionCloneAlg.cpp | 2 +- GaudiSvc/src/RndmGenSvc/RndmGenSvc.cpp | 2 +- GaudiUtils/GaudiUtils/IFileCatalog.h | 2 +- GaudiUtils/src/component/MultiFileCatalog.cpp | 10 +- RootHistCnv/src/RHistogramCnv.h | 2 +- 39 files changed, 792 insertions(+), 733 deletions(-) delete mode 100644 GaudiKernel/src/Util/DsoUtils.h create mode 100644 GaudiPluginService/Gaudi/Details/PluginServiceCommon.h diff --git a/Gaudi/python/Gaudi/Main.py b/Gaudi/python/Gaudi/Main.py index e4163029a7..85b6884636 100644 --- a/Gaudi/python/Gaudi/Main.py +++ b/Gaudi/python/Gaudi/Main.py @@ -73,7 +73,8 @@ class BootstrapHelper(object): return self.lib.py_helper_printAlgsSequences(self.ptr) def __init__(self): - from ctypes import PyDLL, util, c_void_p, c_bool, c_char_p, c_int + from ctypes import (PyDLL, util, c_void_p, c_bool, c_char_p, c_int, + RTLD_GLOBAL) # Helper class to avoid void* to int conversion # (see http://stackoverflow.com/questions/17840144) @@ -87,7 +88,7 @@ class BootstrapHelper(object): # FIXME: note that we need PyDLL instead of CDLL if the calls to # Python functions are not protected with the GIL. - self.lib = gkl = PyDLL(libname) + self.lib = gkl = PyDLL(libname, mode=RTLD_GLOBAL) functions = [('createApplicationMgr', IInterface_p, []), ('getService', IInterface_p, [IInterface_p, c_char_p]), diff --git a/GaudiCommonSvc/src/AuditorSvc.cpp b/GaudiCommonSvc/src/AuditorSvc.cpp index 0080e451ac..18e4746e3b 100644 --- a/GaudiCommonSvc/src/AuditorSvc.cpp +++ b/GaudiCommonSvc/src/AuditorSvc.cpp @@ -24,7 +24,7 @@ SmartIF AuditorSvc::newAuditor_( MsgStream& log, const std::string& na // locate the auditor factory, instantiate a new auditor, initialize it StatusCode sc; Gaudi::Utils::TypeNameString item( name ); - SmartIF aud{Auditor::Factory::create( item.type(), item.name(), serviceLocator().get() )}; + SmartIF aud{Auditor::Factory::create( item.type(), item.name(), serviceLocator().get() ).release()}; if ( aud ) { if ( m_targetState >= Gaudi::StateMachine::INITIALIZED ) { sc = aud->sysInitialize(); diff --git a/GaudiCoreSvc/src/ApplicationMgr/AlgorithmManager.cpp b/GaudiCoreSvc/src/ApplicationMgr/AlgorithmManager.cpp index 30a4b29dd8..4e94096178 100644 --- a/GaudiCoreSvc/src/ApplicationMgr/AlgorithmManager.cpp +++ b/GaudiCoreSvc/src/ApplicationMgr/AlgorithmManager.cpp @@ -60,7 +60,7 @@ StatusCode AlgorithmManager::createAlgorithm( const std::string& algtype, const actualalgtype = typeAlias->second; } } - algorithm = Algorithm::Factory::create( actualalgtype, algname, serviceLocator().get() ); + algorithm = Algorithm::Factory::create( actualalgtype, algname, serviceLocator().get() ).release(); if ( !algorithm ) { this->error() << "Algorithm of type " << actualalgtype << " is unknown (No factory available)." << endmsg; #ifndef _WIN32 diff --git a/GaudiCoreSvc/src/ApplicationMgr/ServiceManager.cpp b/GaudiCoreSvc/src/ApplicationMgr/ServiceManager.cpp index eaca1fe28e..41dc5d8e89 100644 --- a/GaudiCoreSvc/src/ApplicationMgr/ServiceManager.cpp +++ b/GaudiCoreSvc/src/ApplicationMgr/ServiceManager.cpp @@ -82,7 +82,7 @@ SmartIF& ServiceManager::createService( const Gaudi::Utils::TypeNameSt auto ip = type.find( "__" ); if ( ip != std::string::npos ) type.erase( ip, type.length() ); - IService* service = Service::Factory::create( type, name, this ); + IService* service = Service::Factory::create( type, name, this ).release(); if ( !service ) { fatal() << "No Service factory for " << type << " available." << endmsg; return no_service; diff --git a/GaudiCoreSvc/src/ApplicationMgr/ToolSvc.cpp b/GaudiCoreSvc/src/ApplicationMgr/ToolSvc.cpp index afbac01677..c2337e5b18 100644 --- a/GaudiCoreSvc/src/ApplicationMgr/ToolSvc.cpp +++ b/GaudiCoreSvc/src/ApplicationMgr/ToolSvc.cpp @@ -417,7 +417,7 @@ namespace { // remove previous content if ( m_tool ) remove( m_tools, m_tool.get() ); - m_tool.reset( AlgTool::Factory::create( tooltype, tooltype, fullname, parent ) ); + m_tool = AlgTool::Factory::create( tooltype, tooltype, fullname, parent ); // set new content if ( m_tool ) m_tools.push_back( m_tool.get() ); } diff --git a/GaudiCoreSvc/src/EventSelector/EventCollectionSelector.cpp b/GaudiCoreSvc/src/EventSelector/EventCollectionSelector.cpp index 46ac6885ce..6a3c814e9b 100644 --- a/GaudiCoreSvc/src/EventSelector/EventCollectionSelector.cpp +++ b/GaudiCoreSvc/src/EventSelector/EventCollectionSelector.cpp @@ -128,7 +128,7 @@ StatusCode EventCollectionSelector::connectStatement( const std::string& typ, co std::string seltyp = typ; if ( !seltyp.empty() || !crit.empty() ) { if ( !crit.empty() && seltyp.length() == 0 ) seltyp = "NTuple::Selector"; - SmartIF stmt( ObjFactory::create( seltyp, serviceLocator() ) ); + SmartIF stmt( ObjFactory::create( seltyp, serviceLocator() ).release() ); if ( stmt ) { if ( !crit.empty() ) stmt->setCriteria( crit ); tuple->attachSelector( stmt ).ignore(); diff --git a/GaudiExamples/src/AlgErrAud/MyAudTool.h b/GaudiExamples/src/AlgErrAud/MyAudTool.h index b2669dc42c..78a64577cb 100644 --- a/GaudiExamples/src/AlgErrAud/MyAudTool.h +++ b/GaudiExamples/src/AlgErrAud/MyAudTool.h @@ -25,11 +25,7 @@ public: StatusCode initialize() override; StatusCode finalize() override; -protected: /// Standard destructor ~MyAudTool() override; - -private: - /// Properties }; #endif // GAUDIEXANMPLES_MYAUDTOOL_H diff --git a/GaudiExamples/src/PluginService/SpecialCustomFactory.cpp b/GaudiExamples/src/PluginService/SpecialCustomFactory.cpp index 29bbdb1f42..5455d147ad 100644 --- a/GaudiExamples/src/PluginService/SpecialCustomFactory.cpp +++ b/GaudiExamples/src/PluginService/SpecialCustomFactory.cpp @@ -21,27 +21,19 @@ namespace PluginServiceTest namespace { using PluginServiceTest::CustomFactoryAlgorithm; - class _register__CustomFactoryAlgorithm - { - public: - typedef Algorithm::Factory s_t; - static IAlgorithm* creator( const std::string& name, ISvcLocator*&& svcLoc ) - { - CustomFactoryAlgorithm* p = new CustomFactoryAlgorithm( name, svcLoc ); - // do not print messages if we are created in genconf - const std::string cmd = System::cmdLineArgs()[0]; - if ( cmd.find( "genconf" ) == std::string::npos ) { - std::cout << "created CustomFactoryAlgorithm at " << p << std::endl; - } + std::unique_ptr creator( const std::string& name, ISvcLocator* svcLoc ) + { + auto p = std::make_unique( name, svcLoc ); - return p; + // do not print messages if we are created in genconf + const std::string cmd = System::cmdLineArgs()[0]; + if ( cmd.find( "genconf" ) == std::string::npos ) { + std::cout << "created CustomFactoryAlgorithm at " << p.get() << std::endl; } - _register__CustomFactoryAlgorithm() - { - using ::Gaudi::PluginService::Details::Registry; - Registry::instance().add( - ::Gaudi::PluginService::Details::demangle(), creator ); - } - } _register__CustomFactoryAlgorithm; + + return std::move( p ); + } + + Gaudi::PluginService::DeclareFactory _{creator}; } diff --git a/GaudiKernel/GaudiKernel/AlgTool.h b/GaudiKernel/GaudiKernel/AlgTool.h index 6147c64059..328d3d49d1 100644 --- a/GaudiKernel/GaudiKernel/AlgTool.h +++ b/GaudiKernel/GaudiKernel/AlgTool.h @@ -49,7 +49,7 @@ class GAUDI_API AlgTool PropertyHolder>>> { public: - typedef Gaudi::PluginService::Factory Factory; + using Factory = Gaudi::PluginService::Factory; /// Query for a given interface StatusCode queryInterface( const InterfaceID& riid, void** ppvUnknown ) override; diff --git a/GaudiKernel/GaudiKernel/Algorithm.h b/GaudiKernel/GaudiKernel/Algorithm.h index 3889e5ede0..a873f0c67c 100644 --- a/GaudiKernel/GaudiKernel/Algorithm.h +++ b/GaudiKernel/GaudiKernel/Algorithm.h @@ -80,7 +80,7 @@ class GAUDI_API Algorithm PropertyHolder>>> { public: - typedef Gaudi::PluginService::Factory Factory; + using Factory = Gaudi::PluginService::Factory; friend AlgorithmManager; diff --git a/GaudiKernel/GaudiKernel/Auditor.h b/GaudiKernel/GaudiKernel/Auditor.h index cad3301e37..4d859c47cd 100644 --- a/GaudiKernel/GaudiKernel/Auditor.h +++ b/GaudiKernel/GaudiKernel/Auditor.h @@ -35,7 +35,7 @@ class Algorithm; class GAUDI_API Auditor : public PropertyHolder>> { public: - typedef Gaudi::PluginService::Factory Factory; + using Factory = Gaudi::PluginService::Factory; /** Constructor @param name The algorithm object's name diff --git a/GaudiKernel/GaudiKernel/Converter.h b/GaudiKernel/GaudiKernel/Converter.h index 6f2aa8478d..451ee8cd76 100644 --- a/GaudiKernel/GaudiKernel/Converter.h +++ b/GaudiKernel/GaudiKernel/Converter.h @@ -24,7 +24,7 @@ class IRegistry; class GAUDI_API Converter : public implements { public: - typedef Gaudi::PluginService::Factory Factory; + using Factory = Gaudi::PluginService::Factory; /// Initialize the converter StatusCode initialize() override; diff --git a/GaudiKernel/GaudiKernel/ObjectFactory.h b/GaudiKernel/GaudiKernel/ObjectFactory.h index e2a6b8cb8f..3204b24707 100644 --- a/GaudiKernel/GaudiKernel/ObjectFactory.h +++ b/GaudiKernel/GaudiKernel/ObjectFactory.h @@ -7,10 +7,9 @@ class IInterface; class DataObject; class ContainedObject; -typedef Gaudi::PluginService::Factory ObjFactory; - -typedef Gaudi::PluginService::Factory DataObjFactory; -typedef Gaudi::PluginService::Factory ContainedObjFactory; +using ObjFactory = Gaudi::PluginService::Factory; +using DataObjFactory = Gaudi::PluginService::Factory; +using ContainedObjFactory = Gaudi::PluginService::Factory; // Macros to declare component factories #define DECLARE_OBJECT_FACTORY( x ) DECLARE_FACTORY( x, ObjFactory ) diff --git a/GaudiKernel/GaudiKernel/Service.h b/GaudiKernel/GaudiKernel/Service.h index b0611afc01..511ed7ddac 100644 --- a/GaudiKernel/GaudiKernel/Service.h +++ b/GaudiKernel/GaudiKernel/Service.h @@ -36,7 +36,7 @@ class ServiceManager; class GAUDI_API Service : public PropertyHolder>> { public: - typedef Gaudi::PluginService::Factory Factory; + using Factory = Gaudi::PluginService::Factory; friend class ServiceManager; diff --git a/GaudiKernel/src/Lib/Bootstrap.cpp b/GaudiKernel/src/Lib/Bootstrap.cpp index e04ccaa077..60ce0b1444 100644 --- a/GaudiKernel/src/Lib/Bootstrap.cpp +++ b/GaudiKernel/src/Lib/Bootstrap.cpp @@ -142,20 +142,20 @@ IInterface* Gaudi::createInstance( const std::string& name, const std::string& f //------------------------------------------------------------------------------ { - IInterface* ii = ObjFactory::create( factname, nullptr ); + IInterface* ii = ObjFactory::create( factname, nullptr ).release(); if ( ii ) return ii; - IService* is = Service::Factory::create( factname, name, nullptr ); + IService* is = Service::Factory::create( factname, name, nullptr ).release(); if ( is ) return is; - IAlgorithm* ia = Algorithm::Factory::create( factname, name, nullptr ); + IAlgorithm* ia = Algorithm::Factory::create( factname, name, nullptr ).release(); if ( ia ) return ia; void* libHandle = nullptr; if ( System::loadDynamicLib( dllname, &libHandle ) ) { - ii = ObjFactory::create( factname, nullptr ); + ii = ObjFactory::create( factname, nullptr ).release(); if ( ii ) return ii; - is = Service::Factory::create( factname, name, nullptr ); + is = Service::Factory::create( factname, name, nullptr ).release(); if ( is ) return is; - ia = Algorithm::Factory::create( factname, name, nullptr ); + ia = Algorithm::Factory::create( factname, name, nullptr ).release(); if ( ia ) return ia; } else { // DLL library not loaded. Try in the local module diff --git a/GaudiKernel/src/Lib/ConversionSvc.cpp b/GaudiKernel/src/Lib/ConversionSvc.cpp index 966e0b7b7f..f0e7bed0db 100644 --- a/GaudiKernel/src/Lib/ConversionSvc.cpp +++ b/GaudiKernel/src/Lib/ConversionSvc.cpp @@ -271,10 +271,10 @@ StatusCode ConversionSvc::finalize() /// Create new Converter using factory IConverter* ConversionSvc::createConverter( long typ, const CLID& clid, const ICnvFactory* /*fac*/ ) { - IConverter* pConverter = Converter::Factory::create( ConverterID( typ, clid ), serviceLocator().get() ); + IConverter* pConverter = Converter::Factory::create( ConverterID( typ, clid ), serviceLocator().get() ).release(); if ( !pConverter ) { typ = ( typ < 0xFF ) ? typ : typ & 0xFFFFFF00; - pConverter = Converter::Factory::create( ConverterID( typ, clid ), serviceLocator().get() ); + pConverter = Converter::Factory::create( ConverterID( typ, clid ), serviceLocator().get() ).release(); } return pConverter; } diff --git a/GaudiKernel/src/Lib/DataStreamTool.cpp b/GaudiKernel/src/Lib/DataStreamTool.cpp index c4cf6ec740..73d1460b04 100644 --- a/GaudiKernel/src/Lib/DataStreamTool.cpp +++ b/GaudiKernel/src/Lib/DataStreamTool.cpp @@ -110,7 +110,7 @@ StatusCode DataStreamTool::initializeStream( EventSelectorDataStream* s ) // Create (sub-) Event selector service StatusCode DataStreamTool::createSelector( const std::string& nam, const std::string& typ, IEvtSelector*& sel ) { - auto isvc = make_SmartIF( Service::Factory::create( typ, nam, serviceLocator() ) ); + auto isvc = make_SmartIF( Service::Factory::create( typ, nam, serviceLocator() ).release() ); if ( isvc ) { auto isel = isvc.as(); if ( isel ) { diff --git a/GaudiKernel/src/Util/DsoUtils.h b/GaudiKernel/src/Util/DsoUtils.h deleted file mode 100644 index 71b0491040..0000000000 --- a/GaudiKernel/src/Util/DsoUtils.h +++ /dev/null @@ -1,100 +0,0 @@ -///////////////////////// -*- C++ -*- ///////////////////////////// -/// @file DsoUtils.h -/// @brief A mixed bag of "portable" utils for DSO (Dynamic Shared Objects) -/// @author S.Binet -/// @author Markus Frank (Win32 code) -// note: This, I believe, should be part of Reflex::SharedLibrary - -#ifndef GAUDIKERNEL_DSOUTILS_H -#define GAUDIKERNEL_DSOUTILS_H - -// STL includes -#include - -#include "GaudiKernel/System.h" - -namespace DsoUtils -{ - - inline std::string libNativeName( const std::string& libName ) - { -#if defined( _WIN32 ) - return libName + ".dll"; -#elif defined( __linux ) || defined( __APPLE__ ) - return "lib" + libName + ".so"; -#else - // variant of the GIGO design pattern - return libName; -#endif - } - -#ifdef _GNU_SOURCE -#include - static std::string dsoName( void* addr ) - { - Dl_info info; - if ( dladdr( addr, &info ) == 0 ) return ""; - - const char* pos = strrchr( info.dli_fname, '/' ); - if ( pos ) - ++pos; - else - pos = info.dli_fname; - return pos; - } -#elif defined( _WIN32 ) -#include - - static std::string dsoName( void* addr ) - { - if ( addr ) { - MEMORY_BASIC_INFORMATION mbi; - if ( VirtualQuery( addr, &mbi, sizeof( mbi ) ) ) { - HMODULE h_module = (HMODULE)mbi.AllocationBase; - char mod[1024]; - if ( GetModuleFileName( h_module, mod, sizeof( mod ) ) ) { - const char* pos = strrchr( mod, '\\' ); - if ( pos ) - ++pos; - else - pos = mod; - return pos; - } - } - } - return ""; - } - -#else // dummy implementation for unknown platforms - static std::string dsoName( const ROOT::Reflex::Member& ) { return ""; } - -#endif - - static bool inDso( void* addr, const std::string& dsoname ) - { -#ifdef _WIN32 - char sep = '\\'; -#else - char sep = '/'; -#endif - - std::string srcname = dsoName( addr ); - if ( srcname.empty() ) { - // we do not know the name of the library, let's guess it's OK - return true; - } - - std::string::size_type pos = dsoname.find_last_of( sep ); - std::string curname; - if ( std::string::npos == pos ) { - curname = dsoname; - } else { - curname = dsoname.substr( pos + 1 ); - } - - return srcname == curname; - } - -} // end namespace DsoUtils - -#endif // not GAUDIKERNEL_DSOUTILS_H diff --git a/GaudiKernel/src/Util/genconf.cpp b/GaudiKernel/src/Util/genconf.cpp index 95a0dcacc1..cf57d793e8 100644 --- a/GaudiKernel/src/Util/genconf.cpp +++ b/GaudiKernel/src/Util/genconf.cpp @@ -67,8 +67,6 @@ #include #include -#include "DsoUtils.h" - namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -109,6 +107,13 @@ namespace Unknown }; + const std::map allowedFactories{ + {typeid( Algorithm::Factory::FactoryType ).name(), component_t::Algorithm}, + {typeid( Service::Factory::FactoryType ).name(), component_t::Service}, + {typeid( AlgTool::Factory::FactoryType ).name(), component_t::AlgTool}, + {typeid( Auditor::Factory::FactoryType ).name(), component_t::Auditor}, + }; + const std::string& toString( component_t type ) { static const std::array names = {"Module", "DefaultName", "Algorithm", "AlgTool", @@ -138,6 +143,17 @@ namespace return std::type_index{typeid( T )}; } //----------------------------------------------------------------------------- + inline std::string libNativeName( const std::string& libName ) + { +#if defined( _WIN32 ) + return libName + ".dll"; +#elif defined( __linux ) || defined( __APPLE__ ) + return "lib" + libName + ".so"; +#else + // variant of the GIGO design pattern + return libName; +#endif + } } class configGenerator @@ -454,7 +470,7 @@ int configGenerator::genConfig( const Strings_t& libs, const string& userModule //--- Iterate over component factories -------------------------------------- using Gaudi::PluginService::Details::Registry; - Registry& registry = Registry::instance(); + const Registry& registry = Registry::instance(); auto bkgNames = registry.loadedFactoryNames(); @@ -484,7 +500,13 @@ int configGenerator::genConfig( const Strings_t& libs, const string& userModule continue; } - for ( const auto& factoryName : registry.loadedFactoryNames() ) { + for ( const auto& factoryEntry : registry.factories() ) { + // skip factories not loaded + const auto& info = factoryEntry.second; + if ( !info.is_set() ) continue; + + // skip factories loaded by other libraries + const auto& factoryName = factoryEntry.first; if ( bkgNames.find( factoryName ) != bkgNames.end() ) { if ( Gaudi::PluginService::Details::logger().level() <= 1 ) { LOG_INFO << "\t==> skipping [" << factoryName << "]..."; @@ -492,79 +514,55 @@ int configGenerator::genConfig( const Strings_t& libs, const string& userModule continue; } - const Registry::FactoryInfo info = registry.getInfo( factoryName ); - const std::string& rtype = info.rtype; - // do not generate configurables for the Reflex-compatible aliases - if ( info.properties.find( "ReflexName" ) != info.properties.end() ) continue; + if ( !info.getprop( "ReflexName" ).empty() ) continue; // Atlas contributed code (patch #1247) // Skip the generation of configurables if the component does not come // from the same library we are processing (i.e. we found a symbol that // is coming from a library loaded by the linker). - if ( !DsoUtils::inDso( info.ptr, DsoUtils::libNativeName( iLib ) ) ) { + if ( libNativeName( iLib ) != info.library ) { LOG_WARNING << "library [" << iLib << "] exposes factory [" << factoryName << "] which is declared in [" - << DsoUtils::dsoName( info.ptr ) << "] !!"; + << info.library << "] !!"; continue; } component_t type = component_t::Unknown; - if ( factoryName == "ApplicationMgr" ) - type = component_t::ApplicationMgr; - else if ( rtype == typeid( IInterface* ).name() ) - type = component_t::IInterface; - else if ( rtype == typeid( IAlgorithm* ).name() ) - type = component_t::Algorithm; - else if ( rtype == typeid( IService* ).name() ) - type = component_t::Service; - else if ( rtype == typeid( IAlgTool* ).name() ) - type = component_t::AlgTool; - else if ( rtype == typeid( IAuditor* ).name() ) - type = component_t::Auditor; - else if ( rtype == typeid( IConverter* ).name() ) - type = component_t::Converter; - else if ( rtype == typeid( DataObject* ).name() ) - type = component_t::DataObject; - // handle possible problems with templated components - std::string name = boost::trim_copy( factoryName ); - - if ( type == component_t::IInterface ) { - /// not enough information... - /// skip it - continue; - } - - if ( type == component_t::Converter || type == component_t::DataObject ) { - /// no Properties, so don't bother create Configurables... - continue; + { + const auto ft = allowedFactories.find( info.factory.type().name() ); + if ( ft != allowedFactories.end() ) { + type = ft->second; + } else if ( factoryName == "ApplicationMgr" ) { + type = component_t::ApplicationMgr; + } else + continue; } - if ( type == component_t::Unknown ) { - LOG_WARNING << "Unknown (return) type [" << System::typeinfoName( rtype.c_str() ) << "] !!" - << " Component [" << factoryName << "] is skipped !"; - continue; - } + // handle possible problems with templated components + std::string name = boost::trim_copy( factoryName ); - LOG_INFO << " - component: " << info.className << " (" - << ( info.className != name ? ( name + ": " ) : std::string() ) << type << ")"; + const auto className = info.getprop( "ClassName" ); + LOG_INFO << " - component: " << className << " (" << ( className != name ? ( name + ": " ) : std::string() ) + << type << ")"; string cname = "DefaultName"; SmartIF prop; try { switch ( type ) { case component_t::Algorithm: - prop = SmartIF( Algorithm::Factory::create( factoryName, cname, svcLoc ) ); + prop = SmartIF( Algorithm::Factory::create( factoryName, cname, svcLoc ).release() ); break; case component_t::Service: - prop = SmartIF( Service::Factory::create( factoryName, cname, svcLoc ) ); + prop = SmartIF( Service::Factory::create( factoryName, cname, svcLoc ).release() ); break; case component_t::AlgTool: - prop = SmartIF( AlgTool::Factory::create( factoryName, cname, toString( type ), dummySvc ) ); + prop = + SmartIF( AlgTool::Factory::create( factoryName, cname, toString( type ), dummySvc ).release() ); // FIXME: AlgTool base class increase artificially by 1 the refcount. prop->release(); break; case component_t::Auditor: - prop = SmartIF( Auditor::Factory::create( factoryName, cname, svcLoc ) ); + prop = SmartIF( Auditor::Factory::create( factoryName, cname, svcLoc ).release() ); break; case component_t::ApplicationMgr: prop = SmartIF( svcLoc ); @@ -589,7 +587,6 @@ int configGenerator::genConfig( const Strings_t& libs, const string& userModule prop.reset(); } else { LOG_ERROR << "could not cast IInterface* object to an IProperty* !"; - LOG_ERROR << "return type from PluginSvc is [" << rtype << "]..."; LOG_ERROR << "NO Configurable will be generated for [" << name << "] !"; allGood = false; } diff --git a/GaudiKernel/tests/src/custom_factory.cpp b/GaudiKernel/tests/src/custom_factory.cpp index 4839ff5176..0da23f1866 100644 --- a/GaudiKernel/tests/src/custom_factory.cpp +++ b/GaudiKernel/tests/src/custom_factory.cpp @@ -9,7 +9,7 @@ #include "Gaudi/PluginService.h" struct IMyInterface { - typedef Gaudi::PluginService::Factory Factory; + using Factory = Gaudi::PluginService::Factory; }; struct MyImplementation : virtual public IMyInterface { diff --git a/GaudiPluginService/CMakeLists.txt b/GaudiPluginService/CMakeLists.txt index 39cc5c567b..4754b630ba 100644 --- a/GaudiPluginService/CMakeLists.txt +++ b/GaudiPluginService/CMakeLists.txt @@ -11,6 +11,12 @@ if(GAUDI_REFLEX_COMPONENT_ALIASES) add_definitions(-DGAUDI_REFLEX_COMPONENT_ALIASES) endif() +if(GAUDI_CXX_STANDARD STRLESS "c++17") + find_package(Boost REQUIRED) + set(includes INCLUDE_DIRS Boost) + set(libs Boost) +endif() + # Library gaudi_add_library(GaudiPluginService src/PluginServiceV2.cpp src/PluginServiceV1.cpp src/capi_pluginservice.cpp ${includes} diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceCommon.h b/GaudiPluginService/Gaudi/Details/PluginServiceCommon.h new file mode 100644 index 0000000000..2f875f310b --- /dev/null +++ b/GaudiPluginService/Gaudi/Details/PluginServiceCommon.h @@ -0,0 +1,47 @@ +#ifndef _GAUDI_PLUGIN_SERVICE_COMMON_H_ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ + +/// @author Marco Clemencic + +#ifndef GAUDI_PLUGIN_SERVICE_USE_V2 +#if defined( GAUDI_PLUGIN_SERVICE_V2 ) || !defined( GAUDI_PLUGIN_SERVICE_V1 ) +#define GAUDI_PLUGIN_SERVICE_V2_INLINE inline +#define GAUDI_PLUGIN_SERVICE_V1_INLINE +#define GAUDI_PLUGIN_SERVICE_USE_V2 1 +#else +#define GAUDI_PLUGIN_SERVICE_V2_INLINE +#define GAUDI_PLUGIN_SERVICE_V1_INLINE inline +#define GAUDI_PLUGIN_SERVICE_USE_V2 0 +#endif +#endif + +#if __GNUC__ >= 4 +#define GAUDIPS_HASCLASSVISIBILITY +#endif + +#if defined( GAUDIPS_HASCLASSVISIBILITY ) +#define GAUDIPS_IMPORT __attribute__( ( visibility( "default" ) ) ) +#define GAUDIPS_EXPORT __attribute__( ( visibility( "default" ) ) ) +#define GAUDIPS_LOCAL __attribute__( ( visibility( "hidden" ) ) ) +#else +#define GAUDIPS_IMPORT +#define GAUDIPS_EXPORT +#define GAUDIPS_LOCAL +#endif + +#ifdef GaudiPluginService_EXPORTS +#define GAUDIPS_API GAUDIPS_EXPORT +#else +#define GAUDIPS_API GAUDIPS_IMPORT +#endif + +#endif diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV1.h b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV1.h index 0835d62064..d44f523bac 100644 --- a/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV1.h +++ b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV1.h @@ -13,6 +13,8 @@ /// @author Marco Clemencic +#include "Gaudi/Details/PluginServiceCommon.h" + #include #include #include @@ -229,31 +231,27 @@ namespace Gaudi } } -#if !GAUDI_PLUGIN_SERVICE_USE_V2 - -#define _INTERNAL_FACTORY_REGISTER_CNAME( name, serial ) _register_##_##serial +#define _PS_V1_INTERNAL_FACTORY_REGISTER_CNAME( name, serial ) _register_##_##serial -#define _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, serial ) \ +#define _PS_V1_INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, serial ) \ namespace \ { \ - class _INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ + class _PS_V1_INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ { \ public: \ typedef factory s_t; \ typedef typecreator f_t; \ static s_t::FuncType creator() { return &f_t::create; } \ - _INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ + _PS_V1_INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ { \ using ::Gaudi::PluginService::v1::Details::Registry; \ Registry::instance().add( id, creator() ); \ } \ - } _INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ + } _PS_V1_INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ } -#define _INTERNAL_DECLARE_FACTORY( type, id, factory, serial ) \ - _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, ::Gaudi::PluginService::v1::Details::Factory, id, factory, \ - serial ) - -#endif +#define _PS_V1_INTERNAL_DECLARE_FACTORY( type, id, factory, serial ) \ + _PS_V1_INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, ::Gaudi::PluginService::v1::Details::Factory, id, factory, \ + serial ) #endif //_GAUDI_PLUGIN_SERVICE_DETAILS_H_ diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h index c5ab0f2868..22eedb3389 100644 --- a/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h +++ b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h @@ -13,68 +13,55 @@ /// @author Marco Clemencic +#include "Gaudi/Details/PluginServiceCommon.h" + +#if __cplusplus >= 201703 +#include +#else +#include +namespace std +{ + using boost::any; + using boost::any_cast; + using boost::bad_any_cast; +} +#endif + +#include #include +#include +#include #include #include #include #include #include -#include - namespace Gaudi { namespace PluginService { GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 { + /// \cond FWD_DECL + template + struct Factory; + /// \cond + + /// Implementation details of Gaudi::PluginService namespace Details { - /// Class providing default factory functions. - /// - /// The template argument T is the class to be created, while the methods - /// template argument S is the specific factory signature. - template - class Factory - { - public: - template - static typename S::ReturnType create( Args&&... args ) - { - return new T( std::forward( args )... ); - } - }; + template + struct Traits; - /// Function used to load a specific factory function. - /// @return the pointer to the factory function. - GAUDIPS_API - void* getCreator( const std::string& id, const std::string& type ); - - /// Convoluted implementation of getCreator with an embedded - /// reinterpret_cast, used to avoid the warning - ///
-        /// warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object
-        /// 
- /// It is an ugly trick but works.
- /// See: - ///
    - ///
  • http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
  • - ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#573
  • - ///
  • http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
  • - ///
- template - inline F getCreator( const std::string& id ) - { - union { - void* src; - F dst; - } p2p; - p2p.src = getCreator( id, typeid( F ).name() ); - return p2p.dst; - } + template + struct Traits { + using ReturnType = std::unique_ptr>; + using FactoryType = std::function; + }; /// Return a canonical name for type_info object (implementation borrowed - /// from GaudiKernel/System). + /// from GaudiKernel/System.h). GAUDIPS_API std::string demangle( const std::type_info& id ); @@ -85,108 +72,130 @@ namespace Gaudi return demangle( typeid( T ) ); } + /// Convert a generic `id` to `std::string` via `std::ostream::operator<<`. + template + inline std::string stringify_id( const ID& id ) + { + std::ostringstream o; + o << id; + return o.str(); + } + /// Specialized no-op conversion from `std::string` to `std::string`. + template <> + inline std::string stringify_id( const std::string& id ) + { + return id; + } + + /// Helper to print debug output in case of mismatched FactoryType. + void reportBadAnyCast( const std::type_info& factory_type, const std::string& id ); + + /// Simple logging class, just to provide a default implementation. + class GAUDIPS_API Logger + { + public: + enum Level { Debug = 0, Info = 1, Warning = 2, Error = 3 }; + Logger( Level level = Warning ) : m_level( level ) {} + virtual ~Logger() {} + inline Level level() const { return m_level; } + inline void setLevel( Level level ) { m_level = level; } + inline void info( const std::string& msg ) { report( Info, msg ); } + inline void debug( const std::string& msg ) { report( Debug, msg ); } + inline void warning( const std::string& msg ) { report( Warning, msg ); } + inline void error( const std::string& msg ) { report( Error, msg ); } + + private: + virtual void report( Level lvl, const std::string& msg ); + Level m_level; + }; + + /// Return the current logger instance. + GAUDIPS_API Logger& logger(); + /// Set the logger instance to use. + /// It must be a new instance and the ownership is passed to the function. + GAUDIPS_API void setLogger( Logger* logger ); + /// In-memory database of the loaded factories. class GAUDIPS_API Registry { public: - typedef std::string KeyType; + using KeyType = std::string; /// Type used for the properties implementation. - typedef std::map Properties; + using Properties = std::map; struct FactoryInfo { - FactoryInfo( std::string lib, void* p = nullptr, std::string t = "", std::string rt = "", - std::string cn = "", Properties props = Properties() ) - : library( std::move( lib ) ) - , ptr( p ) - , type( std::move( t ) ) - , rtype( std::move( rt ) ) - , className( std::move( cn ) ) - , properties( std::move( props ) ) - { - } - + std::any factory; std::string library; - void* ptr; - std::string type; - std::string rtype; - std::string className; Properties properties; - FactoryInfo& addProperty( const KeyType& k, std::string v ) + inline bool is_set() const { - properties[k] = std::move( v ); - return *this; +#if __cplusplus >= 201703 + return factory.has_value(); +#else + return !factory.empty(); +#endif } + Properties::mapped_type getprop( const Properties::key_type& name ) const; }; /// Type used for the database implementation. - typedef std::map FactoryMap; - - /// Retrieve the singleton instance of Registry. - static Registry& instance(); + using FactoryMap = std::map; - /// Add a factory to the database. - template - inline FactoryInfo& add( const I& id, typename F::FuncType ptr ) + /// Get the factory function for a given `id` from the registry. + template + F get( const KeyType& id ) { - union { - typename F::FuncType src; - void* dst; - } p2p; - p2p.src = ptr; - std::ostringstream o; - o << id; - return add( o.str(), p2p.dst, typeid( typename F::FuncType ).name(), - typeid( typename F::ReturnType ).name(), demangle() ); + const FactoryInfo& info = Registry::instance().getInfo( id, true ); +#ifdef GAUDI_REFLEX_COMPONENT_ALIASES + if ( !info.getprop( "ReflexName" ).empty() ) { + const std::string real_name = info.getprop( "ClassName" ); + logger().warning( "requesting factory via old name '" + id + "' use '" + + ( real_name.empty() ? "" : real_name ) + "' instead" ); + } +#endif + return std::any_cast( info.factory ); } - /// Retrieve the factory for the given id. - void* get( const std::string& id, const std::string& type ) const; + /// Retrieve the singleton instance of Registry. + static Registry& instance(); + + /// Add factory info to the registry (used internally by DeclareFactory). + FactoryInfo& add( const KeyType& id, FactoryInfo info ); - /// Retrieve the FactoryInfo object for an id. - const FactoryInfo& getInfo( const std::string& id ) const; + /// Retrieve the FactoryInfo object for an `id`. + const FactoryInfo& getInfo( const KeyType& id, const bool load = false ) const; - /// Add a property to an already existing FactoryInfo object (via its id.) - Registry& addProperty( const std::string& id, const std::string& k, const std::string& v ); + /// Add a property to an already existing FactoryInfo object (via its `id`). + Registry& addProperty( const KeyType& id, const KeyType& k, const std::string& v ); /// Return a list of all the known and loaded factories std::set loadedFactoryNames() const; /// Return the known factories (loading the list if not yet done). - inline const FactoryMap& factories() const - { - if ( !m_initialized ) const_cast( this )->initialize(); - return m_factories; - } + /// + /// At the first call, the internal database of known factories is + /// filled with the name of the libraries containing them, using the + /// ".components" files in the `LD_LIBRARY_PATH`. + const FactoryMap& factories() const; private: /// Private constructor for the singleton pattern. - /// At construction time, the internal database of known factories is - /// filled with the name of the libraries containing them, using the - /// ".components" files in the LD_LIBRARY_PATH. Registry(); /// Private copy constructor for the singleton pattern. - Registry( const Registry& ) : m_initialized( false ) {} - - /// Add a factory to the database. - FactoryInfo& add( const std::string& id, void* factory, const std::string& type, const std::string& rtype, - const std::string& className, const Properties& props = Properties() ); + Registry( const Registry& ) = delete; /// Return the known factories (loading the list if not yet done). - inline FactoryMap& factories() - { - if ( !m_initialized ) initialize(); - return m_factories; - } + FactoryMap& factories(); /// Initialize the registry loading the list of factories from the /// .component files in the library search path. void initialize(); /// Flag recording if the registry has been initialized or not. - bool m_initialized; + mutable std::once_flag m_initialized; /// Internal storage for factories. FactoryMap m_factories; @@ -195,30 +204,24 @@ namespace Gaudi mutable std::recursive_mutex m_mutex; }; - /// Simple logging class, just to provide a default implementation. - class GAUDIPS_API Logger - { - public: - enum Level { Debug = 0, Info = 1, Warning = 2, Error = 3 }; - Logger( Level level = Warning ) : m_level( level ) {} - virtual ~Logger() {} - inline Level level() const { return m_level; } - inline void setLevel( Level level ) { m_level = level; } - inline void info( const std::string& msg ) { report( Info, msg ); } - inline void debug( const std::string& msg ) { report( Debug, msg ); } - inline void warning( const std::string& msg ) { report( Warning, msg ); } - inline void error( const std::string& msg ) { report( Error, msg ); } - - private: - virtual void report( Level lvl, const std::string& msg ); - Level m_level; + /// Class providing default factory functions. + /// + /// The template argument T is the class to be created, while the methods + /// template argument S is the specific factory signature. + template + struct DefaultFactory; + template + struct DefaultFactory> { + inline typename Factory::ReturnType operator()( Args... args ) + { + return std::make_unique( std::forward( args )... ); + } }; - /// Return the current logger instance. - GAUDIPS_API Logger& logger(); - /// Set the logger instance to use. - /// It must be a new instance and the ownership is passed to the function. - GAUDIPS_API void setLogger( Logger* logger ); + /// Helper to get the name of the library containing a given pointer to function. + /// + /// Implementation borrowed from `DsoUtils.h` (genconf). + std::string getDSONameFor( void* fptr ); } /// Backward compatibility with Reflex. @@ -229,30 +232,9 @@ namespace Gaudi } } -#if GAUDI_PLUGIN_SERVICE_USE_V2 - -#define _INTERNAL_FACTORY_REGISTER_CNAME( name, serial ) _register_##_##serial - -#define _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, serial ) \ - namespace \ - { \ - class _INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ - { \ - public: \ - typedef factory s_t; \ - typedef typecreator f_t; \ - static s_t::FuncType creator() { return &f_t::create; } \ - _INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ - { \ - using ::Gaudi::PluginService::v2::Details::Registry; \ - Registry::instance().add( id, creator() ); \ - } \ - } _INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ - } - -#define _INTERNAL_DECLARE_FACTORY( type, id, factory, serial ) \ - _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, ::Gaudi::PluginService::v2::Details::Factory, id, factory, serial ) - -#endif +#define _PS_V2_INTERNAL_FACTORY_MAKE_REGISTER_CNAME_TOKEN( serial ) _register_##serial +#define _PS_V2_INTERNAL_FACTORY_MAKE_REGISTER_CNAME( serial ) \ + _PS_V2_INTERNAL_FACTORY_MAKE_REGISTER_CNAME_TOKEN( serial ) +#define _PS_V2_INTERNAL_FACTORY_REGISTER_CNAME _PS_V2_INTERNAL_FACTORY_MAKE_REGISTER_CNAME( __LINE__ ) #endif //_GAUDI_PLUGIN_SERVICE_DETAILS_H_ diff --git a/GaudiPluginService/Gaudi/PluginService.h b/GaudiPluginService/Gaudi/PluginService.h index 5e5f58498c..c40e00fb4b 100644 --- a/GaudiPluginService/Gaudi/PluginService.h +++ b/GaudiPluginService/Gaudi/PluginService.h @@ -14,37 +14,7 @@ /// @author Marco Clemencic /// See @ref GaudiPluginService-readme -#ifndef GAUDI_PLUGIN_SERVICE_USE_V2 -#if defined( GAUDI_PLUGIN_SERVICE_V2 ) || !defined( GAUDI_PLUGIN_SERVICE_V1 ) -#define GAUDI_PLUGIN_SERVICE_V2_INLINE inline -#define GAUDI_PLUGIN_SERVICE_V1_INLINE -#define GAUDI_PLUGIN_SERVICE_USE_V2 1 -#else -#define GAUDI_PLUGIN_SERVICE_V2_INLINE -#define GAUDI_PLUGIN_SERVICE_V1_INLINE inline -#define GAUDI_PLUGIN_SERVICE_USE_V2 0 -#endif -#endif - -#if __GNUC__ >= 4 -#define GAUDIPS_HASCLASSVISIBILITY -#endif - -#if defined( GAUDIPS_HASCLASSVISIBILITY ) -#define GAUDIPS_IMPORT __attribute__( ( visibility( "default" ) ) ) -#define GAUDIPS_EXPORT __attribute__( ( visibility( "default" ) ) ) -#define GAUDIPS_LOCAL __attribute__( ( visibility( "hidden" ) ) ) -#else -#define GAUDIPS_IMPORT -#define GAUDIPS_EXPORT -#define GAUDIPS_LOCAL -#endif - -#ifdef GaudiPluginService_EXPORTS -#define GAUDIPS_API GAUDIPS_EXPORT -#else -#define GAUDIPS_API GAUDIPS_IMPORT -#endif +#include "Gaudi/Details/PluginServiceCommon.h" #if GAUDI_PLUGIN_SERVICE_USE_V2 #include "Gaudi/PluginServiceV2.h" diff --git a/GaudiPluginService/Gaudi/PluginServiceV1.h b/GaudiPluginService/Gaudi/PluginServiceV1.h index 3a6c207010..e0873d20a5 100644 --- a/GaudiPluginService/Gaudi/PluginServiceV1.h +++ b/GaudiPluginService/Gaudi/PluginServiceV1.h @@ -19,24 +19,32 @@ #include #include -#if !GAUDI_PLUGIN_SERVICE_USE_V2 - -#define DECLARE_FACTORY_WITH_ID( type, id, factory ) _INTERNAL_DECLARE_FACTORY( type, id, factory, __LINE__ ) +#define _PS_V1_DECLARE_FACTORY_WITH_ID( type, id, factory ) \ + _PS_V1_INTERNAL_DECLARE_FACTORY( type, id, factory, __LINE__ ) -#define DECLARE_FACTORY( type, factory ) \ - DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::v1::Details::demangle(), factory ) +#define _PS_V1_DECLARE_FACTORY( type, factory ) \ + _PS_V1_DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::v1::Details::demangle(), factory ) -#define DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ - _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, __LINE__ ) +#define _PS_V1_DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ + _PS_V1_INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, __LINE__ ) -#define DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ - DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, ::Gaudi::PluginService::v1::Details::demangle(), \ - factory ) +#define _PS_V1_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ + _PS_V1_DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, \ + ::Gaudi::PluginService::v1::Details::demangle(), factory ) -#define DECLARE_COMPONENT( type ) DECLARE_FACTORY( type, type::Factory ) +#define _PS_V1_DECLARE_COMPONENT( type ) _PS_V1_DECLARE_FACTORY( type, type::Factory ) -#define DECLARE_COMPONENT_WITH_ID( type, id ) DECLARE_FACTORY_WITH_ID( type, id, type::Factory ) +#define _PS_V1_DECLARE_COMPONENT_WITH_ID( type, id ) _PS_V1_DECLARE_FACTORY_WITH_ID( type, id, type::Factory ) +#if !GAUDI_PLUGIN_SERVICE_USE_V2 +#define DECLARE_FACTORY_WITH_ID( type, id, factory ) _PS_V1_DECLARE_FACTORY_WITH_ID( type, id, factory ) +#define DECLARE_FACTORY( type, factory ) _PS_V1_DECLARE_FACTORY( type, factory ) +#define DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ + _PS_V1_DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) +#define DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ + _PS_V1_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) +#define DECLARE_COMPONENT( type ) _PS_V1_DECLARE_COMPONENT( type ) +#define DECLARE_COMPONENT_WITH_ID( type, id ) _PS_V1_DECLARE_COMPONENT_WITH_ID( type, id ) #endif namespace Gaudi diff --git a/GaudiPluginService/Gaudi/PluginServiceV2.h b/GaudiPluginService/Gaudi/PluginServiceV2.h index ad2751f8c6..f0e2f0fa41 100644 --- a/GaudiPluginService/Gaudi/PluginServiceV2.h +++ b/GaudiPluginService/Gaudi/PluginServiceV2.h @@ -12,73 +12,141 @@ \*****************************************************************************/ /// @author Marco Clemencic -/// @see @ref GaudiPluginService-readme +/// See @ref GaudiPluginService-readme #include +#include +#include #include +#include #include #include -#if GAUDI_PLUGIN_SERVICE_USE_V2 - -#define DECLARE_FACTORY_WITH_ID( type, id, factory ) _INTERNAL_DECLARE_FACTORY( type, id, factory, __LINE__ ) - -#define DECLARE_FACTORY( type, factory ) \ - DECLARE_FACTORY_WITH_ID( type, ::Gaudi::PluginService::v2::Details::demangle(), factory ) - -#define DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, id, factory ) \ - _INTERNAL_DECLARE_FACTORY_WITH_CREATOR( type, typecreator, id, factory, __LINE__ ) - -#define DECLARE_FACTORY_WITH_CREATOR( type, typecreator, factory ) \ - DECLARE_FACTORY_WITH_CREATOR_AND_ID( type, typecreator, ::Gaudi::PluginService::v2::Details::demangle(), factory ) - -#define DECLARE_COMPONENT( type ) DECLARE_FACTORY( type, type::Factory ) - -#define DECLARE_COMPONENT_WITH_ID( type, id ) DECLARE_FACTORY_WITH_ID( type, id, type::Factory ) - -#endif - namespace Gaudi { + /// See @ref GaudiPluginService-readme namespace PluginService { GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 { + /// \cond FWD_DECL + template + struct Factory; + /// \endcond + /// Class wrapping the signature for a factory with any number of arguments. template - class Factory - { - public: - typedef R ReturnType; - typedef R ( *FuncType )( Args&&... ); - - static ReturnType create( const std::string& id, Args... args ) - { - const FuncType c = Details::getCreator( id ); - return c ? ( *c )( std::forward( args )... ) : 0; - } + struct Factory { + using Traits = Details::Traits; + using ReturnType = typename Traits::ReturnType; + using FactoryType = typename Traits::FactoryType; + using ReturnValueType = R; + /// Function to call to create an instance of type identified by `id` and that uses this factory signature. template static ReturnType create( const T& id, Args... args ) { - std::ostringstream o; - o << id; - return create( o.str(), std::forward( args )... ); + try { + return Details::Registry::instance().get( Details::stringify_id( id ) )( + std::forward( args )... ); + } catch ( std::bad_any_cast& ) { + Details::reportBadAnyCast( typeid( FactoryType ), Details::stringify_id( id ) ); + return nullptr; + } } }; - class GAUDIPS_EXPORT Exception : public std::exception - { - public: - Exception( std::string msg ); - ~Exception() throw() override; - const char* what() const throw() override; + /// Helper to declare the factory implementation for a user defined type `T`. + /// + /// The basic use is: + /// ```cpp + /// namespace { + /// Gaudi::PluginService::DeclareFactory __some_random_name; + /// } + /// ``` + /// which is the equivalent of `DECLARE_COMPONENT( MyComponent )`. + /// + /// It's possible to specify a custom factory type (instead of the default type alias `MyComponent::Factory`): + /// ```cpp + /// namespace { + /// using namespace Gaudi::PluginService; + /// DeclareFactory> __some_random_name; + /// } + /// ``` + /// + /// We can pass arguments to the constructor to use a custom factory function, or a special _id_, or properties: + /// ```cpp + /// namespace { + /// using namespace Gaudi::PluginService; + /// DeclareFactory __some_random_name( "special-id", + /// []() -> MyComponent::Factory::ReturnType { + /// return std::make_unique( "special-id" ); + /// }, + /// {{"MyProperty", "special"}} ); + /// } + /// ``` + template + struct DeclareFactory { + using DefaultFactory = Details::DefaultFactory; + + DeclareFactory( typename F::FactoryType f = DefaultFactory{}, Details::Registry::Properties props = {} ) + : DeclareFactory( Details::demangle(), std::move( f ), std::move( props ) ) + { + } + + DeclareFactory( const std::string& id, typename F::FactoryType f = DefaultFactory{}, + Details::Registry::Properties props = {} ) + { + using Details::Registry; + + if ( props.find( "ClassName" ) == end( props ) ) props.emplace( "ClassName", Details::demangle() ); + + Registry::instance().add( id, {std::move( f ), libraryName(), std::move( props )} ); + } + + DeclareFactory( Details::Registry::Properties props ) : DeclareFactory( DefaultFactory{}, std::move( props ) ) + { + } private: - std::string m_msg; + /// Helper to record the name of the library that declare the factory. + static std::string libraryName() { return Details::getDSONameFor( reinterpret_cast( libraryName ) ); } }; } } } +#define _PS_V2_DECLARE_COMPONENT( type ) \ + namespace \ + { \ + ::Gaudi::PluginService::v2::DeclareFactory _PS_V2_INTERNAL_FACTORY_REGISTER_CNAME{}; \ + } + +#define _PS_V2_DECLARE_COMPONENT_WITH_ID( type, id ) \ + namespace \ + { \ + ::Gaudi::PluginService::v2::DeclareFactory _PS_V2_INTERNAL_FACTORY_REGISTER_CNAME{ \ + ::Gaudi::PluginService::v2::Details::stringify_id( id )}; \ + } + +#define _PS_V2_DECLARE_FACTORY( type, factory ) \ + namespace \ + { \ + ::Gaudi::PluginService::v2::DeclareFactory _PS_V2_INTERNAL_FACTORY_REGISTER_CNAME{}; \ + } + +#define _PS_V2_DECLARE_FACTORY_WITH_ID( type, id, factory ) \ + namespace \ + { \ + ::Gaudi::PluginService::v2::DeclareFactory _PS_V2_INTERNAL_FACTORY_REGISTER_CNAME{ \ + ::Gaudi::PluginService::v2::Details::stringify_id( id )}; \ + } + +#if GAUDI_PLUGIN_SERVICE_USE_V2 +#define DECLARE_COMPONENT( type ) _PS_V2_DECLARE_COMPONENT( type ) +#define DECLARE_COMPONENT_WITH_ID( type, id ) _PS_V2_DECLARE_COMPONENT_WITH_ID( type, id ) +#define DECLARE_FACTORY( type, factory ) _PS_V2_DECLARE_FACTORY( type, factory ) +#define DECLARE_FACTORY_WITH_ID( type, id, factory ) _PS_V2_DECLARE_FACTORY_WITH_ID( type, id, factory ) +#endif + #endif //_GAUDI_PLUGIN_SERVICE_H_ diff --git a/GaudiPluginService/doc/README.md b/GaudiPluginService/doc/README.md index f8eab973b1..a8d84d04a3 100644 --- a/GaudiPluginService/doc/README.md +++ b/GaudiPluginService/doc/README.md @@ -1,8 +1,6 @@ -Gaudi Plugin Service Instructions {#GaudiPluginService-readme} -================================== +# Gaudi::PluginService {#GaudiPluginService-readme} -Introduction ------------- +## Introduction The Gaudi Plugin Service is a small tool to add to a C++ application the possibility of dynamically instantiate (via _factories_) objects from classes @@ -11,118 +9,180 @@ defined in plug-in (or component) libraries. While being part of Gaudi, it only depends on a Posix system (support for other systems is possible, but very low priority). - -Usage ------- +## Usage To be able to use plug-ins from an application you need: - -- a base class (abstract or not) from a library -- a library that provides a class that inherits from the base class +- a base class (abstract or not) from a library +- a library that provides a class that inherits from the base class In the base class you should declare the signature of the the factory for your derived classes. For example, if your base class is called `Foo` and you want to instantiate the derived classes with one `std::string` argument, you can write something like: +```cpp +#include +#include +class Foo { +public: + using Factory = Gaudi::PluginService::Factory; - #include - #include - class Foo { - public: - typedef Gaudi::PluginService::Factory1 Factory; - - /// Constructor - Foo(const std::string& name); + /// Constructor + Foo( const std::string& name ); - // ... - }; + // ... +}; +``` -The templated class `Gaudi::PluginService::Factory1` takes as first template -argument the return type of the factory and as second argument the type of the -first argument of the factory function (with all the required qualifiers -explicit). There are several variants of the class for different number of -arguments required by the constructor (`Factory0`, `Factory1`, `Factory2`, ...). +The templated class `Gaudi::PluginService::Factory` takes as template +argument the _ideal_ signature of the factory. The in the above example, the +actual signature of the factory is `std::unique_ptr(const std::string&)`, +but we declare is as returning `Foo*` for brevity. The plug-in class `Bar` defined in the dynamically loaded library will require a declaration to the Plugin Service to use it, so in the source file you have to have something like: - - #include "Bar.h" - DECLARE_COMPONENT(Bar) +```cpp +#include "Bar.h" +DECLARE_COMPONENT( Bar ) +``` The library with `Foo` and the library with `Bar` will have to be linked against the library `libGaudiPluginService.so`. To enable the automatic discovery of plugins, the library with `Bar` must be processed by the program `listcomponents` and the output must be stored in a -file with extension `.comonents` in a directory in the `LD_LIBRARY_PATH`. +file with extension `.components` in a directory in the `LD_LIBRARY_PATH`. For example, if the `lib` directory contains `libBar.so` and it is specified in the `LD_LIBRARY_PATH`, you can call the commands: - - listcomponents libBar.so >> lib/MyApp.components +```sh +listcomponents lib/libBar.so >> lib/MyApp.components +``` Note that the `.components` file does not need to be in the same directory as `libBar.so`. The application code, linked against the library providing `Foo` can now instantiate objects of class `Bar` like this: +```cpp +#include "Foo.h" - #include "Foo.h" - - // ... - Foo* myBar = Foo::Factory::create("Bar", "myBar"); - // ... +// ... +std::unique_ptr myBar = Foo::Factory::create( "Bar", "myBar" ); +// ... +``` where the first argument to the function `create` is the name of the class you want to instantiate, and the other arguments are passed to the constructor of the class. - -Special cases -------------- +## Special cases ### Factory aliases Together with the simple usage described above, the Gaudi Plugin Service allows -you to give to use aliases to refer to the plug-in class. +you to use aliases to refer to the plug-in class. For example, for a templated plug-in class you may have: - - #include "TemplatedBar.h" - typedef TemplatedBar > MyBar; - DECLARE_COMPONENT(MyBar) +```cpp +#include "TemplatedBar.h" +typedef TemplatedBar> MyBar; +DECLARE_COMPONENT( MyBar ) +``` but to instantiate it you must call - - Foo* b = Foo::Factory::create("TemplatedBar >", - "MyTemplatedBar"); +```cpp +auto b = Foo::Factory::create( "TemplatedBar >", + "MyTemplatedBar" ); +``` Which is error prone and unreadable, but you can declare the component class with and _id_ (an alias): - - DECLARE_COMPONENT_WITH_ID(MyBar, "MyBar") +```cpp +DECLARE_COMPONENT_WITH_ID( MyBar, "MyBar" ) +``` (note that the _id_ must support the `<<` operator of `std::ostream`). The call in the application becomes: - - Foo* b = Foo::Factory::create("MyBar", "MyTemplatedBar"); - +```cpp +auto b = Foo::Factory::create( "MyBar", "MyTemplatedBar" ); +``` ### Namespaces -You cannot use namespace delimiters in the call to `DECLARE_COMPONENT`, but you -can still use namespaces for you component classes. For example, if you have the -class `Baz::Fun` you can declare it as a component class in any of the following -ways: +When dealing with components in namespaces, you have several ways to invoke +`DECLARE_COMPONENT`. For example, if you have the class `Baz::Fun` you can +declare it as a component class in any of the following ways: +```cpp +DECLARE_COMPONENT( Baz::Fun ) - using Baz::Fun; - DECLARE_COMPONENT(Fun) +using Baz::Fun; +DECLARE_COMPONENT( Fun ) - namespace Baz { - DECLARE_COMPONENT(Fun) - } +namespace Baz { + DECLARE_COMPONENT( Fun ) +} - typedef Baz::Fun BF; - DECLARE_COMPONENT(BF) +typedef Baz::Fun BF; +DECLARE_COMPONENT( BF ) +``` In all cases the name of the factory to be passed to the `create` function will -be "Baz::Fun". +be `Baz::Fun`. + +### Custom Factories + +When using `DECLARE_COMPONENT`, we register as factory for our class a function +equivalent to +```cpp +std::unique_ptr factory(Args... args) { + return std::make_unique(args...); +} +``` +but it's possible to use custom factory functions. This is a rather convoluted +example: +```cpp +// -- declaration -- +struct MyInterface { + virtual ~MyInterface() = default; + + virtual const std::string& name() const = 0; +}; + +struct BaseSetupHelper; + +struct MyBase : MyInterface { + using Factory = Gaudi::PluginService::Factory; + + const std::string& name() const override { return m_name; } +private: + friend BaseSetupHelper; + std::string m_name; +}; + +// -- implementation -- +struct MyComponent : MyBase { + MyComponent() {} +}; + +struct BaseSetupHelper { + static void setName( MyBase* base, const std::string& name ) { base->m_name = name; } +}; + +namespace +{ + std::unique_ptr creator( const std::string& name ) + { + auto p = std::make_unique(); + BaseSetupHelper::setName( p.get(), name ); + return std::move( p ); + } + Gaudi::PluginService::DeclareFactory _{creator}; +} + +// -- use -- +void useComponent() +{ + auto c = MyBase::Factory::create( "MyComponent", "TheName" ); + // ... +} + +``` diff --git a/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py b/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py index 23bb4bdd7f..e8fd4d8c29 100644 --- a/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py +++ b/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py @@ -45,7 +45,7 @@ def _get_filename(): _libname = _get_filename() -_lib = ctypes.cdll.LoadLibrary(_libname) +_lib = ctypes.CDLL(_libname, ctypes.RTLD_GLOBAL) class Registry(ctypes.Structure): @@ -111,10 +111,6 @@ class Factory(ctypes.Structure): def type(self): return _lib.cgaudi_factory_get_type(self) - @property - def rtype(self): - return _lib.cgaudi_factory_get_rtype(self) - @property def classname(self): return _lib.cgaudi_factory_get_classname(self) @@ -131,14 +127,13 @@ class Factory(ctypes.Structure): def load(self): '''load the C++ library hosting this factory ''' - return ctypes.cdll.LoadLibrary(self.library) + return ctypes.CDLL(self.library, ctypes.RTLD_GLOBAL) def __repr__(self): - return "" % ( + return "" % ( self._id, self.library, self.type, - self.rtype, self.classname, len(self.properties), ) @@ -193,11 +188,6 @@ _functions_list = [ ctypes.c_char_p, ), - ("cgaudi_factory_get_rtype", - [Factory], - ctypes.c_char_p, - ), - ("cgaudi_factory_get_classname", [Factory], ctypes.c_char_p, diff --git a/GaudiPluginService/src/PluginServiceV2.cpp b/GaudiPluginService/src/PluginServiceV2.cpp index 0806f257ef..f7eb3ee70f 100644 --- a/GaudiPluginService/src/PluginServiceV2.cpp +++ b/GaudiPluginService/src/PluginServiceV2.cpp @@ -26,6 +26,29 @@ #include #include +#ifdef _GNU_SOURCE +#include +#include +#endif + +#if __GNUC__ >= 8 +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif + +#if __GNUC__ >= 7 +#include +#else +#include +namespace std +{ + using experimental::string_view; +} +#endif + #define REG_SCOPE_LOCK std::lock_guard _guard( m_mutex ); namespace @@ -38,46 +61,19 @@ namespace namespace { - // string trimming functions taken from - // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring - - constexpr struct is_space_t { - bool operator()( int i ) const { return std::isspace( i ); } - } is_space{}; - - // trim from start - static inline std::string& ltrim( std::string& s ) + inline std::string_view trim( std::string_view s ) { - s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), is_space ) ); - return s; - } + const auto isspace = []( const char c ) -> bool { return std::isspace( c ); }; - // trim from end - static inline std::string& rtrim( std::string& s ) - { - s.erase( std::find_if_not( s.rbegin(), s.rend(), is_space ).base(), s.end() ); - return s; + const auto p0 = std::find_if_not( begin( s ), end( s ), isspace ) - begin( s ); + const auto p1 = std::find_if_not( rbegin( s ), rend( s ), isspace ) - rbegin( s ); + + return s.substr( p0, s.size() - p0 - p1 ); } - // trim from both ends - static inline std::string& trim( std::string& s ) { return ltrim( rtrim( s ) ); } } namespace { - /// Helper function used to set values in FactoryInfo data members only - /// if the original value is empty and reporting warnings in case of - /// inconsistencies. - inline void factoryInfoSetHelper( std::string& dest, const std::string value, const std::string& desc, - const std::string& id ) - { - if ( dest.empty() ) { - dest = value; - } else if ( dest != value ) { - Gaudi::PluginService::Details::logger().warning( "new factory loaded for '" + id + "' with different " + desc + - ": " + dest + " != " + value ); - } - } - struct OldStyleCnv { std::string name; void operator()( const char c ) @@ -119,17 +115,8 @@ namespace Gaudi { GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 { - Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {} - Exception::~Exception() throw() {} - const char* Exception::what() const throw() { return m_msg.c_str(); } - namespace Details { - void* getCreator( const std::string& id, const std::string& type ) - { - return Registry::instance().get( id, type ); - } - std::string demangle( const std::string& id ) { int status; @@ -154,13 +141,31 @@ namespace Gaudi return r; } - Registry::Registry() : m_initialized( false ) {} + void reportBadAnyCast( const std::type_info& factory_type, const std::string& id ) + { + if ( logger().level() <= Logger::Debug ) { + std::stringstream msg; + const auto& info = Registry::instance().getInfo( id ); + msg << "bad any_cast: requested factory " << id << " of type " << demangle( factory_type ) << ", got "; + if ( info.is_set() ) + msg << demangle( info.factory.type() ) << " from " << info.library; + else + msg << "nothing"; + logger().debug( msg.str() ); + } + } + + Registry::Properties::mapped_type Registry::FactoryInfo::getprop( const Properties::key_type& name ) const + { + auto p = properties.find( name ); + return ( p != end( properties ) ) ? p->second : Properties::mapped_type{}; + } + + Registry::Registry() {} void Registry::initialize() { REG_SCOPE_LOCK - if ( m_initialized ) return; - m_initialized = true; #if defined( _WIN32 ) const char* envVar = "PATH"; const char sep = ';'; @@ -171,67 +176,55 @@ namespace Gaudi const char* envVar = "LD_LIBRARY_PATH"; const char sep = ':'; #endif - char* search_path = ::getenv( envVar ); - if ( search_path ) { + std::string_view search_path = std::getenv( envVar ); + if ( !search_path.empty() ) { logger().debug( std::string( "searching factories in " ) + envVar ); - std::string path( search_path ); - std::string::size_type pos = 0; - std::string::size_type newpos = 0; - while ( pos != std::string::npos ) { - std::string dirName; - // get the next entry in the path - newpos = path.find( sep, pos ); - if ( newpos != std::string::npos ) { - dirName = path.substr( pos, newpos - pos ); - pos = newpos + 1; - } else { - dirName = path.substr( pos ); - pos = newpos; - } - logger().debug( std::string( " looking into " ) + dirName ); + + std::string_view::size_type start_pos = 0, end_pos = 0; + while ( start_pos != std::string_view::npos ) { + + end_pos = search_path.find( sep, start_pos + 1 ); + fs::path dirName = +#if __GNUC__ >= 7 + search_path.substr( start_pos + 1, end_pos - start_pos - 1 ); +#else + std::string{search_path.substr( start_pos + 1, end_pos - start_pos - 1 )}; +#endif + start_pos = end_pos; + + logger().debug( " looking into " + dirName.string() ); // look for files called "*.components" in the directory - DIR* dir = opendir( dirName.c_str() ); - if ( dir ) { - struct dirent* entry; - while ( ( entry = readdir( dir ) ) ) { - std::string name( entry->d_name ); - // check if the file name ends with ".components" - std::string::size_type extpos = name.find( ".components" ); - if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) { - std::string fullPath = ( dirName + '/' + name ); - { // check if it is a regular file - struct stat buf; - stat( fullPath.c_str(), &buf ); - if ( !S_ISREG( buf.st_mode ) ) continue; - } + if ( is_directory( dirName ) ) { + for ( auto& p : fs::directory_iterator( dirName ) ) { + if ( p.path().extension() == ".components" && is_regular_file( p.path() ) ) { // read the file - logger().debug( std::string( " reading " ) + name ); + const auto& fullPath = p.path().string(); + logger().debug( " reading " + p.path().filename().string() ); std::ifstream factories{fullPath}; - std::string line; + std::string buff; int factoriesCount = 0; int lineCount = 0; while ( !factories.eof() ) { ++lineCount; - std::getline( factories, line ); - trim( line ); + std::getline( factories, buff ); + const auto line = trim( buff ); // skip empty lines and lines starting with '#' if ( line.empty() || line[0] == '#' ) continue; // look for the separator - auto pos = line.find( ':' ); - if ( pos == std::string::npos ) { + const auto pos = line.find( ':' ); + if ( pos == std::string_view::npos ) { logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) ); continue; } - const std::string lib( line, 0, pos ); - const std::string fact( line, pos + 1 ); - m_factories.emplace( fact, FactoryInfo( lib ) ); + const std::string lib{line.substr( 0, pos )}; + const std::string fact{line.substr( pos + 1 )}; + m_factories.emplace( fact, FactoryInfo{{}, lib, {{"ClassName", fact}}} ); #ifdef GAUDI_REFLEX_COMPONENT_ALIASES // add an alias for the factory using the Reflex convention std::string old_name = old_style_name( fact ); if ( fact != old_name ) { - FactoryInfo old_info( lib ); - old_info.properties["ReflexName"] = "true"; - m_factories.emplace( old_name, old_info ); + m_factories.emplace( old_name, + FactoryInfo{{}, lib, {{"ReflexName", "true"}, {"ClassName", fact}}} ); } #endif ++factoriesCount; @@ -241,81 +234,80 @@ namespace Gaudi } } } - closedir( dir ); } } } } - Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type, - const std::string& rtype, const std::string& className, - const Properties& props ) + const Registry::FactoryMap& Registry::factories() const + { + std::call_once( m_initialized, &Registry::initialize, const_cast( this ) ); + return m_factories; + } + + Registry::FactoryMap& Registry::factories() + { + std::call_once( m_initialized, &Registry::initialize, const_cast( this ) ); + return m_factories; + } + + Registry::FactoryInfo& Registry::add( const KeyType& id, FactoryInfo info ) { REG_SCOPE_LOCK FactoryMap& facts = factories(); - auto entry = facts.find( id ); + +#ifdef GAUDI_REFLEX_COMPONENT_ALIASES + // add an alias for the factory using the Reflex convention + const auto old_name = old_style_name( id ); + if ( id != old_name ) { + auto new_info = info; + + new_info.properties["ReflexName"] = "true"; + + add( old_name, new_info ); + } +#endif + + auto entry = facts.find( id ); if ( entry == facts.end() ) { // this factory was not known yet - entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first; + entry = facts.emplace( id, std::move( info ) ).first; } else { // do not replace an existing factory with a new one - if ( !entry->second.ptr ) entry->second.ptr = factory; - factoryInfoSetHelper( entry->second.type, type, "type", id ); - factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id ); - factoryInfoSetHelper( entry->second.className, className, "class", id ); + if ( !entry->second.is_set() ) entry->second = std::move( info ); } -#ifdef GAUDI_REFLEX_COMPONENT_ALIASES - // add an alias for the factory using the Reflex convention - std::string old_name = old_style_name( id ); - if ( id != old_name ) - add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true"; -#endif return entry->second; } - void* Registry::get( const std::string& id, const std::string& type ) const + const Registry::FactoryInfo& Registry::getInfo( const KeyType& id, const bool load ) const { REG_SCOPE_LOCK - const FactoryMap& facts = factories(); - auto f = facts.find( id ); + static const FactoryInfo unknown = {{}, "unknown", {}}; + const FactoryMap& facts = factories(); + auto f = facts.find( id ); + if ( f != facts.end() ) { -#ifdef GAUDI_REFLEX_COMPONENT_ALIASES - const Properties& props = f->second.properties; - if ( props.find( "ReflexName" ) != props.end() ) - logger().warning( "requesting factory via old name '" + id + "'" - "use '" + - f->second.className + "' instead" ); -#endif - if ( !f->second.ptr ) { + if ( load && !f->second.is_set() ) { if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) { logger().warning( "cannot load " + f->second.library + " for factory " + id ); char* dlmsg = dlerror(); if ( dlmsg ) logger().warning( dlmsg ); - return nullptr; + return unknown; } f = facts.find( id ); // ensure that the iterator is valid } - if ( f->second.type == type ) return f->second.ptr; - logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) + - " instead of " + demangle( type ) ); + return f->second; + } else { + return unknown; } - return nullptr; // factory not found } - const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const + Registry& Registry::addProperty( const KeyType& id, const KeyType& k, const std::string& v ) { REG_SCOPE_LOCK - static const FactoryInfo unknown( "unknown" ); - const FactoryMap& facts = factories(); - auto f = facts.find( id ); - return ( f != facts.end() ) ? f->second : unknown; - } + FactoryMap& facts = factories(); + auto f = facts.find( id ); - Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) - { - REG_SCOPE_LOCK - FactoryMap& facts = factories(); - auto f = facts.find( id ); if ( f != facts.end() ) f->second.properties[k] = v; return *this; } @@ -325,7 +317,7 @@ namespace Gaudi REG_SCOPE_LOCK std::set l; for ( const auto& f : factories() ) { - if ( f.second.ptr ) l.insert( f.first ); + if ( f.second.is_set() ) l.insert( f.first ); } return l; } @@ -342,6 +334,23 @@ namespace Gaudi Logger& logger() { return *s_logger; } void setLogger( Logger* logger ) { s_logger.reset( logger ); } + // This chunk of code was taken from GaudiKernel (genconf) DsoUtils.h + std::string getDSONameFor( void* fptr ) + { +#ifdef _GNU_SOURCE + Dl_info info; + if ( dladdr( fptr, &info ) == 0 ) return ""; + + auto pos = std::strrchr( info.dli_fname, '/' ); + if ( pos ) + ++pos; + else + pos = info.dli_fname; + return pos; +#else + return ""; +#endif + } } // namespace Details void SetDebug( int debugLevel ) diff --git a/GaudiPluginService/src/capi_pluginservice.cpp b/GaudiPluginService/src/capi_pluginservice.cpp index f27f6fb824..51658c1019 100644 --- a/GaudiPluginService/src/capi_pluginservice.cpp +++ b/GaudiPluginService/src/capi_pluginservice.cpp @@ -38,20 +38,18 @@ const char* cgaudi_factory_get_library( cgaudi_factory_t self ) const char* cgaudi_factory_get_type( cgaudi_factory_t self ) { - Registry& reg = Registry::instance(); - return reg.getInfo( self.id ).type.c_str(); -} - -const char* cgaudi_factory_get_rtype( cgaudi_factory_t self ) -{ - Registry& reg = Registry::instance(); - return reg.getInfo( self.id ).rtype.c_str(); + Registry& reg = Registry::instance(); + static std::string cache; + cache = demangle( reg.getInfo( self.id ).factory.type() ); + return cache.c_str(); } const char* cgaudi_factory_get_classname( cgaudi_factory_t self ) { - Registry& reg = Registry::instance(); - return reg.getInfo( self.id ).className.c_str(); + Registry& reg = Registry::instance(); + static std::string cache; + cache = reg.getInfo( self.id ).getprop( "ClassName" ); + return cache.c_str(); } int cgaudi_factory_get_property_size( cgaudi_factory_t self ) diff --git a/GaudiPluginService/src/capi_pluginservice.h b/GaudiPluginService/src/capi_pluginservice.h index 884cbfabf4..9949d43a6a 100644 --- a/GaudiPluginService/src/capi_pluginservice.h +++ b/GaudiPluginService/src/capi_pluginservice.h @@ -65,9 +65,6 @@ const char* cgaudi_factory_get_library( cgaudi_factory_t self ); CGAUDI_API const char* cgaudi_factory_get_type( cgaudi_factory_t self ); -CGAUDI_API -const char* cgaudi_factory_get_rtype( cgaudi_factory_t self ); - CGAUDI_API const char* cgaudi_factory_get_classname( cgaudi_factory_t self ); diff --git a/GaudiPluginService/tests/src/LegacyUseCases.cpp b/GaudiPluginService/tests/src/LegacyUseCases.cpp index 688471f03c..de39dc2b61 100644 --- a/GaudiPluginService/tests/src/LegacyUseCases.cpp +++ b/GaudiPluginService/tests/src/LegacyUseCases.cpp @@ -44,18 +44,18 @@ class Component1 : public Base #define _INTERNAL_DECLARE_FACTORY_WITH_CREATOR_AND_PROPS( type, typecreator, id, factory, serial ) \ namespace \ { \ - class _INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ + class _PS_V1_INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ { \ public: \ typedef factory s_t; \ typedef typecreator f_t; \ static s_t::FuncType creator() { return &f_t::create; } \ - _INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ + _PS_V1_INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ { \ using ::Gaudi::PluginService::Details::Registry; \ Registry::instance().add( id, creator() ).addProperty( "name", #type ); \ } \ - } _INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ + } _PS_V1_INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ } DECLARE_COMPONENT_WITH_PROPS( Component1 ) diff --git a/GaudiPluginService/tests/src/UseCases.cpp b/GaudiPluginService/tests/src/UseCases.cpp index 66932aed1c..5a23e894b2 100644 --- a/GaudiPluginService/tests/src/UseCases.cpp +++ b/GaudiPluginService/tests/src/UseCases.cpp @@ -22,7 +22,7 @@ class Base { public: - typedef Gaudi::PluginService::Factory Factory; + typedef Gaudi::PluginService::Factory Factory; virtual ~Base() {} }; class Component0 : public Base @@ -33,29 +33,11 @@ DECLARE_COMPONENT( Component0 ) class Component1 : public Base { }; -#define DECLARE_COMPONENT_WITH_PROPS( type ) DECLARE_FACTORY_WITH_PROPS( type, type::Factory ) -#define DECLARE_FACTORY_WITH_PROPS( type, factory ) \ - DECLARE_FACTORY_WITH_ID_AND_PROPS( type, ::Gaudi::PluginService::Details::demangle(), factory ) -#define DECLARE_FACTORY_WITH_ID_AND_PROPS( type, id, factory ) \ - _INTERNAL_DECLARE_FACTORY_WITH_PROPS( type, id, factory, __LINE__ ) -#define _INTERNAL_DECLARE_FACTORY_WITH_PROPS( type, id, factory, serial ) \ - _INTERNAL_DECLARE_FACTORY_WITH_CREATOR_AND_PROPS( type, ::Gaudi::PluginService::Details::Factory, id, factory, \ - serial ) -#define _INTERNAL_DECLARE_FACTORY_WITH_CREATOR_AND_PROPS( type, typecreator, id, factory, serial ) \ + +#define DECLARE_COMPONENT_WITH_PROPS( type ) \ namespace \ { \ - class _INTERNAL_FACTORY_REGISTER_CNAME( type, serial ) \ - { \ - public: \ - typedef factory s_t; \ - typedef typecreator f_t; \ - static s_t::FuncType creator() { return &f_t::create; } \ - _INTERNAL_FACTORY_REGISTER_CNAME( type, serial )() \ - { \ - using ::Gaudi::PluginService::Details::Registry; \ - Registry::instance().add( id, creator() ).addProperty( "name", #type ); \ - } \ - } _INTERNAL_FACTORY_REGISTER_CNAME( s_##type, serial ); \ + ::Gaudi::PluginService::DeclareFactory _INTERNAL_FACTORY_REGISTER_CNAME{{{"name", #type}}}; \ } DECLARE_COMPONENT_WITH_PROPS( Component1 ) @@ -64,16 +46,21 @@ DECLARE_COMPONENT_WITH_PROPS( Component1 ) class Base2 { public: - typedef Gaudi::PluginService::Factory Factory; + typedef Gaudi::PluginService::Factory Factory; virtual ~Base2() {} + virtual void abstractMethod() = 0; }; + class Component2 : public Base2 { public: Component2( std::string _s, int _i ) : i( _i ), s( std::move( _s ) ) {} + void abstractMethod() override {} + int i; std::string s; }; + DECLARE_COMPONENT( Component2 ) // namespaces @@ -88,21 +75,30 @@ namespace Test class ComponentC : public Base { }; + class ComponentD : public Base + { + }; } +DECLARE_COMPONENT( Test::ComponentA ) + namespace { - using Test::ComponentA; - DECLARE_COMPONENT( ComponentA ) + using Test::ComponentB; + DECLARE_COMPONENT( ComponentB ) } -DECLARE_COMPONENT( Test::ComponentB ) - namespace Test { DECLARE_COMPONENT( ComponentC ) } +namespace +{ + using TC = Test::ComponentD; + DECLARE_COMPONENT( TC ) +} + // using ids DECLARE_COMPONENT_WITH_ID( Component2, "Id2" ) DECLARE_COMPONENT_WITH_ID( Test::ComponentB, "B" ) @@ -111,28 +107,65 @@ DECLARE_COMPONENT_WITH_ID( Test::ComponentB, "B" ) DECLARE_FACTORY_WITH_ID( Test::ComponentA, "A", Base::Factory ) // custom factory example +// -- declaration -- +struct MyInterface { + virtual ~MyInterface() = default; + + virtual const std::string& name() const = 0; +}; + +struct BaseSetupHelper; + +struct MyBase : MyInterface { + using Factory = Gaudi::PluginService::Factory; + + const std::string& name() const override { return m_name; } +private: + friend BaseSetupHelper; + std::string m_name; +}; + +// -- implementation -- +struct MyComponent : MyBase { + MyComponent() {} +}; + +struct BaseSetupHelper { + static void setName( MyBase* base, const std::string& name ) { base->m_name = name; } +}; + namespace { - bool _custom_factory_called = false; + std::unique_ptr creator( const std::string& name ) + { + auto p = std::make_unique(); + BaseSetupHelper::setName( p.get(), name ); + return std::move( p ); + } + Gaudi::PluginService::DeclareFactory _{creator}; +} - struct CompWithCustomFactory : Base { - }; +// -- use -- +void useComponent() +{ + auto c = MyBase::Factory::create( "MyComponent", "TheName" ); + // ... +} - class _register__CompWithCustomFactory - { - public: - typedef Base::Factory s_t; - static Base* creator() - { - _custom_factory_called = true; - return new CompWithCustomFactory{}; - } - _register__CompWithCustomFactory() - { - using ::Gaudi::PluginService::Details::Registry; - Registry::instance().add( "CompWithCustomFactory", creator ); - } - } _register__CompWithCustomFactory; +// factory from lambda +namespace SpecialId +{ + struct MyComponent : Base { + MyComponent( std::string n ) : name{std::move( n )} {} + + std::string name; + }; + using namespace Gaudi::PluginService; + DeclareFactory __some_random_name( "special-id", + []() -> MyComponent::Factory::ReturnType { + return std::make_unique( "special-id" ); + }, + {{"MyProperty", "special"}} ); } // Tests @@ -144,10 +177,10 @@ BOOST_AUTO_TEST_CASE( basic ) { BOOST_CHECK( Base::Factory::create( "Component0" BOOST_AUTO_TEST_CASE( basic_with_args ) { - Base2* instance = Base2::Factory::create( "Component2", "hello", 2 ); + auto instance = Base2::Factory::create( "Component2", "hello", 2 ); BOOST_CHECK( instance != nullptr ); - Component2* c2 = dynamic_cast( instance ); + auto c2 = dynamic_cast( instance.get() ); BOOST_REQUIRE( c2 != nullptr ); BOOST_CHECK( c2->i == 2 ); BOOST_CHECK( c2->s == "hello" ); @@ -158,6 +191,7 @@ BOOST_AUTO_TEST_CASE( namespaces ) BOOST_CHECK( Base::Factory::create( "Test::ComponentA" ) != nullptr ); BOOST_CHECK( Base::Factory::create( "Test::ComponentB" ) != nullptr ); BOOST_CHECK( Base::Factory::create( "Test::ComponentC" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentD" ) != nullptr ); } BOOST_AUTO_TEST_CASE( ids ) @@ -174,15 +208,22 @@ BOOST_AUTO_TEST_CASE( properties ) Registry::Properties props = reg.getInfo( "Component1" ).properties; BOOST_CHECK( props["name"] == "Component1" ); + + BOOST_CHECK( reg.getInfo( "special-id" ).getprop( "MyProperty" ) == "special" ); } BOOST_AUTO_TEST_CASE( custom_factory ) { { - _custom_factory_called = false; - - auto c = Base::Factory::create( "CompWithCustomFactory" ); + auto c = MyBase::Factory::create( "MyComponent", "TheName" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( c->name() == "TheName" ); + } + { + auto c = Base::Factory::create( "special-id" ); BOOST_CHECK( c != nullptr ); - BOOST_CHECK( _custom_factory_called == true ); + auto c2 = dynamic_cast( c.get() ); + BOOST_CHECK( c2 != nullptr ); + BOOST_CHECK( c2->name == "special-id" ); } } diff --git a/GaudiSvc/src/NTupleSvc/CollectionCloneAlg.cpp b/GaudiSvc/src/NTupleSvc/CollectionCloneAlg.cpp index ae63239a99..4dd1df0a40 100644 --- a/GaudiSvc/src/NTupleSvc/CollectionCloneAlg.cpp +++ b/GaudiSvc/src/NTupleSvc/CollectionCloneAlg.cpp @@ -420,7 +420,7 @@ public: if ( !status.isSuccess() ) { return status; } else if ( m_selectorName != "" ) { - SmartIF stmt( ObjFactory::create( m_selectorName, serviceLocator() ) ); + SmartIF stmt( ObjFactory::create( m_selectorName, serviceLocator() ).release() ); if ( stmt ) { if ( !m_criteria.empty() ) stmt->setCriteria( m_criteria ); nt->attachSelector( stmt ); diff --git a/GaudiSvc/src/RndmGenSvc/RndmGenSvc.cpp b/GaudiSvc/src/RndmGenSvc/RndmGenSvc.cpp index 4227a02888..f1b9921a3b 100644 --- a/GaudiSvc/src/RndmGenSvc/RndmGenSvc.cpp +++ b/GaudiSvc/src/RndmGenSvc/RndmGenSvc.cpp @@ -100,7 +100,7 @@ IRndmEngine* RndmGenSvc::engine() { return m_engine.get(); } /// Retrieve a valid generator from the service. StatusCode RndmGenSvc::generator( const IRndmGen::Param& par, IRndmGen*& refpGen ) { - auto pGen = SmartIF( ObjFactory::create( par.type(), m_engine.get() ) ); + auto pGen = SmartIF( ObjFactory::create( par.type(), m_engine.get() ).release() ); if ( !pGen ) { refpGen = nullptr; return StatusCode::FAILURE; diff --git a/GaudiUtils/GaudiUtils/IFileCatalog.h b/GaudiUtils/GaudiUtils/IFileCatalog.h index 9d9309d402..a190696db4 100644 --- a/GaudiUtils/GaudiUtils/IFileCatalog.h +++ b/GaudiUtils/GaudiUtils/IFileCatalog.h @@ -31,7 +31,7 @@ namespace Gaudi /// InterfaceID DeclareInterfaceID( IFileCatalog, 2, 0 ); - typedef Gaudi::PluginService::Factory Factory; + using Factory = Gaudi::PluginService::Factory; /// Public type definitions typedef std::pair NamedItem; diff --git a/GaudiUtils/src/component/MultiFileCatalog.cpp b/GaudiUtils/src/component/MultiFileCatalog.cpp index b761f3be89..c48c3f3ecd 100644 --- a/GaudiUtils/src/component/MultiFileCatalog.cpp +++ b/GaudiUtils/src/component/MultiFileCatalog.cpp @@ -115,14 +115,14 @@ void MultiFileCatalog::addCatalog( CSTR con ) string url = con.substr( id0 + 1 ); IInterface* cat = nullptr; if ( strncasecmp( "xml", typ.c_str(), 3 ) == 0 ) { - cat = IFileCatalog::Factory::create( xml_typ, url, msgSvc().get() ); + cat = IFileCatalog::Factory::create( xml_typ, url, msgSvc().get() ).release(); } else { using Gaudi::PluginService::Details::Registry; Registry& registry = Registry::instance(); - if ( registry.getInfo( typ ).type == typeid( Service::Factory::FuncType ).name() ) { - cat = Service::Factory::create( typ, url, serviceLocator().get() ); - } else if ( registry.getInfo( typ ).type == typeid( IFileCatalog::Factory::FuncType ).name() ) { - cat = IFileCatalog::Factory::create( typ, url, msgSvc().get() ); + if ( registry.getInfo( typ ).factory.type() == typeid( Service::Factory::FactoryType ) ) { + cat = Service::Factory::create( typ, url, serviceLocator().get() ).release(); + } else if ( registry.getInfo( typ ).factory.type() == typeid( IFileCatalog::Factory::FactoryType ) ) { + cat = IFileCatalog::Factory::create( typ, url, msgSvc().get() ).release(); } } if ( cat ) { diff --git a/RootHistCnv/src/RHistogramCnv.h b/RootHistCnv/src/RHistogramCnv.h index a2985d5341..f0d4df4f03 100644 --- a/RootHistCnv/src/RHistogramCnv.h +++ b/RootHistCnv/src/RHistogramCnv.h @@ -40,7 +40,7 @@ namespace RootHistCnv /// Create the transient representation of an object. StatusCode createObj( IOpaqueAddress* pAddr, DataObject*& refpObj ) override { - refpObj = DataObjFactory::create( objType() ); + refpObj = DataObjFactory::create( objType() ).release(); RootObjAddress* r = dynamic_cast( pAddr ); Q* h = dynamic_cast( refpObj ); if ( r && h ) { -- GitLab From 361273458568281990669a3100c2b3b7ed0ded20 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Tue, 26 Jun 2018 12:48:49 +0200 Subject: [PATCH 05/13] Added test for PluginService factory wrapper make sure we can define a function that is called to _wrap_ construction of each derived component class. --- GaudiPluginService/tests/src/UseCases.cpp | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/GaudiPluginService/tests/src/UseCases.cpp b/GaudiPluginService/tests/src/UseCases.cpp index 5a23e894b2..2df0ec9b9e 100644 --- a/GaudiPluginService/tests/src/UseCases.cpp +++ b/GaudiPluginService/tests/src/UseCases.cpp @@ -168,6 +168,55 @@ namespace SpecialId {{"MyProperty", "special"}} ); } +// customized factory wrapper +namespace CustomFactoryWrapper +{ + class Base; + void initBase( Base*, const std::string& ); +} + +namespace Gaudi +{ + namespace PluginService + { + namespace v2 + { + namespace Details + { + template + struct DefaultFactory> { + inline typename Factory::ReturnType + operator()( const std::string& name ) + { + auto p = std::make_unique(); + initBase( p.get(), name ); + return std::move( p ); + } + }; + } + } + } +} + +namespace CustomFactoryWrapper +{ + class Base + { + friend void initBase( Base*, const std::string& ); + std::string m_name; + + public: + using Factory = Gaudi::PluginService::Factory; + const std::string& name() const { return m_name; } + }; + + void initBase( Base* b, const std::string& name ) { b->m_name = name; } + + struct Component : Base { + }; + DECLARE_COMPONENT( Component ) +} + // Tests #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN @@ -227,3 +276,12 @@ BOOST_AUTO_TEST_CASE( custom_factory ) BOOST_CHECK( c2->name == "special-id" ); } } + +BOOST_AUTO_TEST_CASE( custom_factory_wrapper ) +{ + { + auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::Component", "TheName" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( c->name() == "TheName" ); + } +} -- GitLab From 42943430c2439b10f7594e50c4ba424be55823d0 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Tue, 26 Jun 2018 14:31:00 +0200 Subject: [PATCH 06/13] Improved CustomFactoryWrapper test to demonstrate multiple options --- GaudiPluginService/tests/src/UseCases.cpp | 44 +++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/GaudiPluginService/tests/src/UseCases.cpp b/GaudiPluginService/tests/src/UseCases.cpp index 2df0ec9b9e..e43e3f68e2 100644 --- a/GaudiPluginService/tests/src/UseCases.cpp +++ b/GaudiPluginService/tests/src/UseCases.cpp @@ -172,7 +172,27 @@ namespace SpecialId namespace CustomFactoryWrapper { class Base; + + // helper to initialize base class members void initBase( Base*, const std::string& ); + + // helper to use the default constructor of T, followed by initialization with initBase + template + std::enable_if_t::value, std::unique_ptr> + baseConstructorHelper( const std::string& name ) + { + auto p = std::make_unique(); + initBase( p.get(), name ); + return std::move( p ); + } + + // helper to use the special constructor of T (backward compatibility) + template + std::enable_if_t::value, std::unique_ptr> + baseConstructorHelper( const std::string& name ) + { + return std::make_unique( name ); + } } namespace Gaudi @@ -183,14 +203,13 @@ namespace Gaudi { namespace Details { + // custom implementation of DefaultFactory to wrap the call to T constructor template struct DefaultFactory> { inline typename Factory::ReturnType operator()( const std::string& name ) { - auto p = std::make_unique(); - initBase( p.get(), name ); - return std::move( p ); + return CustomFactoryWrapper::baseConstructorHelper( name ); } }; } @@ -207,14 +226,22 @@ namespace CustomFactoryWrapper public: using Factory = Gaudi::PluginService::Factory; + + Base() {} + Base( const std::string& name ) : m_name{name} {} + const std::string& name() const { return m_name; } }; void initBase( Base* b, const std::string& name ) { b->m_name = name; } - struct Component : Base { + struct ComponentNew : Base { }; - DECLARE_COMPONENT( Component ) + struct ComponentOld : Base { + ComponentOld( const std::string& name ) : Base{name} {} + }; + DECLARE_COMPONENT( ComponentNew ) + DECLARE_COMPONENT( ComponentOld ) } // Tests @@ -280,7 +307,12 @@ BOOST_AUTO_TEST_CASE( custom_factory ) BOOST_AUTO_TEST_CASE( custom_factory_wrapper ) { { - auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::Component", "TheName" ); + auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::ComponentNew", "TheName" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( c->name() == "TheName" ); + } + { + auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::ComponentOld", "TheName" ); BOOST_CHECK( c != nullptr ); BOOST_CHECK( c->name() == "TheName" ); } -- GitLab From c99d801f531809c683f5567910bdbc8da416b5d8 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Tue, 26 Jun 2018 16:11:24 +0200 Subject: [PATCH 07/13] Use regex to parse lines in .components files --- GaudiPluginService/src/PluginServiceV2.cpp | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/GaudiPluginService/src/PluginServiceV2.cpp b/GaudiPluginService/src/PluginServiceV2.cpp index f7eb3ee70f..800cd8300c 100644 --- a/GaudiPluginService/src/PluginServiceV2.cpp +++ b/GaudiPluginService/src/PluginServiceV2.cpp @@ -192,6 +192,9 @@ namespace Gaudi #endif start_pos = end_pos; + std::regex line_format{"^([[:space:]]*([^:]+):(.*[^[:space:]]))?[[:space:]]*(#.*)?$"}; + std::smatch m; + logger().debug( " looking into " + dirName.string() ); // look for files called "*.components" in the directory if ( is_directory( dirName ) ) { @@ -201,33 +204,30 @@ namespace Gaudi const auto& fullPath = p.path().string(); logger().debug( " reading " + p.path().filename().string() ); std::ifstream factories{fullPath}; - std::string buff; + std::string line; int factoriesCount = 0; int lineCount = 0; while ( !factories.eof() ) { ++lineCount; - std::getline( factories, buff ); - const auto line = trim( buff ); - // skip empty lines and lines starting with '#' - if ( line.empty() || line[0] == '#' ) continue; - // look for the separator - const auto pos = line.find( ':' ); - if ( pos == std::string_view::npos ) { - logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) ); - continue; - } - const std::string lib{line.substr( 0, pos )}; - const std::string fact{line.substr( pos + 1 )}; - m_factories.emplace( fact, FactoryInfo{{}, lib, {{"ClassName", fact}}} ); + std::getline( factories, line ); + if ( regex_match( line, m, line_format ) ) { + if ( m[2].length() ) { // "empty" lines are ignored + const std::string lib{m[2]}; + const std::string fact{m[3]}; + m_factories.emplace( fact, FactoryInfo{{}, lib, {{"ClassName", fact}}} ); #ifdef GAUDI_REFLEX_COMPONENT_ALIASES - // add an alias for the factory using the Reflex convention - std::string old_name = old_style_name( fact ); - if ( fact != old_name ) { - m_factories.emplace( old_name, - FactoryInfo{{}, lib, {{"ReflexName", "true"}, {"ClassName", fact}}} ); - } + // add an alias for the factory using the Reflex convention + std::string old_name = old_style_name( fact ); + if ( fact != old_name ) { + m_factories.emplace( old_name, + FactoryInfo{{}, lib, {{"ReflexName", "true"}, {"ClassName", fact}}} ); + } #endif - ++factoriesCount; + ++factoriesCount; + } + } else { + logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) ); + } } if ( logger().level() <= Logger::Debug ) { logger().debug( " found " + std::to_string( factoriesCount ) + " factories" ); -- GitLab From 8aefd6add1881c8e973d5a630f81202c9db0ef28 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Wed, 27 Jun 2018 10:18:40 +0200 Subject: [PATCH 08/13] Fixed handling of factories loaded late in genconf --- GaudiKernel/src/Util/genconf.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/GaudiKernel/src/Util/genconf.cpp b/GaudiKernel/src/Util/genconf.cpp index cf57d793e8..3a6092d1cd 100644 --- a/GaudiKernel/src/Util/genconf.cpp +++ b/GaudiKernel/src/Util/genconf.cpp @@ -500,19 +500,21 @@ int configGenerator::genConfig( const Strings_t& libs, const string& userModule continue; } - for ( const auto& factoryEntry : registry.factories() ) { - // skip factories not loaded - const auto& info = factoryEntry.second; - if ( !info.is_set() ) continue; - - // skip factories loaded by other libraries - const auto& factoryName = factoryEntry.first; + const auto& factories = registry.factories(); + for ( const auto& factoryName : registry.loadedFactoryNames() ) { if ( bkgNames.find( factoryName ) != bkgNames.end() ) { if ( Gaudi::PluginService::Details::logger().level() <= 1 ) { LOG_INFO << "\t==> skipping [" << factoryName << "]..."; } continue; } + auto entry = factories.find( factoryName ); + if ( entry == end( factories ) ) { + LOG_ERROR << "inconsistency in component factories list: I cannot find anymore " << factoryName; + continue; + } + const auto& info = entry->second; + if ( !info.is_set() ) continue; // do not generate configurables for the Reflex-compatible aliases if ( !info.getprop( "ReflexName" ).empty() ) continue; -- GitLab From 48935858f0a8dab9a4f7672d9faa5aa9190b44f0 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Thu, 28 Jun 2018 07:24:27 +0200 Subject: [PATCH 09/13] Added PluginService version prefix to .components files --- GaudiPluginService/CMakeLists.txt | 17 +++- GaudiPluginService/src/PluginServiceV1.cpp | 5 + GaudiPluginService/src/PluginServiceV2.cpp | 4 +- GaudiPluginService/src/listcomponents.cpp | 10 +- ...gacyUseCases.cpp => LegacyUseCasesLib.cpp} | 52 ----------- .../tests/src/LegacyUseCasesTests.cpp | 69 ++++++++++++++ .../src/{UseCases.cpp => UseCasesLib.cpp} | 74 --------------- .../tests/src/UseCasesTests.cpp | 91 +++++++++++++++++++ 8 files changed, 186 insertions(+), 136 deletions(-) rename GaudiPluginService/tests/src/{LegacyUseCases.cpp => LegacyUseCasesLib.cpp} (78%) create mode 100644 GaudiPluginService/tests/src/LegacyUseCasesTests.cpp rename GaudiPluginService/tests/src/{UseCases.cpp => UseCasesLib.cpp} (73%) create mode 100644 GaudiPluginService/tests/src/UseCasesTests.cpp diff --git a/GaudiPluginService/CMakeLists.txt b/GaudiPluginService/CMakeLists.txt index 4754b630ba..202c68fb80 100644 --- a/GaudiPluginService/CMakeLists.txt +++ b/GaudiPluginService/CMakeLists.txt @@ -36,12 +36,18 @@ target_link_libraries(listcomponents GaudiPluginService ${CMAKE_DL_LIBS}) find_package(Boost) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) -gaudi_add_unit_test(Test_GaudiPluginService_UseCases tests/src/UseCases.cpp +gaudi_add_library(Test_GaudiPluginService_UseCasesLib tests/src/UseCasesLib.cpp + NO_PUBLIC_HEADERS + LINK_LIBRARIES GaudiPluginService) +gaudi_add_unit_test(Test_GaudiPluginService_UseCases tests/src/UseCasesTests.cpp LINK_LIBRARIES GaudiPluginService TYPE Boost) -gaudi_add_unit_test(Test_GaudiPluginService_LegacyUseCases tests/src/LegacyUseCases.cpp - LINK_LIBRARIES GaudiPluginService +gaudi_add_library(Test_GaudiPluginService_LegacyUseCasesLib tests/src/LegacyUseCasesLib.cpp + NO_PUBLIC_HEADERS + LINK_LIBRARIES GaudiPluginService) +gaudi_add_unit_test(Test_GaudiPluginService_LegacyUseCases tests/src/LegacyUseCasesTests.cpp + LINK_LIBRARIES GaudiPluginService Boost TYPE Boost) gaudi_add_test(listcomponents.usage @@ -56,3 +62,8 @@ gaudi_add_test(listcomponents.help2 gaudi_add_test(listcomponents.wrong_args COMMAND $ -o PASSREGEX "ERROR: missing argument") + +gaudi_add_test(listcomponents.v1 COMMAND $ $ + PASSREGEX "v1::$:Test::ComponentA") +gaudi_add_test(listcomponents.v2 COMMAND $ $ + PASSREGEX "v2::$:special-id") diff --git a/GaudiPluginService/src/PluginServiceV1.cpp b/GaudiPluginService/src/PluginServiceV1.cpp index e1c52feca8..d2bbcde42c 100644 --- a/GaudiPluginService/src/PluginServiceV1.cpp +++ b/GaudiPluginService/src/PluginServiceV1.cpp @@ -216,6 +216,11 @@ namespace Gaudi trim( line ); // skip empty lines and lines starting with '#' if ( line.empty() || line[0] == '#' ) continue; + // only accept "v1" factories + if ( line.substr( 0, 4 ) == "v1::" ) + line = line.substr( 4 ); + else + continue; // look for the separator auto pos = line.find( ':' ); if ( pos == std::string::npos ) { diff --git a/GaudiPluginService/src/PluginServiceV2.cpp b/GaudiPluginService/src/PluginServiceV2.cpp index 800cd8300c..fa2c57ef42 100644 --- a/GaudiPluginService/src/PluginServiceV2.cpp +++ b/GaudiPluginService/src/PluginServiceV2.cpp @@ -192,7 +192,7 @@ namespace Gaudi #endif start_pos = end_pos; - std::regex line_format{"^([[:space:]]*([^:]+):(.*[^[:space:]]))?[[:space:]]*(#.*)?$"}; + std::regex line_format{"^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$"}; std::smatch m; logger().debug( " looking into " + dirName.string() ); @@ -211,7 +211,7 @@ namespace Gaudi ++lineCount; std::getline( factories, line ); if ( regex_match( line, m, line_format ) ) { - if ( m[2].length() ) { // "empty" lines are ignored + if ( m[1] == "v2" ) { // ignore non "v2" and "empty" lines const std::string lib{m[2]}; const std::string fact{m[3]}; m_factories.emplace( fact, FactoryInfo{{}, lib, {{"ClassName", fact}}} ); diff --git a/GaudiPluginService/src/listcomponents.cpp b/GaudiPluginService/src/listcomponents.cpp index 5da03f02f8..90fad3cbed 100644 --- a/GaudiPluginService/src/listcomponents.cpp +++ b/GaudiPluginService/src/listcomponents.cpp @@ -101,11 +101,11 @@ int main( int argc, char* argv[] ) } std::ostream& output = ( output_file ? *output_file : std::cout ); - auto dump_from = [&output, &loaded]( auto& reg, char* lib ) { + auto dump_from = [&output, &loaded]( auto& reg, const char* lib, const char* prefix ) { for ( const auto& factoryName : reg.loadedFactoryNames() ) { auto f = loaded.find( factoryName ); if ( f == loaded.end() ) { - output << lib << ":" << factoryName << std::endl; + output << prefix << "::" << lib << ":" << factoryName << std::endl; loaded.emplace( factoryName, lib ); } else std::cerr << "WARNING: factory '" << factoryName << "' already found in " << f->second << std::endl; @@ -113,10 +113,10 @@ int main( int argc, char* argv[] ) }; // loop over the list of libraries passed on the command line - for ( char* lib : libs ) { + for ( const char* lib : libs ) { if ( dlopen( lib, RTLD_LAZY | RTLD_LOCAL ) ) { - dump_from( reg2, lib ); - dump_from( reg1, lib ); + dump_from( reg2, lib, "v2" ); + dump_from( reg1, lib, "v1" ); } else { std::cerr << "ERROR: failed to load " << lib << ": " << dlerror() << std::endl; return EXIT_FAILURE; diff --git a/GaudiPluginService/tests/src/LegacyUseCases.cpp b/GaudiPluginService/tests/src/LegacyUseCasesLib.cpp similarity index 78% rename from GaudiPluginService/tests/src/LegacyUseCases.cpp rename to GaudiPluginService/tests/src/LegacyUseCasesLib.cpp index de39dc2b61..69035729be 100644 --- a/GaudiPluginService/tests/src/LegacyUseCases.cpp +++ b/GaudiPluginService/tests/src/LegacyUseCasesLib.cpp @@ -134,55 +134,3 @@ namespace } } _register__CompWithCustomFactory; } - -// Tests -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN -#include - -BOOST_AUTO_TEST_CASE( basic ) { BOOST_CHECK( Base::Factory::create( "Component0" ) != nullptr ); } - -BOOST_AUTO_TEST_CASE( basic_with_args ) -{ - Base2* instance = Base2::Factory::create( "Component2", "hello", 2 ); - BOOST_CHECK( instance != nullptr ); - - Component2* c2 = dynamic_cast( instance ); - BOOST_REQUIRE( c2 != nullptr ); - BOOST_CHECK( c2->i == 2 ); - BOOST_CHECK( c2->s == "hello" ); -} - -BOOST_AUTO_TEST_CASE( namespaces ) -{ - BOOST_CHECK( Base::Factory::create( "Test::ComponentA" ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "Test::ComponentB" ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "Test::ComponentC" ) != nullptr ); -} - -BOOST_AUTO_TEST_CASE( ids ) -{ - BOOST_CHECK( Base2::Factory::create( "Id2", "id", -2 ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "A" ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "B" ) != nullptr ); -} - -BOOST_AUTO_TEST_CASE( properties ) -{ - using Gaudi::PluginService::Details::Registry; - Registry& reg = Registry::instance(); - Registry::Properties props = reg.getInfo( "Component1" ).properties; - - BOOST_CHECK( props["name"] == "Component1" ); -} - -BOOST_AUTO_TEST_CASE( custom_factory ) -{ - { - _custom_factory_called = false; - - auto c = Base::Factory::create( "CompWithCustomFactory" ); - BOOST_CHECK( c != nullptr ); - BOOST_CHECK( _custom_factory_called == true ); - } -} diff --git a/GaudiPluginService/tests/src/LegacyUseCasesTests.cpp b/GaudiPluginService/tests/src/LegacyUseCasesTests.cpp new file mode 100644 index 0000000000..b42eb9667a --- /dev/null +++ b/GaudiPluginService/tests/src/LegacyUseCasesTests.cpp @@ -0,0 +1,69 @@ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ +/** + * Compile-time test for all known PluginService use-cases + * + * @author Marco Clemencic + */ + +#include "LegacyUseCasesLib.cpp" + +// Tests +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN +#include + +BOOST_AUTO_TEST_CASE( basic ) { BOOST_CHECK( Base::Factory::create( "Component0" ) != nullptr ); } + +BOOST_AUTO_TEST_CASE( basic_with_args ) +{ + Base2* instance = Base2::Factory::create( "Component2", "hello", 2 ); + BOOST_CHECK( instance != nullptr ); + + Component2* c2 = dynamic_cast( instance ); + BOOST_REQUIRE( c2 != nullptr ); + BOOST_CHECK( c2->i == 2 ); + BOOST_CHECK( c2->s == "hello" ); +} + +BOOST_AUTO_TEST_CASE( namespaces ) +{ + BOOST_CHECK( Base::Factory::create( "Test::ComponentA" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentB" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentC" ) != nullptr ); +} + +BOOST_AUTO_TEST_CASE( ids ) +{ + BOOST_CHECK( Base2::Factory::create( "Id2", "id", -2 ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "A" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "B" ) != nullptr ); +} + +BOOST_AUTO_TEST_CASE( properties ) +{ + using Gaudi::PluginService::Details::Registry; + Registry& reg = Registry::instance(); + Registry::Properties props = reg.getInfo( "Component1" ).properties; + + BOOST_CHECK( props["name"] == "Component1" ); +} + +BOOST_AUTO_TEST_CASE( custom_factory ) +{ + { + _custom_factory_called = false; + + auto c = Base::Factory::create( "CompWithCustomFactory" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( _custom_factory_called == true ); + } +} diff --git a/GaudiPluginService/tests/src/UseCases.cpp b/GaudiPluginService/tests/src/UseCasesLib.cpp similarity index 73% rename from GaudiPluginService/tests/src/UseCases.cpp rename to GaudiPluginService/tests/src/UseCasesLib.cpp index e43e3f68e2..86a35cb05b 100644 --- a/GaudiPluginService/tests/src/UseCases.cpp +++ b/GaudiPluginService/tests/src/UseCasesLib.cpp @@ -243,77 +243,3 @@ namespace CustomFactoryWrapper DECLARE_COMPONENT( ComponentNew ) DECLARE_COMPONENT( ComponentOld ) } - -// Tests -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN -#include - -BOOST_AUTO_TEST_CASE( basic ) { BOOST_CHECK( Base::Factory::create( "Component0" ) != nullptr ); } - -BOOST_AUTO_TEST_CASE( basic_with_args ) -{ - auto instance = Base2::Factory::create( "Component2", "hello", 2 ); - BOOST_CHECK( instance != nullptr ); - - auto c2 = dynamic_cast( instance.get() ); - BOOST_REQUIRE( c2 != nullptr ); - BOOST_CHECK( c2->i == 2 ); - BOOST_CHECK( c2->s == "hello" ); -} - -BOOST_AUTO_TEST_CASE( namespaces ) -{ - BOOST_CHECK( Base::Factory::create( "Test::ComponentA" ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "Test::ComponentB" ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "Test::ComponentC" ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "Test::ComponentD" ) != nullptr ); -} - -BOOST_AUTO_TEST_CASE( ids ) -{ - BOOST_CHECK( Base2::Factory::create( "Id2", "id", -2 ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "A" ) != nullptr ); - BOOST_CHECK( Base::Factory::create( "B" ) != nullptr ); -} - -BOOST_AUTO_TEST_CASE( properties ) -{ - using Gaudi::PluginService::Details::Registry; - Registry& reg = Registry::instance(); - Registry::Properties props = reg.getInfo( "Component1" ).properties; - - BOOST_CHECK( props["name"] == "Component1" ); - - BOOST_CHECK( reg.getInfo( "special-id" ).getprop( "MyProperty" ) == "special" ); -} - -BOOST_AUTO_TEST_CASE( custom_factory ) -{ - { - auto c = MyBase::Factory::create( "MyComponent", "TheName" ); - BOOST_CHECK( c != nullptr ); - BOOST_CHECK( c->name() == "TheName" ); - } - { - auto c = Base::Factory::create( "special-id" ); - BOOST_CHECK( c != nullptr ); - auto c2 = dynamic_cast( c.get() ); - BOOST_CHECK( c2 != nullptr ); - BOOST_CHECK( c2->name == "special-id" ); - } -} - -BOOST_AUTO_TEST_CASE( custom_factory_wrapper ) -{ - { - auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::ComponentNew", "TheName" ); - BOOST_CHECK( c != nullptr ); - BOOST_CHECK( c->name() == "TheName" ); - } - { - auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::ComponentOld", "TheName" ); - BOOST_CHECK( c != nullptr ); - BOOST_CHECK( c->name() == "TheName" ); - } -} diff --git a/GaudiPluginService/tests/src/UseCasesTests.cpp b/GaudiPluginService/tests/src/UseCasesTests.cpp new file mode 100644 index 0000000000..e4e8742187 --- /dev/null +++ b/GaudiPluginService/tests/src/UseCasesTests.cpp @@ -0,0 +1,91 @@ +/*****************************************************************************\ +* (c) Copyright 2013 CERN * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ +/** + * Compile-time test for all known PluginService use-cases + * + * @author Marco Clemencic + */ + +#include "UseCasesLib.cpp" + +// Tests +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN +#include + +BOOST_AUTO_TEST_CASE( basic ) { BOOST_CHECK( Base::Factory::create( "Component0" ) != nullptr ); } + +BOOST_AUTO_TEST_CASE( basic_with_args ) +{ + auto instance = Base2::Factory::create( "Component2", "hello", 2 ); + BOOST_CHECK( instance != nullptr ); + + auto c2 = dynamic_cast( instance.get() ); + BOOST_REQUIRE( c2 != nullptr ); + BOOST_CHECK( c2->i == 2 ); + BOOST_CHECK( c2->s == "hello" ); +} + +BOOST_AUTO_TEST_CASE( namespaces ) +{ + BOOST_CHECK( Base::Factory::create( "Test::ComponentA" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentB" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentC" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "Test::ComponentD" ) != nullptr ); +} + +BOOST_AUTO_TEST_CASE( ids ) +{ + BOOST_CHECK( Base2::Factory::create( "Id2", "id", -2 ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "A" ) != nullptr ); + BOOST_CHECK( Base::Factory::create( "B" ) != nullptr ); +} + +BOOST_AUTO_TEST_CASE( properties ) +{ + using Gaudi::PluginService::Details::Registry; + Registry& reg = Registry::instance(); + Registry::Properties props = reg.getInfo( "Component1" ).properties; + + BOOST_CHECK( props["name"] == "Component1" ); + + BOOST_CHECK( reg.getInfo( "special-id" ).getprop( "MyProperty" ) == "special" ); +} + +BOOST_AUTO_TEST_CASE( custom_factory ) +{ + { + auto c = MyBase::Factory::create( "MyComponent", "TheName" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( c->name() == "TheName" ); + } + { + auto c = Base::Factory::create( "special-id" ); + BOOST_CHECK( c != nullptr ); + auto c2 = dynamic_cast( c.get() ); + BOOST_CHECK( c2 != nullptr ); + BOOST_CHECK( c2->name == "special-id" ); + } +} + +BOOST_AUTO_TEST_CASE( custom_factory_wrapper ) +{ + { + auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::ComponentNew", "TheName" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( c->name() == "TheName" ); + } + { + auto c = CustomFactoryWrapper::Base::Factory::create( "CustomFactoryWrapper::ComponentOld", "TheName" ); + BOOST_CHECK( c != nullptr ); + BOOST_CHECK( c->name() == "TheName" ); + } +} -- GitLab From 502d9373d6d9d1c3a395ee68190b0d07b6c24270 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Fri, 6 Jul 2018 14:40:29 +0200 Subject: [PATCH 10/13] Increased timeout for hive experiments tests --- .../qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco.qmt | 2 +- .../experiments.qms/atlas.qms/mc_reco_full_precedence_dump.qmt | 2 +- .../qmtest/gaudihive.qms/experiments.qms/lhcb.qms/brunel.qmt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco.qmt b/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco.qmt index 148239f06e..11163ae541 100644 --- a/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco.qmt +++ b/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco.qmt @@ -6,5 +6,5 @@ ../../options/AtlasMCRecoScenario.py
true -120 +600 diff --git a/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco_full_precedence_dump.qmt b/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco_full_precedence_dump.qmt index d99a52b783..fdded6ba76 100644 --- a/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco_full_precedence_dump.qmt +++ b/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/atlas.qms/mc_reco_full_precedence_dump.qmt @@ -6,5 +6,5 @@ ../../options/AtlasMCRecoFullPrecedenceDump.py
true -120 +600 diff --git a/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/lhcb.qms/brunel.qmt b/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/lhcb.qms/brunel.qmt index 1727f11f9e..d26cd350a3 100644 --- a/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/lhcb.qms/brunel.qmt +++ b/GaudiHive/tests/qmtest/gaudihive.qms/experiments.qms/lhcb.qms/brunel.qmt @@ -6,5 +6,5 @@ ../../options/BrunelScenarioAvalancheScheduler.py
true -120 +600 -- GitLab From c89d1fb7159efd58c8009f820c7679d933981f31 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Wed, 11 Jul 2018 09:48:27 +0300 Subject: [PATCH 11/13] Added validation of ATLAS custom factories use case --- .../tests/src/LegacyUseCasesLib.cpp | 128 ++++++++++++++++++ GaudiPluginService/tests/src/UseCasesLib.cpp | 123 +++++++++++++++++ 2 files changed, 251 insertions(+) diff --git a/GaudiPluginService/tests/src/LegacyUseCasesLib.cpp b/GaudiPluginService/tests/src/LegacyUseCasesLib.cpp index 69035729be..57088edc28 100644 --- a/GaudiPluginService/tests/src/LegacyUseCasesLib.cpp +++ b/GaudiPluginService/tests/src/LegacyUseCasesLib.cpp @@ -134,3 +134,131 @@ namespace } } _register__CompWithCustomFactory; } + +// ATLAS Custom factories +// see http://acode-browser1.usatlas.bnl.gov/lxr/source/athena/Control/AthenaKernel/AthenaKernel/TPCnvFactory.h +// see http://acode-browser1.usatlas.bnl.gov/lxr/source/athena/Control/AthenaServices/src/test/testConverters.cxx +namespace Athena +{ + struct TPCnvVers { + enum Value { Old = 0, Current = 1 }; + }; + + struct TPCnvType { + enum Value { Athena = 0, ARA = 1, Trigger = 2 }; + }; +} +struct ITPCnvBase { + typedef Gaudi::PluginService::Factory Factory; +}; + +#define DO_ATHTPCNV_FACTORY_REGISTER_CNAME( name, serial ) _register_##_##serial + +#define DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, cnv_type, signature, \ + serial ) \ + namespace \ + { \ + class DO_ATHTPCNV_FACTORY_REGISTER_CNAME( type, serial ) \ + { \ + public: \ + typedef type::Factory s_t; \ + typedef ::Gaudi::PluginService::Details::Factory f_t; \ + static s_t::FuncType creator() { return &f_t::create; } \ + DO_ATHTPCNV_FACTORY_REGISTER_CNAME( type, serial )() \ + { \ + using ::Gaudi::PluginService::Details::Registry; \ + std::string prefix; \ + if ( cnv_type == Athena::TPCnvType::ARA ) \ + prefix = "_ARA"; \ + else if ( cnv_type == Athena::TPCnvType::Trigger ) \ + prefix = "_TRIG"; \ + Registry::instance().add( id, creator() ); \ + if ( is_last_version == Athena::TPCnvVers::Current ) \ + Registry::instance().add( prefix + "_TRANS_" + #trans_type, creator() ); \ + Registry::instance().add( prefix + "_PERS_" + #pers_type, creator() ); \ + } \ + } DO_ATHTPCNV_FACTORY_REGISTER_CNAME( s_##type, serial ); \ + } + +#define DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, cnv_type, signature, serial ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, ::Gaudi::PluginService::Details::demangle(), trans_type, \ + pers_type, is_last_version, cnv_type, signature, serial ) + +#define ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, Athena::TPCnvType::Athena, signature, \ + __LINE__ ) +#define ARATPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, Athena::TPCnvType::ARA, signature, \ + __LINE__ ) +#define TRIGTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, Athena::TPCnvType::Trigger, signature, \ + __LINE__ ) + +#define ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, Athena::TPCnvType::Athena, \ + signature, __LINE__ ) +#define ARATPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, Athena::TPCnvType::ARA, \ + signature, __LINE__ ) +#define TRIGTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, Athena::TPCnvType::Trigger, \ + signature, __LINE__ ) + +//******************************************************************** +// Macros that users should use. +// + +#define DECLARE_TPCNV_FACTORY( x, trans_type, pers_type, is_last_version ) \ + ATHTPCNV_PLUGINSVC_FACTORY( x, trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_ARATPCNV_FACTORY( x, trans_type, pers_type, is_last_version ) \ + ARATPCNV_PLUGINSVC_FACTORY( x, trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_TRIGTPCNV_FACTORY( x, trans_type, pers_type, is_last_version ) \ + TRIGTPCNV_PLUGINSVC_FACTORY( x, trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_NAMED_TPCNV_FACTORY( x, n, trans_type, pers_type, is_last_version ) \ + ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( x, std::string( #n ), trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_NAMED_ARATPCNV_FACTORY( x, n, trans_type, pers_type, is_last_version ) \ + ARATPCNV_PLUGINSVC_FACTORY_WITH_ID( x, std::string( #n ), trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_NAMED_TRIGTPCNV_FACTORY( x, n, trans_type, pers_type, is_last_version ) \ + TRIGTPCNV_PLUGINSVC_FACTORY_WITH_ID( x, std::string( #n ), trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +namespace AthenaServicesTestConverters +{ + class TestConverterBase : public ITPCnvBase + { + }; + + class TestConverter_TA_PA1 : public TestConverterBase + { + }; + class TestConverter_TA_PA2 : public TestConverterBase + { + }; + + class TestConverter_TB_PB1 : public TestConverterBase + { + }; + class TestConverter_TB_PB1_ARA : public TestConverterBase + { + }; + class TestConverter_TBTRIG_PB1 : public TestConverterBase + { + }; + +} // namespace AthenaServicesTestConverters + +DECLARE_TPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TA_PA1, AthenaServicesTestConverters::TA, + AthenaServicesTestConverters::PA1, Athena::TPCnvVers::Old ) +DECLARE_TPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TA_PA2, AthenaServicesTestConverters::TA, + AthenaServicesTestConverters::PA2, Athena::TPCnvVers::Current ) + +DECLARE_TPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TB_PB1, AthenaServicesTestConverters::TB, + AthenaServicesTestConverters::PB1, Athena::TPCnvVers::Current ) +DECLARE_ARATPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TB_PB1_ARA, AthenaServicesTestConverters::TB, + AthenaServicesTestConverters::PB1, Athena::TPCnvVers::Current ) +DECLARE_TRIGTPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TBTRIG_PB1, AthenaServicesTestConverters::TBTRIG, + AthenaServicesTestConverters::PB1, Athena::TPCnvVers::Current ) diff --git a/GaudiPluginService/tests/src/UseCasesLib.cpp b/GaudiPluginService/tests/src/UseCasesLib.cpp index 86a35cb05b..25797da3e8 100644 --- a/GaudiPluginService/tests/src/UseCasesLib.cpp +++ b/GaudiPluginService/tests/src/UseCasesLib.cpp @@ -243,3 +243,126 @@ namespace CustomFactoryWrapper DECLARE_COMPONENT( ComponentNew ) DECLARE_COMPONENT( ComponentOld ) } + +// ATLAS Custom factories +// see http://acode-browser1.usatlas.bnl.gov/lxr/source/athena/Control/AthenaKernel/AthenaKernel/TPCnvFactory.h +// see http://acode-browser1.usatlas.bnl.gov/lxr/source/athena/Control/AthenaServices/src/test/testConverters.cxx +namespace Athena +{ + struct TPCnvVers { + enum Value { Old = 0, Current = 1 }; + }; + + struct TPCnvType { + enum Value { Athena = 0, ARA = 1, Trigger = 2 }; + }; +} +struct ITPCnvBase { + typedef Gaudi::PluginService::Factory Factory; +}; + +#define DO_ATHTPCNV_FACTORY_REGISTER_CNAME( name, serial ) _register_##_##serial + +#define DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, cnv_type, signature, \ + serial ) \ + namespace \ + { \ + struct DO_ATHTPCNV_FACTORY_REGISTER_CNAME( type, serial ) { \ + DO_ATHTPCNV_FACTORY_REGISTER_CNAME( type, serial )() \ + { \ + using ::Gaudi::PluginService::DeclareFactory; \ + std::string prefix; \ + if ( cnv_type == Athena::TPCnvType::ARA ) \ + prefix = "_ARA"; \ + else if ( cnv_type == Athena::TPCnvType::Trigger ) \ + prefix = "_TRIG"; \ + DeclareFactory normal{}; \ + if ( is_last_version == Athena::TPCnvVers::Current ) \ + DeclareFactory transient{prefix + "_TRANS_" + #trans_type}; \ + DeclareFactory persistent{prefix + "_PERS_" + #pers_type}; \ + } \ + } DO_ATHTPCNV_FACTORY_REGISTER_CNAME( s_##type, serial ); \ + } + +#define DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, cnv_type, signature, serial ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, ::Gaudi::PluginService::Details::demangle(), trans_type, \ + pers_type, is_last_version, cnv_type, signature, serial ) + +#define ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, Athena::TPCnvType::Athena, signature, \ + __LINE__ ) +#define ARATPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, Athena::TPCnvType::ARA, signature, \ + __LINE__ ) +#define TRIGTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY( type, trans_type, pers_type, is_last_version, Athena::TPCnvType::Trigger, signature, \ + __LINE__ ) + +#define ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, Athena::TPCnvType::Athena, \ + signature, __LINE__ ) +#define ARATPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, Athena::TPCnvType::ARA, \ + signature, __LINE__ ) +#define TRIGTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, signature ) \ + DO_ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( type, id, trans_type, pers_type, is_last_version, Athena::TPCnvType::Trigger, \ + signature, __LINE__ ) + +//******************************************************************** +// Macros that users should use. +// + +#define DECLARE_TPCNV_FACTORY( x, trans_type, pers_type, is_last_version ) \ + ATHTPCNV_PLUGINSVC_FACTORY( x, trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_ARATPCNV_FACTORY( x, trans_type, pers_type, is_last_version ) \ + ARATPCNV_PLUGINSVC_FACTORY( x, trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_TRIGTPCNV_FACTORY( x, trans_type, pers_type, is_last_version ) \ + TRIGTPCNV_PLUGINSVC_FACTORY( x, trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_NAMED_TPCNV_FACTORY( x, n, trans_type, pers_type, is_last_version ) \ + ATHTPCNV_PLUGINSVC_FACTORY_WITH_ID( x, std::string( #n ), trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_NAMED_ARATPCNV_FACTORY( x, n, trans_type, pers_type, is_last_version ) \ + ARATPCNV_PLUGINSVC_FACTORY_WITH_ID( x, std::string( #n ), trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +#define DECLARE_NAMED_TRIGTPCNV_FACTORY( x, n, trans_type, pers_type, is_last_version ) \ + TRIGTPCNV_PLUGINSVC_FACTORY_WITH_ID( x, std::string( #n ), trans_type, pers_type, is_last_version, ITPCnvBase*() ) + +namespace AthenaServicesTestConverters +{ + class TestConverterBase : public ITPCnvBase + { + }; + + class TestConverter_TA_PA1 : public TestConverterBase + { + }; + class TestConverter_TA_PA2 : public TestConverterBase + { + }; + + class TestConverter_TB_PB1 : public TestConverterBase + { + }; + class TestConverter_TB_PB1_ARA : public TestConverterBase + { + }; + class TestConverter_TBTRIG_PB1 : public TestConverterBase + { + }; + +} // namespace AthenaServicesTestConverters + +DECLARE_TPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TA_PA1, AthenaServicesTestConverters::TA, + AthenaServicesTestConverters::PA1, Athena::TPCnvVers::Old ) +DECLARE_TPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TA_PA2, AthenaServicesTestConverters::TA, + AthenaServicesTestConverters::PA2, Athena::TPCnvVers::Current ) + +DECLARE_TPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TB_PB1, AthenaServicesTestConverters::TB, + AthenaServicesTestConverters::PB1, Athena::TPCnvVers::Current ) +DECLARE_ARATPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TB_PB1_ARA, AthenaServicesTestConverters::TB, + AthenaServicesTestConverters::PB1, Athena::TPCnvVers::Current ) +DECLARE_TRIGTPCNV_FACTORY( AthenaServicesTestConverters::TestConverter_TBTRIG_PB1, AthenaServicesTestConverters::TBTRIG, + AthenaServicesTestConverters::PB1, Athena::TPCnvVers::Current ) -- GitLab From c6e26b04f6d717d19324311ef06fb77c75de9a6b Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Fri, 13 Jul 2018 11:08:59 +0300 Subject: [PATCH 12/13] Swapped FactoryInfo data members for easier backward compatibility --- GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h | 4 ++-- GaudiPluginService/Gaudi/PluginServiceV2.h | 2 +- GaudiPluginService/src/PluginServiceV2.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h index 22eedb3389..4019e9c600 100644 --- a/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h +++ b/GaudiPluginService/Gaudi/Details/PluginServiceDetailsV2.h @@ -125,9 +125,9 @@ namespace Gaudi using Properties = std::map; struct FactoryInfo { - std::any factory; std::string library; - Properties properties; + std::any factory{}; + Properties properties{}; inline bool is_set() const { diff --git a/GaudiPluginService/Gaudi/PluginServiceV2.h b/GaudiPluginService/Gaudi/PluginServiceV2.h index f0e2f0fa41..a09a0af8fe 100644 --- a/GaudiPluginService/Gaudi/PluginServiceV2.h +++ b/GaudiPluginService/Gaudi/PluginServiceV2.h @@ -101,7 +101,7 @@ namespace Gaudi if ( props.find( "ClassName" ) == end( props ) ) props.emplace( "ClassName", Details::demangle() ); - Registry::instance().add( id, {std::move( f ), libraryName(), std::move( props )} ); + Registry::instance().add( id, {libraryName(), std::move( f ), std::move( props )} ); } DeclareFactory( Details::Registry::Properties props ) : DeclareFactory( DefaultFactory{}, std::move( props ) ) diff --git a/GaudiPluginService/src/PluginServiceV2.cpp b/GaudiPluginService/src/PluginServiceV2.cpp index fa2c57ef42..ab9936c20e 100644 --- a/GaudiPluginService/src/PluginServiceV2.cpp +++ b/GaudiPluginService/src/PluginServiceV2.cpp @@ -214,13 +214,13 @@ namespace Gaudi if ( m[1] == "v2" ) { // ignore non "v2" and "empty" lines const std::string lib{m[2]}; const std::string fact{m[3]}; - m_factories.emplace( fact, FactoryInfo{{}, lib, {{"ClassName", fact}}} ); + m_factories.emplace( fact, FactoryInfo{lib, {}, {{"ClassName", fact}}} ); #ifdef GAUDI_REFLEX_COMPONENT_ALIASES // add an alias for the factory using the Reflex convention std::string old_name = old_style_name( fact ); if ( fact != old_name ) { m_factories.emplace( old_name, - FactoryInfo{{}, lib, {{"ReflexName", "true"}, {"ClassName", fact}}} ); + FactoryInfo{lib, {}, {{"ReflexName", "true"}, {"ClassName", fact}}} ); } #endif ++factoriesCount; @@ -282,7 +282,7 @@ namespace Gaudi const Registry::FactoryInfo& Registry::getInfo( const KeyType& id, const bool load ) const { REG_SCOPE_LOCK - static const FactoryInfo unknown = {{}, "unknown", {}}; + static const FactoryInfo unknown = {"unknown"}; const FactoryMap& facts = factories(); auto f = facts.find( id ); -- GitLab From f9b33477cd58ec7c5eac3c4728b75f3d28a0b9e0 Mon Sep 17 00:00:00 2001 From: Marco Clemencic Date: Thu, 19 Jul 2018 22:42:58 +0200 Subject: [PATCH 13/13] Fix handling of first entry in LD_LIBRARY_PATH --- GaudiPluginService/src/PluginServiceV2.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/GaudiPluginService/src/PluginServiceV2.cpp b/GaudiPluginService/src/PluginServiceV2.cpp index ab9936c20e..21add2bc3b 100644 --- a/GaudiPluginService/src/PluginServiceV2.cpp +++ b/GaudiPluginService/src/PluginServiceV2.cpp @@ -176,25 +176,28 @@ namespace Gaudi const char* envVar = "LD_LIBRARY_PATH"; const char sep = ':'; #endif + + std::regex line_format{"^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$"}; + std::smatch m; + std::string_view search_path = std::getenv( envVar ); if ( !search_path.empty() ) { logger().debug( std::string( "searching factories in " ) + envVar ); std::string_view::size_type start_pos = 0, end_pos = 0; while ( start_pos != std::string_view::npos ) { + // correctly handle begin of string or path separator + if ( start_pos ) ++start_pos; - end_pos = search_path.find( sep, start_pos + 1 ); + end_pos = search_path.find( sep, start_pos ); fs::path dirName = #if __GNUC__ >= 7 - search_path.substr( start_pos + 1, end_pos - start_pos - 1 ); + search_path.substr( start_pos, end_pos - start_pos ); #else - std::string{search_path.substr( start_pos + 1, end_pos - start_pos - 1 )}; + std::string{search_path.substr( start_pos, end_pos - start_pos )}; #endif start_pos = end_pos; - std::regex line_format{"^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$"}; - std::smatch m; - logger().debug( " looking into " + dirName.string() ); // look for files called "*.components" in the directory if ( is_directory( dirName ) ) { -- GitLab