Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:14:59

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           if ( search_path ) {
0160             logger().debug( std::string( "searching factories in " ) + envVar );
0161             std::string            path( search_path );
0162             std::string::size_type pos    = 0;
0163             std::string::size_type newpos = 0;
0164             while ( pos != std::string::npos ) {
0165               std::string dirName;
0166               // get the next entry in the path
0167               newpos = path.find( sep, pos );
0168               if ( newpos != std::string::npos ) {
0169                 dirName = path.substr( pos, newpos - pos );
0170                 pos     = newpos + 1;
0171               } else {
0172                 dirName = path.substr( pos );
0173                 pos     = newpos;
0174               }
0175               logger().debug( std::string( " looking into " ) + dirName );
0176               // look for files called "*.components" in the directory
0177               DIR* dir = opendir( dirName.c_str() );
0178               if ( dir ) {
0179                 struct dirent* entry;
0180                 while ( ( entry = readdir( dir ) ) ) {
0181                   std::string name( entry->d_name );
0182                   // check if the file name ends with ".components"
0183                   std::string::size_type extpos = name.find( ".components" );
0184                   if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
0185                     std::string fullPath = ( dirName + '/' + name );
0186                     { // check if it is a regular file
0187                       struct stat buf;
0188                       if ( 0 != ::stat( fullPath.c_str(), &buf ) )
0189             continue;
0190                       else if ( !S_ISREG( buf.st_mode ) )
0191             continue;
0192                     }
0193                     // read the file
0194                     logger().debug( std::string( "  reading " ) + name );
0195                     std::ifstream factories{fullPath};
0196                     std::string   line;
0197                     int           factoriesCount = 0;
0198                     int           lineCount      = 0;
0199                     while ( !factories.eof() ) {
0200                       ++lineCount;
0201                       std::getline( factories, line );
0202                       trim( line );
0203                       // skip empty lines and lines starting with '#'
0204                       if ( line.empty() || line[0] == '#' ) continue;
0205                       // only accept "v1" factories
0206                       if ( line.substr( 0, 4 ) == "v1::" )
0207                         line = line.substr( 4 );
0208                       else
0209                         continue;
0210                       // look for the separator
0211                       auto pos = line.find( ':' );
0212                       if ( pos == std::string::npos ) {
0213                         logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
0214                         continue;
0215                       }
0216                       const std::string lib( line, 0, pos );
0217                       const std::string fact( line, pos + 1 );
0218                       m_factories.emplace( fact, FactoryInfo( lib ) );
0219 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0220                       // add an alias for the factory using the Reflex convention
0221                       std::string old_name = old_style_name( fact );
0222                       if ( fact != old_name ) {
0223                         FactoryInfo old_info( lib );
0224                         old_info.properties["ReflexName"] = "true";
0225                         m_factories.emplace( old_name, old_info );
0226                       }
0227 #endif
0228                       ++factoriesCount;
0229                     }
0230                     if ( logger().level() <= Logger::Debug ) {
0231                       logger().debug( "  found " + std::to_string( factoriesCount ) + " factories" );
0232                     }
0233                   }
0234                 }
0235                 closedir( dir );
0236               }
0237             }
0238           }
0239         }
0240 
0241         Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
0242                                               const std::string& rtype, const std::string& className,
0243                                               const Properties& props ) {
0244           REG_SCOPE_LOCK
0245           FactoryMap& facts = factories();
0246           auto        entry = facts.find( id );
0247           if ( entry == facts.end() ) {
0248             // this factory was not known yet
0249             entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
0250           } else {
0251             // do not replace an existing factory with a new one
0252             if ( !entry->second.ptr ) entry->second.ptr = factory;
0253             factoryInfoSetHelper( entry->second.type, type, "type", id );
0254             factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
0255             factoryInfoSetHelper( entry->second.className, className, "class", id );
0256           }
0257 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0258           // add an alias for the factory using the Reflex convention
0259           std::string old_name = old_style_name( id );
0260           if ( id != old_name )
0261             add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
0262 #endif
0263           return entry->second;
0264         }
0265 
0266         void* Registry::get( const std::string& id, const std::string& type ) const {
0267           REG_SCOPE_LOCK
0268           const FactoryMap& facts = factories();
0269           auto              f     = facts.find( id );
0270           if ( f != facts.end() ) {
0271 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
0272             const Properties& props = f->second.properties;
0273             if ( props.find( "ReflexName" ) != props.end() )
0274               logger().warning( "requesting factory via old name '" + id +
0275                                 "'"
0276                                 "use '" +
0277                                 f->second.className + "' instead" );
0278 #endif
0279             if ( !f->second.ptr ) {
0280               if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
0281                 logger().warning( "cannot load " + f->second.library + " for factory " + id );
0282                 char* dlmsg = dlerror();
0283                 if ( dlmsg ) logger().warning( dlmsg );
0284                 return nullptr;
0285               }
0286               f = facts.find( id ); // ensure that the iterator is valid
0287             }
0288             if ( f->second.type == type ) return f->second.ptr;
0289             logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
0290                               " instead of " + demangle( type ) );
0291           }
0292           return nullptr; // factory not found
0293         }
0294 
0295         const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const {
0296           REG_SCOPE_LOCK
0297           static const FactoryInfo unknown( "unknown" );
0298           const FactoryMap&        facts = factories();
0299           auto                     f     = facts.find( id );
0300           return ( f != facts.end() ) ? f->second : unknown;
0301         }
0302 
0303         Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) {
0304           REG_SCOPE_LOCK
0305           FactoryMap& facts = factories();
0306           auto        f     = facts.find( id );
0307           if ( f != facts.end() ) f->second.properties[k] = v;
0308           return *this;
0309         }
0310 
0311         std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
0312           REG_SCOPE_LOCK
0313           std::set<KeyType> l;
0314           for ( const auto& f : factories() ) {
0315             if ( f.second.ptr ) l.insert( f.first );
0316           }
0317           return l;
0318         }
0319 
0320         void Logger::report( Level lvl, const std::string& msg ) {
0321           static const char* levels[] = {"DEBUG  : ", "INFO   : ", "WARNING: ", "ERROR  : "};
0322           if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
0323         }
0324 
0325         static auto s_logger = std::make_unique<Logger>();
0326         Logger&     logger() { return *s_logger; }
0327         void        setLogger( Logger* logger ) { s_logger.reset( logger ); }
0328 
0329       } // namespace Details
0330 
0331       void SetDebug( int debugLevel ) {
0332         using namespace Details;
0333         Logger& l = logger();
0334         if ( debugLevel > 1 )
0335           l.setLevel( Logger::Debug );
0336         else if ( debugLevel > 0 )
0337           l.setLevel( Logger::Info );
0338         else
0339           l.setLevel( Logger::Warning );
0340       }
0341 
0342       int Debug() {
0343         using namespace Details;
0344         switch ( logger().level() ) {
0345         case Logger::Debug:
0346           return 2;
0347         case Logger::Info:
0348           return 1;
0349         default:
0350           return 0;
0351         }
0352       }
0353     }
0354   } // namespace PluginService
0355 } // namespace Gaudi