Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-12 07:55:12

0001 /*****************************************************************************\
0002 * (c) Copyright 2013 CERN                                                     *
0003 *                                                                             *
0004 * This software is distributed under the terms of the GNU General Public      *
0005 * Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE".   *
0006 *                                                                             *
0007 * In applying this licence, CERN does not waive the privileges and immunities *
0008 * granted to it by virtue of its status as an Intergovernmental Organization  *
0009 * or submit itself to any jurisdiction.                                       *
0010 \*****************************************************************************/
0011 
0012 /// @author Marco Clemencic <marco.clemencic@cern.ch>
0013 
0014 #define GAUDI_PLUGIN_SERVICE_V1
0015 #include <Gaudi/PluginService.h>
0016 
0017 #include <dirent.h>
0018 #include <dlfcn.h>
0019 
0020 #include <cstdlib>
0021 #include <fstream>
0022 #include <iostream>
0023 #include <memory>
0024 #include <regex>
0025 
0026 #include <cxxabi.h>
0027 #include <sys/stat.h>
0028 
0029 #define REG_SCOPE_LOCK std::lock_guard<std::recursive_mutex> _guard( m_mutex );
0030 
0031 namespace {
0032   std::mutex registrySingletonMutex;
0033 }
0034 #define SINGLETON_LOCK std::lock_guard<std::mutex> _guard( ::registrySingletonMutex );
0035 
0036 #include <algorithm>
0037 
0038 namespace {
0039   // string trimming functions taken from
0040   // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
0041 
0042   constexpr struct is_space_t {
0043     bool operator()( int i ) const { return std::isspace( i ); }
0044   } is_space{};
0045 
0046   // trim from start
0047   static inline std::string& ltrim( std::string& s ) {
0048     s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), is_space ) );
0049     return s;
0050   }
0051 
0052   // trim from end
0053   static inline std::string& rtrim( std::string& s ) {
0054     s.erase( std::find_if_not( s.rbegin(), s.rend(), is_space ).base(), s.end() );
0055     return s;
0056   }
0057   // trim from both ends
0058   static inline std::string& trim( std::string& s ) { return ltrim( rtrim( s ) ); }
0059 } // namespace
0060 
0061 namespace {
0062   /// Helper function used to set values in FactoryInfo data members only
0063   /// if the original value is empty and reporting warnings in case of
0064   /// inconsistencies.
0065   inline void factoryInfoSetHelper( std::string& dest, const std::string value, const std::string& desc,
0066                                     const std::string& id ) {
0067     if ( dest.empty() ) {
0068       dest = value;
0069     } else if ( dest != value ) {
0070       Gaudi::PluginService::Details::logger().warning( "new factory loaded for '" + id + "' with different " + desc +
0071                                                        ": " + dest + " != " + value );
0072     }
0073   }
0074 
0075   struct OldStyleCnv {
0076     std::string name;
0077     void        operator()( const char c ) {
0078       switch ( c ) {
0079       case '<':
0080       case '>':
0081       case ',':
0082       case '(':
0083       case ')':
0084       case ':':
0085       case '.':
0086         name.push_back( '_' );
0087         break;
0088       case '&':
0089         name.push_back( 'r' );
0090         break;
0091       case '*':
0092         name.push_back( 'p' );
0093         break;
0094       case ' ':
0095         break;
0096       default:
0097         name.push_back( c );
0098         break;
0099       }
0100     }
0101   };
0102   /// Convert a class name in the string used with the Reflex plugin service
0103   std::string old_style_name( const std::string& name ) {
0104     return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
0105   }
0106 } // namespace
0107 
0108 namespace Gaudi {
0109   namespace PluginService {
0110     GAUDI_PLUGIN_SERVICE_V1_INLINE namespace v1 {
0111       Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {}
0112       Exception::~Exception() throw() {}
0113       const char* Exception::what() const throw() { return m_msg.c_str(); }
0114 
0115       namespace Details {
0116         void* getCreator( const std::string& id, const std::string& type ) {
0117           return Registry::instance().get( id, type );
0118         }
0119 
0120         std::string demangle( const std::string& id ) {
0121           int  status;
0122           auto realname = std::unique_ptr<char, decltype( free )*>(
0123               abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
0124           if ( !realname ) return id;
0125 #if _GLIBCXX_USE_CXX11_ABI
0126           return std::regex_replace(
0127               realname.get(),
0128               std::regex{"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
0129               "std::string" );
0130 #else
0131           return std::string{realname.get()};
0132 #endif
0133         }
0134         std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
0135 
0136         Registry& Registry::instance() {
0137           SINGLETON_LOCK
0138           static Registry r;
0139           return r;
0140         }
0141 
0142         Registry::Registry() : m_initialized( false ) {}
0143 
0144         void Registry::initialize() {
0145           REG_SCOPE_LOCK
0146           if ( m_initialized ) return;
0147           m_initialized = true;
0148 #if defined( _WIN32 )
0149           const char* envVar = "PATH";
0150           const char  sep    = ';';
0151 #elif defined( __APPLE__ )
0152           const char* envVar = "DYLD_LIBRARY_PATH";
0153           const char  sep    = ':';
0154 #else
0155           const char* envVar = "LD_LIBRARY_PATH";
0156           const char  sep    = ':';
0157 #endif
0158           char* search_path = ::getenv( envVar );
0159           logger().debug( std::string( "searching factories in " ) + envVar );
0160           std::string path = search_path ? std::string(search_path) : "/usr/lib64:/usr/lib:/usr/local/lib";
0161           std::string::size_type pos    = 0;
0162           std::string::size_type newpos = 0;
0163           while ( pos != std::string::npos ) {
0164             std::string dirName;
0165             // get the next entry in the path
0166             newpos = path.find( sep, pos );
0167             if ( newpos != std::string::npos ) {
0168               dirName = path.substr( pos, newpos - pos );
0169               pos     = newpos + 1;
0170             } else {
0171               dirName = path.substr( pos );
0172               pos     = newpos;
0173             }
0174             logger().debug( std::string( " looking into " ) + dirName );
0175             // look for files called "*.components" in the directory
0176             DIR* dir = opendir( dirName.c_str() );
0177             if ( dir ) {
0178               struct dirent* entry;
0179               while ( ( entry = readdir( dir ) ) ) {
0180                 std::string name( entry->d_name );
0181                 // check if the file name ends with ".components"
0182                 std::string::size_type extpos = name.find( ".components" );
0183                 if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
0184                   std::string fullPath = ( dirName + '/' + name );
0185                   { // check if it is a regular file
0186                     struct stat buf;
0187                     if ( 0 != ::stat( fullPath.c_str(), &buf ) )
0188                       continue;
0189                     else if ( !S_ISREG( buf.st_mode ) )
0190                       continue;
0191                   }
0192                   // read the file
0193                   logger().debug( std::string( "  reading " ) + name );
0194                   std::ifstream factories{fullPath};
0195                   std::string   line;
0196                   int           factoriesCount = 0;
0197                   int           lineCount      = 0;
0198                   while ( !factories.eof() ) {
0199                     ++lineCount;
0200                     std::getline( factories, line );
0201                     trim( line );
0202                     // skip empty lines and lines starting with '#'
0203                     if ( line.empty() || line[0] == '#' ) continue;
0204                     // only accept "v1" factories
0205                     if ( line.substr( 0, 4 ) == "v1::" )
0206                       line = line.substr( 4 );
0207                     else
0208                       continue;
0209                     // look for the separator
0210                     auto pos = line.find( ':' );
0211                     if ( pos == std::string::npos ) {
0212                       logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
0213                       continue;
0214                     }
0215                     const std::string lib( line, 0, pos );
0216                     const std::string fact( line, pos + 1 );
0217                     m_factories.emplace( fact, FactoryInfo( lib ) );
0218 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0219                     // add an alias for the factory using the Reflex convention
0220                     std::string old_name = old_style_name( fact );
0221                     if ( fact != old_name ) {
0222                       FactoryInfo old_info( lib );
0223                       old_info.properties["ReflexName"] = "true";
0224                       m_factories.emplace( old_name, old_info );
0225                     }
0226 #endif
0227                     ++factoriesCount;
0228                   }
0229                   if ( logger().level() <= Logger::Debug ) {
0230                     logger().debug( "  found " + std::to_string( factoriesCount ) + " factories" );
0231                   }
0232                 }
0233               }
0234               closedir( dir );
0235             }
0236           }
0237         }
0238 
0239         Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
0240                                               const std::string& rtype, const std::string& className,
0241                                               const Properties& props ) {
0242           REG_SCOPE_LOCK
0243           FactoryMap& facts = factories();
0244           auto        entry = facts.find( id );
0245           if ( entry == facts.end() ) {
0246             // this factory was not known yet
0247             entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
0248           } else {
0249             // do not replace an existing factory with a new one
0250             if ( !entry->second.ptr ) entry->second.ptr = factory;
0251             factoryInfoSetHelper( entry->second.type, type, "type", id );
0252             factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
0253             factoryInfoSetHelper( entry->second.className, className, "class", id );
0254           }
0255 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0256           // add an alias for the factory using the Reflex convention
0257           std::string old_name = old_style_name( id );
0258           if ( id != old_name )
0259             add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
0260 #endif
0261           return entry->second;
0262         }
0263 
0264         void* Registry::get( const std::string& id, const std::string& type ) const {
0265           REG_SCOPE_LOCK
0266           const FactoryMap& facts = factories();
0267           auto              f     = facts.find( id );
0268           if ( f != facts.end() ) {
0269 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0270             const Properties& props = f->second.properties;
0271             if ( props.find( "ReflexName" ) != props.end() )
0272               logger().warning( "requesting factory via old name '" + id +
0273                                 "'"
0274                                 "use '" +
0275                                 f->second.className + "' instead" );
0276 #endif
0277             if ( !f->second.ptr ) {
0278               if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
0279                 logger().warning( "cannot load " + f->second.library + " for factory " + id );
0280                 char* dlmsg = dlerror();
0281                 if ( dlmsg ) logger().warning( dlmsg );
0282                 return nullptr;
0283               }
0284               f = facts.find( id ); // ensure that the iterator is valid
0285             }
0286             if ( f->second.type == type ) return f->second.ptr;
0287             logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
0288                               " instead of " + demangle( type ) );
0289           }
0290           return nullptr; // factory not found
0291         }
0292 
0293         const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const {
0294           REG_SCOPE_LOCK
0295           static const FactoryInfo unknown( "unknown" );
0296           const FactoryMap&        facts = factories();
0297           auto                     f     = facts.find( id );
0298           return ( f != facts.end() ) ? f->second : unknown;
0299         }
0300 
0301         Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) {
0302           REG_SCOPE_LOCK
0303           FactoryMap& facts = factories();
0304           auto        f     = facts.find( id );
0305           if ( f != facts.end() ) f->second.properties[k] = v;
0306           return *this;
0307         }
0308 
0309         std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
0310           REG_SCOPE_LOCK
0311           std::set<KeyType> l;
0312           for ( const auto& f : factories() ) {
0313             if ( f.second.ptr ) l.insert( f.first );
0314           }
0315           return l;
0316         }
0317 
0318         void Logger::report( Level lvl, const std::string& msg ) {
0319           static const char* levels[] = {"DEBUG  : ", "INFO   : ", "WARNING: ", "ERROR  : "};
0320           if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
0321         }
0322 
0323         static auto s_logger = std::make_unique<Logger>();
0324         Logger&     logger() { return *s_logger; }
0325         void        setLogger( Logger* logger ) { s_logger.reset( logger ); }
0326 
0327       } // namespace Details
0328 
0329       void SetDebug( int debugLevel ) {
0330         using namespace Details;
0331         Logger& l = logger();
0332         if ( debugLevel > 1 )
0333           l.setLevel( Logger::Debug );
0334         else if ( debugLevel > 0 )
0335           l.setLevel( Logger::Info );
0336         else
0337           l.setLevel( Logger::Warning );
0338       }
0339 
0340       int Debug() {
0341         using namespace Details;
0342         switch ( logger().level() ) {
0343         case Logger::Debug:
0344           return 2;
0345         case Logger::Info:
0346           return 1;
0347         default:
0348           return 0;
0349         }
0350       }
0351     }
0352   } // namespace PluginService
0353 } // namespace Gaudi