diplomat_runtime.hpp (12048B)
1 #ifndef DIPLOMAT_RUNTIME_CPP_H 2 #define DIPLOMAT_RUNTIME_CPP_H 3 4 #include <optional> 5 #include <string> 6 #include <string_view> 7 #include <type_traits> 8 #include <variant> 9 #include <cstdint> 10 #include <functional> 11 #include <memory> 12 #include <limits> 13 14 15 #if __cplusplus >= 202002L 16 #include <span> 17 #else 18 #include <array> 19 #endif 20 21 namespace diplomat { 22 23 namespace capi { 24 extern "C" { 25 26 static_assert(sizeof(char) == sizeof(uint8_t), "your architecture's `char` is not 8 bits"); 27 static_assert(sizeof(char16_t) == sizeof(uint16_t), "your architecture's `char16_t` is not 16 bits"); 28 static_assert(sizeof(char32_t) == sizeof(uint32_t), "your architecture's `char32_t` is not 32 bits"); 29 30 typedef struct DiplomatWrite { 31 void* context; 32 char* buf; 33 size_t len; 34 size_t cap; 35 bool grow_failed; 36 void (*flush)(struct DiplomatWrite*); 37 bool (*grow)(struct DiplomatWrite*, size_t); 38 } DiplomatWrite; 39 40 bool diplomat_is_str(const char* buf, size_t len); 41 42 #define MAKE_SLICES(name, c_ty) \ 43 typedef struct Diplomat##name##View { \ 44 const c_ty* data; \ 45 size_t len; \ 46 } Diplomat##name##View; \ 47 typedef struct Diplomat##name##ViewMut { \ 48 c_ty* data; \ 49 size_t len; \ 50 } Diplomat##name##ViewMut; \ 51 typedef struct Diplomat##name##Array { \ 52 const c_ty* data; \ 53 size_t len; \ 54 } Diplomat##name##Array; 55 56 #define MAKE_SLICES_AND_OPTIONS(name, c_ty) \ 57 MAKE_SLICES(name, c_ty) \ 58 typedef struct Option##name {union { c_ty ok; }; bool is_ok; } Option##name; \ 59 typedef struct Option##name##View {union { Diplomat##name##View ok; }; bool is_ok; } Option##name##View; \ 60 typedef struct Option##name##ViewMut {union { Diplomat##name##ViewMut ok; }; bool is_ok; } Option##name##ViewMut; \ 61 typedef struct Option##name##Array {union { Diplomat##name##Array ok; }; bool is_ok; } Option##name##Array; \ 62 63 MAKE_SLICES_AND_OPTIONS(I8, int8_t) 64 MAKE_SLICES_AND_OPTIONS(U8, uint8_t) 65 MAKE_SLICES_AND_OPTIONS(I16, int16_t) 66 MAKE_SLICES_AND_OPTIONS(U16, uint16_t) 67 MAKE_SLICES_AND_OPTIONS(I32, int32_t) 68 MAKE_SLICES_AND_OPTIONS(U32, uint32_t) 69 MAKE_SLICES_AND_OPTIONS(I64, int64_t) 70 MAKE_SLICES_AND_OPTIONS(U64, uint64_t) 71 MAKE_SLICES_AND_OPTIONS(Isize, intptr_t) 72 MAKE_SLICES_AND_OPTIONS(Usize, size_t) 73 MAKE_SLICES_AND_OPTIONS(F32, float) 74 MAKE_SLICES_AND_OPTIONS(F64, double) 75 MAKE_SLICES_AND_OPTIONS(Bool, bool) 76 MAKE_SLICES_AND_OPTIONS(Char, char32_t) 77 MAKE_SLICES_AND_OPTIONS(String, char) 78 MAKE_SLICES_AND_OPTIONS(String16, char16_t) 79 MAKE_SLICES_AND_OPTIONS(Strings, DiplomatStringView) 80 MAKE_SLICES_AND_OPTIONS(Strings16, DiplomatString16View) 81 82 } // extern "C" 83 } // namespace capi 84 85 extern "C" inline void _flush(capi::DiplomatWrite* w) { 86 std::string* string = reinterpret_cast<std::string*>(w->context); 87 string->resize(w->len); 88 } 89 90 extern "C" inline bool _grow(capi::DiplomatWrite* w, uintptr_t requested) { 91 std::string* string = reinterpret_cast<std::string*>(w->context); 92 string->resize(requested); 93 w->cap = string->length(); 94 w->buf = &(*string)[0]; 95 return true; 96 } 97 98 inline capi::DiplomatWrite WriteFromString(std::string& string) { 99 capi::DiplomatWrite w; 100 w.context = &string; 101 w.buf = &string[0]; 102 w.len = string.length(); 103 w.cap = string.length(); 104 // Will never become true, as _grow is infallible. 105 w.grow_failed = false; 106 w.flush = _flush; 107 w.grow = _grow; 108 return w; 109 } 110 111 template<class T> struct Ok { 112 T inner; 113 Ok(T&& i): inner(std::forward<T>(i)) {} 114 // We don't want to expose an lvalue-capable constructor in general 115 // however there is no problem doing this for trivially copyable types 116 template<typename X = T, typename = typename std::enable_if<std::is_trivially_copyable<X>::value>::type> 117 Ok(T i): inner(i) {} 118 Ok() = default; 119 Ok(Ok&&) noexcept = default; 120 Ok(const Ok &) = default; 121 Ok& operator=(const Ok&) = default; 122 Ok& operator=(Ok&&) noexcept = default; 123 }; 124 125 template<class T> struct Err { 126 T inner; 127 Err(T&& i): inner(std::forward<T>(i)) {} 128 // We don't want to expose an lvalue-capable constructor in general 129 // however there is no problem doing this for trivially copyable types 130 template<typename X = T, typename = typename std::enable_if<std::is_trivially_copyable<X>::value>::type> 131 Err(T i): inner(i) {} 132 Err() = default; 133 Err(Err&&) noexcept = default; 134 Err(const Err &) = default; 135 Err& operator=(const Err&) = default; 136 Err& operator=(Err&&) noexcept = default; 137 }; 138 139 template<class T, class E> 140 class result { 141 private: 142 std::variant<Ok<T>, Err<E>> val; 143 public: 144 result(Ok<T>&& v): val(std::move(v)) {} 145 result(Err<E>&& v): val(std::move(v)) {} 146 result() = default; 147 result(const result &) = default; 148 result& operator=(const result&) = default; 149 result& operator=(result&&) noexcept = default; 150 result(result &&) noexcept = default; 151 ~result() = default; 152 bool is_ok() const { 153 return std::holds_alternative<Ok<T>>(this->val); 154 } 155 bool is_err() const { 156 return std::holds_alternative<Err<E>>(this->val); 157 } 158 159 template<typename U = T, typename std::enable_if_t<!std::is_reference_v<U>, std::nullptr_t> = nullptr> 160 std::optional<T> ok() && { 161 if (!this->is_ok()) { 162 return std::nullopt; 163 } 164 return std::make_optional(std::move(std::get<Ok<T>>(std::move(this->val)).inner)); 165 } 166 167 template<typename U = E, typename std::enable_if_t<!std::is_reference_v<U>, std::nullptr_t> = nullptr> 168 std::optional<E> err() && { 169 if (!this->is_err()) { 170 return std::nullopt; 171 } 172 return std::make_optional(std::move(std::get<Err<E>>(std::move(this->val)).inner)); 173 } 174 175 // std::optional does not work with reference types directly, so wrap them if present 176 template<typename U = T, typename std::enable_if_t<std::is_reference_v<U>, std::nullptr_t> = nullptr> 177 std::optional<std::reference_wrapper<std::remove_reference_t<T>>> ok() && { 178 if (!this->is_ok()) { 179 return std::nullopt; 180 } 181 return std::make_optional(std::reference_wrapper(std::forward<T>(std::get<Ok<T>>(std::move(this->val)).inner))); 182 } 183 184 template<typename U = E, typename std::enable_if_t<std::is_reference_v<U>, std::nullptr_t> = nullptr> 185 std::optional<std::reference_wrapper<std::remove_reference_t<E>>> err() && { 186 if (!this->is_err()) { 187 return std::nullopt; 188 } 189 return std::make_optional(std::reference_wrapper(std::forward<E>(std::get<Err<E>>(std::move(this->val)).inner))); 190 } 191 192 void set_ok(T&& t) { 193 this->val = Ok<T>(std::move(t)); 194 } 195 196 void set_err(E&& e) { 197 this->val = Err<E>(std::move(e)); 198 } 199 200 template<typename T2> 201 result<T2, E> replace_ok(T2&& t) { 202 if (this->is_err()) { 203 return result<T2, E>(Err<E>(std::get<Err<E>>(std::move(this->val)))); 204 } else { 205 return result<T2, E>(Ok<T2>(std::move(t))); 206 } 207 } 208 }; 209 210 class Utf8Error {}; 211 212 // Use custom std::span on C++17, otherwise use std::span 213 #if __cplusplus >= 202002L 214 215 constexpr std::size_t dynamic_extent = std::dynamic_extent; 216 template<class T, std::size_t E = dynamic_extent> using span = std::span<T, E>; 217 218 #else // __cplusplus < 202002L 219 220 // C++-17-compatible-ish std::span 221 constexpr size_t dynamic_extent = std::numeric_limits<std::size_t>::max(); 222 template <class T, std::size_t Extent = dynamic_extent> 223 class span { 224 public: 225 constexpr span(T *data = nullptr, size_t size = Extent) 226 : data_(data), size_(size) {} 227 228 constexpr span(const span<T> &o) 229 : data_(o.data_), size_(o.size_) {} 230 template <size_t N> 231 constexpr span(std::array<typename std::remove_const_t<T>, N> &arr) 232 : data_(const_cast<T *>(arr.data())), size_(N) {} 233 234 constexpr T* data() const noexcept { 235 return this->data_; 236 } 237 constexpr size_t size() const noexcept { 238 return this->size_; 239 } 240 241 constexpr T *begin() const noexcept { return data(); } 242 constexpr T *end() const noexcept { return data() + size(); } 243 244 void operator=(span<T> o) { 245 data_ = o.data_; 246 size_ = o.size_; 247 } 248 249 private: 250 T* data_; 251 size_t size_; 252 }; 253 254 #endif // __cplusplus >= 202002L 255 256 // Interop between std::function & our C Callback wrapper type 257 258 template <typename T, typename = void> 259 struct as_ffi { 260 using type = T; 261 }; 262 263 template <typename T> 264 struct as_ffi<T, std::void_t<decltype(std::declval<std::remove_pointer_t<T>>().AsFFI())>> { 265 using type = decltype(std::declval<std::remove_pointer_t<T>>().AsFFI()); 266 }; 267 268 template<typename T> 269 using as_ffi_t = typename as_ffi<T>::type; 270 271 template<typename T> 272 using replace_string_view_t = std::conditional_t<std::is_same_v<T, std::string_view>, capi::DiplomatStringView, T>; 273 274 template<typename T> 275 using replace_ref_with_ptr_t = std::conditional_t<std::is_reference_v<T>, std::add_pointer_t<std::remove_reference_t<T>>, T>; 276 277 /// Replace the argument types from the std::function with the argument types for th function pointer 278 template<typename T> 279 using replace_fn_t = replace_string_view_t<as_ffi_t<T>>; 280 281 template <typename T> struct fn_traits; 282 template <typename Ret, typename... Args> struct fn_traits<std::function<Ret(Args...)>> { 283 using fn_ptr_t = Ret(Args...); 284 using function_t = std::function<fn_ptr_t>; 285 using ret = Ret; 286 287 // For a given T, creates a function that take in the C ABI version & return the C++ type. 288 template<typename T> 289 static T replace(replace_fn_t<T> val) { 290 if constexpr(std::is_same_v<T, std::string_view>) { 291 return std::string_view{val.data, val.len}; 292 } else if constexpr (!std::is_same_v<T, as_ffi_t<T>>) { 293 if constexpr (std::is_lvalue_reference_v<T>) { 294 return *std::remove_reference_t<T>::FromFFI(val); 295 } 296 else { 297 return T::FromFFI(val); 298 } 299 } 300 else { 301 return val; 302 } 303 } 304 305 static Ret c_run_callback(const void *cb, replace_fn_t<Args>... args) { 306 return (*reinterpret_cast<const function_t *>(cb))(replace<Args>(args)...); 307 } 308 309 static void c_delete(const void *cb) { 310 delete reinterpret_cast<const function_t *>(cb); 311 } 312 313 fn_traits(function_t) {} // Allows less clunky construction (avoids decltype) 314 }; 315 316 // additional deduction guide required 317 template<class T> 318 fn_traits(T) -> fn_traits<T>; 319 320 // Trait for extracting inner types from either std::optional or std::unique_ptr. 321 // These are the two potential types returned by next() functions 322 template<typename T> struct inner { using type = T; }; 323 template<typename T> struct inner<std::unique_ptr<T>> { using type = T; }; 324 template<typename T> struct inner<std::optional<T>>{ using type = T; }; 325 326 template<typename T, typename U = typename inner<T>::type> 327 inline const U get_inner_if_present(T v) { 328 if constexpr(std::is_same_v<T,U>) { 329 return std::move(v); 330 } else { 331 return *std::move(v); 332 } 333 } 334 335 // Adapter for iterator types 336 template<typename T, typename U = void> struct has_next : std::false_type {}; 337 template<typename T> struct has_next < T, std::void_t<decltype(std::declval<T>().next())>> : std::true_type {}; 338 template<typename T> constexpr bool has_next_v = has_next<T>::value; 339 340 /// Helper template enabling native iteration over unique ptrs to objects which implement next() 341 template<typename T> 342 struct next_to_iter_helper { 343 static_assert(has_next_v<T>, "next_to_iter_helper may only be used with types implementing next()"); 344 using next_type = decltype(std::declval<T>().next()); 345 346 // STL Iterator trait definitions 347 using value_type = typename inner<next_type>::type; 348 using difference_type = void; 349 using reference = std::add_lvalue_reference_t<value_type>; 350 using iterator_category = std::input_iterator_tag; 351 352 next_to_iter_helper(std::unique_ptr<T>&& ptr) : _ptr(std::move(ptr)), _curr(_ptr->next()) {} 353 354 // https://en.cppreference.com/w/cpp/named_req/InputIterator requires that the type be copyable 355 next_to_iter_helper(const next_to_iter_helper& o) : _ptr(o._ptr), _curr(o._curr) {} 356 357 void operator++() { _curr = _ptr->next(); } 358 void operator++(int) { ++(*this); } 359 const value_type& operator*() const { return *_curr; } 360 361 bool operator!=(std::nullopt_t) { 362 return (bool)_curr; 363 } 364 365 std::shared_ptr<T> _ptr; // shared to satisfy the copyable requirement 366 next_type _curr; 367 }; 368 369 } // namespace diplomat 370 371 #endif