diff --git a/feature_tests/cpp/include/nested/ns/Nested.d.hpp b/feature_tests/cpp/include/nested/ns/Nested.d.hpp new file mode 100644 index 000000000..ad331baee --- /dev/null +++ b/feature_tests/cpp/include/nested/ns/Nested.d.hpp @@ -0,0 +1,38 @@ +#ifndef nested_ns_Nested_D_HPP +#define nested_ns_Nested_D_HPP + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns { +namespace capi { + struct Nested; +} // namespace capi +} // namespace + +namespace nested::ns { +class Nested { +public: + + inline const nested::ns::capi::Nested* AsFFI() const; + inline nested::ns::capi::Nested* AsFFI(); + inline static const nested::ns::Nested* FromFFI(const nested::ns::capi::Nested* ptr); + inline static nested::ns::Nested* FromFFI(nested::ns::capi::Nested* ptr); + inline static void operator delete(void* ptr); +private: + Nested() = delete; + Nested(const nested::ns::Nested&) = delete; + Nested(nested::ns::Nested&&) noexcept = delete; + Nested operator=(const nested::ns::Nested&) = delete; + Nested operator=(nested::ns::Nested&&) noexcept = delete; + static void operator delete[](void*, size_t) = delete; +}; + +} // namespace +#endif // nested_ns_Nested_D_HPP diff --git a/feature_tests/cpp/include/nested/ns/Nested.hpp b/feature_tests/cpp/include/nested/ns/Nested.hpp new file mode 100644 index 000000000..8bec22bc1 --- /dev/null +++ b/feature_tests/cpp/include/nested/ns/Nested.hpp @@ -0,0 +1,47 @@ +#ifndef nested_ns_Nested_HPP +#define nested_ns_Nested_HPP + +#include "Nested.d.hpp" + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns { +namespace capi { + extern "C" { + + + void namespace_Nested_destroy(Nested* self); + + } // extern "C" +} // namespace capi +} // namespace + +inline const nested::ns::capi::Nested* nested::ns::Nested::AsFFI() const { + return reinterpret_cast<const nested::ns::capi::Nested*>(this); +} + +inline nested::ns::capi::Nested* nested::ns::Nested::AsFFI() { + return reinterpret_cast<nested::ns::capi::Nested*>(this); +} + +inline const nested::ns::Nested* nested::ns::Nested::FromFFI(const nested::ns::capi::Nested* ptr) { + return reinterpret_cast<const nested::ns::Nested*>(ptr); +} + +inline nested::ns::Nested* nested::ns::Nested::FromFFI(nested::ns::capi::Nested* ptr) { + return reinterpret_cast<nested::ns::Nested*>(ptr); +} + +inline void nested::ns::Nested::operator delete(void* ptr) { + nested::ns::capi::namespace_Nested_destroy(reinterpret_cast<nested::ns::capi::Nested*>(ptr)); +} + + +#endif // nested_ns_Nested_HPP diff --git a/feature_tests/cpp/include/nested/ns2/Nested.d.hpp b/feature_tests/cpp/include/nested/ns2/Nested.d.hpp new file mode 100644 index 000000000..9c81a5e30 --- /dev/null +++ b/feature_tests/cpp/include/nested/ns2/Nested.d.hpp @@ -0,0 +1,38 @@ +#ifndef nested_ns2_Nested_D_HPP +#define nested_ns2_Nested_D_HPP + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns2 { +namespace capi { + struct Nested; +} // namespace capi +} // namespace + +namespace nested::ns2 { +class Nested { +public: + + inline const nested::ns2::capi::Nested* AsFFI() const; + inline nested::ns2::capi::Nested* AsFFI(); + inline static const nested::ns2::Nested* FromFFI(const nested::ns2::capi::Nested* ptr); + inline static nested::ns2::Nested* FromFFI(nested::ns2::capi::Nested* ptr); + inline static void operator delete(void* ptr); +private: + Nested() = delete; + Nested(const nested::ns2::Nested&) = delete; + Nested(nested::ns2::Nested&&) noexcept = delete; + Nested operator=(const nested::ns2::Nested&) = delete; + Nested operator=(nested::ns2::Nested&&) noexcept = delete; + static void operator delete[](void*, size_t) = delete; +}; + +} // namespace +#endif // nested_ns2_Nested_D_HPP diff --git a/feature_tests/cpp/include/nested/ns2/Nested.hpp b/feature_tests/cpp/include/nested/ns2/Nested.hpp new file mode 100644 index 000000000..b6ca0ccf9 --- /dev/null +++ b/feature_tests/cpp/include/nested/ns2/Nested.hpp @@ -0,0 +1,47 @@ +#ifndef nested_ns2_Nested_HPP +#define nested_ns2_Nested_HPP + +#include "Nested.d.hpp" + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns2 { +namespace capi { + extern "C" { + + + void namespace_Nested2_destroy(Nested* self); + + } // extern "C" +} // namespace capi +} // namespace + +inline const nested::ns2::capi::Nested* nested::ns2::Nested::AsFFI() const { + return reinterpret_cast<const nested::ns2::capi::Nested*>(this); +} + +inline nested::ns2::capi::Nested* nested::ns2::Nested::AsFFI() { + return reinterpret_cast<nested::ns2::capi::Nested*>(this); +} + +inline const nested::ns2::Nested* nested::ns2::Nested::FromFFI(const nested::ns2::capi::Nested* ptr) { + return reinterpret_cast<const nested::ns2::Nested*>(ptr); +} + +inline nested::ns2::Nested* nested::ns2::Nested::FromFFI(nested::ns2::capi::Nested* ptr) { + return reinterpret_cast<nested::ns2::Nested*>(ptr); +} + +inline void nested::ns2::Nested::operator delete(void* ptr) { + nested::ns2::capi::namespace_Nested2_destroy(reinterpret_cast<nested::ns2::capi::Nested*>(ptr)); +} + + +#endif // nested_ns2_Nested_HPP diff --git a/feature_tests/cpp/tests/attrs.cpp b/feature_tests/cpp/tests/attrs.cpp index 932b5d509..ad616579a 100644 --- a/feature_tests/cpp/tests/attrs.cpp +++ b/feature_tests/cpp/tests/attrs.cpp @@ -2,18 +2,21 @@ #include "../include/ns/AttrOpaque1Renamed.hpp" #include "../include/ns/RenamedAttrEnum.hpp" #include "../include/Unnamespaced.hpp" +#include "../include/nested/ns/Nested.hpp" +#include "../include/nested/ns2/Nested.hpp" #include "assert.hpp" -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ std::unique_ptr<ns::AttrOpaque1Renamed> r = ns::AttrOpaque1Renamed::totally_not_new(); simple_assert_eq("method should call", r->method_renamed(), 77); simple_assert_eq("method should call", r->abirenamed(), 123); // These C names should also resolve - void* renamed = (void*)ns::capi::renamed_on_abi_only; - std::cout<<"Renamed function at "<<renamed<<std::endl; - renamed = (void*)ns::capi::namespace_AttrOpaque1_method; - std::cout<<"Renamed function at "<<renamed<<std::endl; + void *renamed = (void *)ns::capi::renamed_on_abi_only; + std::cout << "Renamed function at " << renamed << std::endl; + renamed = (void *)ns::capi::namespace_AttrOpaque1_method; + std::cout << "Renamed function at " << renamed << std::endl; ns::RenamedAttrEnum e = ns::RenamedAttrEnum::A; diff --git a/feature_tests/src/attrs.rs b/feature_tests/src/attrs.rs index b0f565f32..89c0c8950 100644 --- a/feature_tests/src/attrs.rs +++ b/feature_tests/src/attrs.rs @@ -60,6 +60,16 @@ pub mod ffi { pub fn use_namespaced(&self, _n: &AttrOpaque1) {} } + #[diplomat::opaque] + #[diplomat::attr(auto, namespace = "nested::ns")] + #[diplomat::attr(not(kotlin), rename = "Nested")] + pub struct Nested; + + #[diplomat::opaque] + #[diplomat::attr(auto, namespace = "nested::ns2")] + #[diplomat::attr(not(kotlin), rename = "Nested")] + pub struct Nested2; + #[diplomat::opaque] #[diplomat::attr(not(supports = comparators), disable)] pub struct Comparable(u8); diff --git a/tool/src/cpp/formatter.rs b/tool/src/cpp/formatter.rs index 231a5e01a..1ac5a9fab 100644 --- a/tool/src/cpp/formatter.rs +++ b/tool/src/cpp/formatter.rs @@ -56,7 +56,7 @@ impl<'tcx> Cpp2Formatter<'tcx> { .rename .apply(resolved.name().as_str().into()); if let Some(ref ns) = resolved.attrs().namespace { - let ns = ns.replace("::", std::path::MAIN_SEPARATOR_STR); + let ns = ns.replace("::", "/"); format!("{ns}/{type_name}.d.hpp") } else { format!("{type_name}.d.hpp") @@ -71,7 +71,7 @@ impl<'tcx> Cpp2Formatter<'tcx> { .rename .apply(resolved.name().as_str().into()); if let Some(ref ns) = resolved.attrs().namespace { - let ns = ns.replace("::", std::path::MAIN_SEPARATOR_STR); + let ns = ns.replace("::", "/"); format!("{ns}/{type_name}.hpp") } else { format!("{type_name}.hpp") diff --git a/tool/src/cpp/header.rs b/tool/src/cpp/header.rs index d5cf5818b..04403b5b1 100644 --- a/tool/src/cpp/header.rs +++ b/tool/src/cpp/header.rs @@ -143,31 +143,7 @@ impl fmt::Display for Header { includes: self .includes .iter() - .map(|s| { - if let Some((ns, _)) = self.path.split_once('/') { - if let Some((other_ns, file)) = s.split_once('/') { - if ns == other_ns { - Cow::Borrowed(file) - } else { - Cow::Owned(format!( - "{}{s}", - "../".repeat( - s.chars().filter(|c| *c == '/' || *c == '\\').count() + 1 - ) - )) - } - } else { - Cow::Owned(format!( - "{}{s}", - "../".repeat( - ns.chars().filter(|c| *c == '/' || *c == '\\').count() + 1 - ) - )) - } - } else { - Cow::Borrowed(s.as_str()) - } - }) + .map(|s| get_relative_path(&self.path, s)) .collect(), forwards: &self.forwards, body, @@ -177,3 +153,61 @@ impl fmt::Display for Header { f.write_char('\n') } } + +// As rsplit_once, except the first of the tuple will include the delimiter pattern +fn rsplit_once_inclusive(str: &str, delim: char) -> Option<(&str, &str)> { + str.rfind(delim).map(|i| str.split_at(i + 1)) +} + +/// Returns the path to 'other' along with the # of steps back from 'root' required +/// +/// e.g get_nearest_root("/a/b/c/d.hpp", "a/b/z/d.hpp") -> "z/d.hpp", 2 +/// or get_nearest_root("a/b.hpp", "root.hpp") -> "root.hpp", 1 +fn get_relative_path<'a>(base: &'a str, path: &'a str) -> Cow<'a, str> { + let (mut base_ns, _) = rsplit_once_inclusive(base, '/').unwrap_or(("", base)); + let (mut path_ns, file) = rsplit_once_inclusive(path, '/').unwrap_or(("", path)); + + let mut matching_chars = 0; + // Consume and count the length of the matching section + loop { + let b = base_ns.split_once('/'); + let p = path_ns.split_once('/'); + if let (Some(b), Some(p)) = (b, p) { + if b.0 == p.0 { + base_ns = b.1; + path_ns = p.1; + matching_chars += b.0.len() + 1; // 1 for the consumed delimiter + continue; + } + } + break; + } + + // Base has run out without a mismatch, the relative path is a strict subset of path & can be borrowed + if base_ns.len() == 0 { + return path.split_at(matching_chars).1.into(); + } else { + let up_dirs = base_ns.matches('/').count(); + return ("../".repeat(up_dirs) + path_ns + file).into(); + } +} + +#[test] +fn test_get_relative_path() { + let a = "a/same.hpp"; + let b = "a/same2.hpp"; + assert_eq!(get_relative_path(a, b), "same2.hpp"); + + let a = "root.hpp"; + let b = "a/nested.hpp"; + assert_eq!(get_relative_path(a, b), "a/nested.hpp"); + + let a = "a/nested.hpp"; + let b = "root.hpp"; + assert_eq!(get_relative_path(a, b), "../root.hpp"); + + let a = "a/b/c/d.hpp"; + let b = "a/b/z/c/d.hpp"; + + assert_eq!(get_relative_path(a, b), "../z/c/d.hpp"); +}