DD4hep  1.30.0
Detector Description Toolkit for High Energy Physics
PluginServiceV1.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * (c) Copyright 2013 CERN *
3 * *
4 * This software is distributed under the terms of the GNU General Public *
5 * Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \*****************************************************************************/
11 
13 
14 #define GAUDI_PLUGIN_SERVICE_V1
16 
17 #include <dirent.h>
18 #include <dlfcn.h>
19 
20 #include <cstdlib>
21 #include <fstream>
22 #include <iostream>
23 #include <memory>
24 #include <regex>
25 
26 #include <cxxabi.h>
27 #include <sys/stat.h>
28 
29 #define REG_SCOPE_LOCK std::lock_guard<std::recursive_mutex> _guard( m_mutex );
30 
31 namespace {
32  std::mutex registrySingletonMutex;
33 }
34 #define SINGLETON_LOCK std::lock_guard<std::mutex> _guard( ::registrySingletonMutex );
35 
36 #include <algorithm>
37 
38 namespace {
39  // string trimming functions taken from
40  // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
41 
42  constexpr struct is_space_t {
43  bool operator()( int i ) const { return std::isspace( i ); }
44  } is_space{};
45 
46  // trim from start
47  static inline std::string& ltrim( std::string& s ) {
48  s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), is_space ) );
49  return s;
50  }
51 
52  // trim from end
53  static inline std::string& rtrim( std::string& s ) {
54  s.erase( std::find_if_not( s.rbegin(), s.rend(), is_space ).base(), s.end() );
55  return s;
56  }
57  // trim from both ends
58  static inline std::string& trim( std::string& s ) { return ltrim( rtrim( s ) ); }
59 } // namespace
60 
61 namespace {
65  inline void factoryInfoSetHelper( std::string& dest, const std::string value, const std::string& desc,
66  const std::string& id ) {
67  if ( dest.empty() ) {
68  dest = value;
69  } else if ( dest != value ) {
70  Gaudi::PluginService::Details::logger().warning( "new factory loaded for '" + id + "' with different " + desc +
71  ": " + dest + " != " + value );
72  }
73  }
74 
75  struct OldStyleCnv {
76  std::string name;
77  void operator()( const char c ) {
78  switch ( c ) {
79  case '<':
80  case '>':
81  case ',':
82  case '(':
83  case ')':
84  case ':':
85  case '.':
86  name.push_back( '_' );
87  break;
88  case '&':
89  name.push_back( 'r' );
90  break;
91  case '*':
92  name.push_back( 'p' );
93  break;
94  case ' ':
95  break;
96  default:
97  name.push_back( c );
98  break;
99  }
100  }
101  };
103  std::string old_style_name( const std::string& name ) {
104  return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
105  }
106 } // namespace
107 
108 namespace Gaudi {
109  namespace PluginService {
110  GAUDI_PLUGIN_SERVICE_V1_INLINE namespace v1 {
111  Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {}
113  const char* Exception::what() const throw() { return m_msg.c_str(); }
114 
115  namespace Details {
116  void* getCreator( const std::string& id, const std::string& type ) {
117  return Registry::instance().get( id, type );
118  }
119 
120  std::string demangle( const std::string& id ) {
121  int status;
122  auto realname = std::unique_ptr<char, decltype( free )*>(
123  abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
124  if ( !realname ) return id;
125 #if _GLIBCXX_USE_CXX11_ABI
126  return std::regex_replace(
127  realname.get(),
128  std::regex{"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
129  "std::string" );
130 #else
131  return std::string{realname.get()};
132 #endif
133  }
134  std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
135 
138  static Registry r;
139  return r;
140  }
141 
142  Registry::Registry() : m_initialized( false ) {}
143 
146  if ( m_initialized ) return;
147  m_initialized = true;
148 #if defined( _WIN32 )
149  const char* envVar = "PATH";
150  const char sep = ';';
151 #elif defined( __APPLE__ )
152  const char* envVar = "DYLD_LIBRARY_PATH";
153  const char sep = ':';
154 #else
155  const char* envVar = "LD_LIBRARY_PATH";
156  const char sep = ':';
157 #endif
158  char* search_path = ::getenv( envVar );
159  if ( search_path ) {
160  logger().debug( std::string( "searching factories in " ) + envVar );
161  std::string path( search_path );
162  std::string::size_type pos = 0;
163  std::string::size_type newpos = 0;
164  while ( pos != std::string::npos ) {
165  std::string dirName;
166  // get the next entry in the path
167  newpos = path.find( sep, pos );
168  if ( newpos != std::string::npos ) {
169  dirName = path.substr( pos, newpos - pos );
170  pos = newpos + 1;
171  } else {
172  dirName = path.substr( pos );
173  pos = newpos;
174  }
175  logger().debug( std::string( " looking into " ) + dirName );
176  // look for files called "*.components" in the directory
177  DIR* dir = opendir( dirName.c_str() );
178  if ( dir ) {
179  struct dirent* entry;
180  while ( ( entry = readdir( dir ) ) ) {
181  std::string name( entry->d_name );
182  // check if the file name ends with ".components"
183  std::string::size_type extpos = name.find( ".components" );
184  if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
185  std::string fullPath = ( dirName + '/' + name );
186  { // check if it is a regular file
187  struct stat buf;
188  if ( 0 != ::stat( fullPath.c_str(), &buf ) )
189  continue;
190  else if ( !S_ISREG( buf.st_mode ) )
191  continue;
192  }
193  // read the file
194  logger().debug( std::string( " reading " ) + name );
195  std::ifstream factories{fullPath};
196  std::string line;
197  int factoriesCount = 0;
198  int lineCount = 0;
199  while ( !factories.eof() ) {
200  ++lineCount;
201  std::getline( factories, line );
202  trim( line );
203  // skip empty lines and lines starting with '#'
204  if ( line.empty() || line[0] == '#' ) continue;
205  // only accept "v1" factories
206  if ( line.substr( 0, 4 ) == "v1::" )
207  line = line.substr( 4 );
208  else
209  continue;
210  // look for the separator
211  auto pos = line.find( ':' );
212  if ( pos == std::string::npos ) {
213  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
214  continue;
215  }
216  const std::string lib( line, 0, pos );
217  const std::string fact( line, pos + 1 );
218  m_factories.emplace( fact, FactoryInfo( lib ) );
219 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
220  // add an alias for the factory using the Reflex convention
221  std::string old_name = old_style_name( fact );
222  if ( fact != old_name ) {
223  FactoryInfo old_info( lib );
224  old_info.properties["ReflexName"] = "true";
225  m_factories.emplace( old_name, old_info );
226  }
227 #endif
228  ++factoriesCount;
229  }
230  if ( logger().level() <= Logger::Debug ) {
231  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
232  }
233  }
234  }
235  closedir( dir );
236  }
237  }
238  }
239  }
240 
241  Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
242  const std::string& rtype, const std::string& className,
243  const Properties& props ) {
245  FactoryMap& facts = factories();
246  auto entry = facts.find( id );
247  if ( entry == facts.end() ) {
248  // this factory was not known yet
249  entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
250  } else {
251  // do not replace an existing factory with a new one
252  if ( !entry->second.ptr ) entry->second.ptr = factory;
253  factoryInfoSetHelper( entry->second.type, type, "type", id );
254  factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
255  factoryInfoSetHelper( entry->second.className, className, "class", id );
256  }
257 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
258  // add an alias for the factory using the Reflex convention
259  std::string old_name = old_style_name( id );
260  if ( id != old_name )
261  add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
262 #endif
263  return entry->second;
264  }
265 
266  void* Registry::get( const std::string& id, const std::string& type ) const {
268  const FactoryMap& facts = factories();
269  auto f = facts.find( id );
270  if ( f != facts.end() ) {
271 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
272  const Properties& props = f->second.properties;
273  if ( props.find( "ReflexName" ) != props.end() )
274  logger().warning( "requesting factory via old name '" + id +
275  "'"
276  "use '" +
277  f->second.className + "' instead" );
278 #endif
279  if ( !f->second.ptr ) {
280  if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
281  logger().warning( "cannot load " + f->second.library + " for factory " + id );
282  char* dlmsg = dlerror();
283  if ( dlmsg ) logger().warning( dlmsg );
284  return nullptr;
285  }
286  f = facts.find( id ); // ensure that the iterator is valid
287  }
288  if ( f->second.type == type ) return f->second.ptr;
289  logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
290  " instead of " + demangle( type ) );
291  }
292  return nullptr; // factory not found
293  }
294 
295  const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const {
297  static const FactoryInfo unknown( "unknown" );
298  const FactoryMap& facts = factories();
299  auto f = facts.find( id );
300  return ( f != facts.end() ) ? f->second : unknown;
301  }
302 
303  Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) {
305  FactoryMap& facts = factories();
306  auto f = facts.find( id );
307  if ( f != facts.end() ) f->second.properties[k] = v;
308  return *this;
309  }
310 
311  std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
313  std::set<KeyType> l;
314  for ( const auto& f : factories() ) {
315  if ( f.second.ptr ) l.insert( f.first );
316  }
317  return l;
318  }
319 
320  void Logger::report( Level lvl, const std::string& msg ) {
321  static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
322  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
323  }
324 
325  static auto s_logger = std::make_unique<Logger>();
326  Logger& logger() { return *s_logger; }
327  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
328 
329  } // namespace Details
330 
331  void SetDebug( int debugLevel ) {
332  using namespace Details;
333  Logger& l = logger();
334  if ( debugLevel > 1 )
335  l.setLevel( Logger::Debug );
336  else if ( debugLevel > 0 )
337  l.setLevel( Logger::Info );
338  else
339  l.setLevel( Logger::Warning );
340  }
341 
342  int Debug() {
343  using namespace Details;
344  switch ( logger().level() ) {
345  case Logger::Debug:
346  return 2;
347  case Logger::Info:
348  return 1;
349  default:
350  return 0;
351  }
352  }
353  }
354  } // namespace PluginService
355 } // namespace Gaudi
REG_SCOPE_LOCK
#define REG_SCOPE_LOCK
Definition: PluginServiceV1.cpp:29
Gaudi::PluginService::v1::Details::Registry::add
FactoryInfo & add(const I &id, typename F::FuncType ptr)
Add a factory to the database.
Definition: PluginServiceDetailsV1.h:121
Gaudi::PluginService::v1::Details::Registry::Registry
Registry()
Definition: PluginServiceV1.cpp:142
Gaudi::PluginService::v1::Details::Registry
In-memory database of the loaded factories.
Definition: PluginServiceDetailsV1.h:83
Gaudi::PluginService::v1::Details::Registry::get
void * get(const std::string &id, const std::string &type) const
Retrieve the factory for the given id.
Definition: PluginServiceV1.cpp:266
Gaudi::PluginService::v1::Details::Logger::Info
@ Info
Definition: PluginServiceDetailsV1.h:188
v
View * v
Definition: MultiView.cpp:28
Gaudi::PluginService::v1::Details::Registry::FactoryInfo::properties
Properties properties
Definition: PluginServiceDetailsV1.h:105
Gaudi::PluginService::v1::Details::Registry::loadedFactoryNames
std::set< KeyType > loadedFactoryNames() const
Return a list of all the known and loaded factories.
Definition: PluginServiceV1.cpp:311
Gaudi::PluginService::v1::Details::Logger::level
Level level() const
Definition: PluginServiceDetailsV1.h:191
Gaudi::PluginService::v1::Details::Registry::m_initialized
bool m_initialized
Flag recording if the registry has been initialized or not.
Definition: PluginServiceDetailsV1.h:176
Gaudi::PluginService::v1::Details::logger
GAUDIPS_API Logger & logger()
Return the current logger instance.
Definition: PluginServiceV1.cpp:326
Gaudi::PluginService::v1::Details::Registry::initialize
void initialize()
Definition: PluginServiceV1.cpp:144
Gaudi::PluginService::v1::Details::Registry::getInfo
const FactoryInfo & getInfo(const std::string &id) const
Retrieve the FactoryInfo object for an id.
Definition: PluginServiceV1.cpp:295
Gaudi::PluginService::v1::Details::Logger::debug
void debug(const std::string &msg)
Definition: PluginServiceDetailsV1.h:194
Gaudi::PluginService::v1::Details::Registry::Properties
std::map< KeyType, std::string > Properties
Type used for the properties implementation.
Definition: PluginServiceDetailsV1.h:88
Gaudi::PluginService::v1::Details::Logger::report
virtual void report(Level lvl, const std::string &msg)
Definition: PluginServiceV1.cpp:320
Gaudi::PluginService::v1::Details::Logger::Level
Level
Definition: PluginServiceDetailsV1.h:188
Gaudi::PluginService::v1::Details::Logger::Debug
@ Debug
Definition: PluginServiceDetailsV1.h:188
PluginService.h
Gaudi::PluginService::v1::Details::Logger::Warning
@ Warning
Definition: PluginServiceDetailsV1.h:188
Gaudi::PluginService::v1::Exception::Exception
Exception(std::string msg)
Definition: PluginServiceV1.cpp:111
Gaudi::PluginService::v1::Details::Registry::factories
const FactoryMap & factories() const
Return the known factories (loading the list if not yet done).
Definition: PluginServiceDetailsV1.h:146
Gaudi::PluginService::v1::Details::Registry::FactoryInfo
Definition: PluginServiceDetailsV1.h:90
Gaudi::PluginService::v1::SetDebug
GAUDIPS_API void SetDebug(int debugLevel)
Backward compatibility with Reflex.
Definition: PluginServiceV1.cpp:331
Gaudi::PluginService::v1::Details::getCreator
GAUDIPS_API void * getCreator(const std::string &id, const std::string &type)
Definition: PluginServiceV1.cpp:116
Gaudi::PluginService::v1::Details::Logger::warning
void warning(const std::string &msg)
Definition: PluginServiceDetailsV1.h:195
Gaudi::PluginService::v1::Exception::m_msg
std::string m_msg
Definition: PluginServiceV1.h:80
GAUDI_PLUGIN_SERVICE_V1_INLINE
#define GAUDI_PLUGIN_SERVICE_V1_INLINE
Definition: PluginServiceCommon.h:21
Gaudi::PluginService::v1::Details::Registry::m_factories
FactoryMap m_factories
Internal storage for factories.
Definition: PluginServiceDetailsV1.h:179
Gaudi::PluginService::v1::Details::Registry::addProperty
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.)
Definition: PluginServiceV1.cpp:303
Gaudi::PluginService::v1::Exception::~Exception
~Exception() override
Definition: PluginServiceV1.cpp:112
std
Definition: Plugins.h:30
SINGLETON_LOCK
#define SINGLETON_LOCK
Definition: PluginServiceV1.cpp:34
Gaudi::PluginService::v1::Details::setLogger
GAUDIPS_API void setLogger(Logger *logger)
Definition: PluginServiceV1.cpp:327
Gaudi::PluginService::v1::Details::Registry::FactoryMap
std::map< KeyType, FactoryInfo > FactoryMap
Type used for the database implementation.
Definition: PluginServiceDetailsV1.h:114
Gaudi::PluginService::v1::Exception::what
const char * what() const override
Definition: PluginServiceV1.cpp:113
Gaudi
Definition: PluginServiceDetailsV1.h:27
Gaudi::PluginService::v1::Details::demangle
GAUDIPS_API std::string demangle(const std::type_info &id)
Definition: PluginServiceV1.cpp:134
Gaudi::PluginService::v1::Details::Registry::instance
static Registry & instance()
Retrieve the singleton instance of Registry.
Definition: PluginServiceV1.cpp:136
Gaudi::PluginService::v1::Debug
GAUDIPS_API int Debug()
Backward compatibility with Reflex.
Definition: PluginServiceV1.cpp:342
Gaudi::PluginService::v1::Details::Logger
Simple logging class, just to provide a default implementation.
Definition: PluginServiceDetailsV1.h:186