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