The Gaudi Framework  v32r2 (46d42edc)
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 #else
152  const char* envVar = "LD_LIBRARY_PATH";
153  const char sep = ':';
154 #endif
155  char* search_path = ::getenv( envVar );
156  if ( search_path ) {
157  logger().debug( std::string( "searching factories in " ) + envVar );
158  std::string path( search_path );
159  std::string::size_type pos = 0;
160  std::string::size_type newpos = 0;
161  while ( pos != std::string::npos ) {
162  std::string dirName;
163  // get the next entry in the path
164  newpos = path.find( sep, pos );
165  if ( newpos != std::string::npos ) {
166  dirName = path.substr( pos, newpos - pos );
167  pos = newpos + 1;
168  } else {
169  dirName = path.substr( pos );
170  pos = newpos;
171  }
172  logger().debug( std::string( " looking into " ) + dirName );
173  // look for files called "*.components" in the directory
174  DIR* dir = opendir( dirName.c_str() );
175  if ( dir ) {
176  struct dirent* entry;
177  while ( ( entry = readdir( dir ) ) ) {
178  std::string name( entry->d_name );
179  // check if the file name ends with ".components"
180  std::string::size_type extpos = name.find( ".components" );
181  if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
182  std::string fullPath = ( dirName + '/' + name );
183  { // check if it is a regular file
184  struct stat buf;
185  stat( fullPath.c_str(), &buf );
186  if ( !S_ISREG( buf.st_mode ) ) continue;
187  }
188  // read the file
189  logger().debug( std::string( " reading " ) + name );
190  std::ifstream factories{fullPath};
192  int factoriesCount = 0;
193  int lineCount = 0;
194  while ( !factories.eof() ) {
195  ++lineCount;
197  trim( line );
198  // skip empty lines and lines starting with '#'
199  if ( line.empty() || line[0] == '#' ) continue;
200  // only accept "v1" factories
201  if ( line.substr( 0, 4 ) == "v1::" )
202  line = line.substr( 4 );
203  else
204  continue;
205  // look for the separator
206  auto pos = line.find( ':' );
207  if ( pos == std::string::npos ) {
208  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
209  continue;
210  }
211  const std::string lib( line, 0, pos );
212  const std::string fact( line, pos + 1 );
213  m_factories.emplace( fact, FactoryInfo( lib ) );
214 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
215  // add an alias for the factory using the Reflex convention
216  std::string old_name = old_style_name( fact );
217  if ( fact != old_name ) {
218  FactoryInfo old_info( lib );
219  old_info.properties["ReflexName"] = "true";
220  m_factories.emplace( old_name, old_info );
221  }
222 #endif
223  ++factoriesCount;
224  }
225  if ( logger().level() <= Logger::Debug ) {
226  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
227  }
228  }
229  }
230  closedir( dir );
231  }
232  }
233  }
234  }
235 
236  Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
237  const std::string& rtype, const std::string& className,
238  const Properties& props ) {
240  FactoryMap& facts = factories();
241  auto entry = facts.find( id );
242  if ( entry == facts.end() ) {
243  // this factory was not known yet
244  entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
245  } else {
246  // do not replace an existing factory with a new one
247  if ( !entry->second.ptr ) entry->second.ptr = factory;
248  factoryInfoSetHelper( entry->second.type, type, "type", id );
249  factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
250  factoryInfoSetHelper( entry->second.className, className, "class", id );
251  }
252 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
253  // add an alias for the factory using the Reflex convention
254  std::string old_name = old_style_name( id );
255  if ( id != old_name )
256  add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
257 #endif
258  return entry->second;
259  }
260 
261  void* Registry::get( const std::string& id, const std::string& type ) const {
263  const FactoryMap& facts = factories();
264  auto f = facts.find( id );
265  if ( f != facts.end() ) {
266 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
267  const Properties& props = f->second.properties;
268  if ( props.find( "ReflexName" ) != props.end() )
269  logger().warning( "requesting factory via old name '" + id +
270  "'"
271  "use '" +
272  f->second.className + "' instead" );
273 #endif
274  if ( !f->second.ptr ) {
275  if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
276  logger().warning( "cannot load " + f->second.library + " for factory " + id );
277  char* dlmsg = dlerror();
278  if ( dlmsg ) logger().warning( dlmsg );
279  return nullptr;
280  }
281  f = facts.find( id ); // ensure that the iterator is valid
282  }
283  if ( f->second.type == type ) return f->second.ptr;
284  logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
285  " instead of " + demangle( type ) );
286  }
287  return nullptr; // factory not found
288  }
289 
292  static const FactoryInfo unknown( "unknown" );
293  const FactoryMap& facts = factories();
294  auto f = facts.find( id );
295  return ( f != facts.end() ) ? f->second : unknown;
296  }
297 
300  FactoryMap& facts = factories();
301  auto f = facts.find( id );
302  if ( f != facts.end() ) f->second.properties[k] = v;
303  return *this;
304  }
305 
309  for ( const auto& f : factories() ) {
310  if ( f.second.ptr ) l.insert( f.first );
311  }
312  return l;
313  }
314 
315  void Logger::report( Level lvl, const std::string& msg ) {
316  static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
317  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
318  }
319 
320  static auto s_logger = std::make_unique<Logger>();
321  Logger& logger() { return *s_logger; }
322  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
323 
324  } // namespace Details
325 
326  void SetDebug( int debugLevel ) {
327  using namespace Details;
328  Logger& l = logger();
329  if ( debugLevel > 1 )
330  l.setLevel( Logger::Debug );
331  else if ( debugLevel > 0 )
332  l.setLevel( Logger::Info );
333  else
334  l.setLevel( Logger::Warning );
335  }
336 
337  int Debug() {
338  using namespace Details;
339  switch ( logger().level() ) {
340  case Logger::Debug:
341  return 2;
342  case Logger::Info:
343  return 1;
344  default:
345  return 0;
346  }
347  }
348  }
349  } // namespace PluginService
350 } // 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:523
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:318
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.