blob: 2841598820dcafc007dcf2d2f48dada985c935cf [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9// This example shows some Rust subclasses of C++ classes.
10
11mod billy;
12mod uwu;
13
14use autocxx::prelude::*;
15use autocxx::subclass::prelude::*;
16use cxx::CxxString;
17use std::cell::RefCell;
18use std::rc::Rc;
19
20include_cpp! {
21 #include "messages.h"
22 safety!(unsafe) // unsafety policy; see docs
23}
24
25// Here's the definition of MessageDisplayer from src/messages.h:
26// ```cpp
27// class MessageDisplayer {
28// public:
29// virtual void display_message(const std::string& message) const = 0;
30// virtual ~MessageDisplayer() {};
31// };
32// ```
33// The following lines define a subclass of MessageDisplayer.
34// See the main function at the bottom for how this subclass
35// is instantiated.
36
37#[is_subclass(superclass("MessageDisplayer"))]
38#[derive(Default)]
39pub struct UwuDisplayer {}
40
41impl ffi::MessageDisplayer_methods for UwuDisplayer {
42 fn display_message(&self, msg: &CxxString) {
43 let uwu = uwu::uwu(msg.to_str().unwrap());
44 println!("{}", uwu);
45 }
46}
47
48// And here's a different pure virtual class.
49// Here's its definition from src/messages.h:
50// ```cpp
51// class MessageProducer {
52// public:
53// virtual std::string get_message() const = 0;
54// virtual ~MessageProducer() {};
55// };
56// ```
57// This one is notable only in that the interface of the C++ class
58// involves std::string, yet in Rust the subclass uses
59// std::unique_ptr<std::string> (for all the normal reasons in autocxx -
60// for now, at least, we can't hold non-trivial C++ objects on the Rust stack.)
61// All the boxing and unboxing is done automatically by autocxx layers.
62
63#[is_subclass(superclass("MessageProducer"))]
64#[derive(Default)]
65pub struct QuoteProducer;
66
67// Here we've chosen to have an explicit constructor instead rather than deriving
68// from CppSubclassDefault. It's functionally the same.
69impl QuoteProducer {
70 fn new() -> Rc<RefCell<Self>> {
71 Self::new_rust_owned(Self::default())
72 }
73}
74
75impl ffi::MessageProducer_methods for QuoteProducer {
76 fn get_message(&self) -> cxx::UniquePtr<CxxString> {
77 use ffi::ToCppString;
78 billy::SHAKESPEARE_QUOTES[fastrand::usize(0..billy::SHAKESPEARE_QUOTES.len())].into_cpp()
79 }
80}
81
82// Here's another subclass of the same 'displayer' class.
83// This one is more complex in two ways.
84//
85// First, we actually want to store some data here in our subclass.
86// That means we can't just allocate ourselves with Default::default().
87// And that means we need to be aware of the cpp_peer field which is
88// added by the #[subclass] macro.
89//
90// Second, we're going to simulate the observer/listener type pattern
91// in C++ where a const* is used to send messages around a codebase yet
92// recipients need to react by mutating themselves or otherwise actively
93// doing stuff. In C++ you'd probably need a const_cast. Here we use
94// interior mutability.
95
96#[is_subclass(superclass("MessageDisplayer"))]
97pub struct BoxDisplayer {
98 message_count: RefCell<usize>,
99}
100
101impl BoxDisplayer {
102 fn new() -> Rc<RefCell<Self>> {
103 Self::new_rust_owned(Self {
104 // As we're allocating this class ourselves instead of using [`Default`]
105 // we need to initialize the `cpp_peer` member ourselves. This member is
106 // inserted by the `#[is_subclass]` annotation. autocxx will
107 // later use this to store a pointer back to the C++ peer.
108 cpp_peer: Default::default(),
109 message_count: RefCell::new(1usize),
110 })
111 }
112}
113
114impl ffi::MessageDisplayer_methods for BoxDisplayer {
115 fn display_message(&self, msg: &CxxString) {
116 let msg = textwrap::fill(msg.to_str().unwrap(), 70);
117 let horz_line = std::iter::repeat("#").take(74).collect::<String>();
118 println!("{}", horz_line);
119 let msgmsg = format!("Message {}", self.message_count.borrow());
120 self.message_count.replace_with(|old| *old + 1usize);
121 println!("# {:^70} #", msgmsg);
122 println!("{}", horz_line);
123 for l in msg.lines() {
124 println!("# {:^70} #", l);
125 }
126 println!("{}", horz_line);
127 }
128}
129
130fn main() {
131 ffi::register_cpp_thingies();
132 // Construct a Rust-owned UwuDisplayer. We can also construct
133 // a C++-owned or self-owned subclass - see docs for `CppSubclass`.
134 let uwu = UwuDisplayer::default_rust_owned();
135 // The next line casts the &UwuDisplayerCpp to a &MessageDisplayer.
136 ffi::register_displayer(uwu.as_ref().borrow().as_ref());
137 // Constructs in just the same way as the first one, but using
138 // our explicit constructor.
139 let boxd = BoxDisplayer::new();
140 ffi::register_displayer(boxd.as_ref().borrow().as_ref());
141 let shakespeare = QuoteProducer::new();
142 ffi::register_producer(shakespeare.as_ref().borrow().as_ref());
143 ffi::run_demo();
144 ffi::run_demo();
145}