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