• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef DIPLOMAT_RUNTIME_CPP_H
2 #define DIPLOMAT_RUNTIME_CPP_H
3 
4 #include <optional>
5 #include <string>
6 #include <type_traits>
7 #include <variant>
8 #include <cstdint>
9 #include <functional>
10 
11 #if __cplusplus >= 202002L
12 #include <span>
13 #else
14 #include <array>
15 #endif
16 
17 namespace diplomat {
18 
19 namespace capi {
20 extern "C" {
21 
22 static_assert(sizeof(char) == sizeof(uint8_t), "your architecture's `char` is not 8 bits");
23 static_assert(sizeof(char16_t) == sizeof(uint16_t), "your architecture's `char16_t` is not 16 bits");
24 static_assert(sizeof(char32_t) == sizeof(uint32_t), "your architecture's `char32_t` is not 32 bits");
25 
26 typedef struct DiplomatWrite {
27     void* context;
28     char* buf;
29     size_t len;
30     size_t cap;
31     bool grow_failed;
32     void (*flush)(struct DiplomatWrite*);
33     bool (*grow)(struct DiplomatWrite*, size_t);
34 } DiplomatWrite;
35 
36 bool diplomat_is_str(const char* buf, size_t len);
37 
38 #define MAKE_SLICES(name, c_ty) \
39     typedef struct Diplomat##name##View { \
40         const c_ty* data; \
41         size_t len; \
42     } Diplomat##name##View; \
43     typedef struct Diplomat##name##ViewMut { \
44         c_ty* data; \
45         size_t len; \
46     } Diplomat##name##ViewMut; \
47     typedef struct Diplomat##name##Array { \
48         const c_ty* data; \
49         size_t len; \
50     } Diplomat##name##Array;
51 
52 #define MAKE_SLICES_AND_OPTIONS(name, c_ty) \
53     MAKE_SLICES(name, c_ty) \
54     typedef struct Option##name {union { c_ty ok; }; bool is_ok; } Option##name; \
55     typedef struct Option##name##View {union { Diplomat##name##View ok; }; bool is_ok; } Option##name##View; \
56     typedef struct Option##name##ViewMut {union { Diplomat##name##ViewMut ok; }; bool is_ok; } Option##name##ViewMut; \
57     typedef struct Option##name##Array {union { Diplomat##name##Array ok; }; bool is_ok; } Option##name##Array; \
58 
59 MAKE_SLICES_AND_OPTIONS(I8, int8_t)
60 MAKE_SLICES_AND_OPTIONS(U8, uint8_t)
61 MAKE_SLICES_AND_OPTIONS(I16, int16_t)
62 MAKE_SLICES_AND_OPTIONS(U16, uint16_t)
63 MAKE_SLICES_AND_OPTIONS(I32, int32_t)
64 MAKE_SLICES_AND_OPTIONS(U32, uint32_t)
65 MAKE_SLICES_AND_OPTIONS(I64, int64_t)
66 MAKE_SLICES_AND_OPTIONS(U64, uint64_t)
67 MAKE_SLICES_AND_OPTIONS(Isize, intptr_t)
68 MAKE_SLICES_AND_OPTIONS(Usize, size_t)
69 MAKE_SLICES_AND_OPTIONS(F32, float)
70 MAKE_SLICES_AND_OPTIONS(F64, double)
71 MAKE_SLICES_AND_OPTIONS(Bool, bool)
72 MAKE_SLICES_AND_OPTIONS(Char, char32_t)
73 MAKE_SLICES_AND_OPTIONS(String, char)
74 MAKE_SLICES_AND_OPTIONS(String16, char16_t)
75 MAKE_SLICES_AND_OPTIONS(Strings, DiplomatStringView)
76 MAKE_SLICES_AND_OPTIONS(Strings16, DiplomatString16View)
77 
78 } // extern "C"
79 } // namespace capi
80 
_flush(capi::DiplomatWrite * w)81 extern "C" inline void _flush(capi::DiplomatWrite* w) {
82   std::string* string = reinterpret_cast<std::string*>(w->context);
83   string->resize(w->len);
84 }
85 
_grow(capi::DiplomatWrite * w,uintptr_t requested)86 extern "C" inline bool _grow(capi::DiplomatWrite* w, uintptr_t requested) {
87   std::string* string = reinterpret_cast<std::string*>(w->context);
88   string->resize(requested);
89   w->cap = string->length();
90   w->buf = &(*string)[0];
91   return true;
92 }
93 
WriteFromString(std::string & string)94 inline capi::DiplomatWrite WriteFromString(std::string& string) {
95   capi::DiplomatWrite w;
96   w.context = &string;
97   w.buf = &string[0];
98   w.len = string.length();
99   w.cap = string.length();
100   // Will never become true, as _grow is infallible.
101   w.grow_failed = false;
102   w.flush = _flush;
103   w.grow = _grow;
104   return w;
105 }
106 
107 template<class T> struct Ok {
108   T inner;
Okdiplomat::Ok109   Ok(T&& i): inner(std::forward<T>(i)) {}
110   // We don't want to expose an lvalue-capable constructor in general
111   // however there is no problem doing this for trivially copyable types
112   template<typename X = T, typename = typename std::enable_if<std::is_trivially_copyable<X>::value>::type>
Okdiplomat::Ok113   Ok(T i): inner(i) {}
114   Ok() = default;
115   Ok(Ok&&) noexcept = default;
116   Ok(const Ok &) = default;
117   Ok& operator=(const Ok&) = default;
118   Ok& operator=(Ok&&) noexcept = default;
119 };
120 
121 template<class T> struct Err {
122   T inner;
Errdiplomat::Err123   Err(T&& i): inner(std::forward<T>(i)) {}
124   // We don't want to expose an lvalue-capable constructor in general
125   // however there is no problem doing this for trivially copyable types
126   template<typename X = T, typename = typename std::enable_if<std::is_trivially_copyable<X>::value>::type>
Errdiplomat::Err127   Err(T i): inner(i) {}
128   Err() = default;
129   Err(Err&&) noexcept = default;
130   Err(const Err &) = default;
131   Err& operator=(const Err&) = default;
132   Err& operator=(Err&&) noexcept = default;
133 };
134 
135 template<class T, class E>
136 class result {
137 private:
138     std::variant<Ok<T>, Err<E>> val;
139 public:
result(Ok<T> && v)140   result(Ok<T>&& v): val(std::move(v)) {}
result(Err<E> && v)141   result(Err<E>&& v): val(std::move(v)) {}
142   result() = default;
143   result(const result &) = default;
144   result& operator=(const result&) = default;
145   result& operator=(result&&) noexcept = default;
146   result(result &&) noexcept = default;
147   ~result() = default;
is_ok() const148   bool is_ok() const {
149     return std::holds_alternative<Ok<T>>(this->val);
150   }
is_err() const151   bool is_err() const {
152     return std::holds_alternative<Err<E>>(this->val);
153   }
154 
ok()155   std::optional<T> ok() && {
156     if (!this->is_ok()) {
157       return std::nullopt;
158     }
159     return std::make_optional(std::move(std::get<Ok<T>>(std::move(this->val)).inner));
160   }
err()161   std::optional<E> err() && {
162     if (!this->is_err()) {
163       return std::nullopt;
164     }
165     return std::make_optional(std::move(std::get<Err<E>>(std::move(this->val)).inner));
166   }
167 
set_ok(T && t)168   void set_ok(T&& t) {
169     this->val = Ok<T>(std::move(t));
170   }
171 
set_err(E && e)172   void set_err(E&& e) {
173     this->val = Err<E>(std::move(e));
174   }
175 
176   template<typename T2>
replace_ok(T2 && t)177   result<T2, E> replace_ok(T2&& t) {
178     if (this->is_err()) {
179       return result<T2, E>(Err<E>(std::get<Err<E>>(std::move(this->val))));
180     } else {
181       return result<T2, E>(Ok<T2>(std::move(t)));
182     }
183   }
184 };
185 
186 class Utf8Error {};
187 
188 // Use custom std::span on C++17, otherwise use std::span
189 #if __cplusplus >= 202002L
190 
191 template<class T> using span = std::span<T>;
192 
193 #else // __cplusplus < 202002L
194 
195 // C++-17-compatible std::span
196 template<class T>
197 class span {
198 
199 public:
span(T * data,size_t size)200   constexpr span(T* data, size_t size)
201     : data_(data), size_(size) {}
202   template<size_t N>
span(std::array<typename std::remove_const<T>::type,N> & arr)203   constexpr span(std::array<typename std::remove_const<T>::type, N>& arr)
204     : data_(const_cast<T*>(arr.data())), size_(N) {}
data() const205   constexpr T* data() const noexcept {
206     return this->data_;
207   }
size() const208   constexpr size_t size() const noexcept {
209     return this->size_;
210   }
211 private:
212   T* data_;
213   size_t size_;
214 };
215 
216 #endif // __cplusplus >= 202002L
217 
218 // Interop between std::function & our C Callback wrapper type
219 
220 template <typename T> struct fn_traits;
221 template <typename Ret, typename... Args> struct fn_traits<std::function<Ret(Args...)>> {
222     using fn_ptr_t = Ret(Args...);
223     using function_t = std::function<fn_ptr_t>;
224     using ret = Ret;
225 
226     template <typename T, typename = void>
227     struct as_ffi {
228       using type = T;
229     };
230 
231     template <typename T>
232     struct as_ffi<T, std::void_t<decltype(&T::AsFFI)>> {
233       using type = decltype(std::declval<T>().AsFFI());
234     };
235 
236     template<typename T>
237     using as_ffi_t = typename as_ffi<T>::type;
238 
239     template<typename T>
240     using replace_string_view_t = std::conditional_t<std::is_same_v<T, std::string_view>, capi::DiplomatStringView, T>;
241 
242     template<typename T>
243     using replace_fn_t = replace_string_view_t<as_ffi_t<T>>;
244 
245     // For a given T, creates a function that take in the C ABI version & return the C++ type.
246     template<typename T>
replacediplomat::fn_traits247     static T replace(replace_fn_t<T> val) {
248         if constexpr(std::is_same_v<T, std::string_view>)   {
249             return std::string_view{val.data, val.len};
250         } else if constexpr(!std::is_same_v<T, as_ffi_t<T>>) {
251             return T::FromFFI(val);
252         }
253         else {
254             return val;
255         }
256     }
257 
c_run_callbackdiplomat::fn_traits258     static Ret c_run_callback(const void *cb, replace_fn_t<Args>... args) {
259         return (*reinterpret_cast<const function_t *>(cb))(replace<Args>(args)...);
260     }
261 
c_deletediplomat::fn_traits262     static void c_delete(const void *cb) {
263         delete reinterpret_cast<const function_t *>(cb);
264     }
265 
fn_traitsdiplomat::fn_traits266     fn_traits(function_t) {} // Allows less clunky construction (avoids decltype)
267 };
268 
269 // additional deduction guide required
270 template<class T>
271 fn_traits(T) -> fn_traits<T>;
272 
273 } // namespace diplomat
274 
275 #endif