OILS / vendor / souffle / utility / DynamicCasting.h View on Github | oilshell.org

168 lines, 82 significant
1
2/*
3 * Souffle - A Datalog Compiler
4 * Copyright (c) 2021, The Souffle Developers. All rights reserved.
5 * Licensed under the Universal Permissive License v 1.0 as shown at:
6 * - https://opensource.org/licenses/UPL
7 * - <souffle root>/licenses/SOUFFLE-UPL.txt
8 */
9
10/************************************************************************
11 *
12 * @file DynamicCasting.h
13 *
14 * Common utilities for dynamic casting.
15 *
16 ***********************************************************************/
17
18#pragma once
19
20#include "souffle/utility/Types.h"
21#include <cassert>
22#include <type_traits>
23
24namespace souffle {
25
26/**
27 * This class is used to tell as<> that cross-casting is allowed.
28 * I use a named type rather than just a bool to make the code stand out.
29 */
30class AllowCrossCast {};
31
32namespace detail {
33
34/// Tels if A is a valid cross-cast option
35template <typename A>
36constexpr bool is_valid_cross_cast_option = std::is_same_v<A, void> || std::is_same_v<A, AllowCrossCast>;
37
38/// Tells if there is a function `To::classof(From*)`
39template <typename From, typename To, typename = void>
40struct has_classof : std::false_type {};
41
42template <typename From, typename To>
43struct has_classof<From, To, std::void_t<decltype(remove_cvref_t<To>::classof(std::declval<From*>()))>>
44 : std::true_type {};
45} // namespace detail
46
47/// Takes a non-null pointer and return whether it is pointing to a derived class of `To`.
48template <typename To, typename CastType = void, typename From,
49 typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>>
50inline bool isA(From* p) noexcept {
51 if constexpr (detail::has_classof<From, To>::value) {
52 // use classof when available
53 return remove_cvref_t<To>::classof(p);
54 } else {
55 // fallback to dynamic_cast
56 return dynamic_cast<std::add_pointer_t<copy_const<From, To>>>(p) != nullptr;
57 }
58}
59
60/// forward isA when From is not a pointer
61template <typename To, typename CastType = void, typename From,
62 typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>,
63 typename = std::enable_if_t<std::is_same_v<CastType, AllowCrossCast> || std::is_base_of_v<From, To>>,
64 typename = std::enable_if_t<std::is_class_v<From> && !is_pointer_like<From>>>
65inline bool isA(From& p) noexcept {
66 return isA<To>(&p);
67}
68
69/// forward isA when From is supposed to be a unique or shared pointer
70template <typename To, typename CastType = void, typename From,
71 typename = std::enable_if_t<is_pointer_like<From>>>
72inline bool isA(const From& p) noexcept {
73 return isA<To, CastType>(p.get());
74}
75
76/// Takes a non-null pointer and dynamic-cast to `To`.
77///
78/// Leverage `To::classof` when available to avoid costly `dynamic_cast`.
79template <typename To, typename CastType = void, typename From,
80 typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>>
81inline auto as(From* p) noexcept {
82 using ToClass = remove_cvref_t<To>;
83 using FromClass = remove_cvref_t<From>;
84 if constexpr (std::is_base_of_v<ToClass, FromClass>) {
85 // trivial conversion from pointer to derived class to pointer to base class
86 return static_cast<std::add_pointer_t<copy_const<From, ToClass>>>(p);
87 } else if constexpr (std::is_base_of_v<FromClass, ToClass> &&
88 can_static_cast<FromClass*, ToClass*>::value) {
89 // cast using isA when converting from pointer to non-virtual base class to pointer to derived class
90 using ResultType = remove_cvref_t<To>;
91 return isA<ResultType>(p) ? static_cast<std::add_pointer_t<copy_const<From, ToClass>>>(p) : nullptr;
92 } else if constexpr (std::is_same_v<CastType, AllowCrossCast> ||
93 !can_static_cast<FromClass*, ToClass*>::value) {
94 // dynamic cast when converting across type hierarchies or
95 // converting from pointer to virtual base class to pointer to derived class
96 return dynamic_cast<std::add_pointer_t<copy_const<From, ToClass>>>(p);
97 } else {
98 // cross-hierarchy dynamic cast not allowed unless CastType = AllowCrossCast
99 static_assert(std::is_base_of_v<FromClass, ToClass>,
100 "`as<B, A>` does not allow cross-type dyn casts. "
101 "(i.e. `as<B, A>` where `B <: A` is not true.) "
102 "Such a cast is likely a mistake or typo.");
103 }
104}
105
106/// Takes a possibly null pointer and dynamic-cast to `To`.
107template <typename To, typename CastType = void, typename From,
108 typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>>
109inline auto as_or_null(From* p) noexcept {
110 using ToClass = remove_cvref_t<To>;
111 if (p == nullptr) {
112 return static_cast<std::add_pointer_t<copy_const<From, ToClass>>>(nullptr);
113 }
114 return as<To, CastType, From>(p);
115}
116
117template <typename To, typename CastType = void, typename From,
118 typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>,
119 typename = std::enable_if_t<std::is_same_v<CastType, AllowCrossCast> || std::is_base_of_v<From, To>>,
120 typename = std::enable_if_t<std::is_class_v<From> && !is_pointer_like<From>>>
121inline auto as(From& x) {
122 return as<To, CastType>(&x);
123}
124
125template <typename To, typename CastType = void, typename From>
126inline auto as(const std::unique_ptr<From>& x) {
127 return as<To, CastType>(x.get());
128}
129
130template <typename To, typename CastType = void, typename From>
131inline auto as(const std::reference_wrapper<From>& x) {
132 return as<To, CastType>(x.get());
133}
134
135/**
136 * Down-casts and checks the cast has succeeded
137 */
138template <typename To, typename CastType = void, typename From>
139auto& asAssert(From&& a) {
140 auto* cast = as<To, CastType>(std::forward<From>(a));
141 assert(cast && "Invalid cast");
142 return *cast;
143}
144
145template <typename B, typename CastType = void, typename A>
146Own<B> UNSAFE_cast(Own<A> x) {
147 if constexpr (std::is_assignable_v<Own<B>, Own<A>>) {
148 return x;
149 } else {
150 if (!x) return {};
151
152 auto y = Own<B>(as<B, CastType>(x));
153 assert(y && "incorrect typed return");
154 x.release(); // release after assert so dbgr can report `x` if it fails
155 return y;
156 }
157}
158
159///**
160// * Checks if the object of type Source can be casted to type Destination.
161// */
162// template <typename B, typename CastType = void, typename A>
163//// [[deprecated("Use `as` and implicit boolean conversion instead.")]]
164// bool isA(A&& src) {
165// return as<B, CastType>(std::forward<A>(src));
166// }
167
168} // namespace souffle