Repository: dtolnay/cxx (language: rust)

Issue:
Move all function implementations to the bottom of cxx.h
Every class template in include/cxx.h currently has its member functions defined inside the class. For example:

https://github.com/dtolnay/cxx/blob/47ee894a66a4203ce2497eff1c4f4943c2d21a4c/include/cxx.h#L141-L144

I'd like for all this implementation code to move down to the bottom, keeping just declarations in each class that are easier to read off.

```cpp
  explicit Box(const T &val);
```

This requires some rearchitecting in [cxx/gen/include.rs](https://github.com/dtolnay/cxx/blob/47ee894a66a4203ce2497eff1c4f4943c2d21a4c/gen/include.rs) which currently expects to find a single `ifdef`-delimited section in the header corresponding to each public type. We'd need it to look for two, the class template with declarations and then a separate block for all the function implementations.


Discussion / hints:

Fix patch:
diff --git a/.clang-format b/.clang-format
index 181605d50..208599798 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,1 +1,2 @@
 AlwaysBreakTemplateDeclarations: true
+MaxEmptyLinesToKeep: 3
diff --git a/gen/src/include.rs b/gen/src/include.rs
index be0bd8eb6..0ed76ac16 100644
--- a/gen/src/include.rs
+++ b/gen/src/include.rs
@@ -1,21 +1,42 @@
+use crate::gen::out::OutFile;
 use std::fmt::{self, Display};
 
 pub static HEADER: &str = include_str!("include/cxx.h");
 
-pub fn get(guard: &str) -> &'static str {
+pub(super) fn write(out: &mut OutFile, needed: bool, guard: &str) {
     let ifndef = format!("#ifndef {}", guard);
+    let define = format!("#define {}", guard);
     let endif = format!("#endif // {}", guard);
-    let begin = find_line(&ifndef);
-    let end = find_line(&endif);
-    if let (Some(begin), Some(end)) = (begin, end) {
-        &HEADER[begin..end + endif.len()]
-    } else {
-        panic!("not found in cxx.h header: {}", guard)
+
+    let mut offset = 0;
+    loop {
+        let begin = find_line(offset, &ifndef);
+        let end = find_line(offset, &endif);
+        if let (Some(begin), Some(end)) = (begin, end) {
+            if !needed {
+                return;
+            }
+            out.next_section();
+            if offset == 0 {
+                writeln!(out, "{}", ifndef);
+                writeln!(out, "{}", define);
+            }
+            for line in HEADER[begin + ifndef.len()..end].trim().lines() {
+                if line != define && !line.trim_start().starts_with("//") {
+                    writeln!(out, "{}", line);
+                }
+            }
+            offset = end + endif.len();
+        } else if offset == 0 {
+            panic!("not found in cxx.h header: {}", guard)
+        } else {
+            writeln!(out, "{}", endif);
+            return;
+        }
     }
 }
 
-fn find_line(line: &str) -> Option<usize> {
-    let mut offset = 0;
+fn find_line(mut offset: usize, line: &str) -> Option<usize> {
     loop {
         offset += HEADER[offset..].find(line)?;
         let rest = &HEADER[offset + line.len()..];
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 12f2839c6..c06fce9f8 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -249,15 +249,15 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) {
         writeln!(out, "struct unsafe_bitcopy_t;");
     }
 
-    write_header_section(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING");
-    write_header_section(out, needs_rust_str, "CXXBRIDGE03_RUST_STR");
-    write_header_section(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE");
-    write_header_section(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX");
-    write_header_section(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC");
-    write_header_section(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN");
-    write_header_section(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR");
-    write_header_section(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE");
-    write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY");
+    include::write(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING");
+    include::write(out, needs_rust_str, "CXXBRIDGE03_RUST_STR");
+    include::write(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE");
+    include::write(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX");
+    include::write(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC");
+    include::write(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN");
+    include::write(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR");
+    include::write(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE");
+    include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY");
 
     if needs_manually_drop {
         out.next_section();
@@ -311,18 +311,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) {
     out.end_block("namespace rust");
 }
 
-fn write_header_section(out: &mut OutFile, needed: bool, section: &str) {
-    let section = include::get(section);
-    if needed {
-        out.next_section();
-        for line in section.lines() {
-            if !line.trim_start().starts_with("//") {
-                writeln!(out, "{}", line);
-            }
-        }
-    }
-}
-
 fn write_struct(out: &mut OutFile, strct: &Struct) {
     for line in strct.doc.to_string().lines() {
         writeln!(out, "//{}", line);
diff --git a/include/cxx.h b/include/cxx.h
index b56c14a9e..9f4fb4767 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -15,13 +15,7 @@
 namespace rust {
 inline namespace cxxbridge03 {
 
-#ifndef CXXBRIDGE03_RUST_BITCOPY
-#define CXXBRIDGE03_RUST_BITCOPY
-struct unsafe_bitcopy_t {
-  explicit unsafe_bitcopy_t() = default;
-};
-constexpr unsafe_bitcopy_t unsafe_bitcopy{};
-#endif // CXXBRIDGE03_RUST_BITCOPY
+struct unsafe_bitcopy_t;
 
 #ifndef CXXBRIDGE03_RUST_STRING
 #define CXXBRIDGE03_RUST_STRING
@@ -91,23 +85,18 @@ class Str final {
 #endif // CXXBRIDGE03_RUST_STR
 
 #ifndef CXXBRIDGE03_RUST_SLICE
-#define CXXBRIDGE03_RUST_SLICE
 template <typename T>
 class Slice final {
 public:
-  Slice() noexcept : repr(Repr{reinterpret_cast<const T *>(this), 0}) {}
-  Slice(const Slice<T> &) noexcept = default;
-
-  Slice(const T *s, size_t size) noexcept : repr(Repr{s, size}) {}
+  Slice() noexcept;
+  Slice(const Slice<T> &) noexcept;
+  Slice(const T *, size_t count) noexcept;
 
-  Slice &operator=(Slice<T> other) noexcept {
-    this->repr = other.repr;
-    return *this;
-  }
+  Slice &operator=(Slice<T>) noexcept;
 
-  const T *data() const noexcept { return this->repr.ptr; }
-  size_t size() const noexcept { return this->repr.len; }
-  size_t length() const noexcept { return this->repr.len; }
+  const T *data() const noexcept;
+  size_t size() const noexcept;
+  size_t length() const noexcept;
 
   // Repr is PRIVATE; must not be used other than by our generated code.
   //
@@ -118,8 +107,8 @@ class Slice final {
     const T *ptr;
     size_t len;
   };
-  Slice(Repr repr_) noexcept : repr(repr_) {}
-  explicit operator Repr() noexcept { return this->repr; }
+  Slice(Repr) noexcept;
+  explicit operator Repr() noexcept;
 
 private:
   Repr repr;
@@ -127,7 +116,6 @@ class Slice final {
 #endif // CXXBRIDGE03_RUST_SLICE
 
 #ifndef CXXBRIDGE03_RUST_BOX
-#define CXXBRIDGE03_RUST_BOX
 template <typename T>
 class Box final {
 public:
@@ -136,70 +124,32 @@ class Box final {
       typename std::add_pointer<typename std::add_const<T>::type>::type;
   using pointer = typename std::add_pointer<T>::type;
 
-  Box(const Box &other) : Box(*other) {}
-  Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; }
-  explicit Box(const T &val) {
-    this->uninit();
-    ::new (this->ptr) T(val);
-  }
-  explicit Box(T &&val) {
-    this->uninit();
-    ::new (this->ptr) T(std::move(val));
-  }
-  Box &operator=(const Box &other) {
-    if (this != &other) {
-      if (this->ptr) {
-        **this = *other;
-      } else {
-        this->uninit();
-        ::new (this->ptr) T(*other);
-      }
-    }
-    return *this;
-  }
-  Box &operator=(Box &&other) noexcept {
-    if (this->ptr) {
-      this->drop();
-    }
-    this->ptr = other.ptr;
-    other.ptr = nullptr;
-    return *this;
-  }
-  ~Box() noexcept {
-    if (this->ptr) {
-      this->drop();
-    }
-  }
+  Box(const Box &);
+  Box(Box &&) noexcept;
+  ~Box() noexcept;
 
-  const T *operator->() const noexcept { return this->ptr; }
-  const T &operator*() const noexcept { return *this->ptr; }
-  T *operator->() noexcept { return this->ptr; }
-  T &operator*() noexcept { return *this->ptr; }
+  explicit Box(const T &);
+  explicit Box(T &&);
+
+  Box &operator=(const Box &);
+  Box &operator=(Box &&) noexcept;
+
+  const T *operator->() const noexcept;
+  const T &operator*() const noexcept;
+  T *operator->() noexcept;
+  T &operator*() noexcept;
 
   template <typename... Fields>
-  static Box in_place(Fields &&... fields) {
-    Box box;
-    box.uninit();
-    ::new (box.ptr) T{std::forward<Fields>(fields)...};
-    return box;
-  }
+  static Box in_place(Fields &&...);
 
   // Important: requires that `raw` came from an into_raw call. Do not pass a
   // pointer from `new` or any other source.
-  static Box from_raw(T *raw) noexcept {
-    Box box;
-    box.ptr = raw;
-    return box;
-  }
+  static Box from_raw(T *) noexcept;
 
-  T *into_raw() noexcept {
-    T *raw = this->ptr;
-    this->ptr = nullptr;
-    return raw;
-  }
+  T *into_raw() noexcept;
 
 private:
-  Box() noexcept {}
+  Box() noexcept;
   void uninit() noexcept;
   void drop() noexcept;
   T *ptr;
@@ -207,30 +157,19 @@ class Box final {
 #endif // CXXBRIDGE03_RUST_BOX
 
 #ifndef CXXBRIDGE03_RUST_VEC
-#define CXXBRIDGE03_RUST_VEC
 template <typename T>
 class Vec final {
 public:
   using value_type = T;
 
   Vec() noexcept;
-  Vec(Vec &&other) noexcept {
-    this->repr = other.repr;
-    new (&other) Vec();
-  }
-  ~Vec() noexcept { this->drop(); }
+  Vec(Vec &&) noexcept;
+  ~Vec() noexcept;
 
-  Vec &operator=(Vec &&other) noexcept {
-    if (this != &other) {
-      this->drop();
-      this->repr = other.repr;
-      new (&other) Vec();
-    }
-    return *this;
-  }
+  Vec &operator=(Vec &&) noexcept;
 
   size_t size() const noexcept;
-  bool empty() const noexcept { return size() == 0; }
+  bool empty() const noexcept;
   const T *data() const noexcept;
 
   class const_iterator {
@@ -243,27 +182,12 @@ class Vec final {
         typename std::add_const<T>::type>::type;
     using iterator_category = std::forward_iterator_tag;
 
-    const T &operator*() const noexcept {
-      return *static_cast<const T *>(this->pos);
-    }
-    const T *operator->() const noexcept {
-      return static_cast<const T *>(this->pos);
-    }
-    const_iterator &operator++() noexcept {
-      this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
-      return *this;
-    }
-    const_iterator operator++(int) noexcept {
-      auto ret = const_iterator(*this);
-      this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
-      return ret;
-    }
-    bool operator==(const const_iterator &other) const noexcept {
-      return this->pos == other.pos;
-    }
-    bool operator!=(const const_iterator &other) const noexcept {
-      return this->pos != other.pos;
-    }
+    const T &operator*() const noexcept;
+    const T *operator->() const noexcept;
+    const_iterator &operator++() noexcept;
+    const_iterator operator++(int) noexcept;
+    bool operator==(const const_iterator &) const noexcept;
+    bool operator!=(const const_iterator &) const noexcept;
 
   private:
     friend class Vec;
@@ -271,20 +195,11 @@ class Vec final {
     size_t stride;
   };
 
-  const_iterator begin() const noexcept {
-    const_iterator it;
-    it.pos = this->data();
-    it.stride = this->stride();
-    return it;
-  }
-  const_iterator end() const noexcept {
-    const_iterator it = this->begin();
-    it.pos = static_cast<const uint8_t *>(it.pos) + it.stride * this->size();
-    return it;
-  }
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
 
   // Internal API only intended for the cxxbridge code generator.
-  Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {}
+  Vec(unsafe_bitcopy_t, const Vec &) noexcept;
 
 private:
   static size_t stride() noexcept;
@@ -353,6 +268,11 @@ using fn = Fn<Signature, Throws>;
 template <typename Signature>
 using try_fn = TryFn<Signature>;
 
+
+
+////////////////////////////////////////////////////////////////////////////////
+/// end public API, begin implementation details
+
 template <typename Ret, typename... Args, bool Throws>
 Ret Fn<Ret(Args...), Throws>::operator()(Args... args) const noexcept(!Throws) {
   return (*this->trampoline)(std::move(args)..., this->fn);
@@ -363,5 +283,238 @@ Fn<Ret(Args...), Throws> Fn<Ret(Args...), Throws>::operator*() const noexcept {
   return *this;
 }
 
+#ifndef CXXBRIDGE03_RUST_BITCOPY
+#define CXXBRIDGE03_RUST_BITCOPY
+struct unsafe_bitcopy_t {
+  explicit unsafe_bitcopy_t() = default;
+};
+
+constexpr unsafe_bitcopy_t unsafe_bitcopy{};
+#endif // CXXBRIDGE03_RUST_BITCOPY
+
+#ifndef CXXBRIDGE03_RUST_SLICE
+#define CXXBRIDGE03_RUST_SLICE
+template <typename T>
+Slice<T>::Slice() noexcept : repr(Repr{reinterpret_cast<const T *>(this), 0}) {}
+
+template <typename T>
+Slice<T>::Slice(const Slice<T> &) noexcept = default;
+
+template <typename T>
+Slice<T>::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {}
+
+template <typename T>
+Slice<T> &Slice<T>::operator=(Slice<T> other) noexcept {
+  this->repr = other.repr;
+  return *this;
+}
+
+template <typename T>
+const T *Slice<T>::data() const noexcept {
+  return this->repr.ptr;
+}
+
+template <typename T>
+size_t Slice<T>::size() const noexcept {
+  return this->repr.len;
+}
+
+template <typename T>
+size_t Slice<T>::length() const noexcept {
+  return this->repr.len;
+}
+
+template <typename T>
+Slice<T>::Slice(Repr repr_) noexcept : repr(repr_) {}
+
+template <typename T>
+Slice<T>::operator Repr() noexcept {
+  return this->repr;
+}
+#endif // CXXBRIDGE03_RUST_SLICE
+
+#ifndef CXXBRIDGE03_RUST_BOX
+#define CXXBRIDGE03_RUST_BOX
+template <typename T>
+Box<T>::Box(const Box &other) : Box(*other) {}
+
+template <typename T>
+Box<T>::Box(Box &&other) noexcept : ptr(other.ptr) {
+  other.ptr = nullptr;
+}
+
+template <typename T>
+Box<T>::Box(const T &val) {
+  this->uninit();
+  ::new (this->ptr) T(val);
+}
+
+template <typename T>
+Box<T>::Box(T &&val) {
+  this->uninit();
+  ::new (this->ptr) T(std::move(val));
+}
+
+template <typename T>
+Box<T>::~Box() noexcept {
+  if (this->ptr) {
+    this->drop();
+  }
+}
+
+template <typename T>
+Box<T> &Box<T>::operator=(const Box &other) {
+  if (this != &other) {
+    if (this->ptr) {
+      **this = *other;
+    } else {
+      this->uninit();
+      ::new (this->ptr) T(*other);
+    }
+  }
+  return *this;
+}
+
+template <typename T>
+Box<T> &Box<T>::operator=(Box &&other) noexcept {
+  if (this->ptr) {
+    this->drop();
+  }
+  this->ptr = other.ptr;
+  other.ptr = nullptr;
+  return *this;
+}
+
+template <typename T>
+const T *Box<T>::operator->() const noexcept {
+  return this->ptr;
+}
+
+template <typename T>
+const T &Box<T>::operator*() const noexcept {
+  return *this->ptr;
+}
+
+template <typename T>
+T *Box<T>::operator->() noexcept {
+  return this->ptr;
+}
+
+template <typename T>
+T &Box<T>::operator*() noexcept {
+  return *this->ptr;
+}
+
+template <typename T>
+template <typename... Fields>
+Box<T> Box<T>::in_place(Fields &&... fields) {
+  Box box;
+  box.uninit();
+  ::new (box.ptr) T{std::forward<Fields>(fields)...};
+  return box;
+}
+
+template <typename T>
+Box<T> Box<T>::from_raw(T *raw) noexcept {
+  Box box;
+  box.ptr = raw;
+  return box;
+}
+
+template <typename T>
+T *Box<T>::into_raw() noexcept {
+  T *raw = this->ptr;
+  this->ptr = nullptr;
+  return raw;
+}
+
+template <typename T>
+Box<T>::Box() noexcept {}
+#endif // CXXBRIDGE03_RUST_BOX
+
+#ifndef CXXBRIDGE03_RUST_VEC
+#define CXXBRIDGE03_RUST_VEC
+template <typename T>
+Vec<T>::Vec(Vec &&other) noexcept {
+  this->repr = other.repr;
+  new (&other) Vec();
+}
+
+template <typename T>
+Vec<T>::~Vec() noexcept {
+  this->drop();
+}
+
+template <typename T>
+Vec<T> &Vec<T>::operator=(Vec &&other) noexcept {
+  if (this != &other) {
+    this->drop();
+    this->repr = other.repr;
+    new (&other) Vec();
+  }
+  return *this;
+}
+
+template <typename T>
+bool Vec<T>::empty() const noexcept {
+  return size() == 0;
+}
+
+template <typename T>
+const T &Vec<T>::const_iterator::operator*() const noexcept {
+  return *static_cast<const T *>(this->pos);
+}
+
+template <typename T>
+const T *Vec<T>::const_iterator::operator->() const noexcept {
+  return static_cast<const T *>(this->pos);
+}
+
+template <typename T>
+typename Vec<T>::const_iterator &Vec<T>::const_iterator::operator++() noexcept {
+  this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
+  return *this;
+}
+
+template <typename T>
+typename Vec<T>::const_iterator
+Vec<T>::const_iterator::operator++(int) noexcept {
+  auto ret = const_iterator(*this);
+  this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
+  return ret;
+}
+
+template <typename T>
+bool Vec<T>::const_iterator::operator==(const const_iterator &other) const
+    noexcept {
+  return this->pos == other.pos;
+}
+
+template <typename T>
+bool Vec<T>::const_iterator::operator!=(const const_iterator &other) const
+    noexcept {
+  return this->pos != other.pos;
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::begin() const noexcept {
+  const_iterator it;
+  it.pos = this->data();
+  it.stride = this->stride();
+  return it;
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::end() const noexcept {
+  const_iterator it = this->begin();
+  it.pos = static_cast<const uint8_t *>(it.pos) + it.stride * this->size();
+  return it;
+}
+
+// Internal API only intended for the cxxbridge code generator.
+template <typename T>
+Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {}
+#endif // CXXBRIDGE03_RUST_VEC
+
 } // namespace cxxbridge03
 } // namespace rust


Test patch: