maybeCastObj.cc
Go to the documentation of this file.
2 // vim: set sw=2:
3 
5 #include "cetlib_except/demangle.h"
6 
7 #include <cxxabi.h>
8 
9 #include <iostream>
10 #include <string>
11 #include <typeinfo>
12 
13 using namespace art;
14 using namespace std;
15 
16 #ifdef _LIBCPPABI_VERSION
17 // When using libc++, under the assumption that it uses libc++abi,
18 // libc++abi implements the __class_type_info classes, but does
19 // not expose them in cxxabi.h. However, seem to be able to
20 // provide the declarations, link to c++abi, and use them.
21 // TODO:
22 // 1. Note sure about providing *definition* of constructors
23 // vs just declarations of destructors
24 // 2. Configure time check on availability of __class_type_info?
25 // 3. Configure time check on which c++ ABI library is used?
26 //
27 namespace __cxxabiv1 {
28  // Type information for a class
29  class __class_type_info : public std::type_info {
30  public:
31  explicit __class_type_info(char const* n) : type_info(n) {}
32  virtual ~__class_type_info() override;
33  };
34 
35  // Type information for a class with a single non-virtual base
36  class __si_class_type_info : public __class_type_info {
37  public:
38  __class_type_info const* __base_type;
39  explicit __si_class_type_info(char const* n, __class_type_info const* base)
40  : __class_type_info(n), __base_type(base)
41  {}
42  virtual ~__si_class_type_info() override;
43  };
44 
45  // Helper class for __vmi_class_type.
46  struct __base_class_type_info {
47  __class_type_info const* __base_type;
48 #if defined _GLIBCXX_LLP64
49  long long __offset_flags;
50 #else
51  long __offset_flags;
52 #endif
53  enum __offset_flags_masks {
54  __virtual_mask = 0x1,
55  __public_mask = 0x2,
56  __offset_shift = 8
57  };
58  };
59 
60  // Type information for a class with multiple and/or virtual bases.
61  class __vmi_class_type_info : public __class_type_info {
62  public:
63  unsigned int __flags;
64  unsigned int __base_count;
65  __base_class_type_info __base_info[1];
66 
67  enum __flags_masks {
68  __non_diamond_repeat_mask = 0x1,
69  __diamond_shaped_mask = 0x2,
70  __flags_unknown_mask = 0x10
71  };
72 
73  explicit __vmi_class_type_info(char const* n, int flags)
74  : __class_type_info(n), __flags(flags), __base_count(0)
75  {}
76  virtual ~__vmi_class_type_info() override;
77  };
78 }
79 #endif // _LIBCPPABI_VERSION
80 
81 namespace {
82 
83  class upcast_result {
84  public:
85  bool first_call = true;
86  bool found = false;
87  bool is_ambiguous = false;
88  long offset = 0L;
89 
90  public:
91  void print [[maybe_unused]] () const;
92  void reset [[maybe_unused]] ();
93  };
94 
95  void
96  upcast_result::print() const
97  {
98  cout << " found: " << found << endl;
99  cout << "is_ambiguous: " << is_ambiguous << endl;
100  cout << " offset: " << offset << endl;
101  }
102 
103  void
105  {
106  first_call = true;
107  found = false;
108  is_ambiguous = false;
109  offset = 0L;
110  }
111 
112  void
113  visit_class_for_upcast(abi::__class_type_info const* ci,
114  abi::__class_type_info const* dest,
115  long offset,
116  upcast_result& res)
117  {
118  if (res.first_call) {
119  res.first_call = false;
120  } else if (ci == dest) {
121  // We found a possible answer.
122  if (!res.found) {
123  res.found = true;
124  res.offset = offset;
125  } else {
126  res.is_ambiguous = true;
127  }
128  }
129  // Visit bases, if any.
130  if (auto si = dynamic_cast<abi::__si_class_type_info const*>(ci)) {
131  // Class is part of a public non-virtual single inheritance chain.
132  visit_class_for_upcast(si->__base_type, dest, offset, res);
133  return;
134  }
135  if (auto vmi = dynamic_cast<abi::__vmi_class_type_info const*>(ci)) {
136  // Class is part of a more complicated inheritance chain.
137  for (auto i = 0U; i < vmi->__base_count; ++i) {
138  auto const& base = vmi->__base_info[i];
139  bool is_public = false;
140  if (base.__offset_flags & abi::__base_class_type_info::__public_mask) {
141  is_public = true;
142  }
143  long boff =
144  (base.__offset_flags >> abi::__base_class_type_info::__offset_shift);
145  if (is_public && (boff > -1)) {
146  // Base is public access, and boff is not the offset to the
147  // virtual base offset.
148  visit_class_for_upcast(base.__base_type, dest, offset + boff, res);
149  }
150  }
151  return;
152  }
153  // Was a leaf class.
154  }
155 
156 } // unnamed namespace
157 
158 bool
159 detail::upcastAllowed(type_info const& tid_from, type_info const& tid_to)
160 {
161  if (tid_from == tid_to) {
162  // Trivial, nothing to do.
163  return true;
164  }
165  auto ci_from = dynamic_cast<abi::__class_type_info const*>(&tid_from);
166  auto ci_to = dynamic_cast<abi::__class_type_info const*>(&tid_to);
167  if (ci_from == nullptr) {
168  // Not a class, done.
169  return false;
170  }
171  if (ci_to == nullptr) {
172  // Not a class, done.
173  return false;
174  }
175  if (ci_from == ci_to) {
176  // Trivial, same types, nothing to do.
177  return true;
178  }
179  upcast_result res;
180  visit_class_for_upcast(ci_from, ci_to, 0L, res);
181  return res.found && !res.is_ambiguous;
182 }
183 
184 void const*
185 detail::maybeCastObj(void const* ptr,
186  type_info const& tid_from,
187  type_info const& tid_to)
188 {
189  if (tid_from == tid_to) {
190  // Trivial, nothing to do.
191  return ptr;
192  }
193  auto ci_from = dynamic_cast<abi::__class_type_info const*>(&tid_from);
194  auto ci_to = dynamic_cast<abi::__class_type_info const*>(&tid_to);
195  if (ci_from == nullptr) {
196  // Not a class, done.
197  return ptr;
198  }
199  if (ci_to == nullptr) {
200  // Not a class, done.
201  return ptr;
202  }
203  if (ci_from == ci_to) {
204  // Trivial, same types, nothing to do.
205  return ptr;
206  }
207  upcast_result res;
208  visit_class_for_upcast(ci_from, ci_to, 0L, res);
209  if (!res.found) {
211  << "maybeCastObj : unable to convert type: "
212  << cet::demangle_symbol(tid_from.name())
213  << "\nto: " << cet::demangle_symbol(tid_to.name()) << "\n"
214  << "No suitable base found.\n";
215  }
216  if (res.is_ambiguous) {
218  << "MaybeCastObj : unable to convert type: "
219  << cet::demangle_symbol(tid_from.name())
220  << "\nto: " << cet::demangle_symbol(tid_to.name()) << "\n"
221  << "Base class is ambiguous.\n";
222  }
223  return static_cast<char const*>(ptr) + res.offset;
224 }
STL namespace.
QTextStream & reset(QTextStream &s)
std::void_t< T > n
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
bool upcastAllowed(std::type_info const &tiFrom, std::type_info const &tiTo)
void const * maybeCastObj(void const *address, std::type_info const &tiFrom, std::type_info const &tiTo)
QTextStream & endl(QTextStream &s)