cxx/include/cxx.h
Adrian Taylor f5dd552036 Adding &[u8] support.
This change adds specifically, support for &[u8] with a corresponding
rust::Slice<uint8_t> type. No other types of slice are permitted. The
rationale is that it may be common to pass binary data back and forth
across the FFI boundary, so it's more urgent to get this in place sooner.
Broader support for other slices can wait for the future.

But, both C++ and Rust-side bindings should allow the existing support
to be broadened to other Slice types in future without code changes.

A few specific notes:

* The name "rust::Slice" might be better as "rust::SliceRef" but I'm
  following the precedent of "rust::Str".
* It would be good to add constructors from std::span but as that's
  a C++20 feature, that may have to wait until C++ feature detection
  is resolved.
* Internally, this follows the pattern of &str, where the parser will
  initially recognize this as Type::Ref (of Type::Slice) but then
  will replace that with Type::SliceRefU8. Type::Slice should not
  persist through later stages. As we later come to support other
  types of slice, we would probably want to remove Type::SliceRefU8.
2020-04-14 14:13:48 -07:00

281 lines
6.7 KiB
C++

#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <iosfwd>
#include <string>
#include <type_traits>
#include <utility>
#if defined(_WIN32)
#include <BaseTsd.h>
#endif
namespace rust {
inline namespace cxxbridge02 {
struct unsafe_bitcopy_t;
#ifndef CXXBRIDGE02_RUST_SLICE
#define CXXBRIDGE02_RUST_SLICE
template<typename T>
class Slice final {
public:
Slice() noexcept : repr(Repr{reinterpret_cast<const T *>(this), 0}) {}
Slice(const Slice<T> &) noexcept = default;
Slice(const T* s, size_t size) : repr(Repr{s, size}) {}
Slice &operator=(Slice<T> other) noexcept {
this->repr = other.repr;
return *this;
}
const T *data() const noexcept { return this->repr.ptr; }
size_t size() const noexcept { return this->repr.len; }
size_t length() const noexcept { return this->repr.len; }
// Repr is PRIVATE; must not be used other than by our generated code.
//
// At present this class is only used for &[u8] slices.
// Not necessarily ABI compatible with &[u8]. Codegen will translate to
// cxx::rust_slice_u8::RustSlice which matches this layout.
struct Repr {
const T *ptr;
size_t len;
};
Slice(Repr repr_) noexcept : repr(repr_) {}
explicit operator Repr() noexcept { return this->repr; }
private:
Repr repr;
};
#endif // CXXBRIDGE02_RUST_SLICE
#ifndef CXXBRIDGE02_RUST_STRING
#define CXXBRIDGE02_RUST_STRING
class String final {
public:
String() noexcept;
String(const String &) noexcept;
String(String &&) noexcept;
~String() noexcept;
String(const std::string &);
String(const char *);
String &operator=(const String &) noexcept;
String &operator=(String &&) noexcept;
explicit operator std::string() const;
// Note: no null terminator.
const char *data() const noexcept;
size_t size() const noexcept;
size_t length() const noexcept;
// Internal API only intended for the cxxbridge code generator.
String(unsafe_bitcopy_t, const String &) noexcept;
private:
// Size and alignment statically verified by rust_string.rs.
std::array<uintptr_t, 3> repr;
};
#endif // CXXBRIDGE02_RUST_STRING
#ifndef CXXBRIDGE02_RUST_STR
#define CXXBRIDGE02_RUST_STR
class Str final {
public:
Str() noexcept;
Str(const Str &) noexcept;
Str(const std::string &);
Str(const char *);
Str(std::string &&) = delete;
Str &operator=(Str) noexcept;
explicit operator std::string() const;
// Note: no null terminator.
const char *data() const noexcept;
size_t size() const noexcept;
size_t length() const noexcept;
// Repr is PRIVATE; must not be used other than by our generated code.
//
// Not necessarily ABI compatible with &str. Codegen will translate to
// cxx::rust_str::RustStr which matches this layout.
struct Repr {
const char *ptr;
size_t len;
};
Str(Repr) noexcept;
explicit operator Repr() noexcept;
private:
Repr repr;
};
#endif // CXXBRIDGE02_RUST_STR
#ifndef CXXBRIDGE02_RUST_BOX
#define CXXBRIDGE02_RUST_BOX
template <typename T>
class Box final {
public:
using value_type = T;
using const_pointer = typename std::add_pointer<
typename std::add_const<value_type>::type>::type;
using pointer = typename std::add_pointer<value_type>::type;
Box(const Box &other) : Box(*other) {}
Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; }
explicit Box(const T &val) {
this->uninit();
::new (this->ptr) T(val);
}
explicit Box(T &&val) {
this->uninit();
::new (this->ptr) T(std::move(val));
}
Box &operator=(const Box &other) {
if (this != &other) {
if (this->ptr) {
**this = *other;
} else {
this->uninit();
::new (this->ptr) T(*other);
}
}
return *this;
}
Box &operator=(Box &&other) noexcept {
if (this->ptr) {
this->drop();
}
this->ptr = other.ptr;
other.ptr = nullptr;
return *this;
}
~Box() noexcept {
if (this->ptr) {
this->drop();
}
}
const T *operator->() const noexcept { return this->ptr; }
const T &operator*() const noexcept { return *this->ptr; }
T *operator->() noexcept { return this->ptr; }
T &operator*() noexcept { return *this->ptr; }
template <typename... Fields>
static Box in_place(Fields &&... fields) {
Box box;
box.uninit();
::new (box.ptr) T{std::forward<Fields>(fields)...};
return box;
}
// Important: requires that `raw` came from an into_raw call. Do not pass a
// pointer from `new` or any other source.
static Box from_raw(T *raw) noexcept {
Box box;
box.ptr = raw;
return box;
}
T *into_raw() noexcept {
T *raw = this->ptr;
this->ptr = nullptr;
return raw;
}
private:
Box() noexcept {}
void uninit() noexcept;
void drop() noexcept;
T *ptr;
};
#endif // CXXBRIDGE02_RUST_BOX
#ifndef CXXBRIDGE02_RUST_FN
#define CXXBRIDGE02_RUST_FN
template <typename Signature, bool Throws = false>
class Fn;
template <typename Ret, typename... Args, bool Throws>
class Fn<Ret(Args...), Throws> {
public:
Ret operator()(Args... args) const noexcept(!Throws);
Fn operator*() const noexcept;
private:
Ret (*trampoline)(Args..., void *fn) noexcept(!Throws);
void *fn;
};
template <typename Signature>
using TryFn = Fn<Signature, true>;
#endif // CXXBRIDGE02_RUST_FN
#ifndef CXXBRIDGE02_RUST_ERROR
#define CXXBRIDGE02_RUST_ERROR
class Error final : std::exception {
public:
Error(const Error &);
Error(Error &&) noexcept;
Error(Str::Repr) noexcept;
~Error() noexcept;
const char *what() const noexcept override;
private:
Str::Repr msg;
};
#endif // CXXBRIDGE02_RUST_ERROR
#ifndef CXXBRIDGE02_RUST_ISIZE
#define CXXBRIDGE02_RUST_ISIZE
#if defined(_WIN32)
using isize = SSIZE_T;
#else
using isize = ssize_t;
#endif
#endif // CXXBRIDGE02_RUST_ISIZE
std::ostream &operator<<(std::ostream &, const String &);
std::ostream &operator<<(std::ostream &, const Str &);
// Snake case aliases for use in code that uses this style for type names.
using string = String;
using str = Str;
template <class T>
using box = Box<T>;
using error = Error;
template <typename Signature, bool Throws = false>
using fn = Fn<Signature, Throws>;
template <typename Signature>
using try_fn = TryFn<Signature>;
#ifndef CXXBRIDGE02_RUST_BITCOPY
#define CXXBRIDGE02_RUST_BITCOPY
struct unsafe_bitcopy_t {
explicit unsafe_bitcopy_t() = default;
};
constexpr unsafe_bitcopy_t unsafe_bitcopy{};
#endif // CXXBRIDGE02_RUST_BITCOPY
template <typename Ret, typename... Args, bool Throws>
Ret Fn<Ret(Args...), Throws>::operator()(Args... args) const noexcept(!Throws) {
return (*this->trampoline)(std::move(args)..., this->fn);
}
template <typename Ret, typename... Args, bool Throws>
Fn<Ret(Args...), Throws> Fn<Ret(Args...), Throws>::operator*() const noexcept {
return *this;
}
} // namespace cxxbridge02
} // namespace rust