2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
5#ifndef ASGTOOLS_ANATOOLHANDLE_ICC
6#define ASGTOOLS_ANATOOLHANDLE_ICC
8#include <AsgTools/MessageCheckAsgTools.h>
9#include "CxxUtils/checker_macros.h"
13#ifndef XAOD_STANDALONE
14#include "Gaudi/Interfaces/IOptionsSvc.h"
23 StatusCode makeToolRootCore (const std::string& type,
24 const std::string& name,
27 StatusCode readToolConfig (AsgToolConfig& config, const std::string& toolName);
28 StatusCode copyPropertiesInCatalogue( const std::string& fromTool, const std::string& toTool );
29 StatusCode hasPropertiesInCatalogue( const std::string& toolName );
30 StatusCode addPropertyToCatalogue( const std::string& toolName, const std::string& property, const std::string& val);
31 StatusCode removePropertyFromCatalogue( const std::string& toolName, const std::string& property);
32 StatusCode toolExists( const std::string& fullName, interfaceType_t*& tool );
37 /// \brief do a checked cast from one ToolHandle interface to
42 /// interface not supported for tool
43 template<typename T1,typename T2> StatusCode
44 toolHandleCast (ToolHandle<T1>& to, ToolHandle<T2>& from)
46 using namespace msgUserCode;
49 to = ToolHandle<T1> (dynamic_cast<T1*>(&*from));
50 if (!from.empty() && to.operator->() == nullptr)
52 ANA_MSG_ERROR ("failed to cast from type " << typeid(T2).name() << " to " << typeid(T1).name());
53 return StatusCode::FAILURE;
56 to = ToolHandle<T1> (from.typeAndName(), from.parent());
58 ANA_CHECK (to.retrieve());
60 return StatusCode::SUCCESS;
67 /// \brief the information needed to share a tool
75 /// \brief standard constructor
81 AnaToolShare (const ToolHandle<interfaceType_t>& val_th,
82 std::shared_ptr<void> val_cleanup,
83 std::vector<std::function<StatusCode ()>>&& extraInit);
86 /// \brief the tool we contain
89 /// \post result != nullptr
91 ToolHandle<interfaceType_t>& th ();
99 /// \brief the value of \ref tool
101 ToolHandle<interfaceType_t> m_th;
103 /// \brief resources to release when we are releasing the tool
105 std::vector<std::function<StatusCode ()>> m_extraInit;
107 /// \brief resources to release when we are releasing the tool
109 std::shared_ptr<void> m_cleanup;
116 /// \brief a service for sharing tools of a given type
118 class AnaToolShareList
124 /// \brief the singleton we are using
128 static AnaToolShareList& singleton () noexcept;
131 /// \brief get the share for the given name, or nullptr if no
132 /// share has been defined
138 std::shared_ptr<AnaToolShare>
139 getShare (const std::string& name) const;
142 /// \brief set the share for the given name
143 /// \return the share to use, this is either the share passed in
144 /// or the already set share
150 std::shared_ptr<AnaToolShare>
151 setShare (const std::string& name,
152 std::unique_ptr<AnaToolShare> val_share);
155 /// \brief make or get a share for the given name
156 /// \return the share to use, either freshly created or
157 /// retrieved from the store
161 /// tool creation failures\n
165 makeShare (const std::string& name,
166 const AsgToolConfig& config,
167 std::vector<std::function<StatusCode ()>>&& extraInit,
168 std::shared_ptr<AnaToolShare>& result);
176 /// \brief the shares we manage
178 std::map<std::string,std::weak_ptr<AnaToolShare> > m_shared;
179 mutable std::recursive_mutex m_mutex;
180 typedef std::lock_guard<std::recursive_mutex> lock_t;
186 template <typename T>
187 std::ostream& operator << (std::ostream& str, const AnaToolHandle<T>& obj)
189 return str << obj.getHandle();
194 template<class T> void AnaToolHandle<T> ::
195 testInvariant () const
197#define CHECK_INVARIANT(x) \
198 if (!(x)) { std::cerr << __FILE__ << ":" << __LINE__ << ": invariant violated: " << #x << std::endl; std::abort(); }
200 // General requirements
201 CHECK_INVARIANT (m_handleUser != nullptr);
202 CHECK_INVARIANT (m_name == m_config.name());
204#undef CHECK_INVARIANT
209 template<class T> AnaToolHandle<T> ::
210 AnaToolHandle (const std::string& val_name, parentType_t *val_parent)
211 : m_parentPtr (val_parent),
212 m_handleUser (new ToolHandle<T> (val_name, val_parent)),
213 m_allowEmpty (val_name.empty())
215 setTypeAndName (val_name);
218 this->testInvariant ();
224 template<class T> AnaToolHandle<T> ::
225 AnaToolHandle (AnaToolHandle<T>&& that)
233 template<class T> AnaToolHandle<T> ::
234 AnaToolHandle (const AnaToolHandle<T>& that)
235 : m_config (that.m_config),
236 m_name (that.m_name),
237 m_parentPtr (that.m_parentPtr),
238 m_handleUser (new ToolHandle<T> (*that.m_handleUser)),
239 m_originalTypeAndName (that.m_originalTypeAndName),
240 m_isInitialized (that.m_isInitialized.load()),
241 m_allowEmpty (that.m_allowEmpty)
243 if (m_isInitialized.load())
245 m_toolPtr = that.m_toolPtr.load();
246 m_mode = that.m_mode;
247 m_extraInit = that.m_extraInit;
248 m_cleanup = that.m_cleanup;
251#ifndef XAOD_STANDALONE
252 if (!m_handleUser->empty())
253 (*m_handleUser)->release();
257 that.testInvariant ();
264 template<class T> AnaToolHandle<T>& AnaToolHandle<T> ::
265 operator = (const AnaToolHandle<T>& that)
268 AnaToolHandle<T> (that).swap (*this);
274 template<class T> void AnaToolHandle<T> ::
275 swap (AnaToolHandle<T>& that) noexcept
279 that.testInvariant ();
282 std::swap (m_extraInit, that.m_extraInit);
283 std::swap (m_cleanup, that.m_cleanup);
284 std::swap (m_config, that.m_config);
285 m_name.swap (that.m_name);
286 std::swap (m_parentPtr, that.m_parentPtr);
288 ToolHandle<T> tmp = *m_handleUser;
289#ifndef XAOD_STANDALONE
294 catch (const GaudiException&) {
299 *m_handleUser = *that.m_handleUser;
300 *that.m_handleUser = std::move(tmp);
302 m_originalTypeAndName.swap (that.m_originalTypeAndName);
304 const auto tmp = m_isInitialized.load();
305 m_isInitialized = that.m_isInitialized.load();
306 that.m_isInitialized = tmp;
309 const auto tmp = m_toolPtr.load();
310 m_toolPtr = that.m_toolPtr.load();
311 that.m_toolPtr = tmp;
313 std::swap (m_mode, that.m_mode);
314 std::swap (m_allowEmpty, that.m_allowEmpty);
318 that.testInvariant ();
324 template<class T> AnaToolHandle<T>& AnaToolHandle<T> ::
325 operator = (AnaToolHandle<T>&& that)
334 template<class T> AnaToolHandle<T> ::
335 ~AnaToolHandle () noexcept
337 using namespace msgToolHandle;
339 this->testInvariant ();
344 template<class T> bool
349 this->testInvariant ();
353 case detail::AnaToolHandleMode::EMPTY:
355 case detail::AnaToolHandleMode::CREATE_PRIVATE:
356 case detail::AnaToolHandleMode::CREATE_SHARED:
357 case detail::AnaToolHandleMode::RETRIEVE_SHARED:
359 case detail::AnaToolHandleMode::USER:
360 return m_handleUser->empty();
362 return false; //compiler dummy
367 template<class T> inline bool AnaToolHandle<T> ::
368 isPublic () const noexcept
371 this->testInvariant ();
373 return m_parentPtr == nullptr;
378 template<class T> inline const std::string& AnaToolHandle<T> ::
379 type () const noexcept
382 this->testInvariant ();
384 return m_config.type();
389 template<class T> inline const std::string&
391 name () const noexcept
394 this->testInvariant ();
401 template<typename T> template<typename T2>
402 StatusCode AnaToolHandle<T> ::
403 setProperty (const std::string& property, const T2& value)
405 using namespace msgToolHandle;
408 this->testInvariant ();
411 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
416 return m_config.setProperty (property, value);
421 template<typename T> template<typename T2>
422 typename std::enable_if<std::is_base_of_v<parentType_t, T2>, StatusCode>::type
424 setProperty (const std::string& property,
425 const ToolHandle<T2>& value)
427 using namespace msgToolHandle;
430 this->testInvariant ();
433 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
438 ANA_MSG_DEBUG ("adding sub-tool from ToolHandle: " << value);
440 return setProperty (property, std::string ());
441#ifndef XAOD_STANDALONE
442 else if (value.isPublic())
444 else if (value.parentName() == "ToolSvc")
447 return setProperty (property,
448 value.type() + "/" + value.name());
451#ifndef XAOD_STANDALONE
452 AsgToolConfig subToolConfig;
453 ANA_CHECK (detail::readToolConfig (subToolConfig, value.parentName() + "." + value.name()));
454 ANA_CHECK (m_config.addPrivateTool (property, std::move(subToolConfig)));
455 return StatusCode::SUCCESS;
457 return setProperty (property, value.name());
464 template<typename T> template<typename T2>
465 typename std::enable_if<std::is_base_of_v<parentType_t, T2>, StatusCode>::type
467 setProperty (const std::string& property,
468 const PublicToolHandle<T2>& value)
470 // without this the generic overload wins and stringifies the handle
471 return setProperty (property, static_cast<const ToolHandle<T2>&> (value));
476 template<typename T> template<typename T2>
477 StatusCode AnaToolHandle<T> ::
478 setProperty (const std::string& property,
479 const AnaToolHandle<T2>& value)
481 using namespace msgToolHandle;
484 this->testInvariant ();
487 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
492 // once initialized the AnaToolHandle is not that different from
493 // a regular ToolHandle and we just use it as such
494 if (value.isInitialized())
495 return setProperty (property, value.getHandle());
497 ANA_MSG_DEBUG ("adding sub-tool from AnaToolHandle with mode " << unsigned (value.mode()) << " and name " << value.name());
498 switch (value.mode())
500 case detail::AnaToolHandleMode::EMPTY:
501 return setProperty (property, ToolHandle<T2> ());
502 case detail::AnaToolHandleMode::CREATE_PRIVATE:
503 return m_config.addPrivateTool (property, value.config());
504 case detail::AnaToolHandleMode::CREATE_SHARED:
505 case detail::AnaToolHandleMode::RETRIEVE_SHARED:
506 m_extraInit.emplace_back ([toolhandle = value] () mutable -> StatusCode
508 ANA_CHECK (toolhandle.initialize());
509 return StatusCode::SUCCESS;
511 return m_config.setProperty (property, value.name());
512 case detail::AnaToolHandleMode::USER:
513 return setProperty (property, value.getHandle());
515 return StatusCode::FAILURE; //compiler dummy
520 template<typename T> template<typename T2>
521 typename std::enable_if<std::is_base_of_v<parentType_t, T2>, StatusCode>::type
523 setProperty (const std::string& property,
524 const ToolHandleArray<T2>& value)
526 using namespace msgToolHandle;
529 this->testInvariant ();
532 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
537 std::vector<std::string> tools;
538 // loop over toolhandles in array, strip off any parent naming
539 // and set property with the result
540 for (auto& toolHandle : value)
542 if (toolHandle.empty())
544 ANA_MSG_ERROR ("trying to initialize ToolHandleArray property " << property << " with empty handle");
545 return StatusCode::FAILURE;
548 tools.push_back (toolHandle.type() + "/" + toolHandle.name());
551 return setProperty (property, tools);
556 template<class T> StatusCode
560 using namespace msgToolHandle;
562 // ensure we don't initialize twice concurrently
563 std::lock_guard<std::recursive_mutex> lock (m_initializeMutex);
566 this->testInvariant ();
569 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
574 std::shared_ptr<detail::AnaToolShare> sharedTool;
575 const detail::AnaToolHandleMode mode = getMode (sharedTool);
577 ToolHandle<T> th (typeAndName(), m_parentPtr);
578 std::shared_ptr<void> cleanup;
582 case detail::AnaToolHandleMode::EMPTY:
584 case detail::AnaToolHandleMode::CREATE_PRIVATE:
585 for (auto& extraInit : m_extraInit)
586 ANA_CHECK (extraInit());
587 ANA_CHECK (m_config.makeTool (th, cleanup, true));
589 case detail::AnaToolHandleMode::CREATE_SHARED:
590 ANA_CHECK (detail::AnaToolShareList::singleton().makeShare (m_name, m_config, std::move (m_extraInit), sharedTool));
592 case detail::AnaToolHandleMode::RETRIEVE_SHARED:
593 assert (sharedTool != nullptr);
594 ANA_CHECK (detail::toolHandleCast (th, sharedTool->th()));
595#ifndef XAOD_STANDALONE
599 cleanup = sharedTool;
601 case detail::AnaToolHandleMode::USER:
603 if (th.empty() && !m_allowEmpty)
605 ANA_MSG_ERROR ("user configured an empty handle for a non-empty AnaToolHandle: " << *this);
606 return StatusCode::FAILURE;
611 T *toolPtr = nullptr;
612 ANA_CHECK (makeToolRetrieve (toolPtr, th));
615#ifndef XAOD_STANDALONE
620 std::swap (m_cleanup, cleanup);
622 m_isInitialized = true;
625 this->testInvariant ();
627 return StatusCode::SUCCESS;
632 template<class T> StatusCode
637 return initialize ();
641 template<class T> bool
643 isConfigurable () const
645 using namespace msgToolHandle;
647 this->testInvariant ();
651 case detail::AnaToolHandleMode::EMPTY:
652 case detail::AnaToolHandleMode::CREATE_PRIVATE:
653 case detail::AnaToolHandleMode::CREATE_SHARED:
655 case detail::AnaToolHandleMode::RETRIEVE_SHARED:
656 case detail::AnaToolHandleMode::USER:
659 return true; //compiler dummy
668 bool AnaToolHandle<T> ::
669 isInitialized () const noexcept
672 this->testInvariant ();
674 return m_isInitialized;
679 template<class T> inline T *
689 template<class T> inline const T *
699 template<class T> inline T&
709 template<class T> inline const T&
719 template<class T> inline T*
723 using namespace msgToolHandle;
726 this->testInvariant ();
729 if (m_isInitialized.load())
733 std::lock_guard<std::recursive_mutex> lock (m_initializeMutex);
734 if (!m_isInitialized)
735 ANA_CHECK_THROW (initialize());
736 assert (m_isInitialized);
743 template<class T> inline const T*
747 AnaToolHandle<T>* this_nc ATLAS_THREAD_SAFE = const_cast<AnaToolHandle<T>*>(this);
748 return this_nc->get();
753 template <class T> template<typename T2> void
755 declarePropertyFor (T2 *tool, const std::string& name,
756 const std::string& description)
758 using namespace msgToolHandle;
760 this->testInvariant ();
763 ANA_MSG_FATAL ("can't declare tool handle " << *this << " as property " << name << " after tool has been instantiated");
768 if (m_parentPtr != nullptr && m_parentPtr != tool)
770 ANA_MSG_FATAL ("can't declare tool handle " << *this << " as property for tool " << tool->name() << " as it has a different parent tool " << m_parentPtr->name());
773 if (m_originalTypeAndName.empty())
774 m_originalTypeAndName = m_handleUser->typeAndName ();
775 ANA_CHECK_THROW (tool->declareProperty (name, *m_handleUser, description));
783 template <class T> const ToolHandle<T>&
785 getHandle () const noexcept
788 this->testInvariant ();
790 return *m_handleUser;
795 template <class T> bool
797 isUserConfigured () const
799 using namespace msgToolHandle;
801 this->testInvariant ();
804 // getMode can throw an exception (from dereferencing the ToolSvc handle),
805 // so this function is not noexcept.
808 case detail::AnaToolHandleMode::EMPTY:
809 case detail::AnaToolHandleMode::CREATE_PRIVATE:
810 case detail::AnaToolHandleMode::CREATE_SHARED:
812 case detail::AnaToolHandleMode::RETRIEVE_SHARED:
813 case detail::AnaToolHandleMode::USER:
816 return false; //compiler dummy
821 template<class T> std::string
826 this->testInvariant ();
828#ifndef XAOD_STANDALONE
829 return m_handleUser->parentName() + "." + name();
831 std::string toolName;
833 toolName = m_parentPtr->name() + "." + name();
835 toolName = "ToolSvc." + name();
842 template<class T> detail::AnaToolHandleMode AnaToolHandle<T> ::
843 getMode (std::shared_ptr<detail::AnaToolShare>& sharedTool) const
845 using namespace msgToolHandle;
847 this->testInvariant ();
850 assert (!m_isInitialized);
852 if (m_handleUser->isSet())
853 return detail::AnaToolHandleMode::USER;
855 if (m_config.empty() && m_name.empty())
856 return detail::AnaToolHandleMode::EMPTY;
858 if (!m_originalTypeAndName.empty() &&
859 m_handleUser->typeAndName() != m_originalTypeAndName)
860 return detail::AnaToolHandleMode::USER;
862#ifdef XAOD_STANDALONE
863 if (m_parentPtr != nullptr)
865 if (m_handleUser->parentName() != m_parentPtr->name())
866 return detail::AnaToolHandleMode::USER;
869 if (m_handleUser->parentName() != "ToolSvc")
870 return detail::AnaToolHandleMode::USER;
876 if ((sharedTool = detail::AnaToolShareList::singleton()
878 return detail::AnaToolHandleMode::RETRIEVE_SHARED;
879#ifdef XAOD_STANDALONE
880 /// \todo check whether this is actually what we want to do
881 if (ToolStore::contains<T> (m_handleUser->name()))
882 return detail::AnaToolHandleMode::USER;
886#ifndef XAOD_STANDALONE
887 //for athena, all we do here is check if the tool already exists
888 interfaceType_t *tool = nullptr;
889 if( detail::toolExists( fullName(), tool ) )
890 return detail::AnaToolHandleMode::USER;
892 if (detail::hasPropertiesInCatalogue (fullName()))
893 return detail::AnaToolHandleMode::USER;
896 if (m_config.empty() && !m_handleUser->typeAndName().empty() && (m_handleUser->type() != this->type() || m_handleUser->name() != this->name()))
897 return detail::AnaToolHandleMode::USER;
900 return detail::AnaToolHandleMode::CREATE_SHARED;
902 return detail::AnaToolHandleMode::CREATE_PRIVATE;
907 template<class T> detail::AnaToolHandleMode AnaToolHandle<T> ::
913 std::shared_ptr<detail::AnaToolShare> sharedTool;
914 return getMode (sharedTool);
919 template<class T> std::string AnaToolHandle<T> ::
923 this->testInvariant ();
925 return m_config.typeAndName();
930 template<class T> void AnaToolHandle<T> ::
931 setType (std::string val_type) noexcept
933 using namespace msgToolHandle;
935 this->testInvariant ();
938 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
943 m_config.setType (std::move (val_type));
944 if (m_originalTypeAndName.empty() &&
945 !this->type().empty() && !this->name().empty())
946 m_handleUser->setTypeAndName (this->type() + "/" + this->name());
949 this->testInvariant ();
955 template<class T> void AnaToolHandle<T> ::
956 setName (std::string val_name) noexcept
958 using namespace msgToolHandle;
960 this->testInvariant ();
963 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
968 m_config.setName (val_name);
969 m_name = std::move (val_name);
970 if (m_originalTypeAndName.empty() &&
971 !this->type().empty() && !this->name().empty())
972 m_handleUser->setTypeAndName (this->type() + "/" + this->name());
975 this->testInvariant ();
981 template<class T> void AnaToolHandle<T> ::
982 setTypeAndName (const std::string& val_typeAndName)
984 using namespace msgToolHandle;
988 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
992 auto split = val_typeAndName.find ("/");
993 if (split != std::string::npos)
995 setTypeAndName (val_typeAndName.substr (0, split),
996 val_typeAndName.substr (split+1));
999 setTypeAndName (val_typeAndName, val_typeAndName);
1005 template<class T> void AnaToolHandle<T> ::
1006 setTypeAndName (std::string val_type, std::string val_name) noexcept
1008 using namespace msgToolHandle;
1010 if (isInitialized())
1012 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
1016 setType (std::move (val_type));
1017 setName (std::move (val_name));
1022 template<class T> StatusCode AnaToolHandle<T> ::
1023 makeToolRetrieve (T*& toolPtr, ToolHandle<T>& toolHandle) const
1025 using namespace msgToolHandle;
1027 if (toolHandle.empty())
1030 return StatusCode::SUCCESS;
1035 toolPtr = toolHandle.operator->();
1036 if (toolPtr == nullptr)
1038 ANA_MSG_ERROR ("failed to retrieve tool from tool handle " << *m_handleUser);
1039 return StatusCode::FAILURE;
1041 return StatusCode::SUCCESS;
1042 } catch (std::exception& e)
1044 ANA_MSG_ERROR ("encountered exception during tool retrieval (" << toolHandle << "): " << e.what());
1045 return StatusCode::FAILURE;
1051 template<class T> detail::AnaToolHandleMode AnaToolHandle<T> ::
1055 this->testInvariant ();
1062 template<class T> const AsgToolConfig& AnaToolHandle<T> ::
1066 this->testInvariant ();
1073 template<class T> bool AnaToolHandle<T> ::
1074 allowEmpty () const noexcept
1077 this->testInvariant ();
1079 return m_allowEmpty;
1084 template<class T> void AnaToolHandle<T> ::
1085 setAllowEmpty (bool val_allowEmpty) noexcept
1087 using namespace msgToolHandle;
1089 this->testInvariant ();
1090 if (isInitialized())
1092 ANA_MSG_FATAL ("AnaToolHandle has already been initialized: " << *this);
1096 m_allowEmpty = val_allowEmpty;
1100#endif // ASGTOOLS_ANATOOLHANDLE_ICC