The Gaudi Framework  v32r1 (f65d50dc)
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
15 #include <Gaudi/PluginService.h>
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 {
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 
121  int status;
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  stat( fullPath.c_str(), &buf );
189  if ( !S_ISREG( buf.st_mode ) ) continue;
190  }
191  // read the file
192  logger().debug( std::string( " reading " ) + name );
193  std::ifstream factories{fullPath};
195  int factoriesCount = 0;
196  int lineCount = 0;
197  while ( !factories.eof() ) {
198  ++lineCount;
200  trim( line );
201  // skip empty lines and lines starting with '#'
202  if ( line.empty() || line[0] == '#' ) continue;
203  // only accept "v1" factories
204  if ( line.substr( 0, 4 ) == "v1::" )
205  line = line.substr( 4 );
206  else
207  continue;
208  // look for the separator
209  auto pos = line.find( ':' );
210  if ( pos == std::string::npos ) {
211  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
212  continue;
213  }
214  const std::string lib( line, 0, pos );
215  const std::string fact( line, pos + 1 );
216  m_factories.emplace( fact, FactoryInfo( lib ) );
217 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
218  // add an alias for the factory using the Reflex convention
219  std::string old_name = old_style_name( fact );
220  if ( fact != old_name ) {
221  FactoryInfo old_info( lib );
222  old_info.properties["ReflexName"] = "true";
223  m_factories.emplace( old_name, old_info );
224  }
225 #endif
226  ++factoriesCount;
227  }
228  if ( logger().level() <= Logger::Debug ) {
229  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
230  }
231  }
232  }
233  closedir( dir );
234  }
235  }
236  }
237  }
238 
239  Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
240  const std::string& rtype, const std::string& className,
241  const Properties& props ) {
243  FactoryMap& facts = factories();
244  auto entry = facts.find( id );
245  if ( entry == facts.end() ) {
246  // this factory was not known yet
247  entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
248  } else {
249  // do not replace an existing factory with a new one
250  if ( !entry->second.ptr ) entry->second.ptr = factory;
251  factoryInfoSetHelper( entry->second.type, type, "type", id );
252  factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
253  factoryInfoSetHelper( entry->second.className, className, "class", id );
254  }
255 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
256  // add an alias for the factory using the Reflex convention
257  std::string old_name = old_style_name( id );
258  if ( id != old_name )
259  add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
260 #endif
261  return entry->second;
262  }
263 
264  void* Registry::get( const std::string& id, const std::string& type ) const {
266  const FactoryMap& facts = factories();
267  auto f = facts.find( id );
268  if ( f != facts.end() ) {
269 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
270  const Properties& props = f->second.properties;
271  if ( props.find( "ReflexName" ) != props.end() )
272  logger().warning( "requesting factory via old name '" + id +
273  "'"
274  "use '" +
275  f->second.className + "' instead" );
276 #endif
277  if ( !f->second.ptr ) {
278  if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
279  logger().warning( "cannot load " + f->second.library + " for factory " + id );
280  char* dlmsg = dlerror();
281  if ( dlmsg ) logger().warning( dlmsg );
282  return nullptr;
283  }
284  f = facts.find( id ); // ensure that the iterator is valid
285  }
286  if ( f->second.type == type ) return f->second.ptr;
287  logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
288  " instead of " + demangle( type ) );
289  }
290  return nullptr; // factory not found
291  }
292 
295  static const FactoryInfo unknown( "unknown" );
296  const FactoryMap& facts = factories();
297  auto f = facts.find( id );
298  return ( f != facts.end() ) ? f->second : unknown;
299  }
300 
303  FactoryMap& facts = factories();
304  auto f = facts.find( id );
305  if ( f != facts.end() ) f->second.properties[k] = v;
306  return *this;
307  }
308 
312  for ( const auto& f : factories() ) {
313  if ( f.second.ptr ) l.insert( f.first );
314  }
315  return l;
316  }
317 
318  void Logger::report( Level lvl, const std::string& msg ) {
319  static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
320  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
321  }
322 
323  static auto s_logger = std::make_unique<Logger>();
324  Logger& logger() { return *s_logger; }
325  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
326 
327  } // namespace Details
328 
329  void SetDebug( int debugLevel ) {
330  using namespace Details;
331  Logger& l = logger();
332  if ( debugLevel > 1 )
333  l.setLevel( Logger::Debug );
334  else if ( debugLevel > 0 )
335  l.setLevel( Logger::Info );
336  else
337  l.setLevel( Logger::Warning );
338  }
339 
340  int Debug() {
341  using namespace Details;
342  switch ( logger().level() ) {
343  case Logger::Debug:
344  return 2;
345  case Logger::Info:
346  return 1;
347  default:
348  return 0;
349  }
350  }
351  }
352  } // namespace PluginService
353 } // namespace Gaudi
const char * what() const override
const FactoryMap & factories() const
Return the known factories (loading the list if not yet done).
FactoryInfo & add(const I &id, typename F::FuncType ptr)
Add a factory to the database.
GAUDIPS_API void * getCreator(const std::string &id, const std::string &type)
Function used to load a specific factory function.
virtual void report(Level lvl, const std::string &msg)
Simple logging class, just to provide a default implementation.
static Registry & instance()
Retrieve the singleton instance of Registry.
FactoryMap m_factories
Internal storage for factories.
GAUDIPS_API void setLogger(Logger *logger)
Set the logger instance to use.
T getline(T... args)
T to_string(T... args)
T endl(T... args)
STL namespace.
T end(T... args)
#define GAUDI_PLUGIN_SERVICE_V1_INLINE
STL class.
void * get(const std::string &id, const std::string &type) const
Retrieve the factory for the given id.
GAUDIPS_API Logger & logger()
Return the current logger instance.
T regex_replace(T... args)
In-memory database of the loaded factories.
std::set< KeyType > loadedFactoryNames() const
Return a list of all the known and loaded factories.
#define REG_SCOPE_LOCK
dictionary l
Definition: gaudirun.py:521
T find_if_not(T... args)
STL class.
GAUDIPS_API int Debug()
Backward compatibility with Reflex.
STL class.
GAUDIPS_API std::string demangle(const std::type_info &id)
Return a canonical name for type_info object (implementation borrowed from GaudiKernel/System).
T c_str(T... args)
T emplace(T... args)
const FactoryInfo & getInfo(const std::string &id) const
Retrieve the FactoryInfo object for an id.
string s
Definition: gaudirun.py:316
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.)
void initialize()
Initialize the registry loading the list of factories from the .component files in the library search...
Registry()
Private constructor for the singleton pattern.
T for_each(T... args)
GAUDIPS_API void SetDebug(int debugLevel)
Backward compatibility with Reflex.
#define SINGLETON_LOCK
T isspace(T... args)
Header file for std:chrono::duration-based Counters.
Definition: __init__.py:1
STL class.
bool m_initialized
Flag recording if the registry has been initialized or not.