RLBox
rlbox_helpers.hpp
1 #pragma once
2 // IWYU pragma: private, include "rlbox.hpp"
3 // IWYU pragma: friend "rlbox_.*\.hpp"
4 
5 #include <cstdlib>
6 #include <iostream>
7 #include <stdexcept>
8 #include <type_traits>
9 #include <utility>
10 
11 #include "rlbox_stdlib_polyfill.hpp"
12 
13 namespace rlbox {
14 namespace detail {
15  const int CompileErrorCode = 42;
16 
17  inline void dynamic_check(bool check, const char* const msg)
18  {
19  // clang-format off
20  if (!check) {
21  #if __cpp_exceptions && defined(RLBOX_USE_EXCEPTIONS)
22  throw std::runtime_error(msg);
23  #else
24  std::cerr << msg << std::endl;
25  std::abort();
26  #endif
27  }
28  // clang-format on
29  }
30 
31 #ifdef RLBOX_NO_COMPILE_CHECKS
32 # if __cpp_exceptions && defined(RLBOX_USE_EXCEPTIONS)
33 # define rlbox_detail_static_fail_because(CondExpr, Message) \
34  ((void)(CondExpr)), throw std::runtime_error(Message)
35 # else
36 # define rlbox_detail_static_fail_because(CondExpr, Message) abort()
37 # endif
38 #else
39 # define rlbox_detail_static_fail_because(CondExpr, Message) \
40  static_assert(!(CondExpr), Message)
41 #endif
42 
43 #ifdef RLBOX_ENABLE_DEBUG_ASSERTIONS
44 # define RLBOX_DEBUG_ASSERT(...) \
45  ::rlbox::detail::dynamic_check(__VA_ARGS__, "Debug assertion failed")
46 #else
47 # define RLBOX_DEBUG_ASSERT(...) (void)0
48 #endif
49 
50 #define RLBOX_UNUSED(...) (void)__VA_ARGS__
51 
52 #define RLBOX_REQUIRE_SEMI_COLON static_assert(true)
53 
54 #define if_constexpr_named(varName, ...) \
55  if constexpr (constexpr auto varName = __VA_ARGS__; varName)
56 
57  template<typename... TArgs>
58  void printTypes()
59  {
60 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
61  std::cout << __PRETTY_FUNCTION__ << std::endl; // NOLINT
62 #elif defined(_MSC_VER)
63  std::cout << __FUNCSIG__ << std::endl; // NOLINT
64 #else
65  std::cout << "Unsupported" << std::endl;
66 #endif
67  }
68 
69 // Create an extension point so applications can provide their own shared lock
70 // implementation
71 #ifndef RLBOX_USE_CUSTOM_SHARED_LOCK
72 # define RLBOX_SHARED_LOCK(name) std::shared_timed_mutex name
73 # define RLBOX_ACQUIRE_SHARED_GUARD(name, ...) \
74  std::shared_lock<std::shared_timed_mutex> name(__VA_ARGS__)
75 # define RLBOX_ACQUIRE_UNIQUE_GUARD(name, ...) \
76  std::unique_lock<std::shared_timed_mutex> name(__VA_ARGS__)
77 #else
78 # if !defined(RLBOX_SHARED_LOCK) || !defined(RLBOX_ACQUIRE_SHARED_GUARD) || \
79  !defined(RLBOX_ACQUIRE_UNIQUE_GUARD)
80 # error \
81  "RLBOX_USE_CUSTOM_SHARED_LOCK defined but missing definitions for RLBOX_SHARED_LOCK, RLBOX_ACQUIRE_SHARED_GUARD, RLBOX_ACQUIRE_UNIQUE_GUARD"
82 # endif
83 #endif
84 
85 #define rlbox_detail_forward_binop_to_base(opSymbol, ...) \
86  template<typename T_Rhs> \
87  inline auto operator opSymbol(T_Rhs rhs) \
88  { \
89  auto b = static_cast<__VA_ARGS__*>(this); \
90  return (*b)opSymbol rhs; \
91  } \
92  RLBOX_REQUIRE_SEMI_COLON
93 
94 #define rlbox_detail_forward_to_const(func_name, result_type) \
95  using T_ConstClassPtr = std::add_pointer_t< \
96  std::add_const_t<std::remove_pointer_t<decltype(this)>>>; \
97  if constexpr (detail::rlbox_is_tainted_v<result_type> && \
98  !std::is_reference_v<result_type>) { \
99  return sandbox_const_cast<detail::rlbox_remove_wrapper_t<result_type>>( \
100  const_cast<T_ConstClassPtr>(this)->func_name()); \
101  } else if constexpr (detail::is_fundamental_or_enum_v<result_type> || \
102  detail::is_std_array_v<result_type> || \
103  detail::is_func_ptr_v<result_type> || \
104  std::is_class_v<result_type>) { \
105  return const_cast<T_ConstClassPtr>(this)->func_name(); \
106  } else { \
107  return const_cast<result_type>( \
108  const_cast<T_ConstClassPtr>(this)->func_name()); \
109  }
110 
111 #define rlbox_detail_forward_to_const_a(func_name, result_type, ...) \
112  using T_ConstClassPtr = std::add_pointer_t< \
113  std::add_const_t<std::remove_pointer_t<decltype(this)>>>; \
114  if constexpr (detail::rlbox_is_tainted_v<result_type> && \
115  !std::is_reference_v<result_type>) { \
116  static_assert(detail::rlbox_is_tainted_v<result_type>); \
117  return sandbox_const_cast<detail::rlbox_remove_wrapper_t<result_type>>( \
118  const_cast<T_ConstClassPtr>(this)->func_name(__VA_ARGS__)); \
119  } else if constexpr (detail::is_fundamental_or_enum_v<result_type> || \
120  detail::is_std_array_v<result_type> || \
121  detail::is_func_ptr_v<result_type> || \
122  std::is_class_v<result_type>) { \
123  return const_cast<T_ConstClassPtr>(this)->func_name(__VA_ARGS__); \
124  } else { \
125  return const_cast<result_type>( \
126  const_cast<T_ConstClassPtr>(this)->func_name(__VA_ARGS__)); \
127  }
128 
129 #define rlbox_detail_member_and_const(sig, ...) \
130  sig __VA_ARGS__ \
131  \
132  sig const __VA_ARGS__ \
133  \
134  static_assert(true)
135 
136  template<typename T>
137  inline auto remove_volatile_from_ptr_cast(T* ptr)
138  {
139  using T_Result = std::add_pointer_t<std::remove_volatile_t<T>>;
140  return const_cast<T_Result>(ptr);
141  }
142 
143  // https://stackoverflow.com/questions/37602057/why-isnt-a-for-loop-a-compile-time-expression
144  namespace compile_time_for_detail {
145  template<std::size_t N>
146  struct num
147  {
148  static const constexpr auto value = N;
149  };
150 
151  template<class F, std::size_t... Is>
152  inline void compile_time_for_helper(F func, std::index_sequence<Is...>)
153  {
154  (func(num<Is>{}), ...);
155  }
156  }
157 
158  template<std::size_t N, typename F>
159  inline void compile_time_for(F func)
160  {
161  compile_time_for_detail::compile_time_for_helper(
162  func, std::make_index_sequence<N>());
163  }
164 
165  template<typename T, typename T2>
166  [[nodiscard]] inline auto return_first_result(T first_task, T2 second_task)
167  {
168  using T_Result = rlbox::detail::polyfill::invoke_result_t<T>;
169 
170  if constexpr (std::is_void_v<T_Result>) {
171  first_task();
172  second_task();
173  } else {
174  auto val = first_task();
175  second_task();
176  return val;
177  }
178  }
179 
180  // Scope Exit guards
181  template<typename T_ExitFunc>
183  {
184  T_ExitFunc exit_func;
185  bool released;
186 
187  public:
188  explicit scope_exit(T_ExitFunc&& cleanup)
189  : exit_func(cleanup)
190  , released(true)
191  {}
192 
193  scope_exit(scope_exit&& rhs)
194  : exit_func(std::move(rhs.exit_func))
195  , released(rhs.released)
196  {
197  rhs.release();
198  }
199 
200  ~scope_exit()
201  {
202  if (released) {
203  exit_func();
204  }
205  }
206 
207  void release() { released = false; }
208 
209  private:
210  explicit scope_exit(const scope_exit&) = delete;
211  scope_exit& operator=(const scope_exit&) = delete;
212  scope_exit& operator=(scope_exit&&) = delete;
213  };
214 
215  template<typename T_ExitFunc>
216  [[nodiscard]] scope_exit<T_ExitFunc> make_scope_exit(
217  T_ExitFunc&& exitFunction)
218  {
219  return scope_exit<T_ExitFunc>(std::move(exitFunction));
220  }
221 
222 /*
223 Make sure classes can access the private memmbers of tainted<T1> and
224 tainted_volatile. Ideally, this should be
225 
226 template <typename U1>
227 friend class tainted<U1, T_Sandbox>;
228 
229 But C++ doesn't seem to allow the above
230 */
231 #define KEEP_CLASSES_FRIENDLY \
232  template<template<typename, typename> typename U1, typename U2, typename U3> \
233  friend class tainted_base_impl; \
234  \
235  template<typename U1, typename U2> \
236  friend class tainted; \
237  \
238  template<typename U1, typename U2> \
239  friend class tainted_volatile; \
240  \
241  template<typename U1> \
242  friend class rlbox_sandbox; \
243  \
244  template<typename U1, typename U2> \
245  friend class sandbox_callback; \
246  \
247  template<typename U1, typename U2> \
248  friend class app_pointer;
249 }
250 
251 }
Definition: rlbox_helpers.hpp:183
Definition: rlbox_helpers.hpp:147