Joos1W Compiler Framework
All Classes Functions Typedefs Pages
Utils.h
1 #pragma once
2 
3 #include <initializer_list>
4 #include <ranges>
5 #include <type_traits>
6 #include <vector>
7 
8 namespace utils {
9 
10 template <typename T>
11 void move_vector(std::pmr::vector<T>& from, std::pmr::vector<T>& to) {
12  to.reserve(from.size());
13  to.insert(to.end(),
14  std::make_move_iterator(from.begin()),
15  std::make_move_iterator(from.end()));
16 }
17 
18 // Remove a pointer or reference from a type.
19 template <typename T>
20 using remove_ptr_or_ref_t = std::conditional_t<
21  /* Condition */ std::is_pointer_v<T>,
22  /* T* -> T */ std::remove_pointer_t<T>,
23  /* T& -> T, or T -> T */ std::remove_reference_t<T>>;
24 
25 // Transfer const from one type to another.
26 template <typename To, typename From>
27 using transfer_const_t = std::conditional_t<
28  /* Condition */ std::is_const_v<From>,
29  /* From const -> To const */ std::add_const_t<To>, To>;
30 
31 // Transfer volatile from one type to another.
32 template <typename To, typename From>
33 using transfer_volatile_t = std::conditional_t<
34  /* Condition */ std::is_volatile_v<From>,
35  /* From volatile -> To volatile */ std::add_volatile_t<To>, To>;
36 
37 // Transfer const and volatile from one type to another.
38 template <typename To, typename From>
39 using transfer_cv_t = transfer_volatile_t<transfer_const_t<To, From>, From>;
40 
41 // Merge const and volatile from two types into U.
42 template <typename U, typename V>
43 using union_cv_t = transfer_cv_t<transfer_cv_t<U, V>, U>;
44 
45 // Transfer pointer or reference from one type to another.
46 template <typename To, typename From>
47 using transfer_ptr_or_ref_t = std::conditional_t<
48  /* Condition */ std::is_pointer_v<From>,
49  /* From* -> To* */ std::add_pointer_t<To>,
50  std::conditional_t<
51  /* Condition */ std::is_reference_v<From>,
52  /* From& -> To& */ std::add_lvalue_reference_t<To>,
53  /* From -> To */ To>>;
54 
55 // Checks if type From can be cast to type To.
56 template <typename To, typename From>
57 consteval bool is_valid_type_cast_f() {
58  constexpr bool c1 = std::is_class_v<remove_ptr_or_ref_t<To>>;
59  static_assert(c1, "To must be a class or a pointer to a class");
60  constexpr bool c2 = std::is_class_v<remove_ptr_or_ref_t<From>>;
61  static_assert(c2, "From must be a class or a pointer to a class");
62  constexpr bool c3 = std::is_pointer_v<From> || std::is_reference_v<From>;
63  static_assert(c3, "From must be a pointer or a reference");
64  constexpr bool c4 = !(std::is_pointer_v<From> && std::is_reference_v<To>);
65  static_assert(c4, "From* -> To& is illegal");
66  constexpr bool c5 = !(std::is_reference_v<From> && std::is_pointer_v<To>);
67  static_assert(c5, "From& -> To* is illegal");
68  constexpr bool c6 = !std::is_reference_v<To>;
69  static_assert(c6, "Casting to a reference is illegal");
70  return c1 && c2 && c3 && c4 && c5 && c6;
71 }
72 
73 } // namespace utils
74 
75 /**
76  * @brief Given a type To and a type From, returns the canonicalized type.
77  * Strips To and From of their pointer and reference types, and unions
78  * const and volatile between From and To.
79  */
80 template <typename To, typename From>
81 using canonicalize_t = utils::union_cv_t<utils::remove_ptr_or_ref_t<To>,
82  utils::remove_ptr_or_ref_t<From>>;
83 
84 /**
85  * @brief Casts a pointer of type From to a pointer type of: To or To*.
86  * Will assert, if the cast is invalid.
87  *
88  * @tparam To A class or pointer to a class type.
89  * @tparam From A pointer to a class type.
90  */
91 template <typename To, typename From>
92 /* To* */ std::enable_if_t<std::is_pointer_v<From>, canonicalize_t<To, From>*>
93 cast(From from) {
94  if constexpr(utils::is_valid_type_cast_f<To, From>()) {
95  assert(from && "Tried to cast a nullptr");
96  auto ptr = dynamic_cast<canonicalize_t<To, From>*>(from);
97  assert(ptr && "Invalid cast");
98  return ptr;
99  }
100  return nullptr;
101 }
102 
103 /**
104  * @brief Casts a reference of type From to a pointer of type of: To or To*.
105  * Same as cast, but for references.
106  *
107  * @tparam To A class or pointer to a class type.
108  * @tparam From A reference to a class type.
109  */
110 template <typename To, typename From>
111 /* To* */ std::enable_if_t<!std::is_pointer_v<From>, canonicalize_t<To, From>*>
112 cast(From& from) {
113  return cast<To>(&from);
114 }
115 
116 /**
117  * @brief Casts a pointer of type From to a pointer type of: To or To*.
118  * Will assert, if the from pointer is nullptr. Will return
119  * nullptr if the cast is invalid.
120  *
121  * @tparam To A class or pointer to a class type.
122  * @tparam From A pointer to a class type.
123  */
124 template <typename To, typename From>
125 /* To* */ std::enable_if_t<std::is_pointer_v<From>, canonicalize_t<To, From>*>
126 dyn_cast(From from) {
127  if constexpr(utils::is_valid_type_cast_f<To, From>()) {
128  assert(from && "Tried to dyn_cast a nullptr");
129  return dynamic_cast<canonicalize_t<To, From>*>(from);
130  }
131  return nullptr;
132 }
133 
134 /**
135  * @brief Casts a reference of type From to a pointer type of: To or To*.
136  * Same as dyn_cast, but for references.
137  *
138  * @tparam To A class or pointer to a class type.
139  * @tparam From A reference to a class type.
140  */
141 template <typename To, typename From>
142 /* To* */ std::enable_if_t<!std::is_pointer_v<From>, canonicalize_t<To, From>*>
143 dyn_cast(From& from) {
144  return dyn_cast<To>(&from);
145 }
146 
147 /**
148  * @brief Same as dyn_cast, but returns nullptr if the from pointer is nullptr.
149  *
150  * @tparam To A class or pointer to a class type.
151  * @tparam From A pointer to a class type.
152  */
153 template <typename To, typename From>
154 /* To* */ std::enable_if_t<std::is_pointer_v<From>, canonicalize_t<To, From>*>
155 dyn_cast_or_null(From from) {
156  if constexpr(utils::is_valid_type_cast_f<To, From>()) {
157  return dynamic_cast<canonicalize_t<To, From>*>(from);
158  }
159  return nullptr;
160 }
161 
162 /* ===--------------------------------------------------------------------=== */
163 // function_ref taken from here:
164 // https://vittorioromeo.info/index/blog/passing_functions_to_functions.html
165 /* ===--------------------------------------------------------------------=== */
166 
167 namespace utils::details {
168 
169 template <typename...>
170 using void_t = void;
171 
172 template <class T, class R = void, class = void>
173 struct is_callable : std::false_type {};
174 
175 template <class T>
176 struct is_callable<T, void, void_t<std::result_of_t<T>>> : std::true_type {};
177 
178 template <class T, class R>
179 struct is_callable<T, R, void_t<std::result_of_t<T>>>
180  : std::is_convertible<std::result_of_t<T>, R> {};
181 
182 template <typename TSignature>
183 struct signature_helper;
184 
185 template <typename TReturn, typename... TArgs>
186 struct signature_helper<TReturn(TArgs...)> {
187  using fn_ptr_type = TReturn (*)(TArgs...);
188 };
189 
190 template <typename TSignature>
191 using fn_ptr = typename signature_helper<TSignature>::fn_ptr_type;
192 
193 template <typename T>
194 struct dependent_false : std::false_type {};
195 
196 template <typename TSignature>
197 class function_ref;
198 
199 template <typename TReturn, typename... TArgs>
200 class function_ref<TReturn(TArgs...)> final {
201 private:
202  using signature_type = TReturn(void*, TArgs...);
203  void* _ptr;
204  TReturn (*_erased_fn)(void*, TArgs...);
205 
206 public:
207  template <typename T, typename = std::enable_if_t<
208  is_callable<T&(TArgs...)>{} &&
209  !std::is_same<std::decay_t<T>, function_ref>{}>>
210  function_ref(T&& x) noexcept : _ptr{(void*)std::addressof(x)} {
211  _erased_fn = [](void* ptr, TArgs... xs) -> TReturn {
212  return (*reinterpret_cast<std::add_pointer_t<T>>(ptr))(
213  std::forward<TArgs>(xs)...);
214  };
215  }
216  decltype(auto) operator()(TArgs... xs) const
217  noexcept(noexcept(_erased_fn(_ptr, std::forward<TArgs>(xs)...))) {
218  return _erased_fn(_ptr, std::forward<TArgs>(xs)...);
219  }
220 };
221 
222 } // namespace utils::details
223 
224 namespace utils {
225 
226 /**
227  * @brief A non-owning, lightweight view of a range whose element types are
228  * convertible to T
229  *
230  * @tparam T The type of the elements in the range
231  */
232 template <typename T>
233 class range_ref {
234 public:
235  range_ref() {
236  range_ = nullptr;
237  foreach_ = nullptr;
238  sz_ = 0;
239  }
240 
241  template<typename U, class Tp>
242  requires std::convertible_to<U, T>
243  range_ref(std::vector<U, Tp>& vec) {
244  range_ = const_cast<void*>(static_cast<void const*>(&vec));
245  foreach_ = [](void* r, details::function_ref<void(T)> callback) {
246  for(auto&& v : *reinterpret_cast<decltype(&vec)>(r)) callback(v);
247  };
248  sz_ = vec.size();
249  }
250 
251  template<std::ranges::view R>
252  requires std::convertible_to<std::ranges::range_value_t<R>, T>
253  range_ref(R&& range) {
254  range_ = const_cast<void*>(static_cast<void const*>(&range));
255  foreach_ = [](void* r, details::function_ref<void(T)> callback) {
256  for(auto&& v : *reinterpret_cast<decltype(&range)>(r)) callback(v);
257  };
258  sz_ = std::ranges::size(range);
259  }
260 
261  template<typename U>
262  requires std::convertible_to<U, T>
263  range_ref(std::initializer_list<U>&& list) {
264  range_ = const_cast<void*>(static_cast<void const*>(&list));
265  foreach_ = [](void* r, details::function_ref<void(T)> callback) {
266  for(auto&& v : *reinterpret_cast<decltype(&list)>(r)) callback(v);
267  };
268  sz_ = list.size();
269  }
270 
271  inline void for_each(details::function_ref<void(T)> callback) {
272  if(foreach_) foreach_(range_, callback);
273  }
274 
275  inline std::size_t size() const {
276  return sz_;
277  }
278 
279 private:
280  using range_fun_t = void (*)(void*, details::function_ref<void(T)>);
281  void* range_ = nullptr;
282  range_fun_t foreach_ = nullptr;
283  std::size_t sz_ = 0;
284 };
285 
286 } // namespace utils