2 Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
5 * @file AthenaeKernel/tools/safe_clid.icc
8 * @brief Find the class ID of a type, without triggering an error
10 * Implementation file.
13 #include "AthenaKernel/ClassID_traits.h"
14 #include <type_traits>
20 //-------------------------------------------------------------------------
21 // Here's the first part. @a safe_clid has the job of calling one of the
22 // two @a safe_clid_1 overrides, depending on the state of the traits
23 // class @a has_classID_tag definition. We want the first one to be called
24 // when @a has_classID_tag is @a true_tag, and the second to be called
25 // in other cases (including the possibility that the typedef does not exist).
26 // There are three possibilities to consider.
28 // 1. The traits class defines @a has_classID_tag as @a true_tag.
29 // Here, both decl 1 and 2 match the call. It turns out
30 // that we can't use partial ordering to discriminate between
31 // these two on the basis of the tag (second) argument alone.
32 // That's the purpose of the first (dummy) argument. By making
33 // it a pointer in decl 1 and completely generic in decl 2,
34 // we ensure that decl 1 is a more specific match when both
37 // 2. The traits class defines @a has_classID_tag as something
38 // other than @a true_tag. In this case, decl 1 doesn't match
39 // the call, but decl 2 will. So decl 2 will be called.
41 // 3. The traits class has no definition for @a has_classID_tag.
42 // In this case, the type named in decl 1 does not exist.
43 // However, that is @e not an error --- it just means that
44 // decl 1 won't be considered as a candidate overload.
45 // (Look up ``substitution failure is not an error'' (SFINAE).)
46 // So again, decl 2 gets called. Note that the requirement
47 // to handle this case (which comes about because the unspecialized
48 // version of @a ClassID_traits does not define this typedef)
49 // is why this may look backwards, using @a true_tag in the call,
50 // and the traits class typedef in the argument.
55 constexpr CLID safe_clid_1 (T*, typename ClassID_traits<T>::has_classID_tag);
58 template <class T, class U>
59 constexpr CLID safe_clid_1 (T, U);
61 // This is the public entry point.
63 constexpr CLID safe_clid()
65 return safe_clid_1 ((T*)0, std::true_type());
69 //-------------------------------------------------------------------------
70 // Here is the definition for decl 1. This gets called when the
71 // traits class defines @a has_classID_tag as @a true_tag.
72 // So we can go ahead and ask for the ID in this case.
75 constexpr CLID safe_clid_1 (T*, typename ClassID_traits<T>::has_classID_tag)
77 return ClassID_traits<T>::ID();
81 //-------------------------------------------------------------------------
82 // Here is the definition for decl 2. This gets called when the traits
83 // class does not define @a has_classID_tag is @a true_tag.
84 // But there is one further decision to be made. If @a T derives
85 // from @a DataObject, then we can still get the class ID.
86 // Otherwise, we must return @a CLID_NULL.
88 // This is called if @a T does not derive from @a DataObject.
89 // Return @a CLID_NULL.
90 template <class T, bool B>
93 constexpr static CLID clid() { return CLID_NULL; }
96 // This specialization is used if @a T does derive from @a DataObject.
97 // This returns the class ID.
99 struct safe_clid_2<T, true>
101 constexpr static CLID clid() { return ClassID_traits<T>::ID(); }
104 // This is the definition corresponding to decl 2 above.
105 // We test @a s_isDataObject in the traits class.
106 // Note that here @a T will be a pointer to the class we're
107 // actually interested in, so we need to strip a pointer.
108 template <class T, class U>
109 constexpr CLID safe_clid_1 (T, U)
111 using typ = std::remove_pointer_t<T>;
112 return safe_clid_2<typ, ClassID_traits<typ>::s_isDataObject>::clid();