Squashed 'third_party/autocxx/' content from commit 629e8fa53
git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/examples/chromium-fake-render-frame-host/Cargo.toml b/examples/chromium-fake-render-frame-host/Cargo.toml
new file mode 100644
index 0000000..a961708
--- /dev/null
+++ b/examples/chromium-fake-render-frame-host/Cargo.toml
@@ -0,0 +1,21 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-chromium-fake-render-frame-host-example"
+version = "0.1.0"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+[dependencies]
+cxx = "1.0.54"
+autocxx = { path = "../..", version="0.22.0" }
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.22.0" }
+miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/chromium-fake-render-frame-host/build.rs b/examples/chromium-fake-render-frame-host/build.rs
new file mode 100644
index 0000000..b533af4
--- /dev/null
+++ b/examples/chromium-fake-render-frame-host/build.rs
@@ -0,0 +1,20 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+ let path = std::path::PathBuf::from("src");
+ let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
+ b.flag_if_supported("-std=c++17") // clang
+ .flag_if_supported("/std:c++17") // msvc
+ .file("src/fake-chromium-src.cc")
+ .compile("autocxx-fake-render-frame-host-example");
+ println!("cargo:rerun-if-changed=src/main.rs");
+ println!("cargo:rerun-if-changed=src/fake-chromium-src.cc");
+ println!("cargo:rerun-if-changed=src/fake-chromium-header.h");
+ Ok(())
+}
diff --git a/examples/chromium-fake-render-frame-host/src/fake-chromium-header.h b/examples/chromium-fake-render-frame-host/src/fake-chromium-header.h
new file mode 100644
index 0000000..8f1c532
--- /dev/null
+++ b/examples/chromium-fake-render-frame-host/src/fake-chromium-header.h
@@ -0,0 +1,78 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#pragma once
+#include <memory>
+#include <string>
+#include <vector>
+
+// This is supposed to be a _fairly_ faithful representation of a few
+// Chromium codebase APIs. Just enough that we can start to experiment
+// with ownership patterns.
+
+namespace content {
+
+class RenderFrameHost {
+public:
+ static RenderFrameHost *FromId(int process_id, int frame_id);
+ virtual int GetRoutingID() = 0;
+
+ /// Returns the assigned name of the frame, the name of the iframe tag
+ /// declaring it. For example, <iframe name="framename">[...]</iframe>. It is
+ /// quite possible for a frame to have no name, in which case GetFrameName
+ /// will return an empty string.
+ virtual std::string GetFrameName() = 0;
+ virtual ~RenderFrameHost() {}
+};
+
+class CreateParams {
+public:
+ CreateParams(const std::string &);
+ std::string main_frame_name_;
+};
+
+class WebContentsObserver;
+
+class WebContents {
+public:
+ static std::unique_ptr<WebContents> Create(const CreateParams ¶ms);
+
+ static WebContents *FromFrameTreeNodeId(int frame_tree_node_id);
+
+ // TODO - should not be in WebContents, just WebContentsImpl
+ virtual void AddObserver(WebContentsObserver *) {}
+ virtual void RemoveObserver(WebContentsObserver *) {}
+
+ virtual ~WebContents(){};
+
+ virtual const std::string &GetTitle() = 0;
+};
+
+class WebContentsObserver {
+public:
+ virtual void RenderFrameCreated(RenderFrameHost *) {}
+ virtual void RenderFrameDeleted(RenderFrameHost *) {}
+ virtual ~WebContentsObserver() {}
+};
+
+class WebContentsImpl : public WebContents {
+public:
+ void AddObserver(WebContentsObserver *);
+ void RemoveObserver(WebContentsObserver *);
+ const std::string &GetTitle();
+ WebContentsImpl(const CreateParams &);
+ void DeleteRFH();
+
+private:
+ std::string title_;
+ std::vector<WebContentsObserver *> observers_;
+ std::vector<std::unique_ptr<RenderFrameHost>> rfhs_;
+};
+} // namespace content
+
+void SimulateRendererShutdown(int frame_id);
\ No newline at end of file
diff --git a/examples/chromium-fake-render-frame-host/src/fake-chromium-src.cc b/examples/chromium-fake-render-frame-host/src/fake-chromium-src.cc
new file mode 100644
index 0000000..2f9a9d4
--- /dev/null
+++ b/examples/chromium-fake-render-frame-host/src/fake-chromium-src.cc
@@ -0,0 +1,81 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "fake-chromium-header.h"
+#include <map>
+#include <algorithm>
+
+using namespace content;
+
+// This is all appalling. None of this is real Chromium code.
+// It's just designed to be the bare minimum required
+// to knock together a quick Rust-side demo. In some future realities, all
+// this is replaced with real Chromium code.
+
+int latest_rfh_id = 0;
+std::map<int, RenderFrameHost *> render_frame_hosts;
+WebContentsImpl *the_only_web_contents; // for this daft demo
+
+CreateParams::CreateParams(const std::string &main_frame_name)
+ : main_frame_name_(main_frame_name) {}
+
+RenderFrameHost *RenderFrameHost::FromId(int, int frame_id) {
+ return render_frame_hosts.at(frame_id);
+}
+
+class RenderFrameHostImpl : public RenderFrameHost {
+public:
+ RenderFrameHostImpl(const std::string name, int routing_id)
+ : routing_id_(routing_id), name_(name) {}
+ virtual int GetRoutingID() { return routing_id_; }
+ virtual std::string GetFrameName() { return name_; }
+
+private:
+ int routing_id_;
+ std::string name_;
+};
+
+std::unique_ptr<WebContents> WebContents::Create(const CreateParams ¶ms) {
+ auto wc = std::make_unique<WebContentsImpl>(params);
+ the_only_web_contents = wc.get();
+ return wc;
+}
+
+WebContentsImpl::WebContentsImpl(const CreateParams ¶ms)
+ : title_(params.main_frame_name_) {
+ int id = latest_rfh_id++;
+ std::unique_ptr<RenderFrameHost> new_rfh(
+ new RenderFrameHostImpl(params.main_frame_name_, id));
+ render_frame_hosts.insert(
+ std::pair<int, RenderFrameHost *>(id, new_rfh.get()));
+ for (auto obs : observers_) {
+ obs->RenderFrameCreated(new_rfh.get());
+ }
+ rfhs_.push_back(std::move(new_rfh));
+}
+
+void WebContentsImpl::AddObserver(WebContentsObserver *observer) {
+ observers_.push_back(observer);
+}
+void WebContentsImpl::RemoveObserver(WebContentsObserver *observer) {
+ std::remove(std::begin(observers_), std::end(observers_), observer);
+}
+
+void WebContentsImpl::DeleteRFH() {
+ for (auto obs : observers_) {
+ obs->RenderFrameDeleted(rfhs_[0].get());
+ }
+ rfhs_.clear();
+}
+
+const std::string &WebContentsImpl::GetTitle() { return title_; }
+
+void SimulateRendererShutdown(int frame_id) {
+ render_frame_hosts.erase(frame_id);
+ the_only_web_contents->DeleteRFH();
+}
diff --git a/examples/chromium-fake-render-frame-host/src/main.rs b/examples/chromium-fake-render-frame-host/src/main.rs
new file mode 100644
index 0000000..d2504dc
--- /dev/null
+++ b/examples/chromium-fake-render-frame-host/src/main.rs
@@ -0,0 +1,73 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx::prelude::*;
+mod render_frame_host;
+use render_frame_host::RenderFrameHostForWebContents;
+use render_frame_host::RenderFrameHostHandle;
+
+include_cpp! {
+ #include "fake-chromium-header.h"
+ safety!(unsafe) // unsafety policy; see docs
+ generate!("content::WebContents")
+ generate!("content::RenderFrameHost")
+ generate!("content::CreateParams")
+ generate!("SimulateRendererShutdown")
+ subclass!("content::WebContentsObserver",RenderFrameHostForWebContents)
+}
+
+use ffi::ToCppString;
+
+fn main() {
+ // Create some fake toy WebContents.
+ let create_params = ffi::content::CreateParams::new(&"silly-frame".into_cpp()).within_unique_ptr();
+ let mut frame = ffi::content::WebContents::Create(&create_params);
+
+ // This object is a memory-safe handle to a RenderFrameHost.
+ // On creation, we pass it the WebContents, such that it can register
+ // to be informed of the destruction of the RenderFrameHost.
+ // It also happens to store a reference to that WebContents,
+ // so the compiler will prove that this RenderFrameHostHandle
+ // can't outlive the WebContents. That's nice. But currently
+ // it stores an exclusive (a.k.a. mutable) reference, and we may
+ // well want to relax that in future.
+ // (This relates to https://github.com/google/autocxx/issues/622)
+ let mut rfh_handle = RenderFrameHostHandle::from_id(c_int(3), c_int(0), frame.pin_mut());
+
+ // We can directly call methods on the RFH.
+ // (If this were a 'const' method, the `.pin_mut()` wouldn't be necessary).
+ let frame_name = rfh_handle.pin_mut().GetFrameName();
+ println!("Frame name is {}", frame_name.to_str().unwrap());
+
+ {
+ // We can also borrow the RFH and use Rust's borrow checker to ensure
+ // no other code can do so. This also gives us a chance to explicitly
+ // handle the case where the RFH was already destroyed, in case
+ // we want to do something smarter than panicking.
+ let mut rfh_borrowed = rfh_handle
+ .try_borrow_mut()
+ .expect("Oh! The RFH was already destroyed!");
+ // Nobody else can borrow it during this time...
+ // let mut rfh_borrowed_again = rfh_handle.try_borrow_mut().unwrap();
+ // Gives compile-time error "second mutable borrow occurs here..."
+ let frame_name = rfh_borrowed.pin_mut().GetFrameName();
+ println!("Frame name is {}", frame_name.to_str().unwrap());
+ let frame_name = rfh_borrowed.pin_mut().GetFrameName();
+ println!("Frame name is {}", frame_name.to_str().unwrap());
+
+ // Supposing we end up calling some code deep in the Chrome C++
+ // stack which destroys the RFH whilst it's still borrowed.
+ // That will result in a runtime panic...
+ // ffi::SimulateRendererShutdown(c_int(0)); // would panic
+ }
+
+ // But let's assume we've now returned to the event loop.
+ // None of the previous borrows still exist. It's perfectly OK to now
+ // delete the RFH.
+ ffi::SimulateRendererShutdown(c_int(0));
+}
diff --git a/examples/chromium-fake-render-frame-host/src/render_frame_host.rs b/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
new file mode 100644
index 0000000..9c2bf51
--- /dev/null
+++ b/examples/chromium-fake-render-frame-host/src/render_frame_host.rs
@@ -0,0 +1,242 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx::subclass::prelude::*;
+use autocxx::{c_int, PinMut};
+use std::cell::{Ref, RefCell, RefMut};
+use std::ops::Deref;
+use std::pin::Pin;
+use std::rc::Rc;
+
+use crate::ffi;
+
+/// A memory-safe handle to a C++ RenderFrameHost.
+///
+/// This is a toy, hypothetical, example.
+///
+/// Creation: in this sample, the only option is to use [`RenderFrameHostHandle::from_id`]
+/// which corresponds to the equivalent method in C++ `RenderFrameHost`. Unlike
+/// the C++ version, you must pass a WebContents so that Rust wrappers can listen for
+/// destruction events.
+///
+/// The returned handle is memory safe and can be used to access the methods
+/// of [`ffi::content::RenderFrameHost`]. To use such a method, you have three options:
+/// * If you believe there is no chance that the `RenderFrameHost` has been
+/// destroyed, and if the method is const, you can just go ahead and call methods
+/// on this object. As it implements [`std::ops::Deref`], that will just work -
+/// but your code will panic if the `RenderFrameHost` was already destroyed.
+/// * If the method is non-const, you'll have to call `.pin_mut().method()` instead
+// but otherwise this is functionally identical.
+/// * If you believe that there is a chance that the `RenderFrameHost` was already
+/// destroyed, use [`RenderFrameHostHandle::try_borrow`] or
+/// [`RenderFrameHostHandle::try_borrow_mut`]. This will return
+/// a guard object which guarantees the existence of the `RenderFrameHost`
+/// during its lifetime.
+///
+/// # Performance characteristics
+///
+/// The existence of this object registers an observer with the `WebContents`
+/// and deregisters it on destruction. That is, of course, an overhead, but
+/// that's necessary to keep track of liveness. (A more efficient
+/// implementation would use a single observer for multiple such handles - but
+/// this is a toy implementation).
+///
+/// In addition, each time you extract the value from this
+/// `RenderFrameHostHandle`, a liveness check is performed. This involves
+/// not just a null check but also some reference count manipulation.
+/// If you're going to access the `RenderFrameHost` multiple times, it's
+/// advised that you call [`RenderFrameHostHandle::try_borrow`] or
+/// [`RenderFrameHostHandle::try_borrow_mut`] and then use
+/// the result multiple times. The liveness check for the `RenderFrameHost`
+/// will be performed only once at runtime.
+///
+/// # Destruction of RenderFrameHosts while borrowed
+///
+/// If you have called [`RenderFrameHostHandle::try_borrow`] (or its mutable
+/// equivalent) and still have an outstanding borrow, any code path - via C++
+/// - which results it the destruction of the `RenderFrameHost` will result in
+/// a runtime panic.
+pub struct RenderFrameHostHandle<'wc> {
+ obs: Rc<RefCell<RenderFrameHostForWebContents>>,
+ web_contents: Pin<&'wc mut ffi::content::WebContents>,
+}
+
+impl<'wc> RenderFrameHostHandle<'wc> {
+ /// Create a memory-safe handle to a RenderFrameHost using its
+ /// process ID and frame ID.
+ pub fn from_id(
+ render_process_id: c_int,
+ render_frame_id: c_int,
+ mut web_contents: Pin<&'wc mut ffi::content::WebContents>,
+ ) -> Self {
+ // Instantiate our WebContentsObserver subclass.
+ let obs = RenderFrameHostForWebContents::new_rust_owned(RenderFrameHostForWebContents {
+ rfh: ffi::content::RenderFrameHost::FromId(render_process_id, render_frame_id),
+ cpp_peer: Default::default(),
+ });
+
+ // And now register it.
+ // This nasty line will go away when autocxx is a bit more sophisticated.
+ let superclass_ptr = cast_to_superclass(obs.as_ref().borrow_mut().peer_mut());
+
+ // But this will remain unsafe. cxx policy is that any raw pointer
+ // passed into a C++ function requires an unsafe {} block and that
+ // is sensible. We may of course provide an ergonomic Rust wrapper
+ // around WebContents which provides safe Rust equivalents
+ // (using references or similar rather than pointers) in which case
+ // this unsafe block would go away.
+ unsafe { web_contents.as_mut().AddObserver(superclass_ptr) };
+
+ Self { obs, web_contents }
+ }
+
+ /// Tries to return a mutable reference to the RenderFrameHost.
+ /// Because this requires `self` to be `&mut`, and that lifetime is
+ /// applied to the returned `RenderFrameHost`, the compiler will prevent
+ /// multiple such references existing in Rust at the same time.
+ /// This will return `None` if the RenderFrameHost were already destroyed.
+ pub fn try_borrow_mut<'a>(
+ &'a mut self,
+ ) -> Option<impl PinMut<ffi::content::RenderFrameHost> + 'a> {
+ let ref_mut = self.obs.as_ref().borrow_mut();
+ if ref_mut.rfh.is_null() {
+ None
+ } else {
+ Some(RenderFrameHostRefMut(ref_mut))
+ }
+ }
+
+ /// Tries to return a reference to the RenderFrameHost.
+ /// The compiler will prevent calls to this if anyone has an outstanding
+ /// mutable reference from [`RenderFrameHostHandle::try_borrow_mut`].
+ /// This will return `None` if the RenderFrameHost were already destroyed.
+ #[allow(dead_code)]
+ pub fn try_borrow<'a>(&'a self) -> Option<impl AsRef<ffi::content::RenderFrameHost> + 'a> {
+ let ref_non_mut = self.obs.as_ref().borrow();
+ if ref_non_mut.rfh.is_null() {
+ None
+ } else {
+ Some(RenderFrameHostRef(ref_non_mut))
+ }
+ }
+}
+
+impl<'wc> Drop for RenderFrameHostHandle<'wc> {
+ fn drop(&mut self) {
+ // Unregister our observer.
+ let superclass_ptr = cast_to_superclass(self.obs.as_ref().borrow_mut().peer_mut());
+ unsafe { self.web_contents.as_mut().RemoveObserver(superclass_ptr) };
+ }
+}
+
+impl<'wc> AsRef<ffi::content::RenderFrameHost> for RenderFrameHostHandle<'wc> {
+ fn as_ref(&self) -> &ffi::content::RenderFrameHost {
+ let ref_non_mut = self.obs.as_ref().borrow();
+ // Safety: the .rfh field is guaranteed to be a RenderFrameHost
+ // and we are observing its lifetime so it will be reset to null
+ // if destroyed.
+ unsafe { ref_non_mut.rfh.as_ref() }.expect("This RenderFrameHost was already destroyed")
+ }
+}
+
+impl<'wc> PinMut<ffi::content::RenderFrameHost> for RenderFrameHostHandle<'wc> {
+ fn pin_mut(&mut self) -> Pin<&mut ffi::content::RenderFrameHost> {
+ let ref_mut = self.obs.as_ref().borrow_mut();
+ // Safety: the .rfh field is guaranteed to be a RenderFrameHost
+ // and we are observing its lifetime so it will be reset to null
+ // if destroyed.
+ unsafe { ref_mut.rfh.as_mut().map(|p| Pin::new_unchecked(p)) }
+ .expect("This RenderFrameHost was already destroyed")
+ }
+}
+
+impl<'wc> Deref for RenderFrameHostHandle<'wc> {
+ type Target = ffi::content::RenderFrameHost;
+ fn deref(&self) -> &Self::Target {
+ self.as_ref()
+ }
+}
+
+#[doc(hidden)]
+struct RenderFrameHostRefMut<'a>(RefMut<'a, RenderFrameHostForWebContents>);
+
+#[doc(hidden)]
+struct RenderFrameHostRef<'a>(Ref<'a, RenderFrameHostForWebContents>);
+
+impl<'a> AsRef<ffi::content::RenderFrameHost> for RenderFrameHostRef<'a> {
+ fn as_ref(&self) -> &ffi::content::RenderFrameHost {
+ // Safety:
+ // Creation precondition is that self.0.rfh is not null
+ // and it can't be destroyed whilst this borrow exists.
+ unsafe { self.0.rfh.as_ref().unwrap() }
+ }
+}
+
+impl<'a> PinMut<ffi::content::RenderFrameHost> for RenderFrameHostRefMut<'a> {
+ fn pin_mut(&mut self) -> Pin<&mut ffi::content::RenderFrameHost> {
+ // Safety:
+ // Creation precondition is that self.0.rfh is not null
+ // and it can't be destroyed whilst this borrow exists.
+ unsafe { Pin::new_unchecked(self.0.rfh.as_mut().unwrap()) }
+ }
+}
+
+impl<'a> AsRef<ffi::content::RenderFrameHost> for RenderFrameHostRefMut<'a> {
+ fn as_ref(&self) -> &ffi::content::RenderFrameHost {
+ // Safety:
+ // Creation precondition is that self.0.rfh is not null
+ // and it can't be destroyed whilst this borrow exists.
+ unsafe { self.0.rfh.as_ref().unwrap() }
+ }
+}
+
+impl<'a> Deref for RenderFrameHostRef<'a> {
+ type Target = ffi::content::RenderFrameHost;
+ fn deref(&self) -> &Self::Target {
+ self.as_ref()
+ }
+}
+
+impl<'a> Deref for RenderFrameHostRefMut<'a> {
+ type Target = ffi::content::RenderFrameHost;
+ fn deref(&self) -> &Self::Target {
+ self.as_ref()
+ }
+}
+
+#[is_subclass(superclass("content::WebContentsObserver"))]
+#[doc(hidden)]
+pub struct RenderFrameHostForWebContents {
+ rfh: *mut ffi::content::RenderFrameHost,
+}
+
+impl ffi::content::WebContentsObserver_methods for RenderFrameHostForWebContents {
+ unsafe fn RenderFrameDeleted(&mut self, destroyed_rfh: *mut ffi::content::RenderFrameHost) {
+ if self.rfh == destroyed_rfh {
+ self.rfh = std::ptr::null_mut()
+ }
+ }
+}
+
+fn cast_to_superclass(
+ obs: Pin<&mut ffi::RenderFrameHostForWebContentsCpp>,
+) -> *mut ffi::content::WebContentsObserver {
+ // This horrid code will all go away once we implement
+ // https://github.com/google/autocxx/issues/592; safe wrappers will
+ // be automatically generated to allow upcasting to superclasses.
+ // NB this code is probably actually _wrong_ too meanwhile; we need to cast
+ // on the C++ side.
+ let subclass_obs_ptr =
+ unsafe { Pin::into_inner_unchecked(obs) } as *mut ffi::RenderFrameHostForWebContentsCpp;
+ unsafe {
+ std::mem::transmute::<
+ *mut ffi::RenderFrameHostForWebContentsCpp,
+ *mut ffi::content::WebContentsObserver,
+ >(subclass_obs_ptr)
+ }
+}
diff --git a/examples/llvm/Cargo.toml b/examples/llvm/Cargo.toml
new file mode 100644
index 0000000..d8806c8
--- /dev/null
+++ b/examples/llvm/Cargo.toml
@@ -0,0 +1,21 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-llvm-example"
+version = "0.1.0"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+[dependencies]
+cxx = "1.0.54"
+autocxx = { path = "../..", version="0.17.2" }
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.17.2" }
+miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/llvm/build.rs b/examples/llvm/build.rs
new file mode 100644
index 0000000..1bb82ce
--- /dev/null
+++ b/examples/llvm/build.rs
@@ -0,0 +1,23 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::path::PathBuf;
+fn main() -> miette::Result<()> {
+ let mut b = autocxx_build::Builder::new(
+ "src/lib.rs",
+ &[
+ PathBuf::from("/usr/include/llvm-13"),
+ PathBuf::from("/usr/include/llvm-c-13"),
+ ],
+ )
+ .build()?;
+
+ b.flag_if_supported("-std=c++14").compile("llvm");
+ println!("cargo:rerun-if-changed=src/lib.rs");
+ Ok(())
+}
diff --git a/examples/llvm/src/lib.rs b/examples/llvm/src/lib.rs
new file mode 100644
index 0000000..bd18880
--- /dev/null
+++ b/examples/llvm/src/lib.rs
@@ -0,0 +1,23 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx::prelude::*; // use all the main autocxx functions
+
+include_cpp! {
+ #include "llvm/Support/MemoryBuffer.h"
+ safety!(unsafe)
+ generate!("llvm::MemoryBuffer")
+}
+
+// Simply re-export the MemoryBuffer API to users of this library.
+// Generally speaking, the APIs generated by autocxx (or similar tools)
+// still contain C++-isms, and it's wise to consider making idiomatic
+// Rust wrappers instead of doing this.
+pub use ffi::llvm::MemoryBuffer;
+
+// It would be advisable to add tests here.
diff --git a/examples/non-trivial-type-on-stack/Cargo.toml b/examples/non-trivial-type-on-stack/Cargo.toml
new file mode 100644
index 0000000..ff227d4
--- /dev/null
+++ b/examples/non-trivial-type-on-stack/Cargo.toml
@@ -0,0 +1,21 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-non-trivial-type-on-stack-example"
+version = "0.1.0"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+[dependencies]
+cxx = "1.0.54"
+autocxx = { path = "../..", version="0.22.0" }
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.22.0" }
+miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/non-trivial-type-on-stack/build.rs b/examples/non-trivial-type-on-stack/build.rs
new file mode 100644
index 0000000..f83a228
--- /dev/null
+++ b/examples/non-trivial-type-on-stack/build.rs
@@ -0,0 +1,17 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+ let path = std::path::PathBuf::from("src");
+ let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
+ b.flag_if_supported("-std=c++14")
+ .compile("autocxx-non-trivial-type-on-stack-example");
+ println!("cargo:rerun-if-changed=src/main.rs");
+ println!("cargo:rerun-if-changed=src/cpp.h");
+ Ok(())
+}
diff --git a/examples/non-trivial-type-on-stack/src/cpp.h b/examples/non-trivial-type-on-stack/src/cpp.h
new file mode 100644
index 0000000..9cb6502
--- /dev/null
+++ b/examples/non-trivial-type-on-stack/src/cpp.h
@@ -0,0 +1,24 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#pragma once
+#include <iostream>
+
+class MessageBuffer {
+public:
+ // std::string is not a trivial type because in some STL implementations
+ // it may contain a self-referential pointer.
+ void add_blurb(std::string blurb) {
+ message += blurb;
+ }
+ std::string get() const {
+ return message;
+ }
+private:
+ std::string message;
+};
diff --git a/examples/non-trivial-type-on-stack/src/main.rs b/examples/non-trivial-type-on-stack/src/main.rs
new file mode 100644
index 0000000..32dcdd7
--- /dev/null
+++ b/examples/non-trivial-type-on-stack/src/main.rs
@@ -0,0 +1,30 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx::prelude::*;
+
+include_cpp! {
+ // C++ headers we want to include.
+ #include "cpp.h"
+ safety!(unsafe)
+ // A non-trivial C++ type
+ generate!("MessageBuffer")
+}
+
+fn main() {
+ // Put the non-trivial C++ type on the Rust stack.
+ moveit! { let mut msg = ffi::MessageBuffer::new(); }
+ // Call methods on it.
+ msg.as_mut().add_blurb("Hello");
+ msg.as_mut().add_blurb(" world!");
+
+ assert_eq!(
+ msg.get().as_ref().unwrap().to_string_lossy(),
+ "Hello world!"
+ );
+}
diff --git a/examples/pod/Cargo.toml b/examples/pod/Cargo.toml
new file mode 100644
index 0000000..71bd5a0
--- /dev/null
+++ b/examples/pod/Cargo.toml
@@ -0,0 +1,21 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-pod-example"
+version = "0.1.0"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+[dependencies]
+cxx = "1.0.54"
+autocxx = { path = "../..", version="0.22.0" }
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.22.0" }
+miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/pod/build.rs b/examples/pod/build.rs
new file mode 100644
index 0000000..a6bd95c
--- /dev/null
+++ b/examples/pod/build.rs
@@ -0,0 +1,17 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+ let path = std::path::PathBuf::from("src");
+ let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
+ b.flag_if_supported("-std=c++14")
+ .compile("autocxx-pod-example");
+ println!("cargo:rerun-if-changed=src/main.rs");
+ println!("cargo:rerun-if-changed=src/cpp.h");
+ Ok(())
+}
diff --git a/examples/pod/src/cpp.h b/examples/pod/src/cpp.h
new file mode 100644
index 0000000..94d1466
--- /dev/null
+++ b/examples/pod/src/cpp.h
@@ -0,0 +1,27 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#pragma once
+#include <iostream>
+
+struct Point {
+ int x;
+ int y;
+};
+
+class Rect {
+public:
+ Point top_left;
+ Point bottom_right;
+ int width() const { return bottom_right.x - top_left.x; }
+ int height() const { return bottom_right.y - top_left.y; }
+};
+
+inline void print_point(Point p) {
+ std::cout << "(" << p.x << ", " << p.y << ")\n";
+}
diff --git a/examples/pod/src/main.rs b/examples/pod/src/main.rs
new file mode 100644
index 0000000..7a8b659
--- /dev/null
+++ b/examples/pod/src/main.rs
@@ -0,0 +1,40 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx::prelude::*;
+
+include_cpp! {
+ // C++ headers we want to include.
+ #include "cpp.h"
+ // Safety policy. We are marking that this whole C++ inclusion is unsafe
+ // which means the functions themselves do not need to be marked
+ // as unsafe. Other policies are possible.
+ safety!(unsafe)
+ // What types and functions we want to generate
+ generate_pod!("Rect")
+ generate!("print_point")
+}
+
+use ffi::{Point, Rect};
+
+// A simple example dealing with plain-old-data types.
+
+fn main() {
+ let r = Rect {
+ top_left: Point { x: 3, y: 3 },
+ bottom_right: Point { x: 12, y: 15 },
+ };
+ // r.width() and r.height() return an autocxx::c_int
+ // which we need to unpackage. It is hoped that one day cxx will
+ // natively support 'int' and friends, and that won't be necessary.
+ let center = Point {
+ x: r.top_left.x + r.width().0 / 2,
+ y: r.top_left.y + r.height().0 / 2,
+ };
+ ffi::print_point(center);
+}
diff --git a/examples/s2/Cargo.toml b/examples/s2/Cargo.toml
new file mode 100644
index 0000000..994a381
--- /dev/null
+++ b/examples/s2/Cargo.toml
@@ -0,0 +1,23 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-s2-example"
+version = "0.1.0"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cxx = "1.0.54"
+autocxx = { path = "../..", version="0.22.0" }
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.22.0" }
+miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/s2/README.md b/examples/s2/README.md
new file mode 100644
index 0000000..36a56ca
--- /dev/null
+++ b/examples/s2/README.md
@@ -0,0 +1,5 @@
+To build this example:
+* `git submodule update --init --recursive s2geometry`
+* `cargo run`
+
+Thanks to @nside for inspiring this example.
diff --git a/examples/s2/build.rs b/examples/s2/build.rs
new file mode 100644
index 0000000..e61c1af
--- /dev/null
+++ b/examples/s2/build.rs
@@ -0,0 +1,20 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+ // It's necessary to use an absolute path here because the
+ // C++ codegen and the macro codegen appears to be run from different
+ // working directories.
+ let path = std::path::PathBuf::from("s2geometry/src");
+ let path2 = std::path::PathBuf::from("src");
+ let mut b = autocxx_build::Builder::new("src/main.rs", &[&path, &path2]).build()?;
+ b.flag_if_supported("-std=c++14")
+ .compile("autocxx-s2-example");
+ println!("cargo:rerun-if-changed=src/main.rs");
+ Ok(())
+}
diff --git a/examples/s2/s2geometry b/examples/s2/s2geometry
new file mode 160000
index 0000000..0c4c460
--- /dev/null
+++ b/examples/s2/s2geometry
@@ -0,0 +1 @@
+Subproject commit 0c4c460bdfe696da303641771f9def900b3e440f
diff --git a/examples/s2/src/extras.h b/examples/s2/src/extras.h
new file mode 100644
index 0000000..927a30d
--- /dev/null
+++ b/examples/s2/src/extras.h
@@ -0,0 +1,27 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "s2/r2.h"
+#include <sstream>
+
+// autocxx isn't yet smart enough to do anything with the R2Point
+// structure, so here we've manually made a cheeky little API to
+// do something useful with it.
+inline std::string describe_point(R2Point pt) {
+ std::ostringstream oss;
+ oss << pt.x() << ", " << pt.y();
+ return oss.str();
+}
diff --git a/examples/s2/src/main.rs b/examples/s2/src/main.rs
new file mode 100644
index 0000000..141b80e
--- /dev/null
+++ b/examples/s2/src/main.rs
@@ -0,0 +1,53 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx::prelude::*;
+
+include_cpp! {
+ // C++ headers we want to include.
+ #include "s2/r2rect.h"
+ #include "extras.h"
+ // Safety policy. We are marking that this whole C++ inclusion is unsafe
+ // which means the functions themselves do not need to be marked
+ // as unsafe. Other policies are possible.
+ safety!(unsafe)
+ // What types and functions we want to generate
+ generate!("R1Interval")
+ generate!("R2Rect")
+ generate!("describe_point")
+}
+
+// Everything that we care about is inlined, so we don't have to do
+// anything fancy to build or link any external code.
+fn main() {
+ // Create a couple of R1Intervals using their pre-existing C++
+ // constructors. Actually these will be cxx::UniquePtr<R1Interval>s.
+ let i1 = ffi::R1Interval::new(1.0f64, 2.0f64).within_unique_ptr();
+ let i2 = ffi::R1Interval::new(5.0f64, 6.0f64).within_unique_ptr();
+ // Create a rect, passing references to the intervals.
+ // Note this is 'new1' because R2Rect has multiple
+ // overloaded constructors. 'cargo expand', `cargo doc`
+ // or a rust-analyzer IDE is useful here.
+ let r = ffi::R2Rect::new1(&i1, &i2).within_unique_ptr();
+ // Call a method on one of these objects. As it happens,
+ // this returns a
+ // UniquePtr< ... opaque object representing a point ...>.
+ let center = r.GetCenter();
+ // As the object is too complex for autocxx to understand,
+ // we can't do much with it except to send it into other
+ // C++ APIs. We'll make our own which describes the point.
+ // This will return a std::string, which autocxx will
+ // convert to a UniquePtr<CxxString>. We can convert that
+ // back to a Rust string and print it, so long as we
+ // take care to decide how to deal with non-UTF8
+ // characters (hence the unwrap).
+ println!(
+ "Center of rectangle is {}",
+ ffi::describe_point(center).to_str().unwrap()
+ );
+}
diff --git a/examples/steam-mini/Cargo.toml b/examples/steam-mini/Cargo.toml
new file mode 100644
index 0000000..b292f25
--- /dev/null
+++ b/examples/steam-mini/Cargo.toml
@@ -0,0 +1,23 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-steam-example"
+version = "0.1.0"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cxx = "1.0.54"
+autocxx = { path = "../..", version="0.22.0" }
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.22.0" }
+miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/steam-mini/README.md b/examples/steam-mini/README.md
new file mode 100644
index 0000000..325c899
--- /dev/null
+++ b/examples/steam-mini/README.md
@@ -0,0 +1,2 @@
+This example is supposed to simulate something a bit like the Steam API,
+where you're given a pointer to a collection of virtual methods.
diff --git a/examples/steam-mini/build.rs b/examples/steam-mini/build.rs
new file mode 100644
index 0000000..d9b3c68
--- /dev/null
+++ b/examples/steam-mini/build.rs
@@ -0,0 +1,17 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+ let path = std::path::PathBuf::from("steam/src");
+ let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).build()?;
+ b.flag_if_supported("-std=c++14")
+ .file("steam/src/steam.cc")
+ .compile("autocxx-steam-example");
+ println!("cargo:rerun-if-changed=src/main.rs");
+ Ok(())
+}
diff --git a/examples/steam-mini/src/main.rs b/examples/steam-mini/src/main.rs
new file mode 100644
index 0000000..e5d6e64
--- /dev/null
+++ b/examples/steam-mini/src/main.rs
@@ -0,0 +1,65 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx::prelude::*;
+
+include_cpp! {
+ // C++ headers we want to include.
+ #include "steam.h"
+ // Safety policy. We are marking that this whole C++ inclusion is unsafe
+ // which means the functions themselves do not need to be marked
+ // as unsafe. Other policies are possible.
+ safety!(unsafe)
+ // What types and functions we want to generate
+ generate!("GetSteamEngine")
+ generate!("IEngine")
+}
+
+fn main() {
+ // The "Steam" API gives us a void* on which we call virtual functions.
+ // This is a void*.
+ let steam_engine = ffi::GetSteamEngine();
+ // We need to know three things about this void*:
+ // 1. What is it? We know from the (fake) Steam documentation that it's
+ // an IEngine*
+ // 2. Do we gain ownership of it? i.e. is it our responsibility to
+ // destroy it?
+ // 3. If not, C++ presumably continues to own it. Does C++ ever destroy
+ // it?
+ // None of these things are really encoded in the nature of a void*
+ // so you have to figure them out from the documentation.
+ //
+ // In this case, the first is easy:
+ let steam_engine = steam_engine as *mut ffi::IEngine;
+ //
+ // You then need to figure out how to expose it in Rust. Ideally, any
+ // such lifetime invariants would be handled by the compiler.
+ // If C++ is passing ownership of this object to us, and we have the
+ // prerogative to destroy it whenever we wish, then
+ // simply use [`cxx::UniquePtr::from_raw`]. If it goes out of scope in
+ // Rust the underlying C++ object will be deleted.
+ //
+ // Let's assume life is more complicated, and we must never destroy this
+ // object (because it's owned by C++). In that case, we ideally want
+ // to convert the pointer into a Rust reference with the lifetime of
+ // the program.
+ //
+ // We also have to promise to Rust that it'll never move in memory.
+ // C++ doesn't do that, so that's OK.
+ let mut steam_engine = unsafe { std::pin::Pin::new_unchecked(&mut *steam_engine) };
+ // Now we have steam_engine which is a Pin<&mut SteamEngine>
+ // Each time we call a method we need to add `as_mut()`
+ // as per the pattern explained in
+ // https://doc.rust-lang.org/std/pin/struct.Pin.html#method.as_mut
+ steam_engine
+ .as_mut()
+ .ConnectToGlobalUser(autocxx::c_int(12));
+ steam_engine
+ .as_mut()
+ .DisconnectGlobalUser(autocxx::c_int(12));
+}
diff --git a/examples/steam-mini/steam/src/steam.cc b/examples/steam-mini/steam/src/steam.cc
new file mode 100644
index 0000000..2c45488
--- /dev/null
+++ b/examples/steam-mini/steam/src/steam.cc
@@ -0,0 +1,28 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include <iostream>
+#include "steam.h"
+
+// This is a simulation of _something like_ the way the steam API works.
+// None of this code is really from Steam.
+
+class SteamEngine : public IEngine {
+ int ConnectToGlobalUser(int user_id) {
+ std::cout << "ConnectToGlobalUser, passed " << user_id << std::endl;
+ return 42;
+ }
+ void DisconnectGlobalUser(int user_id) {
+ std::cout << "DisconnectGlobalUser, passed " << user_id << std::endl;
+ }
+};
+
+void* GetSteamEngine() {
+ return new SteamEngine();
+}
+
diff --git a/examples/steam-mini/steam/src/steam.h b/examples/steam-mini/steam/src/steam.h
new file mode 100644
index 0000000..f07aee3
--- /dev/null
+++ b/examples/steam-mini/steam/src/steam.h
@@ -0,0 +1,19 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#pragma once
+
+// This is a simulation of _something like_ the way the steam API works.
+
+class IEngine {
+public:
+ virtual int ConnectToGlobalUser(int) = 0;
+ virtual void DisconnectGlobalUser(int user_id) = 0;
+};
+
+void* GetSteamEngine(); // return an IEngine*
diff --git a/examples/subclass/Cargo.toml b/examples/subclass/Cargo.toml
new file mode 100644
index 0000000..8a01205
--- /dev/null
+++ b/examples/subclass/Cargo.toml
@@ -0,0 +1,27 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-subclass-example"
+version = "0.1.0"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cxx = "1.0.54"
+autocxx = { path = "../..", version="0.22.0" }
+uwuify = "0.2.2"
+textwrap = "0.14"
+fastrand = "1.5.0"
+
+[build-dependencies]
+autocxx-build = { path = "../../gen/build", version="0.22.0" }
+regex = "1.5.4"
+miette = { version="4.3", features = [ "fancy" ] }
diff --git a/examples/subclass/README.md b/examples/subclass/README.md
new file mode 100644
index 0000000..b46d001
--- /dev/null
+++ b/examples/subclass/README.md
@@ -0,0 +1 @@
+This example shows Rust types 'inheriting' from C++ subclasses.
diff --git a/examples/subclass/build.rs b/examples/subclass/build.rs
new file mode 100644
index 0000000..b41e924
--- /dev/null
+++ b/examples/subclass/build.rs
@@ -0,0 +1,93 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() -> miette::Result<()> {
+ let path = std::path::PathBuf::from("src");
+ let mut b = autocxx_build::Builder::new("src/main.rs", &[&path])
+ .auto_allowlist(true)
+ .build()?;
+ b.flag_if_supported("-std=c++17")
+ .file("src/messages.cc")
+ .compile("autocxx-subclass-example");
+ println!("cargo:rerun-if-changed=src/main.rs");
+ println!("cargo:rerun-if-changed=src/messages.cc");
+ println!("cargo:rerun-if-changed=src/messages.h");
+
+ // The following line is *unrelated* to autocxx builds and is
+ // just designed to ensure that example code doesn't get out of sync
+ // from copies in comments.
+ ensure_comments_match_real_code(&std::path::PathBuf::from("src/main.rs"));
+ Ok(())
+}
+
+use std::fs::File;
+use std::io::BufRead;
+use std::io::BufReader;
+use std::io::Lines;
+use std::path::Path;
+
+enum CommentMatcherState {
+ Searching,
+ EatingBacktickLine(Lines<BufReader<File>>),
+ SearchingForFirstLine(Lines<BufReader<File>>),
+ Found(Lines<BufReader<File>>),
+}
+
+fn ensure_comments_match_real_code(rs_file: &Path) {
+ use regex::Regex;
+ let start_re = Regex::new(r"// .*from ([\w/]+\.\w+).*").unwrap();
+ let strip_comment_re = Regex::new(r"// (.*)").unwrap();
+ let file = File::open(rs_file).unwrap();
+ let lines = BufReader::new(file).lines();
+ let mut state = CommentMatcherState::Searching;
+ for line in lines {
+ let line = line.unwrap();
+ state = match state {
+ CommentMatcherState::Searching => match start_re.captures(&line) {
+ Some(captures) => {
+ let fname = captures.get(1).unwrap().as_str();
+ let srcfile = File::open(fname).unwrap();
+ let srclines = BufReader::new(srcfile).lines();
+ CommentMatcherState::EatingBacktickLine(srclines)
+ }
+ None => CommentMatcherState::Searching,
+ },
+ CommentMatcherState::EatingBacktickLine(srclines) => {
+ CommentMatcherState::SearchingForFirstLine(srclines)
+ }
+ CommentMatcherState::SearchingForFirstLine(mut srclines) => {
+ match strip_comment_re.captures(&line) {
+ Some(captures) => {
+ let mut found = false;
+ while !found {
+ let srcline = srclines.next().unwrap().unwrap();
+ found = captures.get(1).unwrap().as_str() == srcline;
+ }
+ CommentMatcherState::Found(srclines)
+ }
+ None => CommentMatcherState::Searching,
+ }
+ }
+ CommentMatcherState::Found(mut srclines) => {
+ if line == "// ```" {
+ CommentMatcherState::Searching
+ } else {
+ match strip_comment_re.captures(&line) {
+ Some(captures) => {
+ let actual = captures.get(1).unwrap().as_str();
+ let expected = srclines.next().unwrap().unwrap();
+ assert_eq!(expected, actual);
+ CommentMatcherState::Found(srclines)
+ }
+ None => CommentMatcherState::Searching,
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/subclass/src/billy.rs b/examples/subclass/src/billy.rs
new file mode 100644
index 0000000..5defe1b
--- /dev/null
+++ b/examples/subclass/src/billy.rs
@@ -0,0 +1,20 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub(crate) static SHAKESPEARE_QUOTES: [&str; 10] = [
+ "All that glitters is not gold",
+ "Hell is empty and all the devils are here.",
+ "Good night, good night! parting is such sweet sorrow, That I shall say good night till it be morrow.",
+ "These violent delights have violent ends...",
+ "Something is rotten in the state of Denmark.",
+ "Love all, trust a few, do wrong to none.",
+ "The lady doth protest too much, methinks.",
+ "Brevity is the soul of wit.",
+ "Uneasy lies the head that wears a crown.",
+ "Now is the winter of our discontent.",
+];
diff --git a/examples/subclass/src/main.rs b/examples/subclass/src/main.rs
new file mode 100644
index 0000000..2841598
--- /dev/null
+++ b/examples/subclass/src/main.rs
@@ -0,0 +1,145 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This example shows some Rust subclasses of C++ classes.
+
+mod billy;
+mod uwu;
+
+use autocxx::prelude::*;
+use autocxx::subclass::prelude::*;
+use cxx::CxxString;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+include_cpp! {
+ #include "messages.h"
+ safety!(unsafe) // unsafety policy; see docs
+}
+
+// Here's the definition of MessageDisplayer from src/messages.h:
+// ```cpp
+// class MessageDisplayer {
+// public:
+// virtual void display_message(const std::string& message) const = 0;
+// virtual ~MessageDisplayer() {};
+// };
+// ```
+// The following lines define a subclass of MessageDisplayer.
+// See the main function at the bottom for how this subclass
+// is instantiated.
+
+#[is_subclass(superclass("MessageDisplayer"))]
+#[derive(Default)]
+pub struct UwuDisplayer {}
+
+impl ffi::MessageDisplayer_methods for UwuDisplayer {
+ fn display_message(&self, msg: &CxxString) {
+ let uwu = uwu::uwu(msg.to_str().unwrap());
+ println!("{}", uwu);
+ }
+}
+
+// And here's a different pure virtual class.
+// Here's its definition from src/messages.h:
+// ```cpp
+// class MessageProducer {
+// public:
+// virtual std::string get_message() const = 0;
+// virtual ~MessageProducer() {};
+// };
+// ```
+// This one is notable only in that the interface of the C++ class
+// involves std::string, yet in Rust the subclass uses
+// std::unique_ptr<std::string> (for all the normal reasons in autocxx -
+// for now, at least, we can't hold non-trivial C++ objects on the Rust stack.)
+// All the boxing and unboxing is done automatically by autocxx layers.
+
+#[is_subclass(superclass("MessageProducer"))]
+#[derive(Default)]
+pub struct QuoteProducer;
+
+// Here we've chosen to have an explicit constructor instead rather than deriving
+// from CppSubclassDefault. It's functionally the same.
+impl QuoteProducer {
+ fn new() -> Rc<RefCell<Self>> {
+ Self::new_rust_owned(Self::default())
+ }
+}
+
+impl ffi::MessageProducer_methods for QuoteProducer {
+ fn get_message(&self) -> cxx::UniquePtr<CxxString> {
+ use ffi::ToCppString;
+ billy::SHAKESPEARE_QUOTES[fastrand::usize(0..billy::SHAKESPEARE_QUOTES.len())].into_cpp()
+ }
+}
+
+// Here's another subclass of the same 'displayer' class.
+// This one is more complex in two ways.
+//
+// First, we actually want to store some data here in our subclass.
+// That means we can't just allocate ourselves with Default::default().
+// And that means we need to be aware of the cpp_peer field which is
+// added by the #[subclass] macro.
+//
+// Second, we're going to simulate the observer/listener type pattern
+// in C++ where a const* is used to send messages around a codebase yet
+// recipients need to react by mutating themselves or otherwise actively
+// doing stuff. In C++ you'd probably need a const_cast. Here we use
+// interior mutability.
+
+#[is_subclass(superclass("MessageDisplayer"))]
+pub struct BoxDisplayer {
+ message_count: RefCell<usize>,
+}
+
+impl BoxDisplayer {
+ fn new() -> Rc<RefCell<Self>> {
+ Self::new_rust_owned(Self {
+ // As we're allocating this class ourselves instead of using [`Default`]
+ // we need to initialize the `cpp_peer` member ourselves. This member is
+ // inserted by the `#[is_subclass]` annotation. autocxx will
+ // later use this to store a pointer back to the C++ peer.
+ cpp_peer: Default::default(),
+ message_count: RefCell::new(1usize),
+ })
+ }
+}
+
+impl ffi::MessageDisplayer_methods for BoxDisplayer {
+ fn display_message(&self, msg: &CxxString) {
+ let msg = textwrap::fill(msg.to_str().unwrap(), 70);
+ let horz_line = std::iter::repeat("#").take(74).collect::<String>();
+ println!("{}", horz_line);
+ let msgmsg = format!("Message {}", self.message_count.borrow());
+ self.message_count.replace_with(|old| *old + 1usize);
+ println!("# {:^70} #", msgmsg);
+ println!("{}", horz_line);
+ for l in msg.lines() {
+ println!("# {:^70} #", l);
+ }
+ println!("{}", horz_line);
+ }
+}
+
+fn main() {
+ ffi::register_cpp_thingies();
+ // Construct a Rust-owned UwuDisplayer. We can also construct
+ // a C++-owned or self-owned subclass - see docs for `CppSubclass`.
+ let uwu = UwuDisplayer::default_rust_owned();
+ // The next line casts the &UwuDisplayerCpp to a &MessageDisplayer.
+ ffi::register_displayer(uwu.as_ref().borrow().as_ref());
+ // Constructs in just the same way as the first one, but using
+ // our explicit constructor.
+ let boxd = BoxDisplayer::new();
+ ffi::register_displayer(boxd.as_ref().borrow().as_ref());
+ let shakespeare = QuoteProducer::new();
+ ffi::register_producer(shakespeare.as_ref().borrow().as_ref());
+ ffi::run_demo();
+ ffi::run_demo();
+}
diff --git a/examples/subclass/src/messages.cc b/examples/subclass/src/messages.cc
new file mode 100644
index 0000000..4d24aab
--- /dev/null
+++ b/examples/subclass/src/messages.cc
@@ -0,0 +1,70 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This example shows Rust subclasses of C++ classes.
+// See messages.h and main.rs for most of the interesting code.
+
+#include "messages.h"
+#include <ctime>
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <functional>
+
+class CppExampleProducer : public MessageProducer {
+public:
+ CppExampleProducer() {}
+ std::string get_message() const {
+ std::time_t result = std::time(nullptr);
+ std::ostringstream st;
+ st << std::asctime(std::localtime(&result))
+ << result << " seconds since the Epoch";
+ return st.str();
+ }
+};
+
+class CppExampleDisplayer : public MessageDisplayer {
+public:
+ CppExampleDisplayer() {}
+ void display_message(const std::string& msg) const {
+ std::cout << "Message: " << msg << std::endl;
+ }
+};
+
+std::vector<std::reference_wrapper<const MessageProducer>> producers;
+std::vector<std::reference_wrapper<const MessageDisplayer>> displayers;
+CppExampleProducer cpp_producer;
+CppExampleDisplayer cpp_displayer;
+
+
+// Maybe we should use a language which tracks lifetimes
+// better than this. If only such a language existed.
+void register_displayer(const MessageDisplayer& displayer) {
+ displayers.push_back(displayer);
+}
+
+void register_producer(const MessageProducer& producer) {
+ producers.push_back(producer);
+}
+
+void register_cpp_thingies() {
+ register_producer(cpp_producer);
+ register_displayer(cpp_displayer);
+}
+
+void run_demo() {
+ for (auto& producer: producers) {
+ auto msg = producer.get().get_message();
+ for (auto& displayer: displayers) {
+ displayer.get().display_message(msg);
+ std::cout << std::endl;
+ }
+ std::cout << std::endl;
+ }
+}
+
diff --git a/examples/subclass/src/messages.h b/examples/subclass/src/messages.h
new file mode 100644
index 0000000..02ff248
--- /dev/null
+++ b/examples/subclass/src/messages.h
@@ -0,0 +1,33 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This example shows Rust subclasses of C++ classes.
+// Here are the C++ classes which we're subclassing.
+
+#pragma once
+
+#include <string>
+#include <memory>
+
+class MessageProducer {
+public:
+ virtual std::string get_message() const = 0;
+ virtual ~MessageProducer() {};
+};
+
+class MessageDisplayer {
+public:
+ virtual void display_message(const std::string& message) const = 0;
+ virtual ~MessageDisplayer() {};
+};
+
+void register_cpp_thingies();
+void register_producer(const MessageProducer& producer);
+void register_displayer(const MessageDisplayer& displayer);
+
+void run_demo();
diff --git a/examples/subclass/src/uwu.rs b/examples/subclass/src/uwu.rs
new file mode 100644
index 0000000..ce02510
--- /dev/null
+++ b/examples/subclass/src/uwu.rs
@@ -0,0 +1,17 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub(crate) fn uwu(msg: &str) -> String {
+ uwuifier::uwuify_str_sse(msg)
+}
+
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+pub(crate) fn uwu(_msg: &str) -> String {
+ "uwuification is unavailable for this pwatform :(".to_string()
+}