diff --git a/Cargo.lock b/Cargo.lock index c0080d357..c1d9e8456 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1115,6 +1115,7 @@ dependencies = [ "wasm-encoder 0.229.0", "wit-bindgen-c", "wit-bindgen-core", + "wit-bindgen-cpp", "wit-bindgen-csharp", "wit-bindgen-markdown", "wit-bindgen-moonbit", @@ -1132,6 +1133,21 @@ dependencies = [ "wit-parser", ] +[[package]] +name = "wit-bindgen-cpp" +version = "0.41.0" +dependencies = [ + "anyhow", + "clap", + "heck", + "test-helpers", + "wasm-encoder 0.229.0", + "wasm-metadata 0.229.0", + "wit-bindgen-c", + "wit-bindgen-core", + "wit-component", +] + [[package]] name = "wit-bindgen-csharp" version = "0.41.0" diff --git a/Cargo.toml b/Cargo.toml index b1c160d3f..50111f58f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ wit-component = "0.229.0" wit-bindgen-core = { path = 'crates/core', version = '0.41.0' } wit-bindgen-c = { path = 'crates/c', version = '0.41.0' } +wit-bindgen-cpp = { path = 'crates/cpp', version = '0.41.0' } wit-bindgen-rust = { path = "crates/rust", version = "0.41.0" } wit-bindgen-csharp = { path = 'crates/csharp', version = '0.41.0' } wit-bindgen-markdown = { path = 'crates/markdown', version = '0.41.0' } @@ -56,6 +57,7 @@ clap = { workspace = true, features = ['wrap_help'] } wit-bindgen-core = { workspace = true } wit-bindgen-rust = { workspace = true, features = ['clap'], optional = true } wit-bindgen-c = { workspace = true, features = ['clap'], optional = true } +wit-bindgen-cpp = { workspace = true, features = ['clap'], optional = true } wit-bindgen-markdown = { workspace = true, features = ['clap'], optional = true } wit-bindgen-moonbit = { workspace = true, features = ['clap'], optional = true } wit-bindgen-csharp = { workspace = true, features = ['clap'], optional = true } @@ -71,10 +73,12 @@ default = [ 'markdown', 'go', 'csharp', + 'cpp', 'moonbit', 'async', ] c = ['dep:wit-bindgen-c'] +cpp = ['dep:wit-bindgen-cpp'] rust = ['dep:wit-bindgen-rust'] markdown = ['dep:wit-bindgen-markdown'] go = [] diff --git a/README.md b/README.md index 08f0aff38..904172cf9 100644 --- a/README.md +++ b/README.md @@ -385,6 +385,14 @@ Then, you can generate the bindings for your project: wit-bindgen-go generate ``` +### Guest: C++-17+ + +The cpp crate contains code to generate C++ code which uses the std types +optional, string, string_view, vector, expected to represent generic +WIT types. + +This relies on wasi-SDK for guest compilation. + ### Guest: MoonBit MoonBit can be compiled to WebAssembly using [its toolchain](https://moonbitlang.com/download): diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 22ec1b01d..4dddddff7 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1,4 +1,4 @@ -mod component_type_object; +pub mod component_type_object; use anyhow::Result; use heck::*; @@ -3136,7 +3136,7 @@ impl Source { } } -fn wasm_type(ty: WasmType) -> &'static str { +pub fn wasm_type(ty: WasmType) -> &'static str { match ty { WasmType::I32 => "int32_t", WasmType::I64 => "int64_t", diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml new file mode 100644 index 000000000..346c9e21a --- /dev/null +++ b/crates/cpp/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "wit-bindgen-cpp" +authors = ["Christof Petig "] +version = "0.41.0" +edition.workspace = true +repository = 'https://github.com/cpetig/wit-bindgen' +license = "Apache-2.0 WITH LLVM-exception" +description = """ +C++ guest and host binding generator for WIT and the component model. +""" + +[lib] +doctest = false +test = false + +[dependencies] +wit-bindgen-core = { workspace = true } +wit-component = { workspace = true } +wasm-encoder = { workspace = true } +wasm-metadata = { workspace = true } +wit-bindgen-c = { workspace = true } +anyhow = { workspace = true } +heck = { workspace = true } +clap = { workspace = true, optional = true } + +[dev-dependencies] +test-helpers = { path = '../test-helpers' } diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md new file mode 100644 index 000000000..c750a906c --- /dev/null +++ b/crates/cpp/DESIGN.md @@ -0,0 +1,98 @@ +# Type mapping + +| Code | Environment | +| --- | --- | +| G-- | guest side | +| H-- | host side | +| -I- | guest-import (guest calls) | +| -E- | guest-export (host calls) | +| --A | argument | +| --R | result | +| --S | in struct | + +| mode | | +| --- | --- | +| v | passed by value | +| t | owernership transferred | +| p | cabi_post_ cleans up | + +| API | | | ABI | | +| --- | --- | --- | --- | --- | +| 🕸 | old | | 📘 | canonical | +| 💎 | new | | 🪞 | symmetric | + +| Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | +| --- | --- | --- | --- | --- | --- | --- | +| GIA | v | string | &str[^1] | string_view (17) | addr, len | | +| | | list | &[T] | wit::span [^5] | addr, len | | +| | | tuple | (...) | std::tuple | 0, 1, ...| | +| | | tuple | (&str, &[T]) | std::tuple<...> | a,l,a,l | +| | | record{string, list} | &T | T const& | a,l,a,l | +| | | large-struct (>16 args) | &T | T const& | &t | +| | | result | Result<&str, &[]> | std::expected | d,a,l | +| | | option\ | Option\<&str> | optional const& | d,a,l| +| | | list\ | &[\&Resrc]? | vector const& | a,l| +| GIR | t | string | String | wit::string[^2] | &(addr, len) [^8] | | +| | | list | Vec | wit::vector | &(a,l) | +| | | result | Result | std::expected | &(d,a,l) | +| GEA | t | string | String | 🕸 wit::string&& | addr, len | +| | | | | 💎 string_view | | +| | | result | Result | 🕸 std::expected&& | d,a,l | +| | | | | 💎 std::expected | | +| GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^7] | +| | | | | | 🪞 &(a,l) | +| | | result | Result | std::expected | 📘 -> &(d,a,l) cabi_post | +| --S | ? | string | String | wit::string | addr, len | +| HIA | v | string | | string_view | a,l | +| HIR | t | string | | wit::string[^3] | &(a,l) | +| HEA | t | string | | 🕸 wit::string[^4] | a,l | +| | | | | 💎 string_view [^6] | | +| HER | p | string | | 🕸 wit::guest_owned | 📘 -> &(a,l) | +| | | | | 💎 wit::string [^6] | 🪞 &(a,l) | + +[^1]: The host never frees memory (is never passed ownership)! + +[^2]: A wit::string is identical to the canonical representation, so it can be part of structures. On the guest a wit::string owns the memory and frees it after use. +On the host a wit::string can be constructed(=allocated) with an exec_env argument. Thus, without an exec_env a wit::string on the host is inaccessible. +Complex (non-POD) struct elements on the host will need exec_env to decode or construct. + +[^3]: A wit::string requires exec_env inside the host implementation. ~~Perhaps a flexible type (either std::string or wit::string would be possible), or make this a generation option?~~ std::string requires a copy, wit::string requires passing exec_env to the method (which is necessary for methods anyway). + +[^4]: A host side wit::string doesn't own the data (not free in dtor), thus no move semantics. + +[^5]: std::span requires C++-20, this alias should give minimal functionality with older compiler targets. + +[^6]: Not implemented, for now symmetric is priority + +[^7]: Here the callee (guest) allocates the memory for the set on its side + +[^8]: Caller passes address of the return object as argument + +## [Symmetric ABI](https://github.com/WebAssembly/component-model/issues/386) + +The idea is to directly connect (link) components to each other. + +Thus imported and exported functions and resources need to be compatible +at the ABI level. + +For now for functions the guest import convention is used in both directions: + +- The imported function ABI is used with the following properties + + - (unchanged) List and string arguments are passed as Views, no free + required, lifetime is constrained until the end of the call + + - (unchanged) Owned resources in arguments or results pass ownership + to the callee + + - (unchanged) If there are too many (>1) flat results, a local + uninitialized ret_area is passed via the last argument + + - (unchanged) Returned objects are owned. + For functional safety, i.e. avoiding all + allocations in the hot path, the hope is with [#385](https://github.com/WebAssembly/component-model/issues/385). + +- The imported resource ABI is used also for exporting + with one modification: + + Resource IDs become usize, so you can optimize the resource table away. diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h new file mode 100644 index 000000000..68957569b --- /dev/null +++ b/crates/cpp/helper-types/wit-common.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include // size_t +#include +#if __cplusplus > 202001L +#include +#else +#include +#endif + +namespace wit { +#if __cplusplus > 202001L +using std::span; +#else +/// Minimal span (vector view) implementation for older C++ environments +template class span { + T const *address; + size_t length; + +public: + T const *data() const { return address; } + size_t size() const { return length; } + + typedef T const *const_iterator; + + const_iterator begin() const { return address; } + const_iterator end() const { return address + length; } + bool empty() const { return !length; } + T const &operator[](size_t index) const { return address[index]; } + span(T *a, size_t l) : address(a), length(l) {} + // create from any compatible vector (borrows data!) + template + span(std::vector const &vec) : address(vec.data()), length(vec.size()) {} +}; +#endif + +/// @brief Helper class to map between IDs and resources +/// @tparam R Type of the Resource +template class ResourceTable { + static std::map resources; + +public: + static R *lookup_resource(int32_t id) { + auto result = resources.find(id); + return result == resources.end() ? nullptr : &result->second; + } + static int32_t store_resource(R &&value) { + auto last = resources.rbegin(); + int32_t id = last == resources.rend() ? 0 : last->first + 1; + resources.insert(std::pair(id, std::move(value))); + return id; + } + static std::optional remove_resource(int32_t id) { + auto iter = resources.find(id); + std::optional result; + if (iter != resources.end()) { + result = std::move(iter->second); + resources.erase(iter); + } + return std::move(result); + } +}; + +/// @brief Replaces void in the error position of a result +struct Void {}; +} // namespace wit diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h new file mode 100644 index 000000000..e1d44f8fa --- /dev/null +++ b/crates/cpp/helper-types/wit-guest.h @@ -0,0 +1,213 @@ +#pragma once +#include "wit-common.h" +#include +#include // unique_ptr +#include +#include +#include +#include // memcpy +#include // free +#include + +namespace wit { +/// A string in linear memory, freed unconditionally using free +/// +/// A normal C++ string makes no guarantees about where the characters +/// are stored and how this is freed. +class string { + uint8_t const *data_; + size_t length; + // C++ is horrible! + //constexpr uint8_t const *const empty_ptr = (uint8_t const *)1; + static uint8_t const* empty_ptr() { return (uint8_t const *)1; } + +public: + // this constructor is helpful for creating vector + string(string const &b) : string(string::from_view(b.get_view())) {} + string(string &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } + string &operator=(string const &) = delete; + string &operator=(string &&b) { + if (data_ && data_!=empty_ptr()) { + free(const_cast(data_)); + } + data_ = b.data_; + length = b.length; + b.data_ = nullptr; + return *this; + } + string(char const *d, size_t l) : data_((uint8_t const *)d), length(l) {} + char const *data() const { return (char const *)data_; } + size_t size() const { return length; } + bool empty() const { return !length; } + ~string() { + if (data_ && data_!=empty_ptr()) { + free(const_cast(data_)); + } + } + // leak the memory + void leak() { data_ = nullptr; } + // typically called by post + static void drop_raw(void *ptr) { free(ptr); } + std::string_view get_view() const { + return std::string_view((const char *)data_, length); + } + std::string to_string() const { + return std::string((const char *)data_, length); + } + static string from_view(std::string_view v) { + if (!v.size()) return string((char const*)empty_ptr(), 0); + char* addr = (char*)malloc(v.size()); + memcpy(addr, v.data(), v.size()); + return string(addr, v.size()); + } +}; + +/// A vector in linear memory, freed unconditionally using free +/// +/// You can't detach the data memory from a vector, nor create one +/// in a portable way from a buffer and lenght without copying. +template class vector { + T *data_; + size_t length; + + static T* empty_ptr() { return (T*)alignof(T); } + +public: + vector(vector const &) = delete; + vector(vector &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } + vector &operator=(vector const &) = delete; + vector &operator=(vector &&b) { + if (data_ && length>0) { + free(const_cast(data_)); + } + data_ = b.data_; + length = b.length; + b.data_ = nullptr; + return *this; + } + vector(T *d, size_t l) : data_(d), length(l) {} + // Rust needs a nonzero pointer here (alignment is typical) + vector() : data_(empty_ptr()), length() {} + T const *data() const { return data_; } + T *data() { return data_; } + T &operator[](size_t n) { return data_[n]; } + T const &operator[](size_t n) const { return data_[n]; } + size_t size() const { return length; } + bool empty() const { return !length; } + ~vector() { + if (data_ && length>0) { + for (unsigned i=0;i allocate(size_t len) { + if (!len) return vector(empty_ptr(), 0); + return vector((T*)malloc(sizeof(T)*len), len); + } + void initialize(size_t n, T&& elem) { + new ((void*)(data_+n)) T(std::move(elem)); + } + // leak the memory + T* leak() { T*result = data_; data_ = nullptr; return result; } + // typically called by post + static void drop_raw(void *ptr) { if (ptr!=empty_ptr()) free(ptr); } + wit::span get_view() const { return wit::span(data_, length); } + wit::span get_const_view() const { return wit::span(data_, length); } + template static vector from_view(wit::span const& a) { + auto result = vector::allocate(a.size()); + for (uint32_t i=0;i class ResourceExportBase { +public: + struct Deregister { + void operator()(R *ptr) const { + // probably always true because of unique_ptr wrapping, TODO: check +#ifdef WIT_SYMMETRIC + if (ptr->handle != nullptr) +#else + if (ptr->handle >= 0) +#endif + { + // we can't deallocate because the host calls Dtor + R::ResourceDrop(ptr->handle); + } + } + }; + typedef std::unique_ptr Owned; + +#ifdef WIT_SYMMETRIC + typedef uint8_t *handle_t; + static constexpr handle_t invalid = nullptr; +#else + typedef int32_t handle_t; + static const handle_t invalid = -1; +#endif + + handle_t handle; + + ResourceExportBase() : handle(R::ResourceNew((R *)this)) {} + // because this function is called by the host via Dtor we must not deregister + ~ResourceExportBase() {} + ResourceExportBase(ResourceExportBase const &) = delete; + ResourceExportBase(ResourceExportBase &&) = delete; + ResourceExportBase &operator=(ResourceExportBase &&b) = delete; + ResourceExportBase &operator=(ResourceExportBase const &) = delete; + handle_t get_handle() const { return handle; } + handle_t into_handle() { + handle_t result = handle; + handle = invalid; + return result; + } +}; + +/// @brief A Resource imported from the host (guest side) +/// +/// Wraps the identifier and can be forwarded but not duplicated +class ResourceImportBase { +public: +#ifdef WIT_SYMMETRIC + typedef uint8_t *handle_t; + static constexpr handle_t invalid = nullptr; +#else + typedef int32_t handle_t; + static const handle_t invalid = -1; +#endif + +protected: + handle_t handle; + +public: + ResourceImportBase(handle_t h = invalid) : handle(h) {} + ResourceImportBase(ResourceImportBase &&r) : handle(r.handle) { + r.handle = invalid; + } + ResourceImportBase(ResourceImportBase const &) = delete; + void set_handle(handle_t h) { handle = h; } + handle_t get_handle() const { return handle; } + handle_t into_handle() { + handle_t h = handle; + handle = invalid; + return h; + } + ResourceImportBase &operator=(ResourceImportBase &&r) { + assert(handle == invalid); + handle = r.handle; + r.handle = invalid; + return *this; + } + ResourceImportBase &operator=(ResourceImportBase const &r) = delete; +}; +} // namespace wit diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs new file mode 100644 index 000000000..2427d52b3 --- /dev/null +++ b/crates/cpp/src/lib.rs @@ -0,0 +1,3238 @@ +use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Write as FmtWrite, + io::{Read, Write}, + process::{Command, Stdio}, + str::FromStr, +}; +use symbol_name::{make_external_component, make_external_symbol}; +use wit_bindgen_c::to_c_ident; +use wit_bindgen_core::{ + abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, + uwrite, uwriteln, + wit_parser::{ + Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, + Resolve, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + }, + Files, InterfaceGenerator, Source, WorldGenerator, +}; + +// mod wamr; +mod symbol_name; + +pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase"; +pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; +pub const RESOURCE_TABLE_NAME: &str = "ResourceTable"; +pub const OWNED_CLASS_NAME: &str = "Owned"; +pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)"; +// these types are always defined in the non-exports namespace +const NOT_IN_EXPORTED_NAMESPACE: bool = false; + +type CppType = String; + +#[derive(Clone, Copy, Debug)] +enum Flavor { + Argument(AbiVariant), + Result(AbiVariant), + InStruct, + BorrowedArgument, +} + +impl Flavor { + fn is_guest_export(&self) -> bool { + match self { + Flavor::Argument(var) => matches!(var, AbiVariant::GuestExport), + Flavor::Result(var) => matches!(var, AbiVariant::GuestExport), + Flavor::InStruct | Flavor::BorrowedArgument => false, + } + } +} + +#[derive(Default)] +struct HighlevelSignature { + /// this is a constructor or destructor without a written type + // implicit_result: bool, -> empty result + const_member: bool, + static_member: bool, + result: CppType, + arguments: Vec<(String, CppType)>, + name: String, + namespace: Vec, + implicit_self: bool, + post_return: bool, +} + +// follows https://google.github.io/styleguide/cppguide.html + +#[derive(Default)] +struct Includes { + needs_vector: bool, + needs_expected: bool, + needs_string: bool, + needs_string_view: bool, + needs_optional: bool, + needs_cstring: bool, + // needs_guest_alloc: bool, + needs_imported_resources: bool, + needs_exported_resources: bool, + needs_variant: bool, + needs_tuple: bool, + needs_assert: bool, + // needs wit types + needs_wit: bool, + needs_memory: bool, +} + +#[derive(Default)] +struct SourceWithState { + src: Source, + namespace: Vec, +} + +#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)] +enum Direction { + Import, + Export, +} + +#[derive(Default)] +struct Cpp { + opts: Opts, + c_src: SourceWithState, + h_src: SourceWithState, + c_src_head: Source, + // interface_includes: Vec, + // interface_header: SourceWithState, + extern_c_decls: Source, + dependencies: Includes, + includes: Vec, + // host_functions: HashMap>, + world: String, + world_id: Option, + imported_interfaces: HashSet, + user_class_files: HashMap, + defined_types: HashSet<(Vec, String)>, + + // needed for symmetric disambiguation + interface_prefixes: HashMap<(Direction, WorldKey), String>, + import_prefix: Option, +} + +#[derive(Default, Debug, Clone, Copy)] +pub enum Ownership { + /// Generated types will be composed entirely of owning fields, regardless + /// of whether they are used as parameters to imports or not. + #[default] + Owning, + + /// Generated types used as parameters to imports will be "deeply + /// borrowing", i.e. contain references rather than owned values when + /// applicable. + Borrowing { + /// Whether or not to generate "duplicate" type definitions for a single + /// WIT type if necessary, for example if it's used as both an import + /// and an export, or if it's used both as a parameter to an import and + /// a return value from an import. + duplicate_if_necessary: bool, + }, +} + +impl FromStr for Ownership { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "owning" => Ok(Self::Owning), + "borrowing" => Ok(Self::Borrowing { + duplicate_if_necessary: false, + }), + "borrowing-duplicate-if-necessary" => Ok(Self::Borrowing { + duplicate_if_necessary: true, + }), + _ => Err(format!( + "unrecognized ownership: `{s}`; \ + expected `owning`, `borrowing`, or `borrowing-duplicate-if-necessary`" + )), + } + } +} + +impl core::fmt::Display for Ownership { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.write_str(match self { + Ownership::Owning => "owning", + Ownership::Borrowing { + duplicate_if_necessary: false, + } => "borrowing", + Ownership::Borrowing { + duplicate_if_necessary: true, + } => "borrowing-duplicate-if-necessary", + }) + } +} + +#[derive(Default, Debug, Clone)] +#[cfg_attr(feature = "clap", derive(clap::Args))] +pub struct Opts { + /// Call clang-format on the generated code + #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))] + pub format: bool, + + /// Place each interface in its own file, + /// this enables sharing bindings across projects + #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))] + pub split_interfaces: bool, + + /// Optionally prefix any export names with the specified value. + /// + /// This is useful to avoid name conflicts when testing. + #[cfg_attr(feature = "clap", arg(long))] + pub export_prefix: Option, + + /// Wrap all C++ classes inside a custom namespace. + /// + /// This avoids identical names across components, useful for native + #[cfg_attr(feature = "clap", arg(long))] + pub internal_prefix: Option, + + /// Whether to generate owning or borrowing type definitions. + /// + /// Valid values include: + /// + /// - `owning`: Generated types will be composed entirely of owning fields, + /// regardless of whether they are used as parameters to imports or not. + /// + /// - `borrowing`: Generated types used as parameters to imports will be + /// "deeply borrowing", i.e. contain references rather than owned values + /// when applicable. + /// + /// - `borrowing-duplicate-if-necessary`: As above, but generating distinct + /// types for borrowing and owning, if necessary. + #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))] + pub ownership: Ownership, + + /// Symmetric API, same API for imported and exported functions. + /// Reduces the allocation overhead for symmetric ABI. + #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))] + pub new_api: bool, +} + +impl Opts { + pub fn build(self) -> Box { + let mut r = Cpp::new(); + r.opts = self; + Box::new(r) + } + + fn is_only_handle(&self, variant: AbiVariant) -> bool { + false == matches!(variant, AbiVariant::GuestExport) + } + + fn ptr_type(&self) -> &'static str { + "int32_t" + } +} + +impl Cpp { + fn new() -> Cpp { + Cpp::default() + } + + pub fn is_first_definition(&mut self, ns: &Vec, name: &str) -> bool { + let owned = (ns.to_owned(), name.to_owned()); + if !self.defined_types.contains(&owned) { + self.defined_types.insert(owned); + true + } else { + false + } + } + + fn include(&mut self, s: &str) { + self.includes.push(s.to_string()); + } + + fn interface<'a>( + &'a mut self, + resolve: &'a Resolve, + name: Option<&'a WorldKey>, + in_guest_import: bool, + wasm_import_module: Option, + ) -> CppInterfaceGenerator<'a> { + let mut sizes = SizeAlign::default(); + sizes.fill(resolve); + + CppInterfaceGenerator { + _src: Source::default(), + gen: self, + resolve, + interface: None, + _name: name, + sizes, + // public_anonymous_types: BTreeSet::new(), + in_guest_import, + // export_funcs: Vec::new(), + // return_pointer_area_size: 0, + // return_pointer_area_align: 0, + wasm_import_module, + } + } + + fn clang_format(code: &mut String) { + let mut child = Command::new("clang-format") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to spawn `clang-format`"); + child + .stdin + .take() + .unwrap() + .write_all(code.as_bytes()) + .unwrap(); + code.truncate(0); + child.stdout.take().unwrap().read_to_string(code).unwrap(); + let status = child.wait().unwrap(); + assert!(status.success()); + } + + fn perform_cast(&mut self, op: &str, cast: &Bitcast) -> String { + match cast { + Bitcast::I32ToF32 | Bitcast::I64ToF32 => { + format!("((union {{ int32_t a; float b; }}){{ {} }}).b", op) + } + Bitcast::F32ToI32 | Bitcast::F32ToI64 => { + format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op) + } + Bitcast::I64ToF64 => { + format!("((union {{ int64_t a; double b; }}){{ {} }}).b", op) + } + Bitcast::F64ToI64 => { + format!("((union {{ double a; int64_t b; }}){{ {} }}).b", op) + } + Bitcast::I32ToI64 | Bitcast::LToI64 | Bitcast::PToP64 => { + format!("(int64_t) {}", op) + } + Bitcast::I64ToI32 | Bitcast::PToI32 | Bitcast::LToI32 => { + format!("(int32_t) {}", op) + } + Bitcast::P64ToI64 | Bitcast::None | Bitcast::I64ToP64 => op.to_string(), + Bitcast::P64ToP | Bitcast::I32ToP | Bitcast::LToP => { + format!("(uint8_t*) {}", op) + } + Bitcast::PToL | Bitcast::I32ToL | Bitcast::I64ToL => { + format!("(size_t) {}", op) + } + Bitcast::Sequence(sequence) => { + let [first, second] = &**sequence; + let inner = self.perform_cast(op, first); + self.perform_cast(&inner, second) + } + } + } + + fn finish_includes(&mut self) { + self.include(""); + self.include(""); // for std::move + if self.dependencies.needs_string { + self.include(""); + } + if self.dependencies.needs_string_view { + self.include(""); + } + if self.dependencies.needs_vector { + self.include(""); + } + if self.dependencies.needs_expected { + self.include(""); + } + if self.dependencies.needs_optional { + self.include(""); + } + if self.dependencies.needs_cstring { + self.include(""); + } + if self.dependencies.needs_imported_resources { + self.include(""); + } + if self.dependencies.needs_exported_resources { + self.include(""); + } + if self.dependencies.needs_variant { + self.include(""); + } + if self.dependencies.needs_tuple { + self.include(""); + } + if self.dependencies.needs_wit { + self.include(""); + } + if self.dependencies.needs_memory { + self.include(""); + } + } + + fn start_new_file(&mut self, condition: Option) -> FileContext { + if condition == Some(true) || self.opts.split_interfaces { + FileContext { + includes: std::mem::replace(&mut self.includes, Default::default()), + src: std::mem::replace(&mut self.h_src, Default::default()), + dependencies: std::mem::replace(&mut self.dependencies, Default::default()), + } + } else { + Default::default() + } + } + + fn finish_file(&mut self, namespace: &[String], store: FileContext) { + if !store.src.src.is_empty() { + // self.opts.split_interfaces { + let mut header = String::default(); + self.finish_includes(); + self.h_src.change_namespace(&Default::default()); + uwriteln!(header, "#pragma once"); + for include in self.includes.iter() { + uwriteln!(header, "#include {include}"); + } + header.push_str(&self.h_src.src); + let mut filename = namespace.join("-"); + filename.push_str(".h"); + if self.opts.format { + Self::clang_format(&mut header); + } + self.user_class_files.insert(filename.clone(), header); + + let _ = std::mem::replace(&mut self.includes, store.includes); + let _ = std::mem::replace(&mut self.h_src, store.src); + let _ = std::mem::replace(&mut self.dependencies, store.dependencies); + self.includes.push(String::from("\"") + &filename + "\""); + } + } +} + +#[derive(Default)] +struct FileContext { + includes: Vec, + src: SourceWithState, + dependencies: Includes, +} + +impl WorldGenerator for Cpp { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + let name = &resolve.worlds[world].name; + self.world = name.to_string(); + self.world_id = Some(world); + // self.sizes.fill(resolve); + uwriteln!( + self.c_src_head, + r#"#include "{}_cpp.h" + #include // realloc + + extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); + + __attribute__((__weak__, __export_name__("cabi_realloc"))) + void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {{ + (void) old_size; + if (new_size == 0) return (void*) align; + void *ret = realloc(ptr, new_size); + if (!ret) abort(); + return ret; + }} + + "#, + self.world.to_snake_case(), + ); + } + + fn import_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, + id: InterfaceId, + _files: &mut Files, + ) -> anyhow::Result<()> { + if let Some(prefix) = self + .interface_prefixes + .get(&(Direction::Import, name.clone())) + { + self.import_prefix = Some(prefix.clone()); + } + + let store = self.start_new_file(None); + self.imported_interfaces.insert(id); + let wasm_import_module = resolve.name_world_key(name); + let binding = Some(name); + let mut gen = self.interface(resolve, binding, true, Some(wasm_import_module)); + gen.interface = Some(id); + gen.types(id); + let namespace = namespace(resolve, &TypeOwner::Interface(id), false, &gen.gen.opts); + + for (_name, func) in resolve.interfaces[id].functions.iter() { + if matches!(func.kind, FunctionKind::Freestanding) { + gen.gen.h_src.change_namespace(&namespace); + gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport); + } + } + self.finish_file(&namespace, store); + let _ = self.import_prefix.take(); + Ok(()) + } + + fn export_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, + id: InterfaceId, + _files: &mut Files, + ) -> anyhow::Result<()> { + let old_prefix = self.opts.export_prefix.clone(); + if let Some(prefix) = self + .interface_prefixes + .get(&(Direction::Export, name.clone())) + { + self.opts.export_prefix = + Some(prefix.clone() + old_prefix.as_ref().unwrap_or(&String::new())); + } + let store = self.start_new_file(None); + self.h_src + .src + .push_str(&format!("// export_interface {name:?}\n")); + self.imported_interfaces.remove(&id); + let wasm_import_module = resolve.name_world_key(name); + let binding = Some(name); + let mut gen = self.interface(resolve, binding, false, Some(wasm_import_module)); + gen.interface = Some(id); + gen.types(id); + let namespace = namespace(resolve, &TypeOwner::Interface(id), true, &gen.gen.opts); + + for (_name, func) in resolve.interfaces[id].functions.iter() { + if matches!(func.kind, FunctionKind::Freestanding) { + gen.gen.h_src.change_namespace(&namespace); + gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestExport); + } + } + self.finish_file(&namespace, store); + self.opts.export_prefix = old_prefix; + Ok(()) + } + + fn import_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = WorldKey::Name("$root".to_string()); //WorldKey::Name(resolve.worlds[world].name.clone()); + let wasm_import_module = resolve.name_world_key(&name); + let binding = Some(name); + let mut gen = self.interface(resolve, binding.as_ref(), true, Some(wasm_import_module)); + let namespace = namespace(resolve, &TypeOwner::World(world), false, &gen.gen.opts); + + for (_name, func) in funcs.iter() { + if matches!(func.kind, FunctionKind::Freestanding) { + gen.gen.h_src.change_namespace(&namespace); + gen.generate_function(func, &TypeOwner::World(world), AbiVariant::GuestImport); + } + } + } + + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) -> anyhow::Result<()> { + let name = WorldKey::Name(resolve.worlds[world].name.clone()); + // let wasm_import_module = resolve.name_world_key(&name); + let binding = Some(name); + let mut gen = self.interface(resolve, binding.as_ref(), false, None); + let namespace = namespace(resolve, &TypeOwner::World(world), true, &gen.gen.opts); + + for (_name, func) in funcs.iter() { + if matches!(func.kind, FunctionKind::Freestanding) { + gen.gen.h_src.change_namespace(&namespace); + gen.generate_function(func, &TypeOwner::World(world), AbiVariant::GuestExport); + } + } + Ok(()) + } + + fn import_types( + &mut self, + _resolve: &Resolve, + _world: WorldId, + types: &[(&str, TypeId)], + _files: &mut Files, + ) { + for i in types.iter() { + uwriteln!(self.h_src.src, "// import_type {}", i.0); + } + } + + fn finish( + &mut self, + resolve: &Resolve, + world_id: WorldId, + files: &mut Files, + ) -> std::result::Result<(), anyhow::Error> { + let world = &resolve.worlds[world_id]; + let snake = world.name.to_snake_case(); + let linking_symbol = wit_bindgen_c::component_type_object::linking_symbol(&world.name); + + let mut h_str = SourceWithState::default(); + let mut c_str = SourceWithState::default(); + + let version = env!("CARGO_PKG_VERSION"); + uwriteln!( + h_str.src, + "// Generated by `wit-bindgen` {version}. DO NOT EDIT!" + ); + + uwrite!( + h_str.src, + "#ifndef __CPP_GUEST_BINDINGS_{0}_H + #define __CPP_GUEST_BINDINGS_{0}_H\n", + world.name.to_shouty_snake_case(), + ); + self.finish_includes(); + + for include in self.includes.iter() { + uwriteln!(h_str.src, "#include {include}"); + } + + uwriteln!( + c_str.src, + "// Generated by `wit-bindgen` {version}. DO NOT EDIT!" + ); + uwriteln!( + c_str.src, + "\n// Ensure that the *_component_type.o object is linked in" + ); + uwrite!( + c_str.src, + "#ifdef __wasm32__ + extern void {linking_symbol}(void); + void {linking_symbol}_public_use_in_this_compilation_unit(void) {{ + {linking_symbol}(); + }} + #endif + ", + ); + if self.dependencies.needs_assert { + uwriteln!(c_str.src, "#include "); + } + + h_str.change_namespace(&Vec::default()); + + self.c_src.change_namespace(&Vec::default()); + c_str.src.push_str(&self.c_src_head); + c_str.src.push_str(&self.extern_c_decls); + c_str.src.push_str(&self.c_src.src); + self.h_src.change_namespace(&Vec::default()); + h_str.src.push_str(&self.h_src.src); + + uwriteln!(c_str.src, "\n// Component Adapters"); + + uwriteln!( + h_str.src, + " + #endif" + ); + + if self.opts.format { + Self::clang_format(&mut c_str.src.as_mut_string()); + Self::clang_format(&mut h_str.src.as_mut_string()); + } + + files.push(&format!("{snake}.cpp"), c_str.src.as_bytes()); + files.push(&format!("{snake}_cpp.h"), h_str.src.as_bytes()); + for (name, content) in self.user_class_files.iter() { + // if the user class file exists create an updated .template + if std::path::Path::exists(&std::path::PathBuf::from(name)) { + files.push(&(String::from(name) + ".template"), content.as_bytes()); + } else { + files.push(name, content.as_bytes()); + } + } + files.push( + &format!("{snake}_component_type.o",), + wit_bindgen_c::component_type_object::object( + resolve, + world_id, + &world.name, + wit_component::StringEncoding::UTF8, + None, + ) + .unwrap() + .as_slice(), + ); + Ok(()) + } +} + +// determine namespace (for the lifted C++ function) +fn namespace(resolve: &Resolve, owner: &TypeOwner, guest_export: bool, opts: &Opts) -> Vec { + let mut result = Vec::default(); + if let Some(prefix) = &opts.internal_prefix { + result.push(prefix.clone()); + } + if guest_export { + result.push(String::from("exports")); + } + match owner { + TypeOwner::World(w) => result.push(resolve.worlds[*w].name.to_snake_case()), + TypeOwner::Interface(i) => { + let iface = &resolve.interfaces[*i]; + let pkg = &resolve.packages[iface.package.unwrap()]; + result.push(pkg.name.namespace.to_snake_case()); + result.push(pkg.name.name.to_snake_case()); + if let Some(name) = &iface.name { + result.push(name.to_snake_case()); + } + } + TypeOwner::None => (), + } + result +} + +impl SourceWithState { + fn change_namespace(&mut self, target: &Vec) { + let mut same = 0; + // itertools::fold_while? + for (a, b) in self.namespace.iter().zip(target.iter()) { + if a == b { + same += 1; + } else { + break; + } + } + for _i in same..self.namespace.len() { + uwrite!(self.src, "}}"); + } + if same != self.namespace.len() { + // finish closing brackets by a newline + uwriteln!(self.src, ""); + } + self.namespace.truncate(same); + for i in target.iter().skip(same) { + uwrite!(self.src, "namespace {} {{", i); + self.namespace.push(i.clone()); + } + } + + fn qualify(&mut self, target: &Vec) { + let mut same = 0; + // let mut subpart = false; + // itertools::fold_while? + for (a, b) in self.namespace.iter().zip(target.iter()) { + if a == b { + same += 1; + } else { + break; + } + } + if same == 0 && !target.is_empty() { + // if the root namespace exists below the current namespace we need to start at root + if self.namespace.contains(&target.first().unwrap()) { + self.src.push_str("::"); + } + } + for i in target.iter().skip(same) { + uwrite!(self.src, "{i}::"); + } + } +} + +struct CppInterfaceGenerator<'a> { + _src: Source, + gen: &'a mut Cpp, + resolve: &'a Resolve, + interface: Option, + _name: Option<&'a WorldKey>, + sizes: SizeAlign, + in_guest_import: bool, + // return_pointer_area_size: usize, + // return_pointer_area_align: usize, + pub wasm_import_module: Option, +} + +// I wish this was possible +// impl Equivalent<(Vec, String)> for (&Vec, &str) { + +// } + +impl CppInterfaceGenerator<'_> { + fn types(&mut self, iface: InterfaceId) { + let iface = &self.resolve().interfaces[iface]; + for (name, id) in iface.types.iter() { + self.define_type(name, *id); + } + } + + fn define_type(&mut self, name: &str, id: TypeId) { + let ty = &self.resolve().types[id]; + match &ty.kind { + TypeDefKind::Record(record) => self.type_record(id, name, record, &ty.docs), + TypeDefKind::Resource => self.type_resource(id, name, &ty.docs), + TypeDefKind::Flags(flags) => self.type_flags(id, name, flags, &ty.docs), + TypeDefKind::Tuple(tuple) => self.type_tuple(id, name, tuple, &ty.docs), + TypeDefKind::Enum(enum_) => self.type_enum(id, name, enum_, &ty.docs), + TypeDefKind::Variant(variant) => self.type_variant(id, name, variant, &ty.docs), + TypeDefKind::Option(t) => self.type_option(id, name, t, &ty.docs), + TypeDefKind::Result(r) => self.type_result(id, name, r, &ty.docs), + TypeDefKind::List(t) => self.type_list(id, name, t, &ty.docs), + TypeDefKind::Type(t) => self.type_alias(id, name, t, &ty.docs), + TypeDefKind::Future(_) => todo!("generate for future"), + TypeDefKind::Stream(_) => todo!("generate for stream"), + TypeDefKind::Handle(_) => todo!("generate for handle"), + TypeDefKind::Unknown => unreachable!(), + } + } + + /// This describes the C++ side name + fn func_namespace_name( + &self, + func: &Function, + guest_export: bool, + cpp_file: bool, + ) -> (Vec, String) { + let (object, owner) = match &func.kind { + FunctionKind::Freestanding => None, + FunctionKind::Method(i) => Some(i), + FunctionKind::Static(i) => Some(i), + FunctionKind::Constructor(i) => Some(i), + FunctionKind::AsyncFreestanding => todo!(), + FunctionKind::AsyncMethod(_id) => todo!(), + FunctionKind::AsyncStatic(_id) => todo!(), + } + .map(|i| { + let ty = &self.resolve.types[*i]; + (ty.name.as_ref().unwrap().to_pascal_case(), ty.owner) + }) + .unwrap_or(( + Default::default(), + self.interface + .map(|id| TypeOwner::Interface(id)) + .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())), + )); + let mut namespace = namespace(self.resolve, &owner, guest_export, &self.gen.opts); + let is_drop = is_special_method(func); + let func_name_h = if !matches!(&func.kind, FunctionKind::Freestanding) { + namespace.push(object.clone()); + if let FunctionKind::Constructor(_i) = &func.kind { + if guest_export && cpp_file { + String::from("New") + } else { + object.clone() + } + } else { + match is_drop { + SpecialMethod::ResourceDrop => { + if guest_export { + "ResourceDrop".to_string() + } else { + "~".to_string() + &object + } + } + SpecialMethod::Dtor => "Dtor".to_string(), + SpecialMethod::ResourceNew => "ResourceNew".to_string(), + SpecialMethod::ResourceRep => "ResourceRep".to_string(), + SpecialMethod::Allocate => "New".to_string(), + // SpecialMethod::Deallocate => "Deallocate".to_string(), + SpecialMethod::None => func.item_name().to_pascal_case(), + } + } + } else { + func.name.to_pascal_case() + }; + (namespace, func_name_h) + } + + // print the signature of the guest export (lowered (wasm) function calling into highlevel) + fn print_export_signature(&mut self, func: &Function, variant: AbiVariant) -> Vec { + let is_drop = is_special_method(func); + let id_type = WasmType::I32; + let signature = match is_drop { + SpecialMethod::ResourceDrop => WasmSignature { + params: vec![id_type], + results: Vec::new(), + indirect_params: false, + retptr: false, + }, + SpecialMethod::ResourceRep => WasmSignature { + params: vec![id_type], + results: vec![WasmType::Pointer], + indirect_params: false, + retptr: false, + }, + SpecialMethod::Dtor => WasmSignature { + params: vec![WasmType::Pointer], + results: Vec::new(), + indirect_params: false, + retptr: false, + }, + SpecialMethod::ResourceNew => WasmSignature { + params: vec![WasmType::Pointer], + results: vec![id_type], + indirect_params: false, + retptr: false, + }, + SpecialMethod::None => { + // TODO perhaps remember better names for the arguments + self.resolve.wasm_signature(variant, func) + } + SpecialMethod::Allocate => WasmSignature { + params: vec![], + results: vec![], + indirect_params: false, + retptr: false, + }, + }; + let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); + let symbol_variant = variant; + if matches!(variant, AbiVariant::GuestExport) + && matches!( + is_drop, + SpecialMethod::ResourceNew + | SpecialMethod::ResourceDrop + | SpecialMethod::ResourceRep + ) + { + module_name = Some(String::from("[export]") + &module_name.unwrap()); + } + let func_name = func.name.clone(); + let module_prefix = module_name.as_ref().map_or(String::default(), |name| { + let mut res = name.clone(); + res.push('#'); + res + }); + uwriteln!( + self.gen.c_src.src, + r#"extern "C" __attribute__((__export_name__("{module_prefix}{func_name}")))"# + ); + let return_via_pointer = false; + self.gen + .c_src + .src + .push_str(if signature.results.is_empty() || return_via_pointer { + "void" + } else { + wit_bindgen_c::wasm_type(signature.results[0]) + }); + self.gen.c_src.src.push_str(" "); + let export_name = match module_name { + Some(ref module_name) => make_external_symbol(&module_name, &func_name, symbol_variant), + None => make_external_component(&func_name), + }; + if let Some(prefix) = self.gen.opts.export_prefix.as_ref() { + self.gen.c_src.src.push_str(prefix); + } + self.gen.c_src.src.push_str(&export_name); + self.gen.c_src.src.push_str("("); + let mut first_arg = true; + let mut params = Vec::new(); + for (n, ty) in signature.params.iter().enumerate() { + let name = format!("arg{n}"); + if !first_arg { + self.gen.c_src.src.push_str(", "); + } else { + first_arg = false; + } + self.gen.c_src.src.push_str(wit_bindgen_c::wasm_type(*ty)); + self.gen.c_src.src.push_str(" "); + self.gen.c_src.src.push_str(&name); + params.push(name); + } + if return_via_pointer { + if !first_arg { + self.gen.c_src.src.push_str(", "); + } + // else { + // first_arg = false; + // } + self.gen.c_src.src.push_str(self.gen.opts.ptr_type()); + self.gen.c_src.src.push_str(" resultptr"); + params.push("resultptr".into()); + } + self.gen.c_src.src.push_str(")\n"); + params + } + + fn high_level_signature( + &mut self, + func: &Function, + abi_variant: AbiVariant, + // import: bool, + _from_namespace: &Vec, + ) -> HighlevelSignature { + let mut res = HighlevelSignature::default(); + // let abi_variant = if import ^ self.gen.opts.host_side() { + // AbiVariant::GuestImport + // } else { + // AbiVariant::GuestExport + // }; + + let (namespace, func_name_h) = + self.func_namespace_name(func, matches!(abi_variant, AbiVariant::GuestExport), false); + res.name = func_name_h; + res.namespace = namespace; + let is_drop = is_special_method(func); + // we might want to separate c_sig and h_sig + // let mut sig = String::new(); + // not for ctor nor imported dtor on guest + if !matches!(&func.kind, FunctionKind::Constructor(_)) + && !(matches!(is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport)) + { + if let Some(ty) = &func.result { + res.result.push_str(&self.type_name( + ty, + &res.namespace, + Flavor::Result(abi_variant), + )); + } else { + res.result = "void".into(); + } + if matches!(abi_variant, AbiVariant::GuestExport) + && abi::guest_export_needs_post_return(self.resolve, func) + { + res.post_return = true; + } + } + if matches!(func.kind, FunctionKind::Static(_)) + && !(matches!(&is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport)) + { + res.static_member = true; + } + for (i, (name, param)) in func.params.iter().enumerate() { + if i == 0 && name == "self" && matches!(&func.kind, FunctionKind::Method(_)) + || (matches!(&is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport)) + { + res.implicit_self = true; + continue; + } + res.arguments.push(( + name.to_snake_case(), + self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)), + )); + } + // default to non-const when exporting a method + let import = matches!(abi_variant, AbiVariant::GuestImport); + if matches!(func.kind, FunctionKind::Method(_)) && import { + res.const_member = true; + } + res + } + + fn print_signature( + &mut self, + func: &Function, + variant: AbiVariant, + import: bool, + ) -> Vec { + let is_special = is_special_method(func); + let from_namespace = self.gen.h_src.namespace.clone(); + let cpp_sig = self.high_level_signature(func, variant, &from_namespace); + if cpp_sig.static_member { + self.gen.h_src.src.push_str("static "); + } + self.gen.h_src.src.push_str(&cpp_sig.result); + if !cpp_sig.result.is_empty() { + self.gen.h_src.src.push_str(" "); + } + self.gen.h_src.src.push_str(&cpp_sig.name); + self.gen.h_src.src.push_str("("); + for (num, (arg, typ)) in cpp_sig.arguments.iter().enumerate() { + if num > 0 { + self.gen.h_src.src.push_str(", "); + } + self.gen.h_src.src.push_str(typ); + self.gen.h_src.src.push_str(" "); + self.gen.h_src.src.push_str(arg); + } + self.gen.h_src.src.push_str(")"); + if cpp_sig.const_member { + self.gen.h_src.src.push_str(" const"); + } + match (&is_special, false, &variant) { + (SpecialMethod::Allocate, _, _) => { + uwrite!( + self.gen.h_src.src, + "{{\ + return {OWNED_CLASS_NAME}(new {}({}));\ + }}", + cpp_sig.namespace.last().unwrap(), //join("::"), + cpp_sig + .arguments + .iter() + .map(|(arg, _)| arg.clone()) + .collect::>() + .join(", ") + ); + // body is inside the header + return Vec::default(); + } + (SpecialMethod::Dtor, _, AbiVariant::GuestImport) + | (SpecialMethod::ResourceDrop, true, _) => { + uwrite!( + self.gen.h_src.src, + "{{\ + delete {};\ + }}", + cpp_sig.arguments.get(0).unwrap().0 + ); + } + // SpecialMethod::None => todo!(), + // SpecialMethod::ResourceDrop => todo!(), + // SpecialMethod::ResourceNew => todo!(), + _ => self.gen.h_src.src.push_str(";\n"), + } + + // we want to separate the lowered signature (wasm) and the high level signature + if !import + && !matches!( + &is_special, + SpecialMethod::ResourceDrop + | SpecialMethod::ResourceNew + | SpecialMethod::ResourceRep + ) + { + self.print_export_signature(func, variant) + } else { + // recalulate with c file namespace + let c_namespace = self.gen.c_src.namespace.clone(); + let cpp_sig = self.high_level_signature(func, variant, &c_namespace); + let mut params = Vec::new(); + self.gen.c_src.src.push_str(&cpp_sig.result); + if !cpp_sig.result.is_empty() { + self.gen.c_src.src.push_str(" "); + } + self.gen.c_src.qualify(&cpp_sig.namespace); + self.gen.c_src.src.push_str(&cpp_sig.name); + self.gen.c_src.src.push_str("("); + if cpp_sig.implicit_self { + params.push("(*this)".into()); + } + for (num, (arg, typ)) in cpp_sig.arguments.iter().enumerate() { + if num > 0 { + self.gen.c_src.src.push_str(", "); + } + self.gen.c_src.src.push_str(typ); + self.gen.c_src.src.push_str(" "); + self.gen.c_src.src.push_str(arg); + params.push(arg.clone()); + } + self.gen.c_src.src.push_str(")"); + if cpp_sig.const_member { + self.gen.c_src.src.push_str(" const"); + } + self.gen.c_src.src.push_str("\n"); + params + } + } + + fn generate_function( + &mut self, + func: &Function, + owner: &TypeOwner, + //interface: InterfaceId, + variant: AbiVariant, + ) { + fn class_namespace( + cifg: &CppInterfaceGenerator, + func: &Function, + variant: AbiVariant, + ) -> Vec { + let owner = &cifg.resolve.types[match &func.kind { + FunctionKind::Static(id) => *id, + _ => panic!("special func should be static"), + }]; + let mut namespace = namespace( + cifg.resolve, + &owner.owner, + matches!(variant, AbiVariant::GuestExport), + &cifg.gen.opts, + ); + namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + namespace + } + + let export = match variant { + AbiVariant::GuestImport => false, + AbiVariant::GuestExport => true, + AbiVariant::GuestImportAsync => todo!(), + AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), + }; + let params = self.print_signature(func, variant, !export); + let special = is_special_method(func); + if !matches!(special, SpecialMethod::Allocate) { + self.gen.c_src.src.push_str("{\n"); + let needs_dealloc = + if self.gen.opts.new_api && matches!(variant, AbiVariant::GuestExport) { + self.gen + .c_src + .src + .push_str("std::vector _deallocate;\n"); + self.gen.dependencies.needs_vector = true; + true + } else { + false + }; + let lift_lower = if export { + LiftLower::LiftArgsLowerResults + } else { + LiftLower::LowerArgsLiftResults + }; + match is_special_method(func) { + SpecialMethod::ResourceDrop => match lift_lower { + LiftLower::LiftArgsLowerResults => { + let module_name = String::from("[export]") + + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let wasm_sig = + self.declare_import(&module_name, &func.name, &[WasmType::I32], &[]); + uwriteln!( + self.gen.c_src.src, + "{wasm_sig}({});", + func.params.get(0).unwrap().0 + ); + } + LiftLower::LowerArgsLiftResults => { + let module_name = + self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let name = + self.declare_import(&module_name, &func.name, &[WasmType::I32], &[]); + uwriteln!( + self.gen.c_src.src, + " if (handle>=0) {{ + {name}(handle); + }}" + ); + } + }, + SpecialMethod::Dtor => { + let classname = class_namespace(self, func, variant).join("::"); + uwriteln!(self.gen.c_src.src, "(({classname}*)arg0)->handle=-1;"); + uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); + } + SpecialMethod::ResourceNew => { + let module_name = String::from("[export]") + + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let wasm_sig = self.declare_import( + &module_name, + &func.name, + &[WasmType::Pointer], + &[WasmType::I32], + ); + uwriteln!( + self.gen.c_src.src, + "return {wasm_sig}(({}){});", + self.gen.opts.ptr_type(), + func.params.get(0).unwrap().0 + ); + } + SpecialMethod::ResourceRep => { + let module_name = String::from("[export]") + + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let wasm_sig = self.declare_import( + &module_name, + &func.name, + &[WasmType::I32], + &[WasmType::Pointer], + ); + let classname = class_namespace(self, func, variant).join("::"); + uwriteln!( + self.gen.c_src.src, + "return ({}*){wasm_sig}({});", + classname, + func.params.get(0).unwrap().0 + ); + } + SpecialMethod::Allocate => unreachable!(), + SpecialMethod::None => { + // normal methods + let namespace = if matches!(func.kind, FunctionKind::Freestanding) { + namespace( + self.resolve, + owner, + matches!(variant, AbiVariant::GuestExport), + &self.gen.opts, + ) + } else { + let owner = &self.resolve.types[match &func.kind { + FunctionKind::Static(id) => *id, + FunctionKind::Constructor(id) => *id, + FunctionKind::Method(id) => *id, + FunctionKind::Freestanding => unreachable!(), + FunctionKind::AsyncFreestanding => todo!(), + FunctionKind::AsyncMethod(_id) => todo!(), + FunctionKind::AsyncStatic(_id) => todo!(), + }] + .clone(); + let mut namespace = namespace( + self.resolve, + &owner.owner, + matches!(variant, AbiVariant::GuestExport), + &self.gen.opts, + ); + namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + namespace + }; + let mut f = FunctionBindgen::new(self, params); + if !export { + f.namespace = namespace.clone(); + // f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); + } + f.variant = variant; + f.needs_dealloc = needs_dealloc; + f.cabi_post = None; + abi::call(f.gen.resolve, variant, lift_lower, func, &mut f, false); + let code = String::from(f.src); + self.gen.c_src.src.push_str(&code); + } + } + self.gen.c_src.src.push_str("}\n"); + // cabi_post + if matches!(variant, AbiVariant::GuestExport) + && abi::guest_export_needs_post_return(self.resolve, func) + { + let sig = self.resolve.wasm_signature(variant, func); + let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); + let export_name = match module_name { + Some(ref module_name) => { + // let symbol_variant = if self.gen.opts.symmetric { + // AbiVariant::GuestImport + // } else { + // variant + // }; + // make_external_symbol(module_name, &func.name, symbol_variant) + format!("{module_name}#{}", func.name) + } + None => make_external_component(&func.name), + }; + //let export_name = func.core_export_name(Some(&module_name)); + let import_name = match module_name { + Some(ref module_name) => { + make_external_symbol(module_name, &func.name, AbiVariant::GuestExport) + } + None => make_external_component(&func.name), + }; + // make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); + // let module_prefix = module_name.as_ref().map_or(String::default(), |name| { + // let mut res = name.clone(); + // res.push('#'); + // res + // }); + uwriteln!( + self.gen.c_src.src, + "extern \"C\" __attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" + ); + uwrite!(self.gen.c_src.src, "void cabi_post_{import_name}("); + + let mut params = Vec::new(); + for (i, result) in sig.results.iter().enumerate() { + let name = format!("arg{i}"); + uwrite!( + self.gen.c_src.src, + "{} {name}", + wit_bindgen_c::wasm_type(*result) + ); + params.push(name); + } + self.gen.c_src.src.push_str(") {\n"); + + let mut f = FunctionBindgen::new(self, params.clone()); + f.params = params; + abi::post_return(f.gen.resolve, func, &mut f); + let FunctionBindgen { src, .. } = f; + self.gen.c_src.src.push_str(&src); + self.gen.c_src.src.push_str("}\n"); + } + } + } + + pub fn type_path(&self, id: TypeId, owned: bool) -> String { + self.type_path_with_name( + id, + if owned { + self.result_name(id) + } else { + self.param_name(id) + }, + ) + } + + fn type_path_with_name(&self, id: TypeId, name: String) -> String { + if let TypeOwner::Interface(id) = self.resolve.types[id].owner { + if let Some(path) = self.path_to_interface(id) { + return format!("{path}::{name}"); + } + } + name + } + + fn path_to_interface(&self, interface: InterfaceId) -> Option { + let iface = &self.resolve.interfaces[interface]; + let name = iface.name.as_ref().unwrap(); + let mut full_path = String::new(); + full_path.push_str(name); + Some(full_path) + } + + fn param_name(&self, ty: TypeId) -> String { + self.resolve.types[ty] + .name + .as_ref() + .unwrap() + .to_upper_camel_case() + } + + fn result_name(&self, ty: TypeId) -> String { + self.resolve.types[ty] + .name + .as_ref() + .unwrap() + .to_upper_camel_case() + } + + // in C this is print_optional_ty + fn optional_type_name( + &mut self, + ty: Option<&Type>, + from_namespace: &Vec, + flavor: Flavor, + ) -> String { + match ty { + Some(ty) => self.type_name(ty, from_namespace, flavor), + None => "void".into(), + } + } + + fn scoped_type_name( + &self, + id: TypeId, + from_namespace: &Vec, + guest_export: bool, + ) -> String { + let ty = &self.resolve.types[id]; + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); + let mut relative = SourceWithState::default(); + relative.namespace = from_namespace.clone(); + relative.qualify(&namespc); + format!( + "{}{}", + relative.src.to_string(), + ty.name.as_ref().unwrap().to_pascal_case() + ) + } + + fn type_name(&mut self, ty: &Type, from_namespace: &Vec, flavor: Flavor) -> String { + match ty { + Type::Bool => "bool".into(), + Type::Char => "uint32_t".into(), + Type::U8 => "uint8_t".into(), + Type::S8 => "int8_t".into(), + Type::U16 => "uint16_t".into(), + Type::S16 => "int16_t".into(), + Type::U32 => "uint32_t".into(), + Type::S32 => "int32_t".into(), + Type::U64 => "uint64_t".into(), + Type::S64 => "int64_t".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), + Type::String => match flavor { + Flavor::BorrowedArgument => { + self.gen.dependencies.needs_string_view = true; + "std::string_view".into() + } + Flavor::Argument(var) + if matches!(var, AbiVariant::GuestImport) || self.gen.opts.new_api => + { + self.gen.dependencies.needs_string_view = true; + "std::string_view".into() + } + Flavor::Argument(AbiVariant::GuestExport) => { + self.gen.dependencies.needs_wit = true; + "wit::string &&".into() + } + _ => { + self.gen.dependencies.needs_wit = true; + "wit::string".into() + } + }, + Type::Id(id) => match &self.resolve.types[*id].kind { + TypeDefKind::Record(_r) => { + self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + } + TypeDefKind::Resource => { + self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + } + TypeDefKind::Handle(Handle::Own(id)) => { + let mut typename = self.type_name(&Type::Id(*id), from_namespace, flavor); + match (false, flavor) { + (false, Flavor::Argument(AbiVariant::GuestImport)) + | (true, Flavor::Argument(AbiVariant::GuestExport)) => { + typename.push_str("&&") + } + (false, Flavor::Argument(AbiVariant::GuestExport)) + | (false, Flavor::Result(AbiVariant::GuestExport)) + | (true, Flavor::Argument(AbiVariant::GuestImport)) + | (true, Flavor::Result(AbiVariant::GuestImport)) => { + typename.push_str(&format!("::{OWNED_CLASS_NAME}")) + } + (false, Flavor::Result(AbiVariant::GuestImport)) + | (true, Flavor::Result(AbiVariant::GuestExport)) => (), + (_, Flavor::InStruct) => (), + (false, Flavor::BorrowedArgument) => (), + (_, _) => todo!(), + } + typename + } + TypeDefKind::Handle(Handle::Borrow(id)) => { + "std::reference_wrapper" + } + TypeDefKind::Flags(_f) => { + self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + } + TypeDefKind::Tuple(t) => { + let types = t.types.iter().fold(String::new(), |mut a, b| { + if !a.is_empty() { + a += ", "; + } + a + &self.type_name(b, from_namespace, flavor) + }); + self.gen.dependencies.needs_tuple = true; + String::from("std::tuple<") + &types + ">" + } + TypeDefKind::Variant(_v) => { + self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + } + TypeDefKind::Enum(_e) => { + self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + } + TypeDefKind::Option(o) => { + self.gen.dependencies.needs_optional = true; + "std::optional<".to_string() + &self.type_name(o, from_namespace, flavor) + ">" + } + TypeDefKind::Result(r) => { + self.gen.dependencies.needs_expected = true; + "std::expected<".to_string() + + &self.optional_type_name(r.ok.as_ref(), from_namespace, flavor) + + ", " + + &self.optional_type_name(r.err.as_ref(), from_namespace, flavor) + + ">" + } + TypeDefKind::List(ty) => { + let inner = self.type_name(ty, from_namespace, flavor); + match flavor { + Flavor::BorrowedArgument => { + self.gen.dependencies.needs_wit = true; + format!("wit::span<{inner} const>") + } + //self.gen.dependencies.needs_vector = true; + Flavor::Argument(var) + if matches!(var, AbiVariant::GuestImport) || self.gen.opts.new_api => + { + self.gen.dependencies.needs_wit = true; + format!("wit::span<{inner} const>") + } + Flavor::Argument(AbiVariant::GuestExport) => { + self.gen.dependencies.needs_wit = true; + format!("wit::vector<{inner}>&&") + } + _ => { + self.gen.dependencies.needs_wit = true; + format!("wit::vector<{inner}>") + } + } + } + TypeDefKind::Future(_) => todo!(), + TypeDefKind::Stream(_) => todo!(), + TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), + TypeDefKind::Unknown => todo!(), + }, + Type::ErrorContext => todo!(), + } + } + + fn declare_import2( + &self, + module_name: &str, + name: &str, + args: &str, + result: &str, + variant: AbiVariant, + ) -> (String, String) { + let extern_name = make_external_symbol(module_name, name, variant); + let import = format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n") + ; + (extern_name, import) + } + + fn declare_import( + &mut self, + module_name: &str, + name: &str, + params: &[WasmType], + results: &[WasmType], + ) -> String { + let mut args = String::default(); + for (n, param) in params.iter().enumerate() { + args.push_str(wit_bindgen_c::wasm_type(*param)); + if n + 1 != params.len() { + args.push_str(", "); + } + } + let result = if results.is_empty() { + "void" + } else { + wit_bindgen_c::wasm_type(results[0]) + }; + let variant = AbiVariant::GuestImport; + let (name, code) = self.declare_import2(module_name, name, &args, result, variant); + self.gen.extern_c_decls.push_str(&code); + name + } + + fn docs(src: &mut Source, docs: &Docs) { + if let Some(docs) = docs.contents.as_ref() { + for line in docs.trim().lines() { + src.push_str("/// "); + src.push_str(line); + src.push_str("\n"); + } + } + } +} + +impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> { + fn resolve(&self) -> &'a Resolve { + self.resolve + } + + fn type_record( + &mut self, + id: TypeId, + name: &str, + record: &wit_bindgen_core::wit_parser::Record, + docs: &wit_bindgen_core::wit_parser::Docs, + ) { + let ty = &self.resolve.types[id]; + let namespc = namespace( + self.resolve, + &ty.owner, + NOT_IN_EXPORTED_NAMESPACE, + &self.gen.opts, + ); + if self.gen.is_first_definition(&namespc, name) { + self.gen.h_src.change_namespace(&namespc); + Self::docs(&mut self.gen.h_src.src, docs); + let pascal = name.to_pascal_case(); + uwriteln!(self.gen.h_src.src, "struct {pascal} {{"); + for field in record.fields.iter() { + Self::docs(&mut self.gen.h_src.src, &field.docs); + let typename = self.type_name(&field.ty, &namespc, Flavor::InStruct); + let fname = field.name.to_snake_case(); + uwriteln!(self.gen.h_src.src, "{typename} {fname};"); + } + uwriteln!(self.gen.h_src.src, "}};"); + } + } + + fn type_resource( + &mut self, + id: TypeId, + name: &str, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + let type_ = &self.resolve.types[id]; + if let TypeOwner::Interface(intf) = type_.owner { + let guest_import = self.gen.imported_interfaces.contains(&intf); + let definition = !(guest_import); + let store = self.gen.start_new_file(Some(definition)); + let mut world_name = self.gen.world.to_snake_case(); + world_name.push_str("::"); + // let mut headerfile = SourceWithState::default(); + let namespc = namespace(self.resolve, &type_.owner, !guest_import, &self.gen.opts); + let pascal = name.to_upper_camel_case(); + let mut user_filename = namespc.clone(); + user_filename.push(pascal.clone()); + //namespc.join("-") + "-" + &pascal + ".h"; + if definition { + // includes should be outside of namespaces + //self.gen.h_src.change_namespace(&Vec::default()); + // temporarily redirect header file declarations to an user controlled include file + //std::mem::swap(&mut headerfile, &mut self.gen.h_src); + uwriteln!( + self.gen.h_src.src, + r#"/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into {pascal}.template. + */"# + ); + } + self.gen.h_src.change_namespace(&namespc); + + if !definition { + self.gen.dependencies.needs_imported_resources = true; + } else { + self.gen.dependencies.needs_exported_resources = true; + } + self.gen.dependencies.needs_wit = true; + // for unique_ptr + // self.gen.dependencies.needs_memory = true; + + let base_type = match (definition, false) { + (true, false) => format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>"), + (false, false) => { + String::from_str("wit::").unwrap() + RESOURCE_IMPORT_BASE_CLASS_NAME + } + (false, true) => { + String::from_str("wit::").unwrap() + RESOURCE_EXPORT_BASE_CLASS_NAME + } + (true, true) => format!("wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}<{pascal}>"), + }; + let derive = format!(" : public {base_type}"); + uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n"); + uwriteln!(self.gen.h_src.src, "public:\n"); + let variant = if guest_import { + AbiVariant::GuestImport + } else { + AbiVariant::GuestExport + }; + { + // destructor + let name = match variant { + AbiVariant::GuestImport => "[resource-drop]", + AbiVariant::GuestExport => "[dtor]", + AbiVariant::GuestImportAsync => todo!(), + AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), + } + // let name = match (variant, self.gen.opts.host_side()) { + // (AbiVariant::GuestImport, false) | (AbiVariant::GuestExport, true) => { + // "[resource-drop]" + // } + // (AbiVariant::GuestExport, false) | (AbiVariant::GuestImport, true) => "[dtor]", + // } + .to_string() + + &name; + let func = Function { + name: name, + kind: FunctionKind::Static(id), + params: vec![("self".into(), Type::Id(id))], + result: None, + docs: Docs::default(), + stability: Stability::Unknown, + }; + self.generate_function(&func, &TypeOwner::Interface(intf), variant); + } + // uwriteln!(self.gen.h_src.src, "struct Deleter {{ + // void operator()({pascal}* ptr) const {{ {pascal}::Dtor(ptr); }} + // }}; + // typedef std::unique_ptr<{pascal}, {pascal}::Deleter> Owned;"); + let funcs = self.resolve.interfaces[intf].functions.values(); + for func in funcs { + if match &func.kind { + FunctionKind::Freestanding => false, + FunctionKind::Method(mid) => *mid == id, + FunctionKind::Static(mid) => *mid == id, + FunctionKind::Constructor(mid) => *mid == id, + FunctionKind::AsyncFreestanding => todo!(), + FunctionKind::AsyncMethod(_id) => todo!(), + FunctionKind::AsyncStatic(_id) => todo!(), + } { + self.generate_function(func, &TypeOwner::Interface(intf), variant); + if matches!(func.kind, FunctionKind::Constructor(_)) + && matches!(variant, AbiVariant::GuestExport) != false + { + // functional safety requires the option to use a different allocator, so move new into the implementation + let func2 = Function { + name: "$alloc".to_string(), + kind: FunctionKind::Static(id), + // same params as constructor + params: func.params.clone(), + result: Some(Type::Id(id)), + docs: Docs::default(), + stability: Stability::Unknown, + }; + self.generate_function(&func2, &TypeOwner::Interface(intf), variant); + } + } + } + + if !definition { + // consuming constructor from handle (bindings) + uwriteln!(self.gen.h_src.src, "{pascal}({base_type} &&);",); + uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;"); + uwriteln!( + self.gen.h_src.src, + "{pascal}& operator=({pascal}&&) = default;" + ); + self.gen.c_src.qualify(&namespc); + uwriteln!( + self.gen.c_src.src, + "{pascal}::{pascal}({base_type}&&b) : {base_type}(std::move(b)) {{}}" + ); + } + if matches!(variant, AbiVariant::GuestExport) { + let id_type = Type::S32; + let func = Function { + name: "[resource-new]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("self".into(), Type::Id(id))], + result: Some(id_type), + docs: Docs::default(), + stability: Stability::Unknown, + }; + self.generate_function(&func, &TypeOwner::Interface(intf), variant); + + let func1 = Function { + name: "[resource-rep]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("id".into(), id_type)], + result: Some(Type::Id(id)), + docs: Docs::default(), + stability: Stability::Unknown, + }; + self.generate_function(&func1, &TypeOwner::Interface(intf), variant); + + let func2 = Function { + name: "[resource-drop]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("id".into(), id_type)], + result: None, + docs: Docs::default(), + stability: Stability::Unknown, + }; + self.generate_function(&func2, &TypeOwner::Interface(intf), variant); + } + uwriteln!(self.gen.h_src.src, "}};\n"); + self.gen.finish_file(&user_filename, store); + // if definition { + // // Finish the user controlled class template + // self.gen.h_src.change_namespace(&Vec::default()); + // std::mem::swap(&mut headerfile, &mut self.gen.h_src); + // uwriteln!(self.gen.h_src.src, "#include \"{user_filename}\""); + // if self.gen.opts.format { + // Cpp::clang_format(&mut headerfile.src); + // } + // self.gen + // .user_class_files + // .insert(user_filename, headerfile.src.to_string()); + // } + } + } + + fn type_flags( + &mut self, + id: TypeId, + name: &str, + flags: &wit_bindgen_core::wit_parser::Flags, + docs: &wit_bindgen_core::wit_parser::Docs, + ) { + let ty = &self.resolve.types[id]; + let namespc = namespace( + self.resolve, + &ty.owner, + NOT_IN_EXPORTED_NAMESPACE, + &self.gen.opts, + ); + if self.gen.is_first_definition(&namespc, name) { + self.gen.h_src.change_namespace(&namespc); + Self::docs(&mut self.gen.h_src.src, docs); + let pascal = name.to_pascal_case(); + let int_repr = wit_bindgen_c::int_repr(wit_bindgen_c::flags_repr(flags)); + uwriteln!(self.gen.h_src.src, "enum class {pascal} : {int_repr} {{"); + uwriteln!(self.gen.h_src.src, "k_None = 0,"); + for (n, field) in flags.flags.iter().enumerate() { + Self::docs(&mut self.gen.h_src.src, &field.docs); + let fname = field.name.to_pascal_case(); + uwriteln!(self.gen.h_src.src, "k{fname} = (1ULL<<{n}),"); + } + uwriteln!(self.gen.h_src.src, "}};"); + uwriteln!( + self.gen.h_src.src, + r#"static inline {pascal} operator|({pascal} a, {pascal} b) {{ return {pascal}({int_repr}(a)|{int_repr}(b)); }} + static inline {pascal} operator&({pascal} a, {pascal} b) {{ return {pascal}({int_repr}(a)&{int_repr}(b)); }}"# + ); + } + } + + fn type_tuple( + &mut self, + _id: TypeId, + _name: &str, + _flags: &wit_bindgen_core::wit_parser::Tuple, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + // I assume I don't need to do anything ... + } + + fn type_variant( + &mut self, + id: TypeId, + name: &str, + variant: &wit_bindgen_core::wit_parser::Variant, + docs: &wit_bindgen_core::wit_parser::Docs, + ) { + let ty = &self.resolve.types[id]; + let namespc = namespace( + self.resolve, + &ty.owner, + NOT_IN_EXPORTED_NAMESPACE, + &self.gen.opts, + ); + self.gen.h_src.change_namespace(&namespc); + Self::docs(&mut self.gen.h_src.src, docs); + let pascal = name.to_pascal_case(); + uwriteln!(self.gen.h_src.src, "struct {pascal} {{"); + let mut all_types = String::new(); + for case in variant.cases.iter() { + Self::docs(&mut self.gen.h_src.src, &case.docs); + let case_pascal = case.name.to_pascal_case(); + if !all_types.is_empty() { + all_types += ", "; + } + all_types += &case_pascal; + uwrite!(self.gen.h_src.src, "struct {case_pascal} {{"); + if let Some(ty) = case.ty.as_ref() { + let typestr = self.type_name(ty, &namespc, Flavor::InStruct); + uwrite!(self.gen.h_src.src, " {typestr} value; ") + } + uwriteln!(self.gen.h_src.src, "}};"); + } + uwriteln!(self.gen.h_src.src, " std::variant<{all_types}> variants;"); + uwriteln!(self.gen.h_src.src, "}};"); + self.gen.dependencies.needs_variant = true; + } + + fn type_option( + &mut self, + _id: TypeId, + _name: &str, + _payload: &wit_bindgen_core::wit_parser::Type, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + // I assume I don't need to do anything ... + } + + fn type_result( + &mut self, + _id: TypeId, + _name: &str, + _result: &wit_bindgen_core::wit_parser::Result_, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + // I assume I don't need to do anything ... + } + + fn type_enum( + &mut self, + id: TypeId, + name: &str, + enum_: &wit_bindgen_core::wit_parser::Enum, + docs: &wit_bindgen_core::wit_parser::Docs, + ) { + let ty = &self.resolve.types[id]; + let namespc = namespace( + self.resolve, + &ty.owner, + NOT_IN_EXPORTED_NAMESPACE, + &self.gen.opts, + ); + if self.gen.is_first_definition(&namespc, name) { + self.gen.h_src.change_namespace(&namespc); + let pascal = name.to_pascal_case(); + Self::docs(&mut self.gen.h_src.src, docs); + let int_t = wit_bindgen_c::int_repr(enum_.tag()); + uwriteln!(self.gen.h_src.src, "enum class {pascal} : {int_t} {{"); + for (i, case) in enum_.cases.iter().enumerate() { + Self::docs(&mut self.gen.h_src.src, &case.docs); + uwriteln!( + self.gen.h_src.src, + " k{} = {i},", + case.name.to_pascal_case(), + ); + } + uwriteln!(self.gen.h_src.src, "}};\n"); + } + } + + fn type_alias( + &mut self, + id: TypeId, + name: &str, + alias_type: &wit_bindgen_core::wit_parser::Type, + docs: &wit_bindgen_core::wit_parser::Docs, + ) { + let ty = &self.resolve.types[id]; + let namespc = namespace( + self.resolve, + &ty.owner, + NOT_IN_EXPORTED_NAMESPACE, + &self.gen.opts, + ); + self.gen.h_src.change_namespace(&namespc); + let pascal = name.to_pascal_case(); + Self::docs(&mut self.gen.h_src.src, docs); + let typename = self.type_name(alias_type, &namespc, Flavor::InStruct); + uwriteln!(self.gen.h_src.src, "using {pascal} = {typename};"); + } + + fn type_list( + &mut self, + _id: TypeId, + _name: &str, + _ty: &wit_bindgen_core::wit_parser::Type, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + // I assume I don't need to do anything ... we could create a typedef though + } + + fn type_builtin( + &mut self, + _id: TypeId, + _name: &str, + _ty: &wit_bindgen_core::wit_parser::Type, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + todo!() + } + + fn type_future(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { + todo!() + } + + fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { + todo!() + } +} + +struct CabiPostInformation { + module: String, + name: String, + ret_type: String, +} + +struct FunctionBindgen<'a, 'b> { + gen: &'b mut CppInterfaceGenerator<'a>, + params: Vec, + tmp: usize, + // import_return_pointer_area_size: usize, + // import_return_pointer_area_align: usize, + namespace: Vec, + src: Source, + block_storage: Vec, + /// intermediate calculations for contained objects + blocks: Vec<(String, Vec)>, + payloads: Vec, + // caching for wasm + variant: AbiVariant, + cabi_post: Option, + needs_dealloc: bool, + leak_on_insertion: Option, +} + +impl<'a, 'b> FunctionBindgen<'a, 'b> { + fn new(gen: &'b mut CppInterfaceGenerator<'a>, params: Vec) -> Self { + Self { + gen, + params, + tmp: 0, + // import_return_pointer_area_size: 0, + // import_return_pointer_area_align: 0, + namespace: Default::default(), + src: Default::default(), + block_storage: Default::default(), + blocks: Default::default(), + payloads: Default::default(), + variant: AbiVariant::GuestImport, + cabi_post: None, + needs_dealloc: false, + leak_on_insertion: None, + } + } + + fn tmp(&mut self) -> usize { + let ret = self.tmp; + self.tmp += 1; + ret + } + + fn tempname(&self, base: &str, idx: usize) -> String { + format!("{base}{idx}") + } + + fn push_str(&mut self, s: &str) { + self.src.push_str(s); + } + + fn typename_lift(&self, id: TypeId) -> String { + self.gen.type_path(id, true) + } + + fn let_results(&mut self, amt: usize, results: &mut Vec) { + if amt > 0 { + let tmp = self.tmp(); + let res = format!("result{}", tmp); + self.push_str("auto "); + self.push_str(&res); + self.push_str(" = "); + if amt == 1 { + results.push(res); + } else { + for i in 0..amt { + results.push(format!("std::get<{i}>({res})")); + } + } + } + } + + fn load( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { + results.push(format!( + "*(({}*) ({} + {}))", + ty, + operands[0], + offset.format(POINTER_SIZE_EXPRESSION) + )); + } + + fn load_ext( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { + self.load(ty, offset, operands, results); + let result = results.pop().unwrap(); + results.push(format!("(int32_t) ({})", result)); + } + + fn store(&mut self, ty: &str, offset: ArchitectureSize, operands: &[String]) { + uwriteln!( + self.src, + "*(({}*)({} + {})) = {};", + ty, + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] + ); + } + + fn has_resources2(&self, ty: &Type) -> bool { + match ty { + Type::Bool + | Type::U8 + | Type::U16 + | Type::U32 + | Type::U64 + | Type::S8 + | Type::S16 + | Type::S32 + | Type::S64 + | Type::F32 + | Type::F64 + | Type::Char => false, + Type::String => false, + Type::Id(id) => self.has_resources(id), + Type::ErrorContext => todo!(), + } + } + fn has_resources(&self, id: &TypeId) -> bool { + match &self.gen.resolve.types[*id].kind { + TypeDefKind::Record(_) => todo!(), + TypeDefKind::Resource => true, + TypeDefKind::Handle(_) => true, + TypeDefKind::Flags(_) => false, + TypeDefKind::Tuple(t) => t.types.iter().any(|ty| self.has_resources2(ty)), + TypeDefKind::Variant(_) => todo!(), + TypeDefKind::Enum(_) => false, + TypeDefKind::Option(_) => todo!(), + TypeDefKind::Result(_) => todo!(), + TypeDefKind::List(_) => todo!(), + TypeDefKind::Future(_) => todo!(), + TypeDefKind::Stream(_) => todo!(), + TypeDefKind::Type(ty) => match ty { + Type::Id(id) => self.has_resources(id), + _ => false, + }, + TypeDefKind::Unknown => todo!(), + } + } +} + +impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { + type Operand = String; + + fn emit( + &mut self, + _resolve: &Resolve, + inst: &wit_bindgen_core::abi::Instruction<'_>, + operands: &mut Vec, + results: &mut Vec, + ) { + let mut top_as = |cvt: &str| { + results.push(format!("({cvt}({}))", operands.pop().unwrap())); + }; + + match inst { + abi::Instruction::GetArg { nth } => { + if *nth == 0 && self.params[0].as_str() == "self" { + if self.gen.in_guest_import { + results.push("(*this)".to_string()); + } else { + results.push("(*lookup_resource(self))".to_string()); + } + } else { + results.push(self.params[*nth].clone()); + } + } + abi::Instruction::I32Const { val } => results.push(format!("(int32_t({}))", val)), + abi::Instruction::Bitcasts { casts } => { + for (cast, op) in casts.iter().zip(operands) { + // let op = op; + results.push(self.gen.gen.perform_cast(op, cast)); + } + } + abi::Instruction::ConstZero { tys } => { + for ty in tys.iter() { + match ty { + WasmType::I32 => results.push("int32_t(0)".to_string()), + WasmType::I64 => results.push("int64_t(0)".to_string()), + WasmType::F32 => results.push("0.0f".to_string()), + WasmType::F64 => results.push("0.0".to_string()), + WasmType::Length => results.push("size_t(0)".to_string()), + WasmType::Pointer => results.push("nullptr".to_string()), + WasmType::PointerOrI64 => results.push("int64_t(0)".to_string()), + } + } + } + abi::Instruction::I32Load { offset } => { + let tmp = self.tmp(); + uwriteln!( + self.src, + "int32_t l{tmp} = *((int32_t const*)({} + {offset}));", + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) + ); + results.push(format!("l{tmp}")); + } + abi::Instruction::I32Load8U { offset } => { + self.load_ext("uint8_t", *offset, operands, results) + } + abi::Instruction::I32Load8S { offset } => { + self.load_ext("int8_t", *offset, operands, results) + } + abi::Instruction::I32Load16U { offset } => { + self.load_ext("uint16_t", *offset, operands, results) + } + abi::Instruction::I32Load16S { offset } => { + self.load_ext("int16_t", *offset, operands, results) + } + abi::Instruction::I64Load { offset } => { + self.load("int64_t", *offset, operands, results) + } + abi::Instruction::F32Load { offset } => self.load("float", *offset, operands, results), + abi::Instruction::F64Load { offset } => self.load("double", *offset, operands, results), + abi::Instruction::I32Store { offset } => self.store("int32_t", *offset, operands), + abi::Instruction::I32Store8 { offset } => self.store("int8_t", *offset, operands), + abi::Instruction::I32Store16 { offset } => self.store("int16_t", *offset, operands), + abi::Instruction::I64Store { offset } => self.store("int64_t", *offset, operands), + abi::Instruction::F32Store { offset } => self.store("float", *offset, operands), + abi::Instruction::F64Store { offset } => self.store("double", *offset, operands), + abi::Instruction::I32FromChar + | abi::Instruction::I32FromBool + | abi::Instruction::I32FromU8 + | abi::Instruction::I32FromS8 + | abi::Instruction::I32FromU16 + | abi::Instruction::I32FromS16 + | abi::Instruction::I32FromU32 + | abi::Instruction::I32FromS32 => top_as("int32_t"), + abi::Instruction::I64FromU64 | abi::Instruction::I64FromS64 => top_as("int64_t"), + abi::Instruction::F32FromCoreF32 => top_as("float"), + abi::Instruction::F64FromCoreF64 => top_as("double"), + abi::Instruction::S8FromI32 => top_as("int8_t"), + abi::Instruction::U8FromI32 => top_as("uint8_t"), + abi::Instruction::S16FromI32 => top_as("int16_t"), + abi::Instruction::U16FromI32 => top_as("uint16_t"), + abi::Instruction::S32FromI32 => top_as("int32_t"), + abi::Instruction::U32FromI32 => top_as("uint32_t"), + abi::Instruction::S64FromI64 => top_as("int64_t"), + abi::Instruction::U64FromI64 => top_as("uint64_t"), + abi::Instruction::CharFromI32 => top_as("uint32_t"), + abi::Instruction::CoreF32FromF32 => top_as("float"), + abi::Instruction::CoreF64FromF64 => top_as("double"), + abi::Instruction::BoolFromI32 => top_as("bool"), + abi::Instruction::ListCanonLower { realloc, .. } => { + let tmp = self.tmp(); + let val = format!("vec{}", tmp); + let ptr = format!("ptr{}", tmp); + let len = format!("len{}", tmp); + // let result = format!("result{}", tmp); + self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!( + "auto {} = ({})({}.data());\n", + ptr, + self.gen.gen.opts.ptr_type(), + val + )); + self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); + if realloc.is_none() { + results.push(ptr); + } else { + if matches!(self.variant, AbiVariant::GuestImport) { + uwriteln!(self.src, "{}.leak();\n", operands[0]); + } + results.push(ptr); + } + results.push(len); + } + abi::Instruction::StringLower { realloc } => { + let tmp = self.tmp(); + let val = format!("vec{}", tmp); + let ptr = format!("ptr{}", tmp); + let len = format!("len{}", tmp); + // let result = format!("result{}", tmp); + self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!( + "auto {} = ({})({}.data());\n", + ptr, + self.gen.gen.opts.ptr_type(), + val + )); + self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); + if realloc.is_none() { + results.push(ptr); + } else { + if matches!(self.variant, AbiVariant::GuestImport) { + uwriteln!(self.src, "{}.leak();\n", operands[0]); + } + results.push(ptr); + } + results.push(len); + } + abi::Instruction::ListLower { + element: _, + realloc, + } => { + let tmp = self.tmp(); + let val = format!("vec{}", tmp); + let ptr = format!("ptr{}", tmp); + let len = format!("len{}", tmp); + self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!( + "auto {} = ({})({}.data());\n", + ptr, + self.gen.gen.opts.ptr_type(), + val + )); + self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); + if realloc.is_none() { + results.push(ptr); + } else { + if matches!(self.variant, AbiVariant::GuestImport) { + uwriteln!(self.src, "{}.leak();\n", operands[0]); + } + results.push(ptr); + } + results.push(len); + } + abi::Instruction::ListCanonLift { element, .. } => { + let tmp = self.tmp(); + let len = format!("len{}", tmp); + let inner = self + .gen + .type_name(element, &self.namespace, Flavor::InStruct); + self.push_str(&format!("auto {} = {};\n", len, operands[1])); + let result = if self.gen.gen.opts.new_api + && matches!(self.variant, AbiVariant::GuestExport) + { + format!( + "wit::vector<{inner} const>(({inner}*)({}), {len}).get_view()", + operands[0] + ) + } else { + format!("wit::vector<{inner}>(({inner}*)({}), {len})", operands[0]) + }; + results.push(result); + } + abi::Instruction::StringLift => { + let tmp = self.tmp(); + let len = format!("len{}", tmp); + uwriteln!(self.src, "auto {} = {};\n", len, operands[1]); + let result = format!("wit::string((char const*)({}), {len})", operands[0]); + results.push(result); + } + abi::Instruction::ListLift { element, .. } => { + let body = self.blocks.pop().unwrap(); + let tmp = self.tmp(); + let size = self.gen.sizes.size(element); + let _align = self.gen.sizes.align(element); + let flavor = if self.gen.gen.opts.new_api + && matches!(self.variant, AbiVariant::GuestExport) + { + Flavor::BorrowedArgument + } else { + Flavor::InStruct + }; + let vtype = self.gen.type_name(element, &self.namespace, flavor); + let len = format!("len{tmp}"); + let base = format!("base{tmp}"); + let result = format!("result{tmp}"); + self.push_str(&format!( + "auto {base} = {operand0};\n", + operand0 = operands[0] + )); + self.push_str(&format!( + "auto {len} = {operand1};\n", + operand1 = operands[1] + )); + self.push_str(&format!( + r#"auto {result} = wit::vector<{vtype}>::allocate({len}); + "#, + )); + if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) { + assert!(self.needs_dealloc); + self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n")); + } + + uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{"); + uwriteln!( + self.src, + "auto base = {base} + i * {size};", + size = size.format(POINTER_SIZE_EXPRESSION) + ); + uwrite!(self.src, "{}", body.0); + uwriteln!(self.src, "auto e{tmp} = {};", body.1[0]); + if let Some(code) = self.leak_on_insertion.take() { + assert!(self.needs_dealloc); + uwriteln!(self.src, "{code}"); + } + // inplace construct + uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); + uwriteln!(self.src, "}}"); + if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) { + results.push(format!("{result}.get_const_view()")); + if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) + { + self.leak_on_insertion.replace(format!( + "if ({len}>0) _deallocate.push_back((void*){result}.leak());\n" + )); + } + } else { + results.push(format!("std::move({result})")); + } + } + abi::Instruction::IterElem { .. } => results.push("IterElem".to_string()), + abi::Instruction::IterBasePointer => results.push("base".to_string()), + abi::Instruction::RecordLower { record, .. } => { + let op = &operands[0]; + for f in record.fields.iter() { + results.push(format!("({}).{}", op, to_c_ident(&f.name))); + } + } + abi::Instruction::RecordLift { record, ty, .. } => { + // let t = self.gen.resolve().types[*ty]; + let mut result = + self.gen + .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); + // self.typename_lift(*ty); + result.push_str("{"); + for (_field, val) in record.fields.iter().zip(operands) { + result.push_str("std::move("); + result.push_str(&val); + result.push_str("), "); + } + result.push_str("}"); + results.push(result); + } + abi::Instruction::HandleLower { + handle: Handle::Own(_ty), + .. + } => { + let op = &operands[0]; + if matches!(self.variant, AbiVariant::GuestImport) { + results.push(format!("{op}.into_handle()")); + } else { + results.push(format!("{op}.release()->handle")); + } + } + abi::Instruction::HandleLower { + handle: Handle::Borrow(_), + .. + } => { + let op = &operands[0]; + if op == "(*this)" { + // TODO is there a better way to decide? + results.push(format!("{op}.get_handle()")); + } else { + results.push(format!("{op}.get().get_handle()")); + } + } + abi::Instruction::HandleLift { handle, .. } => { + let op = &operands[0]; + match (handle, false) { + (Handle::Own(ty), true) => match self.variant { + AbiVariant::GuestExport => { + results.push(format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}{{{op}}}")) + } + AbiVariant::GuestImport => { + let tmp = self.tmp(); + let var = self.tempname("obj", tmp); + let tname = self.gen.type_name( + &Type::Id(*ty), + &self.namespace, + Flavor::Argument(self.variant), + ); + uwriteln!( + self.src, + "auto {var} = {tname}::remove_resource({op}); + assert({var}.has_value());" + ); + results.push(format!("{tname}::Owned(*{var})")); + } + AbiVariant::GuestImportAsync => todo!(), + AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), + }, + (Handle::Own(ty), false) => match self.variant { + AbiVariant::GuestImport => { + results.push(format!("wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}{{{op}}}")) + } + AbiVariant::GuestExport => { + let tmp = self.tmp(); + let var = self.tempname("obj", tmp); + let tname = self.gen.type_name( + &Type::Id(*ty), + &self.namespace, + Flavor::Argument(self.variant), + ); + uwriteln!( + self.src, + "auto {var} = {tname}::Owned({tname}::ResourceRep({op}));" + ); + + uwriteln!(self.src, "{var}->into_handle();"); + + results.push(format!("std::move({var})")) + } + AbiVariant::GuestImportAsync => todo!(), + AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), + }, + (Handle::Borrow(ty), true) => { + let tname = self.gen.type_name( + &Type::Id(*ty), + &self.namespace, + Flavor::Argument(self.variant), + ); + results.push(format!("**{tname}::lookup_resource({op})")); + } + (Handle::Borrow(ty), false) => match self.variant { + AbiVariant::GuestImport => results.push(op.clone()), + AbiVariant::GuestExport => { + let tname = self.gen.type_name( + &Type::Id(*ty), + &self.namespace, + Flavor::Argument(self.variant), + ); + results.push(format!("std::ref(*({tname} *){op})")); + } + AbiVariant::GuestImportAsync => todo!(), + AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), + }, + } + } + abi::Instruction::TupleLower { tuple, .. } => { + let op = &operands[0]; + for n in 0..tuple.types.len() { + results.push(format!("std::get<{n}>({op})")); + } + } + abi::Instruction::TupleLift { tuple, .. } => { + let name = format!("tuple{}", self.tmp()); + uwrite!(self.src, "auto {name} = std::tuple<"); + self.src.push_str( + &(tuple + .types + .iter() + .map(|t| self.gen.type_name(t, &self.namespace, Flavor::InStruct))) + .collect::>() + .join(", "), + ); + self.src.push_str(">("); + self.src.push_str(&operands.join(", ")); + self.src.push_str(");\n"); + results.push(format!("std::move({name})")); + } + abi::Instruction::FlagsLower { flags, ty, .. } => { + match wit_bindgen_c::flags_repr(flags) { + Int::U8 | Int::U16 | Int::U32 => { + results.push(format!("((int32_t){})", operands.pop().unwrap())); + } + Int::U64 => { + let name = + self.gen + .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); + let tmp = self.tmp(); + let tempname = self.tempname("flags", tmp); + uwriteln!(self.src, "{name} {tempname} = {};", operands[0]); + results.push(format!("(int32_t)(((uint64_t){tempname}) & 0xffffffff)")); + results.push(format!( + "(int32_t)((((uint64_t){tempname}) >> 32) & 0xffffffff)" + )); + } + } + } + abi::Instruction::FlagsLift { flags, ty, .. } => { + let typename = + self.gen + .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); + match wit_bindgen_c::flags_repr(flags) { + Int::U8 | Int::U16 | Int::U32 => { + results.push(format!("(({typename}){})", operands.pop().unwrap())); + } + Int::U64 => { + let op0 = &operands[0]; + let op1 = &operands[1]; + results.push(format!( + "(({typename})(({op0}) | (((uint64_t)({op1})) << 32)))" + )); + } + } + } + abi::Instruction::VariantPayloadName => { + let name = format!("payload{}", self.tmp()); + results.push(name.clone()); + self.payloads.push(name); + } + abi::Instruction::VariantLower { + variant, + results: result_types, + .. + } => { + //let name = self.gen.type_name(*ty); + // let op0 = &operands[0]; + // self.push_str(&format!("({name}){op0}")); + let blocks = self + .blocks + .drain(self.blocks.len() - variant.cases.len()..) + .collect::>(); + let payloads = self + .payloads + .drain(self.payloads.len() - variant.cases.len()..) + .collect::>(); + + let mut variant_results = Vec::with_capacity(result_types.len()); + for ty in result_types.iter() { + let name = format!("variant{}", self.tmp()); + results.push(name.clone()); + self.src.push_str(wit_bindgen_c::wasm_type(*ty)); + self.src.push_str(" "); + self.src.push_str(&name); + self.src.push_str(";\n"); + variant_results.push(name); + } + + let expr_to_match = format!("({}).tag", operands[0]); + + uwriteln!(self.src, "switch ((int32_t) {}) {{", expr_to_match); + for (i, ((case, (block, block_results)), payload)) in + variant.cases.iter().zip(blocks).zip(payloads).enumerate() + { + uwriteln!(self.src, "case {}: {{", i); + if let Some(ty) = case.ty.as_ref() { + let ty = self.gen.type_name(ty, &self.namespace, Flavor::InStruct); + uwrite!( + self.src, + "const {} *{} = &({}).val", + ty, + payload, + operands[0], + ); + self.src.push_str("."); + self.src.push_str(&to_c_ident(&case.name)); + self.src.push_str(";\n"); + } + self.src.push_str(&block); + + for (name, result) in variant_results.iter().zip(&block_results) { + uwriteln!(self.src, "{} = {};", name, result); + } + self.src.push_str("break;\n}\n"); + } + self.src.push_str("}\n"); + } + abi::Instruction::VariantLift { variant, ty, .. } => { + let mut result = String::new(); + result.push_str("{"); + + let named_enum = variant.cases.iter().all(|c| c.ty.is_none()); + // let blocks = self + // .blocks + // .drain(self.blocks.len() - variant.cases.len()..) + // .collect::>(); + let op0 = &operands[0]; + + if named_enum { + // In unchecked mode when this type is a named enum then we know we + // defined the type so we can transmute directly into it. + // result.push_str("#[cfg(not(debug_assertions))]"); + // result.push_str("{"); + // result.push_str("::core::mem::transmute::<_, "); + // result.push_str(&name.to_upper_camel_case()); + // result.push_str(">("); + // result.push_str(op0); + // result.push_str(" as "); + // result.push_str(int_repr(variant.tag())); + // result.push_str(")"); + // result.push_str("}"); + } + + // if named_enum { + // result.push_str("#[cfg(debug_assertions)]"); + // } + let blocks: Vec = Vec::new(); + result.push_str("{"); + result.push_str(&format!("match {op0} {{\n")); + let name = self.typename_lift(*ty); + for (i, (case, block)) in variant.cases.iter().zip(blocks).enumerate() { + let pat = i.to_string(); + let block = if case.ty.is_some() { + format!("({block})") + } else { + String::new() + }; + let case = case.name.to_upper_camel_case(); + // if i == variant.cases.len() - 1 { + // result.push_str("#[cfg(debug_assertions)]"); + // result.push_str(&format!("{pat} => {name}::{case}{block},\n")); + // result.push_str("#[cfg(not(debug_assertions))]"); + // result.push_str(&format!("_ => {name}::{case}{block},\n")); + // } else { + result.push_str(&format!("{pat} => {name}::{case}{block},\n")); + // } + } + // result.push_str("#[cfg(debug_assertions)]"); + // result.push_str("_ => panic!(\"invalid enum discriminant\"),\n"); + result.push_str("}"); + result.push_str("}"); + + result.push_str("}"); + results.push(result); + } + abi::Instruction::EnumLower { .. } => results.push(format!("int32_t({})", operands[0])), + abi::Instruction::EnumLift { ty, .. } => { + let typename = + self.gen + .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); + results.push(format!("({typename}){}", &operands[0])); + } + abi::Instruction::OptionLower { + payload, + results: result_types, + .. + } => { + let (mut some, some_results) = self.blocks.pop().unwrap(); + let (mut none, none_results) = self.blocks.pop().unwrap(); + let some_payload = self.payloads.pop().unwrap(); + let _none_payload = self.payloads.pop().unwrap(); + + for (i, ty) in result_types.iter().enumerate() { + let tmp = self.tmp(); + let name = self.tempname("option", tmp); + results.push(name.clone()); + self.src.push_str(wit_bindgen_c::wasm_type(*ty)); + self.src.push_str(" "); + self.src.push_str(&name); + self.src.push_str(";\n"); + let some_result = &some_results[i]; + uwriteln!(some, "{name} = {some_result};"); + let none_result = &none_results[i]; + uwriteln!(none, "{name} = {none_result};"); + } + + let op0 = &operands[0]; + let flavor = if self.gen.gen.opts.new_api + && matches!(self.variant, AbiVariant::GuestImport) + { + Flavor::BorrowedArgument + } else { + Flavor::InStruct + }; + let ty = self.gen.type_name(payload, &self.namespace, flavor); + let bind_some = format!("{ty} {some_payload} = (std::move({op0})).value();"); + + uwrite!( + self.src, + "\ + if (({op0}).has_value()) {{ + {bind_some} + {some}}} else {{ + {none}}} + " + ); + } + abi::Instruction::OptionLift { payload, .. } => { + let (some, some_results) = self.blocks.pop().unwrap(); + let (_none, none_results) = self.blocks.pop().unwrap(); + assert!(none_results.len() == 0); + assert!(some_results.len() == 1); + // let some_result = &some_results[0]; + let flavor = if self.gen.gen.opts.new_api + && matches!(self.variant, AbiVariant::GuestExport) + { + Flavor::BorrowedArgument + } else { + Flavor::InStruct + }; + let type_name = self.gen.type_name(*payload, &self.namespace, flavor); + let full_type = format!("std::optional<{type_name}>"); + let op0 = &operands[0]; + + let tmp = self.tmp(); + let resultname = self.tempname("option", tmp); + uwriteln!( + self.src, + "{full_type} {resultname}; + if ({op0}) {{ + {some} + {resultname}.emplace({}); + }}", + some_results[0] + ); + results.push(format!("std::move({resultname})")); + } + abi::Instruction::ResultLower { + results: result_types, + result, + .. + } => { + let (mut err, err_results) = self.blocks.pop().unwrap(); + let (mut ok, ok_results) = self.blocks.pop().unwrap(); + let err_payload = self.payloads.pop().unwrap(); + let ok_payload = self.payloads.pop().unwrap(); + + for (i, ty) in result_types.iter().enumerate() { + let tmp = self.tmp(); + let name = self.tempname("result", tmp); + results.push(name.clone()); + self.src.push_str(wit_bindgen_c::wasm_type(*ty)); + self.src.push_str(" "); + self.src.push_str(&name); + self.src.push_str(";\n"); + let ok_result = &ok_results[i]; + uwriteln!(ok, "{name} = {ok_result};"); + let err_result = &err_results[i]; + uwriteln!(err, "{name} = {err_result};"); + } + + let op0 = &operands[0]; + let ok_ty = self.gen.optional_type_name( + result.ok.as_ref(), + &self.namespace, + Flavor::InStruct, + ); + let err_ty = self.gen.optional_type_name( + result.err.as_ref(), + &self.namespace, + Flavor::InStruct, + ); + let bind_ok = if let Some(_ok) = result.ok.as_ref() { + format!("{ok_ty} {ok_payload} = std::move({op0}).value();") + } else { + String::new() + }; + let bind_err = if let Some(_err) = result.err.as_ref() { + format!("{err_ty} {err_payload} = std::move({op0}).error();") + } else { + String::new() + }; + + uwrite!( + self.src, + "\ + if (({op0}).has_value()) {{ + {bind_ok} + {ok}}} else {{ + {bind_err} + {err}}} + " + ); + } + abi::Instruction::ResultLift { result, .. } => { + let (mut err, err_results) = self.blocks.pop().unwrap(); + let (mut ok, ok_results) = self.blocks.pop().unwrap(); + let mut ok_result = String::new(); + let mut err_result = String::new(); + if result.ok.is_none() { + ok.clear(); + } else { + ok_result = format!("std::move({})", ok_results[0]); + } + if result.err.is_none() { + err.clear(); + } else { + err_result = format!("std::move({})", err_results[0]); + } + let ok_type = self.gen.optional_type_name( + result.ok.as_ref(), + &self.namespace, + Flavor::InStruct, + ); + let err_type = self.gen.optional_type_name( + result.err.as_ref(), + &self.namespace, + Flavor::InStruct, + ); + let full_type = format!("std::expected<{ok_type}, {err_type}>",); + let err_type = "std::unexpected"; + let operand = &operands[0]; + + let tmp = self.tmp(); + let resultname = self.tempname("result", tmp); + uwriteln!( + self.src, + "{full_type} {resultname}; + if ({operand}==0) {{ + {ok} + {resultname}.emplace({ok_result}); + }} else {{ + {err} + {resultname}={err_type}{{{err_result}}}; + }}" + ); + results.push(resultname); + } + abi::Instruction::CallWasm { name, sig } => { + let module_name = self + .gen + .wasm_import_module + .as_ref() + .map(|e| { + self.gen + .gen + .import_prefix + .as_ref() + .cloned() + .unwrap_or_default() + + e + }) + .unwrap(); + + let func = self + .gen + .declare_import(&module_name, name, &sig.params, &sig.results); + + // ... then call the function with all our operands + if sig.results.len() > 0 { + self.src.push_str("auto ret = "); + results.push("ret".to_string()); + } + self.src.push_str(&func); + self.src.push_str("("); + self.src.push_str(&operands.join(", ")); + self.src.push_str(");\n"); + } + abi::Instruction::CallInterface { func, .. } => { + // dbg!(func); + self.let_results(if func.result.is_some() { 1 } else { 0 }, results); + let (namespace, func_name_h) = self.gen.func_namespace_name(func, true, true); + if matches!(func.kind, FunctionKind::Method(_)) { + let this = operands.remove(0); + //let objtype = namespace.join("::"); + uwrite!(self.src, "({this}).get()."); + // uwrite!(self.src, "(({objtype}*){this})->",); + } else { + let mut relative = SourceWithState::default(); + // relative.namespace = self.namespace.clone(); + relative.qualify(&namespace); + self.push_str(&relative.src); + // self.gen.gen.c_src.qualify(&namespace); + } + self.src.push_str(&func_name_h); + self.push_str("("); + self.push_str(&operands.join(", ")); + if false + && matches!(func.kind, FunctionKind::Constructor(_)) + && !self.gen.gen.opts.is_only_handle(self.variant) + { + // acquire object from unique_ptr + self.push_str(").release();"); + results[0] = format!("(*({}))", results[0]); + } else { + self.push_str(");\n"); + } + if self.needs_dealloc { + uwriteln!( + self.src, + "for (auto i: _deallocate) {{ free(i); }}\n + _deallocate.clear();" + ); + } + } + abi::Instruction::Return { amt, func } => { + // let guest_import = matches!(self.variant, AbiVariant::GuestImport); + match amt { + 0 => {} + _ => { + assert!(*amt == operands.len()); + match &func.kind { + FunctionKind::Constructor(_) + if self.gen.gen.opts.is_only_handle(self.variant) => + { + // strange but works + if matches!(self.variant, AbiVariant::GuestExport) { + self.src.push_str("this->index = "); + } else { + self.src.push_str("this->handle = "); + } + } + _ => self.src.push_str("return "), + } + if let Some(CabiPostInformation { + module: _, + name: _cabi_post_name, + ret_type: cabi_post_type, + }) = self.cabi_post.as_ref() + { + self.src.push_str("wit::guest_owned<"); + self.src.push_str(&cabi_post_type); + self.src.push_str(">("); + } + if *amt == 1 { + if operands[0].starts_with("std::move(") { + // remove the std::move due to return value optimization (and complex rules about when std::move harms) + self.src.push_str(&operands[0][9..]); + } else { + self.src.push_str(&operands[0]); + } + } else { + todo!(); + } + if let Some(CabiPostInformation { + module: func_module, + name: func_name, + ret_type: _cabi_post_type, + }) = self.cabi_post.as_ref() + { + let cabi_post_name = self.gen.declare_import( + &format!("cabi_post_{func_module}"), + func_name, + &[WasmType::Pointer], + &[], + ); + self.src.push_str(&format!(", ret, {})", cabi_post_name)); + } + if matches!(func.kind, FunctionKind::Constructor(_)) + && self.gen.gen.opts.is_only_handle(self.variant) + { + // we wrapped the handle in an object, so unpack it + + self.src.push_str(".into_handle()"); + } + self.src.push_str(";\n"); + } + } + } + abi::Instruction::Malloc { .. } => todo!(), + abi::Instruction::GuestDeallocate { .. } => { + uwriteln!(self.src, "free((void*) ({}));", operands[0]); + } + abi::Instruction::GuestDeallocateString => { + uwriteln!(self.src, "if (({}) > 0) {{", operands[1]); + uwriteln!( + self.src, + "wit::string::drop_raw((void*) ({}));", + operands[0] + ); + uwriteln!(self.src, "}}"); + } + abi::Instruction::GuestDeallocateList { element } => { + let (body, results) = self.blocks.pop().unwrap(); + assert!(results.is_empty()); + let tmp = self.tmp(); + let ptr = self.tempname("ptr", tmp); + let len = self.tempname("len", tmp); + uwriteln!(self.src, "uint8_t* {ptr} = {};", operands[0]); + uwriteln!(self.src, "size_t {len} = {};", operands[1]); + let i = self.tempname("i", tmp); + uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); + let size = self.gen.sizes.size(element); + uwriteln!( + self.src, + "uint8_t* base = {ptr} + {i} * {size};", + size = size.format(POINTER_SIZE_EXPRESSION) + ); + uwriteln!(self.src, "(void) base;"); + uwrite!(self.src, "{body}"); + uwriteln!(self.src, "}}"); + uwriteln!(self.src, "if ({len} > 0) {{"); + uwriteln!(self.src, "free((void*) ({ptr}));"); + uwriteln!(self.src, "}}"); + } + abi::Instruction::GuestDeallocateVariant { blocks } => { + let blocks = self + .blocks + .drain(self.blocks.len() - blocks..) + .collect::>(); + + uwriteln!(self.src, "switch ((int32_t) {}) {{", operands[0]); + for (i, (block, results)) in blocks.into_iter().enumerate() { + assert!(results.is_empty()); + uwriteln!(self.src, "case {}: {{", i); + self.src.push_str(&block); + self.src.push_str("break;\n}\n"); + } + self.src.push_str("}\n"); + } + abi::Instruction::PointerLoad { offset } => { + let ptr_type = self.gen.gen.opts.ptr_type(); + self.load(ptr_type, *offset, operands, results) + } + abi::Instruction::LengthLoad { offset } => { + self.load("size_t", *offset, operands, results) + } + abi::Instruction::PointerStore { offset } => { + let ptr_type = self.gen.gen.opts.ptr_type(); + self.store(ptr_type, *offset, operands) + } + abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), + abi::Instruction::FutureLower { .. } => todo!(), + abi::Instruction::FutureLift { .. } => todo!(), + abi::Instruction::StreamLower { .. } => todo!(), + abi::Instruction::StreamLift { .. } => todo!(), + abi::Instruction::ErrorContextLower { .. } => todo!(), + abi::Instruction::ErrorContextLift { .. } => todo!(), + abi::Instruction::Flush { amt } => { + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "auto {result} = {};", operands[i]); + results.push(result); + } + } + abi::Instruction::AsyncTaskReturn { .. } => todo!(), + } + } + + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand { + let tmp = self.tmp(); + let size_string = size.format(POINTER_SIZE_EXPRESSION); + //let elems = (size + (align - 1)) / align; + let tp = match align { + Alignment::Bytes(bytes) => match bytes.get() { + 1 => "uint8_t", + 2 => "uint16_t", + 4 => "uint32_t", + 8 => "uint64_t", + _ => todo!(), + }, + Alignment::Pointer => "uintptr_t", + }; + let static_var = if self.gen.in_guest_import { + "" + } else { + "static " + }; + uwriteln!( + self.src, + "{static_var}{tp} ret_area[({size_string}+sizeof({tp})-1)/sizeof({tp})];" + ); + uwriteln!( + self.src, + "{} ptr{tmp} = ({0})(&ret_area);", + self.gen.gen.opts.ptr_type(), + ); + + format!("ptr{}", tmp) + } + + fn push_block(&mut self) { + let prev = core::mem::take(&mut self.src); + self.block_storage.push(prev); + // uwriteln!(self.src, "// push_block()"); + } + + fn finish_block(&mut self, operands: &mut Vec) { + let to_restore = self.block_storage.pop().unwrap(); + let src = core::mem::replace(&mut self.src, to_restore); + self.blocks.push((src.into(), core::mem::take(operands))); + // uwriteln!(self.src, "// finish_block()"); + } + + fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign { + &self.gen.sizes + } + + fn is_list_canonical( + &self, + resolve: &Resolve, + ty: &wit_bindgen_core::wit_parser::Type, + ) -> bool { + if !resolve.all_bits_valid(ty) { + return false; + } + match ty { + Type::Id(id) => !self.has_resources(id), + _ => true, + } + } +} + +/// This describes the common ABI function referenced or implemented, the C++ side might correspond to a different type +enum SpecialMethod { + None, + ResourceDrop, // ([export]) [resource-drop] + ResourceNew, // [export][resource-new] + ResourceRep, // [export][resource-rep] + Dtor, // [dtor] (guest export only) + Allocate, // internal: allocate new object (called from generated code) +} + +fn is_special_method(func: &Function) -> SpecialMethod { + if matches!(func.kind, FunctionKind::Static(_)) { + if func.name.starts_with("[resource-drop]") { + SpecialMethod::ResourceDrop + } else if func.name.starts_with("[resource-new]") { + SpecialMethod::ResourceNew + } else if func.name.starts_with("[resource-rep]") { + SpecialMethod::ResourceRep + } else if func.name.starts_with("[dtor]") { + SpecialMethod::Dtor + } else if func.name == "$alloc" { + SpecialMethod::Allocate + } else { + SpecialMethod::None + } + } else { + SpecialMethod::None + } +} + +// fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool { +// match ty { +// Type::Id(id) => match resolve.types[*id].kind { +// TypeDefKind::Type(t) => is_arg_by_pointer(resolve, &t), +// // this is different from C +// TypeDefKind::Resource => false, +// _ => wit_bindgen_c::is_arg_by_pointer(resolve, ty), +// }, +// _ => wit_bindgen_c::is_arg_by_pointer(resolve, ty), +// } +// } diff --git a/crates/cpp/src/symbol_name.rs b/crates/cpp/src/symbol_name.rs new file mode 100644 index 000000000..4b71d1005 --- /dev/null +++ b/crates/cpp/src/symbol_name.rs @@ -0,0 +1,50 @@ +use wit_bindgen_core::abi; + +fn hexdigit(v: u32) -> char { + if v < 10 { + char::from_u32(('0' as u32) + v).unwrap() + } else { + char::from_u32(('A' as u32) - 10 + v).unwrap() + } +} + +/// encode symbol as alphanumeric by hex-encoding special characters +pub fn make_external_component(input: &str) -> String { + input + .chars() + .map(|c| match c { + 'A'..='Z' | 'a'..='z' | '0'..='9' | '_' => { + let mut s = String::new(); + s.push(c); + s + } + '-' => { + let mut s = String::new(); + s.push('_'); + s + } + _ => { + let mut s = String::from("X"); + s.push(hexdigit((c as u32 & 0xf0) >> 4)); + s.push(hexdigit(c as u32 & 0xf)); + s + } + }) + .collect() +} + +/// encode symbol as alphanumeric by hex-encoding special characters +pub fn make_external_symbol(module_name: &str, name: &str, variant: abi::AbiVariant) -> String { + if module_name.is_empty() || module_name == "$root" { + make_external_component(name) + } else { + let mut res = make_external_component(module_name); + res.push_str(if matches!(variant, abi::AbiVariant::GuestExport) { + "X23" // Hash character + } else { + "X00" // NUL character (some tools use '.' for display) + }); + res.push_str(&make_external_component(name)); + res + } +} diff --git a/crates/cpp/test_headers/expected b/crates/cpp/test_headers/expected new file mode 100644 index 000000000..fc3df0eed --- /dev/null +++ b/crates/cpp/test_headers/expected @@ -0,0 +1,10 @@ +// work around g++ system header limitation +#define unexpected unexpected_old +#include +#undef unexpected +#include + +namespace std { + using ::tl::expected; + using ::tl::unexpected; +} diff --git a/crates/cpp/test_headers/expected.hpp b/crates/cpp/test_headers/expected.hpp new file mode 100644 index 000000000..afee404d4 --- /dev/null +++ b/crates/cpp/test_headers/expected.hpp @@ -0,0 +1,2444 @@ +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP + +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 1 +#define TL_EXPECTED_VERSION_PATCH 0 + +#include +#include +#include +#include + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif + +#if !defined(TL_ASSERT) +//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug +#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49) +#include +#define TL_ASSERT(x) assert(x) +#else +#define TL_ASSERT(x) +#endif +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions + +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// This one will be different for GCC 5.7 if it's ever supported +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif + +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif + +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + +namespace tl { +template class expected; + +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; + +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif + +template class unexpected { +public: + static_assert(!std::is_same::value, "E must not be void"); + + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} + + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} + + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } + +private: + E m_val; +}; + +#ifdef __cpp_deduction_guides +template unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else + (void)e; +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif +#endif +} + +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template < + typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // namespace detail + +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; + +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else + TL_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(rhs.get()); +#endif + } else { + assign_common(rhs); + } + } + + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); +#endif + } else { + assign_common(std::move(rhs)); + } + } + +#else + + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } +#endif + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct() noexcept { this->m_has_val = true; } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; + + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; + + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; + + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base & + operator=(expected_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base(expected_move_assign_base &&rhs) = default; + + expected_move_assign_base & + operator=(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; + +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +// This specialization is for when T is not default constructible +template struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; +} // namespace detail + +template class bad_expected_access : public std::exception { +public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} + + virtual const char *what() const noexcept override { + return "Bad expected access"; + } + + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } + +private: + E m_val; +}; + +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); + + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; + + template ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; +#endif + } + } + +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { + // swapping void is a no-op + } + + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } + + constexpr const T *operator->() const { + TL_ASSERT(has_value()); + return valptr(); + } + TL_EXPECTED_11_CONSTEXPR T *operator->() { + TL_ASSERT(has_value()); + return valptr(); + } + + template ::value> * = nullptr> + constexpr const U &operator*() const & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + TL_ASSERT(has_value()); + return std::move(val()); + } + + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + + constexpr const E &error() const & { + TL_ASSERT(!has_value()); + return err().value(); + } + TL_EXPECTED_11_CONSTEXPR E &error() & { + TL_ASSERT(!has_value()); + return err().value(); + } + constexpr const E &&error() const && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } + TL_EXPECTED_11_CONSTEXPR E &&error() && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } + + template constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif diff --git a/crates/cpp/test_headers/wit-common.h b/crates/cpp/test_headers/wit-common.h new file mode 120000 index 000000000..b8079c722 --- /dev/null +++ b/crates/cpp/test_headers/wit-common.h @@ -0,0 +1 @@ +../helper-types/wit-common.h \ No newline at end of file diff --git a/crates/cpp/test_headers/wit-guest.h b/crates/cpp/test_headers/wit-guest.h new file mode 120000 index 000000000..8b501851e --- /dev/null +++ b/crates/cpp/test_headers/wit-guest.h @@ -0,0 +1 @@ +../helper-types/wit-guest.h \ No newline at end of file diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 2d1499a29..e232c15f6 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -46,6 +46,14 @@ enum Opt { #[clap(flatten)] args: Common, }, + /// Generates bindings for C++ modules. + #[cfg(feature = "cpp")] + Cpp { + #[clap(flatten)] + opts: wit_bindgen_cpp::Opts, + #[clap(flatten)] + args: Common, + }, /// Generates bindings for TinyGo-based Go guest modules (Deprecated) #[cfg(feature = "go")] @@ -128,6 +136,8 @@ fn main() -> Result<()> { Opt::Moonbit { opts, args } => (opts.build(), args), #[cfg(feature = "c")] Opt::C { opts, args } => (opts.build(), args), + #[cfg(feature = "cpp")] + Opt::Cpp { opts, args } => (opts.build(), args), #[cfg(feature = "rust")] Opt::Rust { opts, args } => (opts.build(), args), #[cfg(feature = "go")] diff --git a/tests/runtime/lists/test.new.cpp b/tests/runtime/lists/test.new.cpp new file mode 100644 index 000000000..0e4481a6d --- /dev/null +++ b/tests/runtime/lists/test.new.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include + +uint32_t exports::lists::AllocatedBytes() { + return 0; +} + +static bool equal(wit::string const&a, std::string_view b) { + return a.get_view() == b; +} +static bool equal(wit::string const&a, const char x[]) { + return a.get_view() == x; +} +template +static bool equal(T const&a, S const& b) { + return a == b; +} +template +static bool equal(wit::span const&a, wit::span const& b) { + if (a.size() != b.size()) { return false; } + for (uint32_t i = 0; i +static bool equal(wit::vector const&a, wit::span const& b) { + return equal(a.get_view(), b); +} +template +static bool equal(wit::span const&a, wit::vector const& b) { + return equal(b, a); +} +template +static bool equal(wit::span const&a, std::vector const& b) { + return equal(a, wit::span(b)); +} +template +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); +} +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); +} +template +static bool equal(std::tuple const&a, std::tuple const& b) { + return equal(std::get<0>(a), std::get<0>(b)) && equal(std::get<1>(a), std::get<1>(b)); +} + +void exports::lists::TestImports() { + //let _guard = testRust_wasm::guard(); + + test::lists::test::EmptyListParam(wit::span(std::vector())); + test::lists::test::EmptyStringParam(""); + assert(test::lists::test::EmptyListResult().empty()); + assert(test::lists::test::EmptyStringResult().empty()); + + test::lists::test::ListParam(std::vector{1, 2, 3, 4}); + test::lists::test::ListParam2("foo"); + test::lists::test::ListParam3(std::vector{"foo", "bar", "baz"}); + test::lists::test::ListParam4(std::vector>{ + std::vector{"foo", "bar"}, + std::vector{"baz"}, + }); + assert(equal(test::lists::test::ListResult(), std::vector{1, 2, 3, 4, 5})); + assert(equal(test::lists::test::ListResult2(), "hello!")); + assert(equal(test::lists::test::ListResult3(), std::vector{"hello,", "world!"})); + + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector())), std::vector())); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'h', 'e', 'l', 'l', 'o'})); + + assert(equal(test::lists::test::StringRoundtrip("x"), "x")); + assert(equal(test::lists::test::StringRoundtrip(""), "")); + assert(equal(test::lists::test::StringRoundtrip("hello"), "hello")); + assert(equal(test::lists::test::StringRoundtrip("hello ⚑ world"), "hello ⚑ world")); + + assert(equal( + test::lists::test::ListMinmax8(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}), + std::make_tuple(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax16(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}), + std::make_tuple(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax32(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}), + std::make_tuple(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax64(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}), + std::make_tuple(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}) + )); + assert(equal( + test::lists::test::ListMinmaxFloat( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ), + std::make_tuple( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ) + )); +} + + +void exports::test::lists::test::EmptyListParam(wit::span a) { + assert(a.empty()); +} + +void exports::test::lists::test::EmptyStringParam(std::string_view a) { + assert(a.empty()); +} + +wit::vector exports::test::lists::test::EmptyListResult() { + return wit::vector(); +} + +wit::string exports::test::lists::test::EmptyStringResult() { + return wit::string::from_view(std::string_view()); +} + +void exports::test::lists::test::ListParam(wit::span list) { + assert(equal(list, std::vector{1, 2, 3, 4})); +} + +void exports::test::lists::test::ListParam2(std::string_view ptr) { + assert(equal(ptr, std::string_view("foo"))); +} + +void exports::test::lists::test::ListParam3(wit::span ptr) { + assert(equal(ptr.size(), size_t(3))); + assert(equal(ptr[0], std::string_view("foo"))); + assert(equal(ptr[1], std::string_view("bar"))); + assert(equal(ptr[2], std::string_view("baz"))); +} + +void exports::test::lists::test::ListParam4(wit::span> ptr) { + assert(equal(ptr.size(), size_t(2))); + assert(equal(ptr[0][0], std::string_view("foo"))); + assert(equal(ptr[0][1], std::string_view("bar"))); + assert(equal(ptr[1][0], std::string_view("baz"))); +} + +wit::vector exports::test::lists::test::ListResult() { + return wit::vector::from_view(wit::span(std::vector{1, 2, 3, 4, 5})); +} + +wit::string exports::test::lists::test::ListResult2() { + return wit::string::from_view("hello!"); +} + +wit::vector exports::test::lists::test::ListResult3() { + return wit::vector::from_view(wit::span(std::vector{wit::string::from_view("hello,"), wit::string::from_view("world!")})); +} + +wit::vector exports::test::lists::test::ListRoundtrip(wit::span x) { + return wit::vector::from_view(x); +} + +wit::string exports::test::lists::test::StringRoundtrip(std::string_view x) { + return wit::string::from_view(x); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax8(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax16(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax32(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax64(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmaxFloat(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} diff --git a/tests/runtime/many_arguments/wasm.new.cpp b/tests/runtime/many_arguments/wasm.new.cpp new file mode 100644 index 000000000..80b70603e --- /dev/null +++ b/tests/runtime/many_arguments/wasm.new.cpp @@ -0,0 +1,46 @@ +#include +#include + +template +bool equal(T const&a, T const&b) { + return a==b; +} + +void exports::many_arguments::ManyArguments( + uint64_t a1, + uint64_t a2, + uint64_t a3, + uint64_t a4, + uint64_t a5, + uint64_t a6, + uint64_t a7, + uint64_t a8, + uint64_t a9, + uint64_t a10, + uint64_t a11, + uint64_t a12, + uint64_t a13, + uint64_t a14, + uint64_t a15, + uint64_t a16 +) { + assert(equal(a1, (uint64_t)1)); + assert(equal(a2, (uint64_t)2)); + assert(equal(a3, (uint64_t)3)); + assert(equal(a4, (uint64_t)4)); + assert(equal(a5, (uint64_t)5)); + assert(equal(a6, (uint64_t)6)); + assert(equal(a7, (uint64_t)7)); + assert(equal(a8, (uint64_t)8)); + assert(equal(a9, (uint64_t)9)); + assert(equal(a10, (uint64_t)10)); + assert(equal(a11, (uint64_t)11)); + assert(equal(a12, (uint64_t)12)); + assert(equal(a13, (uint64_t)13)); + assert(equal(a14, (uint64_t)14)); + assert(equal(a15, (uint64_t)15)); + assert(equal(a16, (uint64_t)16)); + ::test::many_arguments::ManyArguments( + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16 + ); +} diff --git a/tests/runtime/numbers/test.cpp b/tests/runtime/numbers/test.cpp new file mode 100644 index 000000000..e1cc759d6 --- /dev/null +++ b/tests/runtime/numbers/test.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +uint8_t exports::test::numbers::test::RoundtripU8(uint8_t a) { + return a; +} + +int8_t exports::test::numbers::test::RoundtripS8(int8_t a) { + return a; +} + +uint16_t exports::test::numbers::test::RoundtripU16(uint16_t a) { + return a; +} + +int16_t exports::test::numbers::test::RoundtripS16(int16_t a) { + return a; +} + +uint32_t exports::test::numbers::test::RoundtripU32(uint32_t a) { + return a; +} + +int32_t exports::test::numbers::test::RoundtripS32(int32_t a) { + return a; +} + +uint64_t exports::test::numbers::test::RoundtripU64(uint64_t a) { + return a; +} + +int64_t exports::test::numbers::test::RoundtripS64(int64_t a) { + return a; +} + +float exports::test::numbers::test::RoundtripF32(float a) { + return a; +} + +double exports::test::numbers::test::RoundtripF64(double a) { + return a; +} + +uint32_t exports::test::numbers::test::RoundtripChar(uint32_t a) { + return a; +} + +static uint32_t SCALAR = 0; + +void exports::test::numbers::test::SetScalar(uint32_t a) { + SCALAR = a; +} + +uint32_t exports::test::numbers::test::GetScalar(void) { + return SCALAR; +} + + +void exports::numbers::TestImports() { + assert(::test::numbers::test::RoundtripU8(1) == 1); + assert(::test::numbers::test::RoundtripU8(0) == 0); + assert(::test::numbers::test::RoundtripU8(UCHAR_MAX) == UCHAR_MAX); + + assert(::test::numbers::test::RoundtripS8(1) == 1); + assert(::test::numbers::test::RoundtripS8(SCHAR_MIN) == SCHAR_MIN); + assert(::test::numbers::test::RoundtripS8(SCHAR_MAX) == SCHAR_MAX); + + assert(::test::numbers::test::RoundtripU16(1) == 1); + assert(::test::numbers::test::RoundtripU16(0) == 0); + assert(::test::numbers::test::RoundtripU16(USHRT_MAX) == USHRT_MAX); + + assert(::test::numbers::test::RoundtripS16(1) == 1); + assert(::test::numbers::test::RoundtripS16(SHRT_MIN) == SHRT_MIN); + assert(::test::numbers::test::RoundtripS16(SHRT_MAX) == SHRT_MAX); + + assert(::test::numbers::test::RoundtripU32(1) == 1); + assert(::test::numbers::test::RoundtripU32(0) == 0); + assert(::test::numbers::test::RoundtripU32(UINT_MAX) == UINT_MAX); + + assert(::test::numbers::test::RoundtripS32(1) == 1); + assert(::test::numbers::test::RoundtripS32(INT_MIN) == INT_MIN); + assert(::test::numbers::test::RoundtripS32(INT_MAX) == INT_MAX); + + assert(::test::numbers::test::RoundtripU64(1) == 1); + assert(::test::numbers::test::RoundtripU64(0) == 0); + assert(::test::numbers::test::RoundtripU64(ULONG_MAX) == ULONG_MAX); + + assert(::test::numbers::test::RoundtripS64(1) == 1); + assert(::test::numbers::test::RoundtripS64(LONG_MIN) == LONG_MIN); + assert(::test::numbers::test::RoundtripS64(LONG_MAX) == LONG_MAX); + + assert(::test::numbers::test::RoundtripF32(1.0) == 1.0); + assert(::test::numbers::test::RoundtripF32(INFINITY) == INFINITY); + assert(::test::numbers::test::RoundtripF32(-INFINITY) == -INFINITY); + assert(isnan(::test::numbers::test::RoundtripF32(NAN))); + + assert(::test::numbers::test::RoundtripF64(1.0) == 1.0); + assert(::test::numbers::test::RoundtripF64(INFINITY) == INFINITY); + assert(::test::numbers::test::RoundtripF64(-INFINITY) == -INFINITY); + assert(isnan(::test::numbers::test::RoundtripF64(NAN))); + + assert(::test::numbers::test::RoundtripChar('a') == 'a'); + assert(::test::numbers::test::RoundtripChar(' ') == ' '); + assert(::test::numbers::test::RoundtripChar(U'?') == U'?'); + + ::test::numbers::test::SetScalar(2); + assert(::test::numbers::test::GetScalar() == 2); + ::test::numbers::test::SetScalar(4); + assert(::test::numbers::test::GetScalar() == 4); +} diff --git a/tests/runtime/options/test.new.cpp b/tests/runtime/options/test.new.cpp new file mode 100644 index 000000000..8925a3f74 --- /dev/null +++ b/tests/runtime/options/test.new.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +template +static bool equal(T const& a, T const& b) { + return a==b; +} +static bool equal(wit::string const& a, std::string const& b) { + return a.get_view() == std::string_view(b); +} +static bool equal(std::optional const& a, std::optional const& b) { + if (a.has_value() != b.has_value()) return false; + if (a.has_value()) { + return equal(a.value(), b.value()); + } + return true; +} + +void exports::options::TestImports() { + using namespace test::options::test; + + OptionNoneParam(std::optional()); + OptionSomeParam(std::optional("foo")); + assert(!OptionNoneResult()); + assert(equal(OptionSomeResult(), std::optional("foo"))); + assert(equal(OptionRoundtrip(std::optional("foo")), std::optional("foo"))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional(42))), std::optional>(std::optional(42)))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional())), std::optional>(std::optional()))); + assert(equal(DoubleOptionRoundtrip(std::optional>()), std::optional>())); +} + +void exports::test::options::test::OptionNoneParam(std::optional a) +{ + assert(!a.has_value()); +} + +std::optional exports::test::options::test::OptionNoneResult() { + return std::optional(); +} + +void exports::test::options::test::OptionSomeParam(std::optional a) { + assert(equal(a, std::optional("foo"))); +} + +std::optional exports::test::options::test::OptionSomeResult() { + return std::optional(wit::string::from_view("foo")); +} + +std::optional exports::test::options::test::OptionRoundtrip(std::optional a) { + if (!a.has_value()) return std::optional(); + return std::optional(wit::string::from_view(*a)); +} + +std::optional> exports::test::options::test::DoubleOptionRoundtrip(std::optional> a) { + return a; +} diff --git a/tests/runtime/records/test.new.cpp b/tests/runtime/records/test.new.cpp new file mode 100644 index 000000000..d5b276f46 --- /dev/null +++ b/tests/runtime/records/test.new.cpp @@ -0,0 +1,75 @@ +#include +#include + +template +bool equal(T const&a, T const&b) { + return a==b; +} + +void exports::records::TestImports() { + using namespace ::test::records::test; + + assert(equal(MultipleResults(), std::tuple(4, 5))); + + assert(equal(SwapTuple(std::tuple(1, 2)), std::tuple(2, 1))); + assert(equal(RoundtripFlags1(::test::records::test::F1::kA), ::test::records::test::F1::kA)); + assert(equal(RoundtripFlags1(::test::records::test::F1::k_None), ::test::records::test::F1::k_None)); + assert(equal(RoundtripFlags1(::test::records::test::F1::kB), ::test::records::test::F1::kB)); + assert(equal(RoundtripFlags1(::test::records::test::F1::kA | ::test::records::test::F1::kB), ::test::records::test::F1::kA | ::test::records::test::F1::kB)); + + assert(equal(RoundtripFlags2(::test::records::test::F2::kC), ::test::records::test::F2::kC)); + assert(equal(RoundtripFlags2(::test::records::test::F2::k_None), ::test::records::test::F2::k_None)); + assert(equal(RoundtripFlags2(::test::records::test::F2::kD), ::test::records::test::F2::kD)); + assert(equal(RoundtripFlags2(::test::records::test::F2::kC | ::test::records::test::F2::kE), ::test::records::test::F2::kC | ::test::records::test::F2::kE)); + + assert(equal( + RoundtripFlags3(::test::records::test::Flag8::kB0, ::test::records::test::Flag16::kB1, ::test::records::test::Flag32::kB2), + std::tuple<::test::records::test::Flag8, ::test::records::test::Flag16, ::test::records::test::Flag32>(::test::records::test::Flag8::kB0, ::test::records::test::Flag16::kB1, ::test::records::test::Flag32::kB2) + )); + + { + auto r = RoundtripRecord1(::test::records::test::R1 { + 8, + ::test::records::test::F1::k_None, + }); + assert(equal(r.a, (uint8_t)8)); + assert(equal(r.b, ::test::records::test::F1::k_None)); + } + + auto r = RoundtripRecord1(::test::records::test::R1 { + 0, + ::test::records::test::F1::kA | ::test::records::test::F1::kB, + }); + assert(equal(r.a, (uint8_t)0)); + assert(equal(r.b, ::test::records::test::F1::kA | ::test::records::test::F1::kB)); + + assert(equal(Tuple1(std::tuple(1)), std::tuple(1))); +} + +std::tuple exports::test::records::test::MultipleResults() { + return std::tuple(100, 200); +} + +std::tuple exports::test::records::test::SwapTuple(std::tuple a) { + return std::tuple(std::get<1>(a), std::get<0>(a)); +} + +test::records::test::F1 exports::test::records::test::RoundtripFlags1(::test::records::test::F1 a) { + return a; +} + +test::records::test::F2 exports::test::records::test::RoundtripFlags2(::test::records::test::F2 a) { + return a; +} + +std::tuple exports::test::records::test::RoundtripFlags3(::test::records::test::Flag8 a, ::test::records::test::Flag16 b, ::test::records::test::Flag32 c) { + return std::tuple<::test::records::test::Flag8, ::test::records::test::Flag16, ::test::records::test::Flag32>(a, b, c); +} + +test::records::test::R1 exports::test::records::test::RoundtripRecord1(::test::records::test::R1 a) { + return a; +} + +std::tuple exports::test::records::test::Tuple1(std::tuple a) { + return std::tuple(std::get<0>(a)); +} diff --git a/tests/runtime/results/test.new.cpp b/tests/runtime/results/test.new.cpp new file mode 100644 index 000000000..68ea1e46a --- /dev/null +++ b/tests/runtime/results/test.new.cpp @@ -0,0 +1,56 @@ +#include +#include + +template +bool equal(T const&a, T const&b) { + return a==b; +} + +std::expected exports::test::results::test::StringError(float a) { + return ::test::results::test::StringError(a); +} + +std::expected exports::test::results::test::EnumError(float a) { + auto result = ::test::results::test::EnumError(a); + if (result.has_value()) { return result.value(); } + return std::unexpected(result.error()); + // if (result.error()==::test::results::test::E::kA) { return std::unexpected(::test::results::test::E::kA); } + // if (result.error()==::test::results::test::E::kB) { return std::unexpected(::test::results::test::E::kB); } + // if (result.error()==::test::results::test::E::kC) { return std::unexpected(::test::results::test::E::kC); } +} + +std::expected exports::test::results::test::RecordError(float a) { + auto result = ::test::results::test::RecordError(a); + if (result.has_value()) { return result.value(); } + return std::unexpected(::test::results::test::E2{ result.error().line, result.error().column }); +} + +std::expected exports::test::results::test::VariantError(float a) { + auto result = ::test::results::test::VariantError(a); + if (result.has_value()) { return result.value(); } + return std::unexpected(result.error()); + + // match test_imports::variant_error(a) { + // Ok(b) => Ok(b), + // Err(test_imports::E3::E1(test_imports::E::A)) => { + // Err(test_exports::E3::E1(test_exports::E::A)) + // } + // Err(test_imports::E3::E1(test_imports::E::B)) => { + // Err(test_exports::E3::E1(test_exports::E::B)) + // } + // Err(test_imports::E3::E1(test_imports::E::C)) => { + // Err(test_exports::E3::E1(test_exports::E::C)) + // } + // Err(test_imports::E3::E2(test_imports::E2 { line, column })) => { + // Err(test_exports::E3::E2(test_exports::E2 { line, column })) + // } + // } +} + +std::expected exports::test::results::test::EmptyError(uint32_t a) { + return ::test::results::test::EmptyError(a); +} + +std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { + return ::test::results::test::DoubleError(a); +} diff --git a/tests/runtime/strings/test.cpp b/tests/runtime/strings/test.cpp new file mode 100644 index 000000000..ecf43f653 --- /dev/null +++ b/tests/runtime/strings/test.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +void assert_str(std::string_view str, const char* expected) { + size_t expected_len = strlen(expected); + assert(str.size() == expected_len); + assert(memcmp(str.data(), expected, expected_len) == 0); +} + +void exports::strings::TestImports() { + test::strings::imports::TakeBasic(std::string_view("latin utf16")); + + wit::string str2 = test::strings::imports::ReturnUnicode(); + assert_str(str2.get_view(), "??? ??"); +} + +wit::string exports::strings::ReturnEmpty() { + // return a non-zero address (follows cabi_realloc logic) + return wit::string((char const*)1, 0); +} + +wit::string exports::strings::Roundtrip(wit::string &&str) { + assert(str.size() > 0); + return std::move(str); +} diff --git a/tests/runtime/strings/test.new.cpp b/tests/runtime/strings/test.new.cpp new file mode 100644 index 000000000..966189074 --- /dev/null +++ b/tests/runtime/strings/test.new.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +void assert_str(std::string_view str, const char* expected) { + size_t expected_len = strlen(expected); + assert(str.size() == expected_len); + assert(memcmp(str.data(), expected, expected_len) == 0); +} + +void exports::strings::TestImports() { + test::strings::imports::TakeBasic(std::string_view("latin utf16")); + + wit::string str2 = test::strings::imports::ReturnUnicode(); + assert_str(str2.get_view(), "??? ??"); +} + +wit::string exports::strings::ReturnEmpty() { + // return a non-zero address (follows cabi_realloc logic) + return wit::string((char const*)1, 0); +} + +// new API: Identical for guest import and export +wit::string exports::strings::Roundtrip(std::string_view str) { + assert(str.size() > 0); + return wit::string::from_view(str); +}