1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef THRIFT_CPP2_H_
18 #define THRIFT_CPP2_H_
19 
20 #include <thrift/lib/cpp/Thrift.h>
21 #include <thrift/lib/cpp2/TypeClass.h>
22 
23 #include <initializer_list>
24 #include <utility>
25 #include <folly/Traits.h>
26 #include <folly/Utility.h>
27 #include <folly/functional/Invoke.h>
28 
29 #include <cstdint>
30 
31 namespace apache {
32 namespace thrift {
33 
34 enum FragileConstructor {
35   FRAGILE,
36 };
37 
38 // re-definition of the same enums from
39 // thrift/compiler/ast/t_exception.h
40 enum class ExceptionKind {
41   UNSPECIFIED = 0,
42   TRANSIENT = 1, // The associated RPC may succeed if retried.
43   STATEFUL = 2, // Server state must be change for the associated RPC to have
44                 // any chance of succeeding.
45   PERMANENT =
46       3, // The associated RPC can never succeed, and should not be retried.
47 };
48 
49 enum class ExceptionBlame {
50   UNSPECIFIED = 0,
51   SERVER = 1, // The error was the fault of the server.
52   CLIENT = 2, // The error was the fault of the client's request.
53 };
54 
55 enum class ExceptionSafety {
56   UNSPECIFIED = 0,
57   SAFE = 1, // It is guarneteed the associated RPC failed completely, and no
58             // significant server state changed while trying to process the
59             // RPC.
60 };
61 
62 namespace detail {
63 namespace st {
64 
65 //  struct_private_access
66 //
67 //  Thrift structures have private members but it may be necessary for the
68 //  Thrift support library to access those private members.
69 struct struct_private_access {
70   //  These should be alias templates but Clang has a bug where it does not
71   //  permit member alias templates of a friend struct to access private
72   //  members of the type to which it is a friend. Making these function
73   //  templates is a workaround.
74   template <typename T>
75   static folly::bool_constant<T::__fbthrift_cpp2_gen_json> //
76   __fbthrift_cpp2_gen_json();
77 
78   template <typename T>
79   static folly::bool_constant<T::__fbthrift_cpp2_gen_nimble> //
80   __fbthrift_cpp2_gen_nimble();
81 
82   template <typename T>
83   static folly::bool_constant<T::__fbthrift_cpp2_gen_has_thrift_uri> //
84   __fbthrift_cpp2_gen_has_thrift_uri();
85 
86   template <typename T>
__fbthrift_cpp2_gen_thrift_uristruct_private_access87   static const char* __fbthrift_cpp2_gen_thrift_uri() {
88     return T::__fbthrift_cpp2_gen_thrift_uri();
89   }
90 
91   template <typename T>
__fbthrift_cpp2_gen_exception_safetystruct_private_access92   static constexpr ExceptionSafety __fbthrift_cpp2_gen_exception_safety() {
93     return T::__fbthrift_cpp2_gen_exception_safety;
94   }
95 
96   template <typename T>
__fbthrift_cpp2_gen_exception_kindstruct_private_access97   static constexpr ExceptionKind __fbthrift_cpp2_gen_exception_kind() {
98     return T::__fbthrift_cpp2_gen_exception_kind;
99   }
100 
101   template <typename T>
__fbthrift_cpp2_gen_exception_blamestruct_private_access102   static constexpr ExceptionBlame __fbthrift_cpp2_gen_exception_blame() {
103     return T::__fbthrift_cpp2_gen_exception_blame;
104   }
105 
106   FOLLY_CREATE_MEMBER_INVOKER(clear_fn, __fbthrift_clear);
107 };
108 
109 template <typename T, typename = void>
110 struct IsThriftClass : std::false_type {};
111 
112 template <typename T>
113 struct IsThriftClass<T, folly::void_t<typename T::__fbthrift_cpp2_type>>
114     : std::true_type {};
115 
116 template <typename T, typename = void>
117 struct IsThriftUnion : std::false_type {};
118 
119 template <typename T>
120 struct IsThriftUnion<T, folly::void_t<typename T::__fbthrift_cpp2_type>>
121     : folly::bool_constant<T::__fbthrift_cpp2_is_union> {};
122 
123 } // namespace st
124 } // namespace detail
125 
126 using clear_fn = detail::st::struct_private_access::clear_fn;
127 FOLLY_INLINE_VARIABLE constexpr clear_fn clear{};
128 
129 template <typename T>
130 constexpr bool is_thrift_class_v =
131     apache::thrift::detail::st::IsThriftClass<T>::value;
132 
133 template <typename T>
134 constexpr bool is_thrift_union_v =
135     apache::thrift::detail::st::IsThriftUnion<T>::value;
136 
137 template <typename T>
138 constexpr bool is_thrift_exception_v = is_thrift_class_v<T>&&
139     std::is_base_of<apache::thrift::TException, T>::value;
140 
141 template <typename T>
142 constexpr bool is_thrift_struct_v =
143     is_thrift_class_v<T> && !is_thrift_union_v<T> && !is_thrift_exception_v<T>;
144 
145 template <typename T, typename Fallback>
146 using type_class_of_thrift_class_or_t = //
147     folly::conditional_t<
148         is_thrift_union_v<T>,
149         type_class::variant,
150         folly::conditional_t<
151             is_thrift_class_v<T>, // struct or exception
152             type_class::structure,
153             Fallback>>;
154 
155 template <typename T, typename Fallback>
156 using type_class_of_thrift_class_enum_or_t = //
157     folly::conditional_t<
158         std::is_enum<T>::value,
159         type_class::enumeration,
160         type_class_of_thrift_class_or_t<T, Fallback>>;
161 
162 template <typename T>
163 using type_class_of_thrift_class_t = type_class_of_thrift_class_or_t<T, void>;
164 
165 template <typename T>
166 using type_class_of_thrift_class_enum_t =
167     type_class_of_thrift_class_enum_or_t<T, void>;
168 
169 namespace detail {
170 
171 template <typename T>
172 struct enum_hash {
173   size_t operator()(T t) const {
174     using underlying_t = typename std::underlying_type<T>::type;
175     return std::hash<underlying_t>()(underlying_t(t));
176   }
177 };
178 
179 } // namespace detail
180 
181 namespace detail {
182 
183 // Adapted from Fatal (https://github.com/facebook/fatal/)
184 // Inlined here to keep the amount of mandatory dependencies at bay
185 // For more context, see http://ericniebler.com/2013/08/07/
186 // - Universal References and the Copy Constructor
187 template <typename, typename...>
188 struct is_safe_overload {
189   using type = std::true_type;
190 };
191 template <typename Class, typename T>
192 struct is_safe_overload<Class, T> {
193   using type = std::integral_constant<
194       bool,
195       !std::is_same<
196           Class,
197           typename std::remove_cv<
198               typename std::remove_reference<T>::type>::type>::value>;
199 };
200 
201 } // namespace detail
202 
203 template <typename Class, typename... Args>
204 using safe_overload_t = typename std::enable_if<
205     apache::thrift::detail::is_safe_overload<Class, Args...>::type::value>::
206     type;
207 
208 } // namespace thrift
209 } // namespace apache
210 
211 #define FBTHRIFT_CPP_DEFINE_MEMBER_INDIRECTION_FN(...)                       \
212   struct __fbthrift_cpp2_indirection_fn {                                    \
213     template <typename __fbthrift_t>                                         \
214     FOLLY_ERASE constexpr auto operator()(__fbthrift_t&& __fbthrift_v) const \
215         noexcept(                                                            \
216             noexcept(static_cast<__fbthrift_t&&>(__fbthrift_v).__VA_ARGS__)) \
217             -> decltype((                                                    \
218                 static_cast<__fbthrift_t&&>(__fbthrift_v).__VA_ARGS__)) {    \
219       return static_cast<__fbthrift_t&&>(__fbthrift_v).__VA_ARGS__;          \
220     }                                                                        \
221   }
222 
223 namespace apache {
224 namespace thrift {
225 
226 template <typename T>
227 using detect_indirection_fn_t = typename T::__fbthrift_cpp2_indirection_fn;
228 
229 template <typename T>
230 using indirection_fn_t =
231     folly::detected_or_t<folly::identity_fn, detect_indirection_fn_t, T>;
232 
233 namespace detail {
234 struct apply_indirection_fn {
235  private:
236   template <typename T>
237   using i = indirection_fn_t<folly::remove_cvref_t<T>>;
238 
239  public:
240   template <typename T>
241   FOLLY_ERASE constexpr auto operator()(T&& t) const
242       noexcept(noexcept(i<T>{}(static_cast<T&&>(t))))
243           -> decltype(i<T>{}(static_cast<T&&>(t))) {
244     return i<T>{}(static_cast<T&&>(t));
245   }
246 };
247 } // namespace detail
248 
249 FOLLY_INLINE_VARIABLE constexpr detail::apply_indirection_fn apply_indirection;
250 
251 class ExceptionMetadataOverrideBase {
252  public:
253   virtual ~ExceptionMetadataOverrideBase() {}
254 
255   ExceptionKind errorKind() const { return errorKind_; }
256 
257   ExceptionBlame errorBlame() const { return errorBlame_; }
258 
259   ExceptionSafety errorSafety() const { return errorSafety_; }
260 
261   virtual const std::type_info* type() const = 0;
262 
263  protected:
264   ExceptionKind errorKind_{ExceptionKind::UNSPECIFIED};
265   ExceptionBlame errorBlame_{ExceptionBlame::UNSPECIFIED};
266   ExceptionSafety errorSafety_{ExceptionSafety::UNSPECIFIED};
267 };
268 
269 template <typename T>
270 class ExceptionMetadataOverride : public T,
271                                   public ExceptionMetadataOverrideBase {
272  public:
273   explicit ExceptionMetadataOverride(const T& t) : T(t) {}
274   explicit ExceptionMetadataOverride(T&& t) : T(std::move(t)) {}
275 
276   const std::type_info* type() const override {
277 #if FOLLY_HAS_RTTI
278     return &typeid(T);
279 #else
280     return nullptr;
281 #endif
282   }
283 
284   // ExceptionKind
285   ExceptionMetadataOverride& setTransient() {
286     errorKind_ = ExceptionKind::TRANSIENT;
287     return *this;
288   }
289   ExceptionMetadataOverride& setPermanent() {
290     errorKind_ = ExceptionKind::PERMANENT;
291     return *this;
292   }
293   ExceptionMetadataOverride& setStateful() {
294     errorKind_ = ExceptionKind::STATEFUL;
295     return *this;
296   }
297 
298   // ExceptionBlame
299   ExceptionMetadataOverride& setClient() {
300     errorBlame_ = ExceptionBlame::CLIENT;
301     return *this;
302   }
303   ExceptionMetadataOverride& setServer() {
304     errorBlame_ = ExceptionBlame::SERVER;
305     return *this;
306   }
307 
308   // ExceptionSafety
309   ExceptionMetadataOverride& setSafe() {
310     errorSafety_ = ExceptionSafety::SAFE;
311     return *this;
312   }
313 };
314 
315 template <typename T>
316 ExceptionMetadataOverride<std::decay_t<T>> overrideExceptionMetadata(T&& ex) {
317   return ExceptionMetadataOverride<std::decay_t<T>>(std::forward<T>(ex));
318 }
319 
320 namespace detail {
321 
322 enum LazyDeserializationState : uint8_t { // Bitfield.
323   UNTAINTED = 1 << 0,
324   DESERIALIZED = 1 << 1,
325 };
326 
327 } // namespace detail
328 
329 } // namespace thrift
330 } // namespace apache
331 
332 #endif // #ifndef THRIFT_CPP2_H_
333