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 |
|
24 | namespace 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 | */
|
30 | class AllowCrossCast {};
|
31 |
|
32 | namespace detail {
|
33 |
|
34 | /// Tels if A is a valid cross-cast option
|
35 | template <typename A>
|
36 | constexpr 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*)`
|
39 | template <typename From, typename To, typename = void>
|
40 | struct has_classof : std::false_type {};
|
41 |
|
42 | template <typename From, typename To>
|
43 | struct 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`.
|
48 | template <typename To, typename CastType = void, typename From,
|
49 | typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>>
|
50 | inline 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
|
61 | template <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>>>
|
65 | inline 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
|
70 | template <typename To, typename CastType = void, typename From,
|
71 | typename = std::enable_if_t<is_pointer_like<From>>>
|
72 | inline 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`.
|
79 | template <typename To, typename CastType = void, typename From,
|
80 | typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>>
|
81 | inline 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`.
|
107 | template <typename To, typename CastType = void, typename From,
|
108 | typename = std::enable_if_t<detail::is_valid_cross_cast_option<CastType>>>
|
109 | inline 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 |
|
117 | template <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>>>
|
121 | inline auto as(From& x) {
|
122 | return as<To, CastType>(&x);
|
123 | }
|
124 |
|
125 | template <typename To, typename CastType = void, typename From>
|
126 | inline auto as(const std::unique_ptr<From>& x) {
|
127 | return as<To, CastType>(x.get());
|
128 | }
|
129 |
|
130 | template <typename To, typename CastType = void, typename From>
|
131 | inline 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 | */
|
138 | template <typename To, typename CastType = void, typename From>
|
139 | auto& asAssert(From&& a) {
|
140 | auto* cast = as<To, CastType>(std::forward<From>(a));
|
141 | assert(cast && "Invalid cast");
|
142 | return *cast;
|
143 | }
|
144 |
|
145 | template <typename B, typename CastType = void, typename A>
|
146 | Own<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
|