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