diff --git a/Cargo.lock b/Cargo.lock index 38c4952f8..02f64f7e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,41 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "askama" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47cbc3cf73fa8d9833727bbee4835ba5c421a0d65b72daf9a7b5d0e0f9cfb57e" +dependencies = [ + "askama_derive", + "askama_escape", + "humansize", + "num-traits", + "percent-encoding", +] + +[[package]] +name = "askama_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22fbe0413545c098358e56966ff22cdd039e10215ae213cfbd65032b119fc94" +dependencies = [ + "basic-toml", + "mime", + "mime_guess", + "nom", + "proc-macro2", + "quote", + "serde", + "syn 2.0.15", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + [[package]] name = "atty" version = "0.2.14" @@ -49,6 +84,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "basic-toml" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.2.1" @@ -171,6 +215,7 @@ version = "0.5.2" name = "diplomat-tool" version = "0.5.2" dependencies = [ + "askama", "clap", "colored", "diplomat_core", @@ -288,6 +333,15 @@ dependencies = [ "libc", ] +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "icu" version = "1.2.0" @@ -637,6 +691,28 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -647,6 +723,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -678,6 +764,12 @@ version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + [[package]] name = "ppv-lite86" version = "0.2.10" diff --git a/core/src/hir/types.rs b/core/src/hir/types.rs index dd296b744..167e5646f 100644 --- a/core/src/hir/types.rs +++ b/core/src/hir/types.rs @@ -68,6 +68,18 @@ impl Type { } } +impl SelfType { + /// Returns whether the self parameter is borrowed immutably. + /// + /// Curently this can only happen with opaque types. + pub fn is_immutably_borrowed(&self) -> bool { + match self { + SelfType::Opaque(opaque_path) => opaque_path.owner.mutability == Mutability::Immutable, + _ => false, + } + } +} + impl Slice { /// Returns the [`TypeLifetime`] contained in either the `Str` or `Primitive` /// variant. diff --git a/feature_tests/c/include/MyEnum.h b/feature_tests/c/include/MyEnum.h index cbc5906ae..21c1c0e2e 100644 --- a/feature_tests/c/include/MyEnum.h +++ b/feature_tests/c/include/MyEnum.h @@ -26,6 +26,7 @@ namespace capi { extern "C" { #endif +int8_t MyEnum_into_value(MyEnum self); void MyEnum_destroy(MyEnum* self); #ifdef __cplusplus diff --git a/feature_tests/c/include/MyStruct.h b/feature_tests/c/include/MyStruct.h index af512c63d..5ed1a4424 100644 --- a/feature_tests/c/include/MyStruct.h +++ b/feature_tests/c/include/MyStruct.h @@ -30,6 +30,8 @@ extern "C" { #endif MyStruct MyStruct_new(); + +uint8_t MyStruct_into_a(MyStruct self); void MyStruct_destroy(MyStruct* self); #ifdef __cplusplus diff --git a/feature_tests/c2/include/MyEnum.h b/feature_tests/c2/include/MyEnum.h index 82fde8271..310e9cc01 100644 --- a/feature_tests/c2/include/MyEnum.h +++ b/feature_tests/c2/include/MyEnum.h @@ -15,7 +15,7 @@ extern "C" { #endif // __cplusplus -// No Content +int8_t MyEnum_into_value(MyEnum self); #ifdef __cplusplus diff --git a/feature_tests/c2/include/MyStruct.h b/feature_tests/c2/include/MyStruct.h index babd37d7a..28761799c 100644 --- a/feature_tests/c2/include/MyStruct.h +++ b/feature_tests/c2/include/MyStruct.h @@ -17,6 +17,8 @@ extern "C" { MyStruct MyStruct_new(); +uint8_t MyStruct_into_a(MyStruct self); + #ifdef __cplusplus } // extern "C" diff --git a/feature_tests/cpp/docs/source/structs_ffi.rst b/feature_tests/cpp/docs/source/structs_ffi.rst index 8b59c6796..12d4ca7e6 100644 --- a/feature_tests/cpp/docs/source/structs_ffi.rst +++ b/feature_tests/cpp/docs/source/structs_ffi.rst @@ -15,6 +15,9 @@ .. cpp:enumerator:: F + .. cpp:function:: int8_t into_value() + + .. cpp:struct:: MyStruct .. cpp:member:: uint8_t a @@ -34,6 +37,9 @@ .. cpp:function:: static MyStruct new_() + .. cpp:function:: uint8_t into_a() + + .. cpp:class:: Opaque .. cpp:function:: static Opaque new_() diff --git a/feature_tests/cpp/include/MyEnum.h b/feature_tests/cpp/include/MyEnum.h index cbc5906ae..21c1c0e2e 100644 --- a/feature_tests/cpp/include/MyEnum.h +++ b/feature_tests/cpp/include/MyEnum.h @@ -26,6 +26,7 @@ namespace capi { extern "C" { #endif +int8_t MyEnum_into_value(MyEnum self); void MyEnum_destroy(MyEnum* self); #ifdef __cplusplus diff --git a/feature_tests/cpp/include/MyStruct.h b/feature_tests/cpp/include/MyStruct.h index af512c63d..5ed1a4424 100644 --- a/feature_tests/cpp/include/MyStruct.h +++ b/feature_tests/cpp/include/MyStruct.h @@ -30,6 +30,8 @@ extern "C" { #endif MyStruct MyStruct_new(); + +uint8_t MyStruct_into_a(MyStruct self); void MyStruct_destroy(MyStruct* self); #ifdef __cplusplus diff --git a/feature_tests/cpp/include/MyStruct.hpp b/feature_tests/cpp/include/MyStruct.hpp index 54ad2d236..418ea1d7a 100644 --- a/feature_tests/cpp/include/MyStruct.hpp +++ b/feature_tests/cpp/include/MyStruct.hpp @@ -24,6 +24,7 @@ struct MyStruct { char32_t f; MyEnum g; static MyStruct new_(); + uint8_t into_a(); }; @@ -31,4 +32,8 @@ inline MyStruct MyStruct::new_() { capi::MyStruct diplomat_raw_struct_out_value = capi::MyStruct_new(); return MyStruct{ .a = std::move(diplomat_raw_struct_out_value.a), .b = std::move(diplomat_raw_struct_out_value.b), .c = std::move(diplomat_raw_struct_out_value.c), .d = std::move(diplomat_raw_struct_out_value.d), .e = std::move(diplomat_raw_struct_out_value.e), .f = std::move(diplomat_raw_struct_out_value.f), .g = std::move(static_cast(diplomat_raw_struct_out_value.g)) }; } +inline uint8_t MyStruct::into_a() { + MyStruct diplomat_wrapped_struct_this = std::move(*this); + return capi::MyStruct_into_a(capi::MyStruct{ .a = diplomat_wrapped_struct_this.a, .b = diplomat_wrapped_struct_this.b, .c = diplomat_wrapped_struct_this.c, .d = diplomat_wrapped_struct_this.d, .e = diplomat_wrapped_struct_this.e, .f = diplomat_wrapped_struct_this.f, .g = static_cast(diplomat_wrapped_struct_this.g) }); +} #endif diff --git a/feature_tests/cpp2/include/MyEnum.d.hpp b/feature_tests/cpp2/include/MyEnum.d.hpp index 5d1318811..4c8558ae0 100644 --- a/feature_tests/cpp2/include/MyEnum.d.hpp +++ b/feature_tests/cpp2/include/MyEnum.d.hpp @@ -24,6 +24,8 @@ class MyEnum { F, }; + inline int8_t into_value(); + inline MyEnum(MyEnum::Value cpp_value); inline MyEnum(capi::MyEnum c_enum) : value(c_enum) {}; diff --git a/feature_tests/cpp2/include/MyEnum.h b/feature_tests/cpp2/include/MyEnum.h index 82fde8271..310e9cc01 100644 --- a/feature_tests/cpp2/include/MyEnum.h +++ b/feature_tests/cpp2/include/MyEnum.h @@ -15,7 +15,7 @@ extern "C" { #endif // __cplusplus -// No Content +int8_t MyEnum_into_value(MyEnum self); #ifdef __cplusplus diff --git a/feature_tests/cpp2/include/MyEnum.hpp b/feature_tests/cpp2/include/MyEnum.hpp index d92e2d8a3..ac2444b73 100644 --- a/feature_tests/cpp2/include/MyEnum.hpp +++ b/feature_tests/cpp2/include/MyEnum.hpp @@ -38,6 +38,11 @@ inline MyEnum::MyEnum(MyEnum::Value cpp_value) { } } +inline int8_t MyEnum::into_value() { + auto result = capi::MyEnum_into_value(this->AsFFI()); + return result; +} + inline capi::MyEnum MyEnum::AsFFI() const { return value; } diff --git a/feature_tests/cpp2/include/MyStruct.d.hpp b/feature_tests/cpp2/include/MyStruct.d.hpp index 6159ae951..63b53c332 100644 --- a/feature_tests/cpp2/include/MyStruct.d.hpp +++ b/feature_tests/cpp2/include/MyStruct.d.hpp @@ -25,6 +25,8 @@ struct MyStruct { inline static MyStruct new_(); + inline uint8_t into_a(); + inline capi::MyStruct AsFFI() const; inline static MyStruct FromFFI(capi::MyStruct c_struct); }; diff --git a/feature_tests/cpp2/include/MyStruct.h b/feature_tests/cpp2/include/MyStruct.h index babd37d7a..28761799c 100644 --- a/feature_tests/cpp2/include/MyStruct.h +++ b/feature_tests/cpp2/include/MyStruct.h @@ -17,6 +17,8 @@ extern "C" { MyStruct MyStruct_new(); +uint8_t MyStruct_into_a(MyStruct self); + #ifdef __cplusplus } // extern "C" diff --git a/feature_tests/cpp2/include/MyStruct.hpp b/feature_tests/cpp2/include/MyStruct.hpp index fc0cc28ee..0352ecea1 100644 --- a/feature_tests/cpp2/include/MyStruct.hpp +++ b/feature_tests/cpp2/include/MyStruct.hpp @@ -19,6 +19,11 @@ inline MyStruct MyStruct::new_() { return MyStruct::FromFFI(result); } +inline uint8_t MyStruct::into_a() { + auto result = capi::MyStruct_into_a(this->AsFFI()); + return result; +} + inline capi::MyStruct MyStruct::AsFFI() const { return capi::MyStruct { diff --git a/feature_tests/cpp2/tests/structs.cpp b/feature_tests/cpp2/tests/structs.cpp index f5014f9fa..a46bea384 100644 --- a/feature_tests/cpp2/tests/structs.cpp +++ b/feature_tests/cpp2/tests/structs.cpp @@ -17,4 +17,7 @@ int main(int argc, char *argv[]) { simple_assert_eq("struct values", s.e, 5991); simple_assert_eq("struct values", (uint32_t)s.f, (uint32_t)U'餐'); simple_assert_eq("struct values", (uint32_t)s.g.AsFFI(), (uint32_t)MyEnum(MyEnum::B).AsFFI()); + + simple_assert_eq("enum fn", s.g.into_value(), -1); + simple_assert_eq("struct fn", s.into_a(), 17); } diff --git a/feature_tests/dotnet/Lib/Generated/MyStruct.cs b/feature_tests/dotnet/Lib/Generated/MyStruct.cs index 5f86ccb87..15758c085 100644 --- a/feature_tests/dotnet/Lib/Generated/MyStruct.cs +++ b/feature_tests/dotnet/Lib/Generated/MyStruct.cs @@ -161,6 +161,19 @@ public static MyStruct New() } } + public byte IntoA() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("MyStruct"); + } + byte retVal = Raw.MyStruct.IntoA(_inner); + return retVal; + } + } + /// /// Returns a copy of the underlying raw representation. /// diff --git a/feature_tests/dotnet/Lib/Generated/RawMyStruct.cs b/feature_tests/dotnet/Lib/Generated/RawMyStruct.cs index 5de81170c..9094bbb8b 100644 --- a/feature_tests/dotnet/Lib/Generated/RawMyStruct.cs +++ b/feature_tests/dotnet/Lib/Generated/RawMyStruct.cs @@ -33,4 +33,7 @@ public partial struct MyStruct [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MyStruct_new", ExactSpelling = true)] public static unsafe extern MyStruct New(); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MyStruct_into_a", ExactSpelling = true)] + public static unsafe extern byte IntoA(MyStruct self); } diff --git a/feature_tests/js/api/MyEnum.d.ts b/feature_tests/js/api/MyEnum.d.ts index 6ba56489a..5d58746ea 100644 --- a/feature_tests/js/api/MyEnum.d.ts +++ b/feature_tests/js/api/MyEnum.d.ts @@ -1,3 +1,4 @@ +import { i8 } from "./diplomat-runtime" /** */ diff --git a/feature_tests/js/api/MyStruct.d.ts b/feature_tests/js/api/MyStruct.d.ts index 3bf89aef1..340fd1955 100644 --- a/feature_tests/js/api/MyStruct.d.ts +++ b/feature_tests/js/api/MyStruct.d.ts @@ -15,4 +15,8 @@ export class MyStruct { /** */ static new(): MyStruct; + + /** + */ + into_a(): u8; } diff --git a/feature_tests/js/api/MyStruct.js b/feature_tests/js/api/MyStruct.js index ac415339b..ebf5ec5c3 100644 --- a/feature_tests/js/api/MyStruct.js +++ b/feature_tests/js/api/MyStruct.js @@ -22,4 +22,15 @@ export class MyStruct { return out; })(); } + + into_a() { + const field_a_this = this["a"]; + const field_b_this = this["b"]; + const field_c_this = this["c"]; + const field_d_this = this["d"]; + const field_e_this = this["e"]; + const field_f_this = this["f"]; + const field_g_this = this["g"]; + return wasm.MyStruct_into_a(field_a_this, field_b_this, field_c_this, field_d_this, field_e_this, diplomatRuntime.extractCodePoint(field_f_this, 'field_f_this'), MyEnum_js_to_rust[field_g_this]); + } } diff --git a/feature_tests/js/docs/source/structs_ffi.rst b/feature_tests/js/docs/source/structs_ffi.rst index 9e87833e9..3e94cd0e5 100644 --- a/feature_tests/js/docs/source/structs_ffi.rst +++ b/feature_tests/js/docs/source/structs_ffi.rst @@ -3,6 +3,8 @@ .. js:class:: MyEnum + .. js:method:: into_value() + .. js:class:: MyStruct .. js:attribute:: a @@ -21,6 +23,8 @@ .. js:function:: new() + .. js:method:: into_a() + .. js:class:: Opaque .. js:function:: new() diff --git a/feature_tests/src/structs.rs b/feature_tests/src/structs.rs index 95024644c..d1a43e158 100644 --- a/feature_tests/src/structs.rs +++ b/feature_tests/src/structs.rs @@ -48,6 +48,12 @@ pub mod ffi { } } + impl MyEnum { + pub fn into_value(self) -> i8 { + self as i8 + } + } + impl MyStruct { pub fn new() -> MyStruct { MyStruct { @@ -61,6 +67,10 @@ pub mod ffi { } } + pub fn into_a(self) -> u8 { + self.a + } + fn assert_value(&self) { assert_eq!(self.a, 17); assert!(self.b); diff --git a/tool/Cargo.toml b/tool/Cargo.toml index 9d3d2949b..39b7ade43 100644 --- a/tool/Cargo.toml +++ b/tool/Cargo.toml @@ -27,6 +27,7 @@ serde = { features = ["derive"], version = "1.0.130" } toml = "0.5.8" heck = "0.4" # conversion between naming convention displaydoc = "0.2" +askama = "0.12" [dev-dependencies] insta = { version = "1.7.1", features = [ "backtrace" ] } diff --git a/tool/src/c2/header.rs b/tool/src/c2/header.rs index dc544e3ae..9fcffd89d 100644 --- a/tool/src/c2/header.rs +++ b/tool/src/c2/header.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeSet; use std::fmt; +use std::fmt::Write; static BASE_INCLUDES: &str = r#" #include @@ -71,7 +72,7 @@ impl fmt::Display for Header { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut includes = String::from(BASE_INCLUDES); for i in &self.includes { - includes += &format!("#include \"{i}\"\n"); + writeln!(includes, "#include \"{i}\"").unwrap(); } let decl_header_include: Cow = match self.decl_include { Some(ref v) => format!("\n#include \"{v}\"\n").into(), diff --git a/tool/src/cpp2/header.rs b/tool/src/cpp2/header.rs index 795c4b0bc..96191e95f 100644 --- a/tool/src/cpp2/header.rs +++ b/tool/src/cpp2/header.rs @@ -1,18 +1,9 @@ +use askama::Template; use std::borrow::{Borrow, Cow}; use std::collections::BTreeSet; -use std::fmt; +use std::fmt::{self, Write}; -static BASE_INCLUDES: &str = r#" -#include -#include -#include -#include -#include -#include -#include "diplomat_runtime.hpp" -"#; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub enum Forward { Class(String), #[allow(dead_code)] @@ -32,6 +23,16 @@ impl Borrow for Forward { } } +#[derive(Template)] +#[template(path = "cpp2/base.h.jinja", escape = "none")] +struct HeaderTemplate<'a> { + header_guard: Cow<'a, str>, + decl_include: Option>, + includes: Vec>, + forwards: Vec, + body: Cow<'a, str>, +} + /// This abstraction allows us to build up headers piece by piece without needing /// to precalculate things like the list of dependent headers or forward declarations #[derive(Default)] @@ -100,25 +101,6 @@ impl fmt::Write for Header { impl fmt::Display for Header { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut forwards = String::new(); - let mut includes = String::from(BASE_INCLUDES); - for i in &self.includes { - includes += &format!("#include \"{i}\"\n"); - } - let decl_header_include: Cow = match self.decl_include { - Some(ref v) => format!("\n#include \"{v}\"\n").into(), - None => "".into(), - }; - if !self.forwards.is_empty() { - forwards.push('\n'); - } - for f in self.forwards.iter() { - forwards += &match f { - Forward::Class(name) => format!("class {name};\n"), - Forward::Struct(name) => format!("struct {name};\n"), - Forward::EnumStruct(name) => format!("class {name};\n"), - }; - } let header_guard = &self.path; let header_guard = header_guard.replace(".d.hpp", "_D_HPP"); let header_guard = header_guard.replace(".hpp", "_HPP"); @@ -128,15 +110,22 @@ impl fmt::Display for Header { self.body.replace('\t', self.indent_str).into() }; - write!( - f, - r#"#ifndef {header_guard} -#define {header_guard} -{decl_header_include}{includes}{forwards} - -{body} -#endif // {header_guard} -"# - ) + HeaderTemplate { + header_guard: header_guard.into(), + decl_include: self + .decl_include + .as_ref() + .map(|s| Cow::Borrowed(s.as_str())), + includes: self + .includes + .iter() + .map(|s| Cow::Borrowed(s.as_str())) + .collect(), + forwards: self.forwards.iter().cloned().collect(), + body, + } + .render_into(f) + .unwrap(); + f.write_char('\n') } } diff --git a/tool/src/cpp2/ty.rs b/tool/src/cpp2/ty.rs index 7855b8651..605b6cf45 100644 --- a/tool/src/cpp2/ty.rs +++ b/tool/src/cpp2/ty.rs @@ -1,11 +1,12 @@ use super::header::{Forward, Header}; use super::Cpp2Context; +use super::Cpp2Formatter; +use askama::Template; use diplomat_core::hir::{ - self, Mutability, OpaqueOwner, ParamSelf, ReturnType, SelfType, SuccessType, TyPosition, Type, - TypeDef, TypeId, + self, Mutability, OpaqueOwner, ReturnType, SelfType, SuccessType, TyPosition, Type, TypeDef, + TypeId, }; use std::borrow::Cow; -use std::fmt::Write; impl<'tcx> super::Cpp2Context<'tcx> { pub fn gen_ty(&self, id: TypeId, ty: TypeDef<'tcx>) { @@ -64,12 +65,42 @@ struct NamedExpression<'a> { expression: Cow<'a, str>, } +/// An expression associated with a variable name having the given suffix. +struct PartiallyNamedExpression<'a> { + suffix: Cow<'a, str>, + expression: Cow<'a, str>, +} + /// A type name with a corresponding variable name, such as a struct field or a function parameter. struct NamedType<'a> { var_name: Cow<'a, str>, type_name: Cow<'a, str>, } +/// Everything needed for rendering a method. +struct MethodInfo<'a> { + /// HIR of the method being rendered + method: &'a hir::Method, + /// The C++ return type + return_ty: Cow<'a, str>, + /// The C++ method name + method_name: Cow<'a, str>, + /// The C method name + c_method_name: Cow<'a, str>, + /// Qualifiers for the function that come before the declaration (like "static") + pre_qualifiers: Vec>, + /// Qualifiers for the function that come after the declaration (like "const") + post_qualifiers: Vec>, + /// Type declarations for the C++ parameters + param_decls: Vec>, + /// C++ conversion code for each parameter of the C function + cpp_to_c_params: Vec>, + /// If the function has a return value, the C++ code for the conversion. Assumes that + /// the C function return value is saved to a variable named `result` or that the + /// writeable, if present, is saved to a variable named `output`. + c_to_cpp_return_expression: Option>, +} + /// Context for generating a particular type's header pub struct TyGenContext<'ccx, 'tcx, 'header> { pub cx: &'ccx Cpp2Context<'tcx>, @@ -87,233 +118,200 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { pub fn gen_enum_def(&mut self, ty: &'tcx hir::EnumDef, id: TypeId) { let type_name = self.cx.formatter.fmt_type_name(id); let ctype = self.cx.formatter.fmt_c_name(&type_name); - self.decl_header - .includes - .insert(self.cx.formatter.fmt_c_decl_header_path(id)); - write!( - self.decl_header, - "class {type_name} {{ -\t{ctype} value; - -public: -\tenum Value {{ -" - ) - .unwrap(); - write!( - self.impl_header, - "inline {type_name}::{type_name}({type_name}::Value cpp_value) {{ -\tswitch (cpp_value) {{ -" - ) - .unwrap(); - for variant in ty.variants.iter() { - let enum_variant = self.cx.formatter.fmt_enum_variant(variant); - let c_enum_variant = self.cx.formatter.fmt_c_enum_variant(&type_name, variant); - writeln!(self.decl_header, "\t\t{enum_variant},").unwrap(); - write!( - self.impl_header, - "\t\tcase {enum_variant}: -\t\t\tvalue = {c_enum_variant}; -\t\t\tbreak; -" - ) - .unwrap(); + + let methods = ty + .methods + .iter() + .flat_map(|method| self.gen_method_info(id, method)) + .collect::>(); + + #[derive(Template)] + #[template(path = "cpp2/enum_decl.h.jinja", escape = "none")] + struct DeclTemplate<'a> { + ty: &'a hir::EnumDef, + fmt: &'a Cpp2Formatter<'a>, + type_name: &'a str, + ctype: &'a str, + methods: &'a [MethodInfo<'a>], } - write!( - self.decl_header, - "\t}}; - -\tinline {type_name}({type_name}::Value cpp_value); -\tinline {type_name}({ctype} c_enum) : value(c_enum) {{}}; -" - ) - .unwrap(); - write!( - self.impl_header, - "\t\tdefault: -\t\t\tabort(); -\t}} -}} -" - ) - .unwrap(); - for method in ty.methods.iter() { - self.gen_method(id, method); + + DeclTemplate { + ty, + fmt: &self.cx.formatter, + type_name: &type_name, + ctype: &ctype, + methods: methods.as_slice(), } - write!( - self.decl_header, - " -\tinline {ctype} AsFFI() const; -\tinline static {type_name} FromFFI({ctype} c_enum); -}};\n\n" - ) + .render_into(self.decl_header) .unwrap(); - write!( - self.impl_header, - " -inline {ctype} {type_name}::AsFFI() const {{ -\treturn value; -}} - -inline {type_name} {type_name}::FromFFI({ctype} c_enum) {{ -\treturn {type_name}(c_enum); -}} -" - ) + + #[derive(Template)] + #[template(path = "cpp2/enum_impl.h.jinja", escape = "none")] + struct ImplTemplate<'a> { + ty: &'a hir::EnumDef, + fmt: &'a Cpp2Formatter<'a>, + type_name: &'a str, + ctype: &'a str, + methods: &'a [MethodInfo<'a>], + } + + ImplTemplate { + ty, + fmt: &self.cx.formatter, + type_name: &type_name, + ctype: &ctype, + methods: methods.as_slice(), + } + .render_into(self.impl_header) .unwrap(); + + self.decl_header + .includes + .insert(self.cx.formatter.fmt_c_decl_header_path(id)); } pub fn gen_opaque_def(&mut self, ty: &'tcx hir::OpaqueDef, id: TypeId) { let type_name = self.cx.formatter.fmt_type_name(id); - let const_ptr = self - .cx - .formatter - .fmt_c_ptr(&type_name, Mutability::Immutable); - let mut_ptr = self.cx.formatter.fmt_c_ptr(&type_name, Mutability::Mutable); let ctype = self.cx.formatter.fmt_c_name(&type_name); - let const_cptr = self.cx.formatter.fmt_c_ptr(&ctype, Mutability::Immutable); - let mut_cptr = self.cx.formatter.fmt_c_ptr(&ctype, Mutability::Mutable); - let const_ref = self - .cx - .formatter - .fmt_borrowed(&type_name, Mutability::Immutable); - let move_ref = self.cx.formatter.fmt_move_ref(&type_name); - self.decl_header - .includes - .insert(self.cx.formatter.fmt_c_decl_header_path(id)); - write!( - self.decl_header, - "class {type_name} {{ -public: -" - ) - .unwrap(); - for method in ty.methods.iter() { - self.gen_method(id, method); + + let methods = ty + .methods + .iter() + .flat_map(|method| self.gen_method_info(id, method)) + .collect::>(); + + #[derive(Template)] + #[template(path = "cpp2/opaque_decl.h.jinja", escape = "none")] + struct DeclTemplate<'a> { + // ty: &'a hir::OpaqueDef, + fmt: &'a Cpp2Formatter<'a>, + type_name: &'a str, + ctype: &'a str, + methods: &'a [MethodInfo<'a>], } - write!( - self.decl_header, - " -\tinline {const_cptr} AsFFI() const; -\tinline {mut_cptr} AsFFI(); -\tinline static {const_ptr} FromFFI({const_cptr} ptr); -\tinline static {mut_ptr} FromFFI({mut_cptr} ptr); -\tinline static void operator delete(void* ptr); -private: -\t{type_name}() = delete; -\t{type_name}({const_ref}) = delete; -\t{type_name}({move_ref}) noexcept = delete; -\t{type_name} operator=({const_ref}) = delete; -\t{type_name} operator=({move_ref}) noexcept = delete; -\tstatic void operator delete[](void*, size_t) = delete; -}}; - -" - ) + + DeclTemplate { + // ty, + fmt: &self.cx.formatter, + type_name: &type_name, + ctype: &ctype, + methods: methods.as_slice(), + } + .render_into(self.decl_header) .unwrap(); - write!( - self.impl_header, - "inline {const_cptr} {type_name}::AsFFI() const {{ -\treturn reinterpret_cast<{const_cptr}>(this); -}} - -inline {mut_cptr} {type_name}::AsFFI() {{ -\treturn reinterpret_cast<{mut_cptr}>(this); -}} - -inline {const_ptr} {type_name}::FromFFI({const_cptr} ptr) {{ -\treturn reinterpret_cast<{const_ptr}>(ptr); -}} - -inline {mut_ptr} {type_name}::FromFFI({mut_cptr} ptr) {{ -\treturn reinterpret_cast<{mut_ptr}>(ptr); -}} - -inline void {type_name}::operator delete(void* ptr) {{ -\t{ctype}_destroy(reinterpret_cast<{mut_cptr}>(ptr)); -}} - -" - ) + + #[derive(Template)] + #[template(path = "cpp2/opaque_impl.h.jinja", escape = "none")] + struct ImplTemplate<'a> { + // ty: &'a hir::OpaqueDef, + fmt: &'a Cpp2Formatter<'a>, + type_name: &'a str, + ctype: &'a str, + methods: &'a [MethodInfo<'a>], + } + + ImplTemplate { + // ty, + fmt: &self.cx.formatter, + type_name: &type_name, + ctype: &ctype, + methods: methods.as_slice(), + } + .render_into(self.impl_header) .unwrap(); + + self.decl_header + .includes + .insert(self.cx.formatter.fmt_c_decl_header_path(id)); } pub fn gen_struct_def(&mut self, def: &'tcx hir::StructDef

, id: TypeId) { let type_name = self.cx.formatter.fmt_type_name(id); let ctype = self.cx.formatter.fmt_c_name(&type_name); - writeln!(self.decl_header, "struct {type_name} {{").unwrap(); - for field in def.fields.iter() { - let NamedType { - var_name, - type_name, - } = self.gen_ty_decl(&field.ty, field.name.as_str()); - writeln!(self.decl_header, "\t{type_name} {var_name};").unwrap(); - } - for method in def.methods.iter() { - self.gen_method(id, method); + + let field_decls = def + .fields + .iter() + .map(|field| self.gen_ty_decl(&field.ty, field.name.as_str())) + .collect::>(); + + let cpp_to_c_fields = def + .fields + .iter() + .flat_map(|field| self.gen_cpp_to_c_for_field("", field)) + .collect::>(); + + let c_to_cpp_fields = def + .fields + .iter() + .map(|field| self.gen_c_to_cpp_for_field("c_struct.", field)) + .collect::>(); + + let methods = def + .methods + .iter() + .flat_map(|method| self.gen_method_info(id, method)) + .collect::>(); + + #[derive(Template)] + #[template(path = "cpp2/struct_decl.h.jinja", escape = "none")] + struct DeclTemplate<'a> { + // ty: &'a hir::OpaqueDef, + // fmt: &'a Cpp2Formatter<'a>, + type_name: &'a str, + ctype: &'a str, + fields: &'a [NamedType<'a>], + methods: &'a [MethodInfo<'a>], } - write!( - self.decl_header, - " -\tinline {ctype} AsFFI() const; -\tinline static {type_name} FromFFI({ctype} c_struct); -}};\n\n" - ) - .unwrap(); - write!( - self.impl_header, - " -inline {ctype} {type_name}::AsFFI() const {{ -\treturn {ctype} {{ -" - ) - .unwrap(); - for field in def.fields.iter() { - let param_name = self.cx.formatter.fmt_param_name(field.name.as_str()); - for NamedExpression { - var_name, - expression, - } in self.gen_cpp_to_c_for_type(&field.ty, ¶m_name) - { - writeln!(self.impl_header, "\t\t.{var_name} = {expression},").unwrap(); - } + + DeclTemplate { + // ty, + // fmt: &self.cx.formatter, + type_name: &type_name, + ctype: &ctype, + fields: field_decls.as_slice(), + methods: methods.as_slice(), } - write!( - self.impl_header, - "\t}}; -}} - -inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ -\treturn {type_name} {{ -" - ) + .render_into(self.decl_header) .unwrap(); - for field in def.fields.iter() { - let param_name = self.cx.formatter.fmt_param_name(field.name.as_str()); - let field_getter = format!("c_struct.{param_name}"); - let conversion = self.gen_c_to_cpp_for_type(&field.ty, &field_getter); - writeln!(self.impl_header, "\t\t.{param_name} = {conversion},").unwrap(); + + #[derive(Template)] + #[template(path = "cpp2/struct_impl.h.jinja", escape = "none")] + struct ImplTemplate<'a> { + // ty: &'a hir::OpaqueDef, + // fmt: &'a Cpp2Formatter<'a>, + type_name: &'a str, + ctype: &'a str, + cpp_to_c_fields: &'a [NamedExpression<'a>], + c_to_cpp_fields: &'a [NamedExpression<'a>], + methods: &'a [MethodInfo<'a>], } - write!( - self.impl_header, - "\t}}; -}} -" - ) + ImplTemplate { + // ty, + // fmt: &self.cx.formatter, + type_name: &type_name, + ctype: &ctype, + cpp_to_c_fields: cpp_to_c_fields.as_slice(), + c_to_cpp_fields: c_to_cpp_fields.as_slice(), + methods: methods.as_slice(), + } + .render_into(self.impl_header) .unwrap(); } - pub fn gen_method(&mut self, id: TypeId, method: &'tcx hir::Method) { + fn gen_method_info( + &mut self, + id: TypeId, + method: &'tcx hir::Method, + ) -> Option> { if method.attrs.disable { - return; + return None; } let _guard = self.cx.errors.set_context_method( self.cx.formatter.fmt_type_name_diagnostics(id), method.name.as_str().into(), ); - let type_name = self.cx.formatter.fmt_type_name(id); let method_name = self.cx.formatter.fmt_method_name(method); let c_method_name = self.cx.formatter.fmt_c_method_name(id, method); let mut param_decls = Vec::new(); @@ -326,11 +324,11 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ for param in method.params.iter() { let decls = self.gen_ty_decl(¶m.ty, param.name.as_str()); param_decls.push(decls); - let conversions = self.gen_cpp_to_c_for_type(¶m.ty, param.name.as_str()); + let conversions = self.gen_cpp_to_c_for_type(¶m.ty, param.name.as_str().into()); cpp_to_c_params.extend( conversions .into_iter() - .map(|named_expression| named_expression.expression), + .map(|PartiallyNamedExpression { expression, .. }| expression), ); } @@ -340,84 +338,32 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ let return_ty = self.gen_cpp_return_type_name(&method.output); - let return_statement: Cow = self - .gen_c_to_cpp_for_return_type(&method.output, "result") - .map(|s| format!("\n\treturn {s};").into()) - .unwrap_or_else(|| "".into()); - - let return_prefix = if method.output.returns_value() { - "auto result = " - } else { - "" - }; - - let mut params = String::new(); - let mut first = true; - for NamedType { - var_name, - type_name, - } in param_decls - { - let comma = if first { - first = false; - "" - } else { - ", " - }; - write!(&mut params, "{comma}{type_name} {var_name}").unwrap(); - } - - let mut c_params = String::new(); - let mut first = true; - for conversion in cpp_to_c_params { - let comma = if first { - first = false; - "" - } else { - ",\n\t\t" - }; - write!(&mut c_params, "{comma}{conversion}").unwrap(); - } + let c_to_cpp_return_expression: Option> = + self.gen_c_to_cpp_for_return_type(&method.output, "result".into()); - let writeable_prefix = if method.is_writeable() { - "std::string output; -\tcapi::DiplomatWriteable writeable = diplomat::WriteableFromString(output); -\t" + let pre_qualifiers = if method.param_self.is_none() { + vec!["static".into()] } else { - "" + vec![] }; - let maybe_static = if method.param_self.is_none() { - "static " - } else { - "" + let post_qualifiers = match &method.param_self { + Some(param_self) if param_self.ty.is_immutably_borrowed() => vec!["const".into()], + Some(_) => vec![], + None => vec![], }; - let qualifiers = match &method.param_self { - Some(ParamSelf { - ty: SelfType::Opaque(opaque_path), - }) if opaque_path.owner.mutability == Mutability::Immutable => " const", - Some(_) => "", - None => "", - }; - - write!( - self.decl_header, - " -\tinline {maybe_static}{return_ty} {method_name}({params}){qualifiers}; -" - ) - .unwrap(); - - write!( - self.impl_header, - "inline {return_ty} {type_name}::{method_name}({params}){qualifiers} {{ -\t{writeable_prefix}{return_prefix}{c_method_name}({c_params});{return_statement} -}} - -" - ) - .unwrap(); + Some(MethodInfo { + method, + return_ty, + method_name, + c_method_name, + pre_qualifiers, + post_qualifiers, + param_decls, + cpp_to_c_params, + c_to_cpp_return_expression, + }) } /// Generates C++ code for referencing a particular type with a given name. @@ -425,12 +371,12 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ where 'ccx: 'a, { - let param_name = self.cx.formatter.fmt_param_name(var_name); - let ty = self.gen_type_name(ty); + let var_name = self.cx.formatter.fmt_param_name(var_name); + let type_name = self.gen_type_name(ty); NamedType { - var_name: param_name, - type_name: ty, + var_name, + type_name, } } @@ -520,74 +466,97 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ fn gen_cpp_to_c_self(&self, ty: &SelfType) -> Cow<'static, str> { match *ty { SelfType::Opaque(..) => "this->AsFFI()".into(), - SelfType::Struct(..) => todo!(), - SelfType::Enum(..) => todo!(), + SelfType::Struct(..) => "this->AsFFI()".into(), + SelfType::Enum(..) => "this->AsFFI()".into(), } } + /// Generates one or two C++ expressions that convert from a C++ field to the corresponding C field. + /// + /// Returns `NamedExpression`s whose `var_name` corresponds to the field of the C struct. + /// + /// `cpp_struct_access` should be code for referencing a field of the C++ struct. + fn gen_cpp_to_c_for_field<'a, P: TyPosition>( + &self, + cpp_struct_access: &str, + field: &'a hir::StructField

, + ) -> Vec> { + let var_name = self.cx.formatter.fmt_param_name(field.name.as_str()); + let field_getter = format!("{cpp_struct_access}{var_name}"); + self.gen_cpp_to_c_for_type(&field.ty, field_getter.into()) + .into_iter() + .map( + |PartiallyNamedExpression { suffix, expression }| NamedExpression { + var_name: format!("{var_name}{suffix}").into(), + expression, + }, + ) + .collect() + } + /// Generates one or two C++ expressions that convert from a C++ type to the corresponding C type. /// - /// If the type is a slice, this function assumes that `{var_name}_data` and `{var_name}_size` resolve - /// to valid expressions referencing the two different C variables for the pointer and the length. + /// Returns `PartiallyNamedExpression`s whose `suffix` is either empty, `_data`, or `_size` for + /// referencing fields of the C struct. fn gen_cpp_to_c_for_type<'a, P: TyPosition>( &self, ty: &Type

, - var_name: &'a str, - ) -> Vec> { + cpp_name: Cow<'a, str>, + ) -> Vec> { match *ty { Type::Primitive(..) => { - vec![NamedExpression { - var_name: var_name.into(), - expression: var_name.into(), + vec![PartiallyNamedExpression { + suffix: "".into(), + expression: cpp_name.clone(), }] } Type::Opaque(ref op) if op.is_optional() => { - vec![NamedExpression { - var_name: var_name.into(), - expression: format!("{var_name} ? {var_name}->AsFFI() : nullptr").into(), + vec![PartiallyNamedExpression { + suffix: "".into(), + expression: format!("{cpp_name} ? {cpp_name}->AsFFI() : nullptr").into(), }] } Type::Opaque(..) => { - vec![NamedExpression { - var_name: var_name.into(), - expression: format!("{var_name}.AsFFI()").into(), + vec![PartiallyNamedExpression { + suffix: "".into(), + expression: format!("{cpp_name}.AsFFI()").into(), }] } Type::Struct(..) => { - vec![NamedExpression { - var_name: var_name.into(), - expression: format!("{var_name}.AsFFI()").into(), + vec![PartiallyNamedExpression { + suffix: "".into(), + expression: format!("{cpp_name}.AsFFI()").into(), }] } Type::Enum(..) => { - vec![NamedExpression { - var_name: var_name.into(), - expression: format!("{var_name}.AsFFI()").into(), + vec![PartiallyNamedExpression { + suffix: "".into(), + expression: format!("{cpp_name}.AsFFI()").into(), }] } Type::Slice(hir::Slice::Str(..)) => { // TODO: This needs to change if an abstraction other than std::string_view is used vec![ - NamedExpression { - var_name: format!("{var_name}_data").into(), - expression: format!("{var_name}.data()").into(), + PartiallyNamedExpression { + suffix: "_data".into(), + expression: format!("{cpp_name}.data()").into(), }, - NamedExpression { - var_name: format!("{var_name}_size").into(), - expression: format!("{var_name}.size()").into(), + PartiallyNamedExpression { + suffix: "_size".into(), + expression: format!("{cpp_name}.size()").into(), }, ] } Type::Slice(hir::Slice::Primitive(..)) => { // TODO: This needs to change if an abstraction other than std::span is used vec![ - NamedExpression { - var_name: format!("{var_name}_data").into(), - expression: format!("{var_name}.data()").into(), + PartiallyNamedExpression { + suffix: "_data".into(), + expression: format!("{cpp_name}.data()").into(), }, - NamedExpression { - var_name: format!("{var_name}_size").into(), - expression: format!("{var_name}.size()").into(), + PartiallyNamedExpression { + suffix: "_size".into(), + expression: format!("{cpp_name}.size()").into(), }, ] } @@ -619,6 +588,23 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ } } + /// Generates a C++ expression that converts from a C field to the corresponding C++ field. + /// + /// `c_struct_access` should be code for referencing a field of the C struct. + fn gen_c_to_cpp_for_field<'a, P: TyPosition>( + &self, + c_struct_access: &str, + field: &'a hir::StructField

, + ) -> NamedExpression<'a> { + let var_name = self.cx.formatter.fmt_param_name(field.name.as_str()); + let field_getter = format!("{c_struct_access}{var_name}"); + let expression = self.gen_c_to_cpp_for_type(&field.ty, field_getter.into()); + NamedExpression { + var_name, + expression, + } + } + /// Generates a C++ expression that converts from a C type to the corresponding C++ type. /// /// If the type is a slice, this function assumes that `{var_name}_data` and `{var_name}_size` resolve @@ -626,10 +612,10 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ fn gen_c_to_cpp_for_type<'a, P: TyPosition>( &self, ty: &Type

, - var_name: &'a str, + var_name: Cow<'a, str>, ) -> Cow<'a, str> { match *ty { - Type::Primitive(..) => var_name.into(), + Type::Primitive(..) => var_name, Type::Opaque(ref op) if op.owner.is_owned() => { let id = op.tcx_id.into(); let type_name = self.cx.formatter.fmt_type_name(id); @@ -681,13 +667,10 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ /// Generates a C++ expression that converts from a C return type to the corresponding C++ return type. /// /// If the type is `Writeable`, this function assumes that there is a variable named `output` in scope. - /// - /// If the type is a slice, this function assumes that `{var_name}_data` and `{var_name}_size` resolve - /// to valid expressions referencing the two different C variables for the pointer and the length. fn gen_c_to_cpp_for_return_type<'a>( &mut self, result_ty: &ReturnType, - var_name: &'a str, + var_name: Cow<'a, str>, ) -> Option> { match *result_ty { ReturnType::Infallible(None) => None, @@ -708,13 +691,13 @@ inline {type_name} {type_name}::FromFFI({ctype} c_struct) {{ None => "std::monostate".into(), }; let ok_conversion = match ok { - // Note: the `output` variable is a string initialized in gen_method + // Note: the `output` variable is a string initialized in the template Some(SuccessType::Writeable) => "std::move(output)".into(), None => "".into(), - Some(SuccessType::OutType(o)) => self.gen_c_to_cpp_for_type(o, &ok_path), + Some(SuccessType::OutType(o)) => self.gen_c_to_cpp_for_type(o, ok_path.into()), }; let err_conversion = match err { - Some(o) => self.gen_c_to_cpp_for_type(o, &err_path), + Some(o) => self.gen_c_to_cpp_for_type(o, err_path.into()), None => "".into(), }; Some( diff --git a/tool/templates/cpp2/base.h.jinja b/tool/templates/cpp2/base.h.jinja new file mode 100644 index 000000000..399e2e018 --- /dev/null +++ b/tool/templates/cpp2/base.h.jinja @@ -0,0 +1,35 @@ +#ifndef {{ header_guard }} +#define {{ header_guard }} + +{%~ match decl_include %} + {% when Some with (include) ~%} + #include "{{ include }}" + {%~ when None %} +{%- endmatch ~%} + +#include +#include +#include +#include +#include +#include +#include "diplomat_runtime.hpp" +{%- for include in includes %} +#include "{{ include }}" +{%- endfor %} + +{%~ if forwards.len() > 0 %} +{%- for forward in forwards %} +{%- match forward %} + {%- when Forward::Class with (name) ~%} + class {{ name }}; + {%- when Forward::Struct with (name) ~%} + struct {{ name }}; + {%- when Forward::EnumStruct with (name) ~%} + class {{ name }}; +{%- endmatch %} +{%- endfor %} +{% endif %} + +{{ body }} +#endif // {{ header_guard }} diff --git a/tool/templates/cpp2/enum_decl.h.jinja b/tool/templates/cpp2/enum_decl.h.jinja new file mode 100644 index 000000000..3bb1be249 --- /dev/null +++ b/tool/templates/cpp2/enum_decl.h.jinja @@ -0,0 +1,23 @@ +class {{type_name}} { + {{ctype}} value; + +public: + enum Value { +{%- for enum_variant in ty.variants %} + {{fmt.fmt_enum_variant(enum_variant)}}, +{%- endfor %} + }; + +{%- for m in methods %} + + {% include "method_decl.h.jinja" %} +{%- endfor %} + + inline {{type_name}}({{type_name}}::Value cpp_value); + inline {{type_name}}({{ctype}} c_enum) : value(c_enum) {}; + + inline {{ctype}} AsFFI() const; + inline static {{type_name}} FromFFI({{ctype}} c_enum); +}; + + diff --git a/tool/templates/cpp2/enum_impl.h.jinja b/tool/templates/cpp2/enum_impl.h.jinja new file mode 100644 index 000000000..a08f8f4fe --- /dev/null +++ b/tool/templates/cpp2/enum_impl.h.jinja @@ -0,0 +1,26 @@ +inline {{type_name}}::{{type_name}}({{type_name}}::Value cpp_value) { + switch (cpp_value) { +{%- for enum_variant in ty.variants %} + case {{fmt.fmt_enum_variant(enum_variant)}}: + value = {{fmt.fmt_c_enum_variant(type_name, enum_variant)}}; + break; +{%- endfor %} + default: + abort(); + } +} + + +{%- for m in methods %} + +{% include "method_impl.h.jinja" %} +{%- endfor %} + +inline {{ctype}} {{type_name}}::AsFFI() const { + return value; +} + +inline {{type_name}} {{type_name}}::FromFFI({{ctype}} c_enum) { + return {{type_name}}(c_enum); +} + diff --git a/tool/templates/cpp2/method_decl.h.jinja b/tool/templates/cpp2/method_decl.h.jinja new file mode 100644 index 000000000..f185f2c66 --- /dev/null +++ b/tool/templates/cpp2/method_decl.h.jinja @@ -0,0 +1,11 @@ +inline {##} +{%- for qualifier in m.pre_qualifiers %}{{qualifier}} {% endfor -%} +{{ m.return_ty }} {##} +{{- m.method_name -}} +( + {%- for param in m.param_decls %} + {%- if !loop.first %}, {% endif -%} + {{ param.type_name }} {{ param.var_name }} + {%- endfor -%} +) +{%- for qualifier in m.post_qualifiers %} {{qualifier}}{% endfor %}; \ No newline at end of file diff --git a/tool/templates/cpp2/method_impl.h.jinja b/tool/templates/cpp2/method_impl.h.jinja new file mode 100644 index 000000000..10480ecd3 --- /dev/null +++ b/tool/templates/cpp2/method_impl.h.jinja @@ -0,0 +1,31 @@ +inline {##} +{{- m.return_ty }} {##} +{{- type_name }}::{{ m.method_name -}} +( + {%- for param in m.param_decls %} + {%- if !loop.first %}, {% endif -%} + {{ param.type_name }} {{ param.var_name }} + {%- endfor -%} +) {##} +{%- for qualifier in m.post_qualifiers %}{{qualifier}} {% endfor -%} +{ + {%- if m.method.is_writeable() %} + std::string output; + capi::DiplomatWriteable writeable = diplomat::WriteableFromString(output); + {%- endif %} + {% if m.method.output.returns_value() -%} + auto result = {##} + {%- endif -%} + {{ m.c_method_name }}( + {%- for param in m.cpp_to_c_params %} + {%- if !loop.first %}, + {% endif -%} + {{ param }} + {%- endfor -%} + ); + {%- match m.c_to_cpp_return_expression %} + {%- when Some with (statement) %} + return {{ statement }}; + {%- when None %} + {%- endmatch %} +} diff --git a/tool/templates/cpp2/opaque_decl.h.jinja b/tool/templates/cpp2/opaque_decl.h.jinja new file mode 100644 index 000000000..fbf7fc7f0 --- /dev/null +++ b/tool/templates/cpp2/opaque_decl.h.jinja @@ -0,0 +1,27 @@ +{% let const_ptr = fmt.fmt_c_ptr(type_name, Mutability::Immutable) -%} +{% let mut_ptr = fmt.fmt_c_ptr(type_name, Mutability::Mutable) -%} +{% let const_cptr = fmt.fmt_c_ptr(ctype, Mutability::Immutable) -%} +{% let mut_cptr = fmt.fmt_c_ptr(ctype, Mutability::Mutable) -%} +{% let const_ref = fmt.fmt_borrowed(type_name, Mutability::Immutable) -%} +{% let move_ref = fmt.fmt_move_ref(type_name) -%} + +class {{type_name}} { +public: +{% for m in methods %} + {% include "method_decl.h.jinja" %} +{% endfor %} + inline {{const_cptr}} AsFFI() const; + inline {{mut_cptr}} AsFFI(); + inline static {{const_ptr}} FromFFI({{const_cptr}} ptr); + inline static {{mut_ptr}} FromFFI({{mut_cptr}} ptr); + inline static void operator delete(void* ptr); +private: + {{type_name}}() = delete; + {{type_name}}({{const_ref}}) = delete; + {{type_name}}({{move_ref}}) noexcept = delete; + {{type_name}} operator=({{const_ref}}) = delete; + {{type_name}} operator=({{move_ref}}) noexcept = delete; + static void operator delete[](void*, size_t) = delete; +}; + + diff --git a/tool/templates/cpp2/opaque_impl.h.jinja b/tool/templates/cpp2/opaque_impl.h.jinja new file mode 100644 index 000000000..490ed695e --- /dev/null +++ b/tool/templates/cpp2/opaque_impl.h.jinja @@ -0,0 +1,33 @@ +{% let const_ptr = fmt.fmt_c_ptr(type_name, Mutability::Immutable) -%} +{% let mut_ptr = fmt.fmt_c_ptr(type_name, Mutability::Mutable) -%} +{% let const_cptr = fmt.fmt_c_ptr(ctype, Mutability::Immutable) -%} +{% let mut_cptr = fmt.fmt_c_ptr(ctype, Mutability::Mutable) -%} +{% let const_ref = fmt.fmt_borrowed(type_name, Mutability::Immutable) -%} +{% let move_ref = fmt.fmt_move_ref(type_name) -%} + +{% for m in methods -%} +{% include "method_impl.h.jinja" %} + +{% endfor -%} + +inline {{const_cptr}} {{type_name}}::AsFFI() const { + return reinterpret_cast<{{const_cptr}}>(this); +} + +inline {{mut_cptr}} {{type_name}}::AsFFI() { + return reinterpret_cast<{{mut_cptr}}>(this); +} + +inline {{const_ptr}} {{type_name}}::FromFFI({{const_cptr}} ptr) { + return reinterpret_cast<{{const_ptr}}>(ptr); +} + +inline {{mut_ptr}} {{type_name}}::FromFFI({{mut_cptr}} ptr) { + return reinterpret_cast<{{mut_ptr}}>(ptr); +} + +inline void {{type_name}}::operator delete(void* ptr) { + {{ctype}}_destroy(reinterpret_cast<{{mut_cptr}}>(ptr)); +} + + diff --git a/tool/templates/cpp2/struct_decl.h.jinja b/tool/templates/cpp2/struct_decl.h.jinja new file mode 100644 index 000000000..326cbd161 --- /dev/null +++ b/tool/templates/cpp2/struct_decl.h.jinja @@ -0,0 +1,12 @@ +struct {{type_name}} { +{%- for field in fields %} + {{field.type_name}} {{field.var_name}}; +{%- endfor %} +{% for m in methods %} + {% include "method_decl.h.jinja" %} +{% endfor %} + inline {{ctype}} AsFFI() const; + inline static {{type_name}} FromFFI({{ctype}} c_struct); +}; + + diff --git a/tool/templates/cpp2/struct_impl.h.jinja b/tool/templates/cpp2/struct_impl.h.jinja new file mode 100644 index 000000000..a6415ef38 --- /dev/null +++ b/tool/templates/cpp2/struct_impl.h.jinja @@ -0,0 +1,22 @@ +{% for m in methods -%} +{% include "method_impl.h.jinja" %} + +{% endfor ~%} + +inline {{ctype}} {{type_name}}::AsFFI() const { + return {{ctype}} { +{%- for field in cpp_to_c_fields %} + .{{field.var_name}} = {{field.expression}}, +{%- endfor %} + }; +} + +inline {{type_name}} {{type_name}}::FromFFI({{ctype}} c_struct) { + return {{type_name}} { +{%- for field in c_to_cpp_fields %} + .{{field.var_name}} = {{field.expression}}, +{%- endfor %} + }; +} + +