The Gaudi Framework  v33r1 (b1225454)
PluginServiceV1.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 2013-2019 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
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 namespace {
30  std::mutex registrySingletonMutex;
31 }
32 
33 #include <algorithm>
34 
35 namespace {
36  // string trimming functions taken from
37  // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
38 
39  constexpr struct is_space_t {
40  bool operator()( int i ) const { return std::isspace( i ); }
41  } is_space{};
42 
43  // trim from start
44  static inline std::string& ltrim( std::string& s ) {
45  s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), is_space ) );
46  return s;
47  }
48 
49  // trim from end
50  static inline std::string& rtrim( std::string& s ) {
51  s.erase( std::find_if_not( s.rbegin(), s.rend(), is_space ).base(), s.end() );
52  return s;
53  }
54  // trim from both ends
55  static inline std::string& trim( std::string& s ) { return ltrim( rtrim( s ) ); }
56 } // namespace
57 
58 namespace {
62  inline void factoryInfoSetHelper( std::string& dest, const std::string value, const std::string& desc,
63  const std::string& id ) {
64  if ( dest.empty() ) {
65  dest = value;
66  } else if ( dest != value ) {
67  Gaudi::PluginService::Details::logger().warning( "new factory loaded for '" + id + "' with different " + desc +
68  ": " + dest + " != " + value );
69  }
70  }
71 
72  struct OldStyleCnv {
74  void operator()( const char c ) {
75  switch ( c ) {
76  case '<':
77  case '>':
78  case ',':
79  case '(':
80  case ')':
81  case ':':
82  case '.':
83  name.push_back( '_' );
84  break;
85  case '&':
86  name.push_back( 'r' );
87  break;
88  case '*':
89  name.push_back( 'p' );
90  break;
91  case ' ':
92  break;
93  default:
94  name.push_back( c );
95  break;
96  }
97  }
98  };
100  std::string old_style_name( const std::string& name ) {
101  return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
102  }
103 } // namespace
104 
105 namespace Gaudi {
106  namespace PluginService {
107  GAUDI_PLUGIN_SERVICE_V1_INLINE namespace v1 {
108  Exception::Exception( std::string msg ) : m_msg( std::move( msg ) ) {}
110  const char* Exception::what() const throw() { return m_msg.c_str(); }
111 
112  namespace Details {
113  void* getCreator( const std::string& id, const std::string& type ) {
114  return Registry::instance().get( id, type );
115  }
116 
118  int status;
120  abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
121  if ( !realname ) return id;
122 #if _GLIBCXX_USE_CXX11_ABI
123  return std::regex_replace(
124  realname.get(),
125  std::regex{"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
126  "std::string" );
127 #else
128  return std::string{realname.get()};
129 #endif
130  }
131  std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
132 
134  auto _guard = std::scoped_lock{::registrySingletonMutex};
135  static Registry r;
136  return r;
137  }
138 
139  Registry::Registry() : m_initialized( false ) {}
140 
142  auto _guard = std::scoped_lock{m_mutex};
143  if ( m_initialized ) return;
144  m_initialized = true;
145 #if defined( _WIN32 )
146  const char* envVar = "PATH";
147  const char sep = ';';
148 #else
149  const char* envVar = "LD_LIBRARY_PATH";
150  const char sep = ':';
151 #endif
152  char* search_path = ::getenv( envVar );
153  if ( search_path ) {
154  logger().debug( std::string( "searching factories in " ) + envVar );
155  std::string path( search_path );
156  std::string::size_type pos = 0;
157  std::string::size_type newpos = 0;
158  while ( pos != std::string::npos ) {
159  std::string dirName;
160  // get the next entry in the path
161  newpos = path.find( sep, pos );
162  if ( newpos != std::string::npos ) {
163  dirName = path.substr( pos, newpos - pos );
164  pos = newpos + 1;
165  } else {
166  dirName = path.substr( pos );
167  pos = newpos;
168  }
169  logger().debug( std::string( " looking into " ) + dirName );
170  // look for files called "*.components" in the directory
171  DIR* dir = opendir( dirName.c_str() );
172  if ( dir ) {
173  struct dirent* entry;
174  while ( ( entry = readdir( dir ) ) ) {
175  std::string name( entry->d_name );
176  // check if the file name ends with ".components"
177  std::string::size_type extpos = name.find( ".components" );
178  if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
179  std::string fullPath = ( dirName + '/' + name );
180  { // check if it is a regular file
181  struct stat buf;
182  stat( fullPath.c_str(), &buf );
183  if ( !S_ISREG( buf.st_mode ) ) continue;
184  }
185  // read the file
186  logger().debug( std::string( " reading " ) + name );
187  std::ifstream factories{fullPath};
189  int factoriesCount = 0;
190  int lineCount = 0;
191  while ( !factories.eof() ) {
192  ++lineCount;
194  trim( line );
195  // skip empty lines and lines starting with '#'
196  if ( line.empty() || line[0] == '#' ) continue;
197  // only accept "v1" factories
198  if ( line.substr( 0, 4 ) == "v1::" )
199  line = line.substr( 4 );
200  else
201  continue;
202  // look for the separator
203  auto pos = line.find( ':' );
204  if ( pos == std::string::npos ) {
205  logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
206  continue;
207  }
208  const std::string lib( line, 0, pos );
209  const std::string fact( line, pos + 1 );
210  m_factories.emplace( fact, FactoryInfo( lib ) );
211 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
212  // add an alias for the factory using the Reflex convention
213  std::string old_name = old_style_name( fact );
214  if ( fact != old_name ) {
215  FactoryInfo old_info( lib );
216  old_info.properties["ReflexName"] = "true";
217  m_factories.emplace( old_name, old_info );
218  }
219 #endif
220  ++factoriesCount;
221  }
222  if ( logger().level() <= Logger::Debug ) {
223  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
224  }
225  }
226  }
227  closedir( dir );
228  }
229  }
230  }
231  }
232 
233  Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
234  const std::string& rtype, const std::string& className,
235  const Properties& props ) {
236  auto _guard = std::scoped_lock{m_mutex};
237  FactoryMap& facts = factories();
238  auto entry = facts.find( id );
239  if ( entry == facts.end() ) {
240  // this factory was not known yet
241  entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
242  } else {
243  // do not replace an existing factory with a new one
244  if ( !entry->second.ptr ) entry->second.ptr = factory;
245  factoryInfoSetHelper( entry->second.type, type, "type", id );
246  factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
247  factoryInfoSetHelper( entry->second.className, className, "class", id );
248  }
249 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
250  // add an alias for the factory using the Reflex convention
251  std::string old_name = old_style_name( id );
252  if ( id != old_name )
253  add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
254 #endif
255  return entry->second;
256  }
257 
258  void* Registry::get( const std::string& id, const std::string& type ) const {
259  auto _guard = std::scoped_lock{m_mutex};
260  const FactoryMap& facts = factories();
261  auto f = facts.find( id );
262  if ( f != facts.end() ) {
263 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
264  const Properties& props = f->second.properties;
265  if ( props.find( "ReflexName" ) != props.end() )
266  logger().warning( "requesting factory via old name '" + id +
267  "'"
268  "use '" +
269  f->second.className + "' instead" );
270 #endif
271  if ( !f->second.ptr ) {
272  if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
273  logger().warning( "cannot load " + f->second.library + " for factory " + id );
274  char* dlmsg = dlerror();
275  if ( dlmsg ) logger().warning( dlmsg );
276  return nullptr;
277  }
278  f = facts.find( id ); // ensure that the iterator is valid
279  }
280  if ( f->second.type == type ) return f->second.ptr;
281  logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
282  " instead of " + demangle( type ) );
283  }
284  return nullptr; // factory not found
285  }
286 
288  auto _guard = std::scoped_lock{m_mutex};
289  static const FactoryInfo unknown( "unknown" );
290  const FactoryMap& facts = factories();
291  auto f = facts.find( id );
292  return ( f != facts.end() ) ? f->second : unknown;
293  }
294 
296  auto _guard = std::scoped_lock{m_mutex};
297  FactoryMap& facts = factories();
298  auto f = facts.find( id );
299  if ( f != facts.end() ) f->second.properties[k] = v;
300  return *this;
301  }
302 
304  auto _guard = std::scoped_lock{m_mutex};
306  for ( const auto& f : factories() ) {
307  if ( f.second.ptr ) l.insert( f.first );
308  }
309  return l;
310  }
311 
312  void Logger::report( Level lvl, const std::string& msg ) {
313  static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
314  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
315  }
316 
317  static auto s_logger = std::make_unique<Logger>();
318  Logger& logger() { return *s_logger; }
319  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
320 
321  } // namespace Details
322 
323  void SetDebug( int debugLevel ) {
324  using namespace Details;
325  Logger& l = logger();
326  if ( debugLevel > 1 )
327  l.setLevel( Logger::Debug );
328  else if ( debugLevel > 0 )
329  l.setLevel( Logger::Info );
330  else
331  l.setLevel( Logger::Warning );
332  }
333 
334  int Debug() {
335  using namespace Details;
336  switch ( logger().level() ) {
337  case Logger::Debug:
338  return 2;
339  case Logger::Info:
340  return 1;
341  default:
342  return 0;
343  }
344  }
345  }
346  } // namespace PluginService
347 } // 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.
dictionary l
Definition: gaudirun.py:543
T find_if_not(T... args)
STL class.
GAUDIPS_API int Debug()
Backward compatibility with Reflex.
std::recursive_mutex m_mutex
Mutex used to control concurrent access to the internal data.
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:328
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.
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.