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