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");
+}