The Gaudi Framework  master (2e52acd2)
Loading...
Searching...
No Matches
PluginServiceV1.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 2013-2026 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
29namespace {
30 std::mutex registrySingletonMutex;
31}
32
33#include <algorithm>
34
35namespace {
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
58namespace {
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 {
73 std::string name;
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
105namespace Gaudi {
106 namespace PluginService {
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
117 std::string demangle( const std::string& id ) {
118 int status;
119 auto realname = std::unique_ptr<char, decltype( free )*>(
120 abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
121 if ( !realname ) return id;
122 return std::regex_replace(
123 realname.get(),
124 std::regex{ "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?" },
125 "std::string" );
126 }
127 std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
128
130 auto _guard = std::scoped_lock{ ::registrySingletonMutex };
131 static Registry r;
132 return r;
133 }
134
136
138 auto _guard = std::scoped_lock{ m_mutex };
139 if ( m_initialized ) return;
140 m_initialized = true;
141#if defined( __APPLE__ )
142 const auto envVars = { "GAUDI_PLUGIN_PATH", "DYLD_LIBRARY_PATH" };
143 const char sep = ':';
144#else
145 const auto envVars = { "GAUDI_PLUGIN_PATH", "LD_LIBRARY_PATH" };
146 const char sep = ':';
147#endif
148
149 for ( const auto& envVar : envVars ) {
150 char* search_path = ::getenv( envVar );
151 if ( search_path ) {
152 logger().debug( std::string( "searching factories in " ) + envVar );
153 std::string path( search_path );
154 std::string::size_type pos = 0;
155 std::string::size_type newpos = 0;
156 while ( pos != std::string::npos ) {
157 std::string dirName;
158 // get the next entry in the path
159 newpos = path.find( sep, pos );
160 if ( newpos != std::string::npos ) {
161 dirName = path.substr( pos, newpos - pos );
162 pos = newpos + 1;
163 } else {
164 dirName = path.substr( pos );
165 pos = newpos;
166 }
167 logger().debug( std::string( " looking into " ) + dirName );
168 // look for files called "*.components" in the directory
169 DIR* dir = opendir( dirName.c_str() );
170 if ( dir ) {
171 struct dirent* entry;
172 while ( ( entry = readdir( dir ) ) ) {
173 std::string name( entry->d_name );
174 // check if the file name ends with ".components"
175 std::string::size_type extpos = name.find( ".components" );
176 if ( ( extpos != std::string::npos ) && ( ( extpos + 11 ) == name.size() ) ) {
177 std::string fullPath = ( dirName + '/' + name );
178 { // check if it is a regular file
179 struct stat buf;
180 stat( fullPath.c_str(), &buf );
181 if ( !S_ISREG( buf.st_mode ) ) continue;
182 }
183 // read the file
184 logger().debug( std::string( " reading " ) + name );
185 std::ifstream factories{ fullPath };
186 std::string line;
187 int factoriesCount = 0;
188 int lineCount = 0;
189 while ( !factories.eof() ) {
190 ++lineCount;
191 std::getline( factories, line );
192 trim( line );
193 // skip empty lines and lines starting with '#'
194 if ( line.empty() || line[0] == '#' ) continue;
195 // only accept "v1" factories
196 if ( line.substr( 0, 4 ) == "v1::" )
197 line = line.substr( 4 );
198 else
199 continue;
200 // look for the separator
201 auto pos = line.find( ':' );
202 if ( pos == std::string::npos ) {
203 logger().warning( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
204 continue;
205 }
206 const std::string lib( line, 0, pos );
207 const std::string fact( line, pos + 1 );
208 m_factories.emplace( fact, FactoryInfo( lib ) );
209#ifdef GAUDI_REFLEX_COMPONENT_ALIASES
210 // add an alias for the factory using the Reflex convention
211 std::string old_name = old_style_name( fact );
212 if ( fact != old_name ) {
213 FactoryInfo old_info( lib );
214 old_info.properties["ReflexName"] = "true";
215 m_factories.emplace( old_name, old_info );
216 }
217#endif
218 ++factoriesCount;
219 }
220 if ( logger().level() <= Logger::Debug ) {
221 logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
222 }
223 }
224 }
225 closedir( dir );
226 }
227 }
228 }
229 }
230 }
231
232 Registry::FactoryInfo& Registry::add( const std::string& id, void* factory, const std::string& type,
233 const std::string& rtype, const std::string& className,
234 const Properties& props ) {
235 auto _guard = std::scoped_lock{ m_mutex };
236 FactoryMap& facts = factories();
237 auto entry = facts.find( id );
238 if ( entry == facts.end() ) {
239 // this factory was not known yet
240 entry = facts.emplace( id, FactoryInfo( "unknown", factory, type, rtype, className, props ) ).first;
241 } else {
242 // do not replace an existing factory with a new one
243 if ( !entry->second.ptr ) entry->second.ptr = factory;
244 factoryInfoSetHelper( entry->second.type, type, "type", id );
245 factoryInfoSetHelper( entry->second.rtype, rtype, "return type", id );
246 factoryInfoSetHelper( entry->second.className, className, "class", id );
247 }
248#ifdef GAUDI_REFLEX_COMPONENT_ALIASES
249 // add an alias for the factory using the Reflex convention
250 std::string old_name = old_style_name( id );
251 if ( id != old_name )
252 add( old_name, factory, type, rtype, className, props ).properties["ReflexName"] = "true";
253#endif
254 return entry->second;
255 }
256
257 void* Registry::get( const std::string& id, const std::string& type ) const {
258 auto _guard = std::scoped_lock{ m_mutex };
259 const FactoryMap& facts = factories();
260 auto f = facts.find( id );
261 if ( f != facts.end() ) {
262#ifdef GAUDI_REFLEX_COMPONENT_ALIASES
263 const Properties& props = f->second.properties;
264 if ( props.find( "ReflexName" ) != props.end() )
265 logger().warning( "requesting factory via old name '" + id +
266 "'"
267 "use '" +
268 f->second.className + "' instead" );
269#endif
270 if ( !f->second.ptr ) {
271 if ( !dlopen( f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
272 logger().warning( "cannot load " + f->second.library + " for factory " + id );
273 char* dlmsg = dlerror();
274 if ( dlmsg ) logger().warning( dlmsg );
275 return nullptr;
276 }
277 f = facts.find( id ); // ensure that the iterator is valid
278 }
279 if ( f->second.type == type ) return f->second.ptr;
280 logger().warning( "found factory " + id + ", but of wrong type: " + demangle( f->second.type ) +
281 " instead of " + demangle( type ) );
282 }
283 return nullptr; // factory not found
284 }
285
286 const Registry::FactoryInfo& Registry::getInfo( const std::string& id ) const {
287 auto _guard = std::scoped_lock{ m_mutex };
288 static const FactoryInfo unknown( "unknown" );
289 const FactoryMap& facts = factories();
290 auto f = facts.find( id );
291 return ( f != facts.end() ) ? f->second : unknown;
292 }
293
294 Registry& Registry::addProperty( const std::string& id, const std::string& k, const std::string& v ) {
295 auto _guard = std::scoped_lock{ m_mutex };
296 FactoryMap& facts = factories();
297 auto f = facts.find( id );
298 if ( f != facts.end() ) f->second.properties[k] = v;
299 return *this;
300 }
301
302 std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
303 auto _guard = std::scoped_lock{ m_mutex };
304 std::set<KeyType> l;
305 for ( const auto& f : factories() ) {
306 if ( f.second.ptr ) l.insert( f.first );
307 }
308 return l;
309 }
310
311 void Logger::report( Level lvl, const std::string& msg ) {
312 static const char* levels[] = { "DEBUG : ", "INFO : ", "WARNING: ", "ERROR : " };
313 if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
314 }
315
316 static auto s_logger = std::make_unique<Logger>();
317 Logger& logger() { return *s_logger; }
318 void setLogger( Logger* logger ) { s_logger.reset( logger ); }
319
320 } // namespace Details
321
322 void SetDebug( int debugLevel ) {
323 using namespace Details;
324 Logger& l = logger();
325 if ( debugLevel > 1 )
326 l.setLevel( Logger::Debug );
327 else if ( debugLevel > 0 )
328 l.setLevel( Logger::Info );
329 else
330 l.setLevel( Logger::Warning );
331 }
332
333 int Debug() {
334 using namespace Details;
335 switch ( logger().level() ) {
336 case Logger::Debug:
337 return 2;
338 case Logger::Info:
339 return 1;
340 default:
341 return 0;
342 }
343 }
344 }
345 } // namespace PluginService
346} // namespace Gaudi
#define GAUDI_PLUGIN_SERVICE_V1_INLINE
Simple logging class, just to provide a default implementation.
virtual void report(Level lvl, const std::string &msg)
const FactoryInfo & getInfo(const std::string &id) const
Retrieve the FactoryInfo object for an id.
bool m_initialized
Flag recording if the registry has been initialized or not.
void * get(const std::string &id, const std::string &type) const
Retrieve the factory for the given id.
static Registry & instance()
Retrieve the singleton instance of Registry.
std::set< KeyType > loadedFactoryNames() const
Return a list of all the known and loaded factories.
std::map< KeyType, FactoryInfo > FactoryMap
Type used for the database implementation.
std::recursive_mutex m_mutex
Mutex used to control concurrent access to the internal data.
FactoryInfo & add(const I &id, typename F::FuncType ptr)
Add a factory to the database.
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.
const FactoryMap & factories() const
Return the known factories (loading the list if not yet done).
FactoryMap m_factories
Internal storage for factories.
const char * what() const override
GAUDIPS_API void setLogger(Logger *logger)
Set the logger instance to use.
GAUDIPS_API Logger & logger()
Return the current logger instance.
std::string demangle()
Return a canonical name for the template argument.
GAUDIPS_API std::string demangle(const std::type_info &id)
Return a canonical name for type_info object (implementation borrowed from GaudiKernel/System).
GAUDIPS_API void * getCreator(const std::string &id, const std::string &type)
Function used to load a specific factory function.
GAUDIPS_API void SetDebug(int debugLevel)
Backward compatibility with Reflex.
GAUDIPS_API int Debug()
Backward compatibility with Reflex.
This file provides a Grammar for the type Gaudi::Accumulators::Axis It allows to use that type from p...
Definition __init__.py:1
STL namespace.