blob: 2a3340a006923b70eb2457a5c46f192b23634fd2 [file] [log] [blame]
// 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 crate::{
builder_modifiers::{
make_clang_arg_adder, make_cpp17_adder, EnableAutodiscover, SetSuppressSystemHeaders,
},
code_checkers::{
make_error_finder, make_rust_code_finder, make_string_finder, CppMatcher,
NoSystemHeadersChecker,
},
};
use autocxx_integration_tests::{
directives_from_lists, do_run_test, do_run_test_manual, run_test, run_test_ex,
run_test_expect_fail, run_test_expect_fail_ex, TestError,
};
use indoc::indoc;
use itertools::Itertools;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{parse_quote, Token};
use test_log::test;
#[test]
fn test_return_void() {
let cxx = indoc! {"
void do_nothing() {
}
"};
let hdr = indoc! {"
void do_nothing();
"};
let rs = quote! {
ffi::do_nothing();
};
run_test(cxx, hdr, rs, &["do_nothing"], &[]);
}
#[test]
fn test_two_funcs() {
let cxx = indoc! {"
void do_nothing1() {
}
void do_nothing2() {
}
"};
let hdr = indoc! {"
void do_nothing1();
void do_nothing2();
"};
let rs = quote! {
ffi::do_nothing1();
ffi::do_nothing2();
};
run_test(cxx, hdr, rs, &["do_nothing1", "do_nothing2"], &[]);
}
#[test]
fn test_two_funcs_with_definition() {
// Test to ensure C++ header isn't included twice
let cxx = indoc! {"
void do_nothing1() {
}
void do_nothing2() {
}
"};
let hdr = indoc! {"
struct Bob {
int a;
};
void do_nothing1();
void do_nothing2();
"};
let rs = quote! {
ffi::do_nothing1();
ffi::do_nothing2();
};
run_test(cxx, hdr, rs, &["do_nothing1", "do_nothing2"], &[]);
}
#[test]
fn test_return_i32() {
let cxx = indoc! {"
uint32_t give_int() {
return 5;
}
"};
let hdr = indoc! {"
#include <cstdint>
uint32_t give_int();
"};
let rs = quote! {
assert_eq!(ffi::give_int(), 5);
};
run_test(cxx, hdr, rs, &["give_int"], &[]);
}
#[test]
fn test_take_i32() {
let cxx = indoc! {"
uint32_t take_int(uint32_t a) {
return a + 3;
}
"};
let hdr = indoc! {"
#include <cstdint>
uint32_t take_int(uint32_t a);
"};
let rs = quote! {
assert_eq!(ffi::take_int(3), 6);
};
run_test(cxx, hdr, rs, &["take_int"], &[]);
}
#[test]
fn test_nested_module() {
let cxx = indoc! {"
void do_nothing() {
}
"};
let hdr = indoc! {"
void do_nothing();
"};
let hexathorpe = Token![#](Span::call_site());
let unexpanded_rust = quote! {
mod a {
use autocxx::prelude::*;
include_cpp!(
#hexathorpe include "input.h"
generate!("do_nothing")
safety!(unsafe)
);
pub use ffi::*;
}
fn main() {
a::do_nothing();
}
};
do_run_test_manual(cxx, hdr, unexpanded_rust, None, None).unwrap();
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/681
#[cfg(target_pointer_width = "64")]
fn test_return_big_ints() {
let cxx = indoc! {"
"};
let hdr = indoc! {"
#include <cstdint>
inline uint32_t give_u32() {
return 5;
}
inline uint64_t give_u64() {
return 5;
}
inline int32_t give_i32() {
return 5;
}
inline int64_t give_i64() {
return 5;
}
inline __int128 give_i128() {
return 5;
}
"};
let rs = quote! {
assert_eq!(ffi::give_u32(), 5);
assert_eq!(ffi::give_u64(), 5);
assert_eq!(ffi::give_i32(), 5);
assert_eq!(ffi::give_i64(), 5);
assert_eq!(ffi::give_i128(), 5);
};
run_test(
cxx,
hdr,
rs,
&["give_u32", "give_u64", "give_i32", "give_i64", "give_i128"],
&[],
);
}
#[test]
#[ignore] // because cxx doesn't support unique_ptrs to primitives.
fn test_give_up_int() {
let cxx = indoc! {"
std::unique_ptr<uint32_t> give_up() {
return std::make_unique<uint32_t>(12);
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
std::unique_ptr<uint32_t> give_up();
"};
let rs = quote! {
assert_eq!(ffi::give_up().as_ref().unwrap(), 12);
};
run_test(cxx, hdr, rs, &["give_up"], &[]);
}
#[test]
#[ignore] // because we don't yet implement UniquePtr etc. for autocxx::c_int and friends
fn test_give_up_ctype() {
let cxx = indoc! {"
std::unique_ptr<int> give_up() {
return std::make_unique<int>(12);
}
"};
let hdr = indoc! {"
#include <memory>
std::unique_ptr<int> give_up();
"};
let rs = quote! {
assert_eq!(ffi::give_up().as_ref().unwrap(), autocxx::c_int(12));
};
run_test(cxx, hdr, rs, &["give_up"], &[]);
}
#[test]
fn test_give_string_up() {
let cxx = indoc! {"
std::unique_ptr<std::string> give_str_up() {
return std::make_unique<std::string>(\"Bob\");
}
"};
let hdr = indoc! {"
#include <memory>
#include <string>
std::unique_ptr<std::string> give_str_up();
"};
let rs = quote! {
assert_eq!(ffi::give_str_up().as_ref().unwrap().to_str().unwrap(), "Bob");
};
run_test(cxx, hdr, rs, &["give_str_up"], &[]);
}
#[test]
fn test_give_string_plain() {
let cxx = indoc! {"
std::string give_str() {
return std::string(\"Bob\");
}
"};
let hdr = indoc! {"
#include <string>
std::string give_str();
"};
let rs = quote! {
assert_eq!(ffi::give_str().as_ref().unwrap(), "Bob");
};
run_test(cxx, hdr, rs, &["give_str"], &[]);
}
#[test]
fn test_cycle_string_up() {
let cxx = indoc! {"
std::unique_ptr<std::string> give_str_up() {
return std::make_unique<std::string>(\"Bob\");
}
uint32_t take_str_up(std::unique_ptr<std::string> a) {
return a->length();
}
"};
let hdr = indoc! {"
#include <memory>
#include <string>
#include <cstdint>
std::unique_ptr<std::string> give_str_up();
uint32_t take_str_up(std::unique_ptr<std::string> a);
"};
let rs = quote! {
let s = ffi::give_str_up();
assert_eq!(ffi::take_str_up(s), 3);
};
run_test(cxx, hdr, rs, &["give_str_up", "take_str_up"], &[]);
}
#[test]
fn test_cycle_string() {
let cxx = indoc! {"
std::string give_str() {
return std::string(\"Bob\");
}
uint32_t take_str(std::string a) {
return a.length();
}
"};
let hdr = indoc! {"
#include <string>
#include <cstdint>
std::string give_str();
uint32_t take_str(std::string a);
"};
let rs = quote! {
let s = ffi::give_str();
assert_eq!(ffi::take_str(s), 3);
};
let generate = &["give_str", "take_str"];
run_test(cxx, hdr, rs, generate, &[]);
}
#[test]
fn test_cycle_string_by_ref() {
let cxx = indoc! {"
std::unique_ptr<std::string> give_str() {
return std::make_unique<std::string>(\"Bob\");
}
uint32_t take_str(const std::string& a) {
return a.length();
}
"};
let hdr = indoc! {"
#include <string>
#include <memory>
#include <cstdint>
std::unique_ptr<std::string> give_str();
uint32_t take_str(const std::string& a);
"};
let rs = quote! {
let s = ffi::give_str();
assert_eq!(ffi::take_str(s.as_ref().unwrap()), 3);
};
let generate = &["give_str", "take_str"];
run_test(cxx, hdr, rs, generate, &[]);
}
#[test]
fn test_cycle_string_by_mut_ref() {
let cxx = indoc! {"
std::unique_ptr<std::string> give_str() {
return std::make_unique<std::string>(\"Bob\");
}
uint32_t take_str(std::string& a) {
return a.length();
}
"};
let hdr = indoc! {"
#include <string>
#include <memory>
#include <cstdint>
std::unique_ptr<std::string> give_str();
uint32_t take_str(std::string& a);
"};
let rs = quote! {
let mut s = ffi::give_str();
assert_eq!(ffi::take_str(s.as_mut().unwrap()), 3);
};
let generate = &["give_str", "take_str"];
run_test(cxx, hdr, rs, generate, &[]);
}
#[test]
fn test_give_pod_by_value() {
let cxx = indoc! {"
Bob give_bob() {
Bob a;
a.a = 3;
a.b = 4;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
Bob give_bob();
"};
let rs = quote! {
assert_eq!(ffi::give_bob().b, 4);
};
run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]);
}
#[test]
fn test_give_pod_class_by_value() {
let cxx = indoc! {"
Bob give_bob() {
Bob a;
a.a = 3;
a.b = 4;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
class Bob {
public:
uint32_t a;
uint32_t b;
};
Bob give_bob();
"};
let rs = quote! {
assert_eq!(ffi::give_bob().b, 4);
};
run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]);
}
#[test]
fn test_give_pod_by_up() {
let cxx = indoc! {"
std::unique_ptr<Bob> give_bob() {
auto a = std::make_unique<Bob>();
a->a = 3;
a->b = 4;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
uint32_t b;
};
std::unique_ptr<Bob> give_bob();
"};
let rs = quote! {
assert_eq!(ffi::give_bob().as_ref().unwrap().b, 4);
};
run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]);
}
#[test]
fn test_take_pod_by_value() {
let cxx = indoc! {"
uint32_t take_bob(Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
uint32_t take_bob(Bob a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob(a), 12);
};
run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_negative_take_as_pod_with_destructor() {
let cxx = indoc! {"
uint32_t take_bob(Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
inline ~Bob() {}
};
uint32_t take_bob(Bob a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob(a), 12);
};
run_test_expect_fail(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_negative_take_as_pod_with_move_constructor() {
let cxx = indoc! {"
uint32_t take_bob(Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <type_traits>
struct Bob {
uint32_t a;
uint32_t b;
inline Bob(Bob&& other_bob) {}
};
uint32_t take_bob(Bob a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob(a), 12);
};
run_test_expect_fail(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_take_as_pod_with_is_relocatable() {
let cxx = indoc! {"
uint32_t take_bob(Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <type_traits>
struct Bob {
uint32_t a;
uint32_t b;
inline Bob() {}
inline ~Bob() {}
inline Bob(Bob&& other_bob) { a = other_bob.a; b = other_bob.b; }
using IsRelocatable = std::true_type;
};
uint32_t take_bob(Bob a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob(a), 12);
};
run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_take_pod_by_ref() {
let cxx = indoc! {"
uint32_t take_bob(const Bob& a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
uint32_t take_bob(const Bob& a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob(&a), 12);
};
run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_take_pod_by_ref_and_ptr() {
let cxx = indoc! {"
uint32_t take_bob_ref(const Bob& a) {
return a.a;
}
uint32_t take_bob_ptr(const Bob* a) {
return a->a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
uint32_t take_bob_ref(const Bob& a);
uint32_t take_bob_ptr(const Bob* a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob_ref(&a), 12);
};
run_test(cxx, hdr, rs, &["take_bob_ref", "take_bob_ptr"], &["Bob"]);
}
#[test]
fn test_return_pod_by_ref_and_ptr() {
let hdr = indoc! {"
#include <cstdint>
struct B {
uint32_t a;
};
struct A {
B b;
};
inline const B& return_b_ref(const A& a) {
return a.b;
}
inline const B* return_b_ptr(const A& a) {
return &a.b;
}
"};
let rs = quote! {
let a = ffi::A { b: ffi::B { a: 3 } };
assert_eq!(ffi::return_b_ref(&a).a, 3);
let b_ptr = ffi::return_b_ptr(&a);
assert_eq!(unsafe { b_ptr.as_ref() }.unwrap().a, 3);
};
run_test("", hdr, rs, &["return_b_ref", "return_b_ptr"], &["A", "B"]);
}
#[test]
fn test_take_pod_by_mut_ref() {
let cxx = indoc! {"
uint32_t take_bob(Bob& a) {
a.b = 14;
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
uint32_t take_bob(Bob& a);
"};
let rs = quote! {
let mut a = Box::pin(ffi::Bob { a: 12, b: 13 });
assert_eq!(ffi::take_bob(a.as_mut()), 12);
assert_eq!(a.b, 14);
};
run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_take_nested_pod_by_value() {
let cxx = indoc! {"
uint32_t take_bob(Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Phil {
uint32_t d;
};
struct Bob {
uint32_t a;
uint32_t b;
Phil c;
};
uint32_t take_bob(Bob a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13, c: ffi::Phil { d: 4 } };
assert_eq!(ffi::take_bob(a), 12);
};
// Should be no need to allowlist Phil below
run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_take_nonpod_by_value() {
let cxx = indoc! {"
Bob::Bob(uint32_t a0, uint32_t b0)
: a(a0), b(b0) {}
uint32_t take_bob(Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Bob {
Bob(uint32_t a, uint32_t b);
uint32_t a;
uint32_t b;
std::string reason_why_this_is_nonpod;
};
uint32_t take_bob(Bob a);
"};
let rs = quote! {
let a = ffi::Bob::new(12, 13).within_unique_ptr();
assert_eq!(ffi::take_bob(a), 12);
};
run_test(cxx, hdr, rs, &["take_bob", "Bob"], &[]);
}
#[test]
fn test_take_nonpod_by_ref() {
let cxx = indoc! {"
uint32_t take_bob(const Bob& a) {
return a.a;
}
std::unique_ptr<Bob> make_bob(uint32_t a) {
auto b = std::make_unique<Bob>();
b->a = a;
return b;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
std::unique_ptr<Bob> make_bob(uint32_t a);
uint32_t take_bob(const Bob& a);
"};
let rs = quote! {
let a = ffi::make_bob(12);
assert_eq!(ffi::take_bob(&a), 12);
};
run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]);
}
#[test]
fn test_take_nonpod_by_up() {
let cxx = indoc! {"
uint32_t take_bob(std::unique_ptr<Bob> a) {
return a->a;
}
std::unique_ptr<Bob> make_bob(uint32_t a) {
auto b = std::make_unique<Bob>();
b->a = a;
return b;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
struct NOP { inline void take_bob(); };
std::unique_ptr<Bob> make_bob(uint32_t a);
uint32_t take_bob(std::unique_ptr<Bob> a);
"};
let rs = quote! {
let a = ffi::make_bob(12);
assert_eq!(ffi::take_bob(a), 12);
};
run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob", "NOP"], &[]);
}
#[test]
fn test_take_nonpod_by_ptr_simple() {
let cxx = indoc! {"
uint32_t take_bob(const Bob* a) {
return a->a;
}
std::unique_ptr<Bob> make_bob(uint32_t a) {
auto b = std::make_unique<Bob>();
b->a = a;
return b;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
std::unique_ptr<Bob> make_bob(uint32_t a);
uint32_t take_bob(const Bob* a);
"};
let rs = quote! {
let a = ffi::make_bob(12);
let a_ptr = a.into_raw();
assert_eq!(unsafe { ffi::take_bob(a_ptr) }, 12);
unsafe { cxx::UniquePtr::from_raw(a_ptr) }; // so we drop
};
run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]);
}
#[test]
fn test_take_nonpod_by_ptr_in_method() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
#include <cstdint>
class A {
public:
A() {};
uint32_t take_bob(const Bob* a) const {
return a->a;
}
std::unique_ptr<Bob> make_bob(uint32_t a) const {
auto b = std::make_unique<Bob>();
b->a = a;
return b;
}
uint16_t a;
};
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
let b = a.as_ref().unwrap().make_bob(12);
let b_ptr = b.into_raw();
assert_eq!(unsafe { a.as_ref().unwrap().take_bob(b_ptr) }, 12);
unsafe { cxx::UniquePtr::from_raw(b_ptr) }; // so we drop
};
run_test("", hdr, rs, &["A", "Bob"], &[]);
}
#[test]
fn test_take_nonpod_by_ptr_in_wrapped_method() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct C {
C() {}
uint32_t a;
};
struct Bob {
uint32_t a;
};
class A {
public:
A() {};
uint32_t take_bob(const Bob* a, C) const {
return a->a;
}
std::unique_ptr<Bob> make_bob(uint32_t a) const {
auto b = std::make_unique<Bob>();
b->a = a;
return b;
}
uint16_t a;
};
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
let c = ffi::C::new().within_unique_ptr();
let b = a.as_ref().unwrap().make_bob(12);
let b_ptr = b.into_raw();
assert_eq!(unsafe { a.as_ref().unwrap().take_bob(b_ptr, c) }, 12);
unsafe { cxx::UniquePtr::from_raw(b_ptr) }; // so we drop
};
run_test("", hdr, rs, &["A", "Bob", "C"], &[]);
}
#[test]
fn test_take_char_by_ptr_in_wrapped_method() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct C {
C() { test = \"hi\"; }
uint32_t a;
const char* test;
};
class A {
public:
A() {};
uint32_t take_char(const char* a, C) const {
return a[0];
}
const char* make_char(C extra) const {
return extra.test;
}
uint16_t a;
};
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
let c1 = ffi::C::new().within_unique_ptr();
let c2 = ffi::C::new().within_unique_ptr();
let ch = a.as_ref().unwrap().make_char(c1);
assert_eq!(unsafe { ch.as_ref()}.unwrap(), &104i8);
assert_eq!(unsafe { a.as_ref().unwrap().take_char(ch, c2) }, 104);
};
run_test("", hdr, rs, &["A", "C"], &[]);
}
#[test]
fn test_take_nonpod_by_mut_ref() {
let cxx = indoc! {"
uint32_t take_bob(Bob& a) {
return a.a;
}
std::unique_ptr<Bob> make_bob(uint32_t a) {
auto b = std::make_unique<Bob>();
b->a = a;
return b;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
std::unique_ptr<Bob> make_bob(uint32_t a);
uint32_t take_bob(Bob& a);
"};
let rs = quote! {
let mut a = ffi::make_bob(12);
assert_eq!(ffi::take_bob(a.pin_mut()), 12);
};
// TODO confirm that the object really was mutated by C++ in this
// and similar tests.
run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]);
}
#[test]
fn test_return_nonpod_by_value() {
let cxx = indoc! {"
Bob::Bob(uint32_t a0, uint32_t b0)
: a(a0), b(b0) {}
Bob give_bob(uint32_t a) {
Bob c(a, 44);
return c;
}
uint32_t take_bob(std::unique_ptr<Bob> a) {
return a->a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
Bob(uint32_t a, uint32_t b);
uint32_t a;
uint32_t b;
};
Bob give_bob(uint32_t a);
uint32_t take_bob(std::unique_ptr<Bob> a);
"};
let rs = quote! {
let a = ffi::give_bob(13).within_unique_ptr();
assert_eq!(ffi::take_bob(a), 13);
};
run_test(cxx, hdr, rs, &["take_bob", "give_bob", "Bob"], &[]);
}
#[test]
fn test_get_str_by_up() {
let cxx = indoc! {"
std::unique_ptr<std::string> get_str() {
return std::make_unique<std::string>(\"hello\");
}
"};
let hdr = indoc! {"
#include <string>
#include <memory>
std::unique_ptr<std::string> get_str();
"};
let rs = quote! {
assert_eq!(ffi::get_str().as_ref().unwrap(), "hello");
};
run_test(cxx, hdr, rs, &["get_str"], &[]);
}
#[test]
fn test_get_str_by_value() {
let cxx = indoc! {"
std::string get_str() {
return \"hello\";
}
"};
let hdr = indoc! {"
#include <string>
std::string get_str();
"};
let rs = quote! {
assert_eq!(ffi::get_str().as_ref().unwrap(), "hello");
};
run_test(cxx, hdr, rs, &["get_str"], &[]);
}
#[test]
fn test_cycle_nonpod_with_str_by_ref() {
let cxx = indoc! {"
uint32_t take_bob(const Bob& a) {
return a.a;
}
std::unique_ptr<Bob> make_bob() {
auto a = std::make_unique<Bob>();
a->a = 32;
a->b = \"hello\";
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
#include <memory>
struct Bob {
uint32_t a;
std::string b;
};
uint32_t take_bob(const Bob& a);
std::unique_ptr<Bob> make_bob();
"};
let rs = quote! {
let a = ffi::make_bob();
assert_eq!(ffi::take_bob(a.as_ref().unwrap()), 32);
};
run_test(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]);
}
#[test]
fn test_make_up() {
let cxx = indoc! {"
Bob::Bob() : a(3) {
}
uint32_t take_bob(const Bob& a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
class Bob {
public:
Bob();
uint32_t a;
};
uint32_t take_bob(const Bob& a);
"};
let rs = quote! {
let a = ffi::Bob::new().within_unique_ptr(); // TODO test with all sorts of arguments.
assert_eq!(ffi::take_bob(a.as_ref().unwrap()), 3);
};
run_test(cxx, hdr, rs, &["Bob", "take_bob"], &[]);
}
#[test]
fn test_make_up_with_args() {
let cxx = indoc! {"
Bob::Bob(uint32_t a0, uint32_t b0)
: a(a0), b(b0) {}
uint32_t take_bob(const Bob& a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
Bob(uint32_t a, uint32_t b);
uint32_t a;
uint32_t b;
};
uint32_t take_bob(const Bob& a);
"};
let rs = quote! {
let a = ffi::Bob::new(12, 13).within_unique_ptr();
assert_eq!(ffi::take_bob(a.as_ref().unwrap()), 12);
};
run_test(cxx, hdr, rs, &["take_bob", "Bob"], &[]);
}
#[test]
#[ignore] // because we don't support unique_ptrs to primitives
fn test_make_up_int() {
let cxx = indoc! {"
Bob::Bob(uint32_t a) : b(a) {
}
"};
let hdr = indoc! {"
#include <cstdint>
class Bob {
public:
Bob(uint32_t a);
uint32_t b;
};
"};
let rs = quote! {
let a = ffi::Bob::new(3).within_unique_ptr();
assert_eq!(a.as_ref().unwrap().b, 3);
};
run_test(cxx, hdr, rs, &["Bob"], &[]);
}
#[test]
fn test_enum_with_funcs() {
let cxx = indoc! {"
Bob give_bob() {
return Bob::BOB_VALUE_2;
}
"};
let hdr = indoc! {"
#include <cstdint>
enum Bob {
BOB_VALUE_1,
BOB_VALUE_2,
};
Bob give_bob();
"};
let rs = quote! {
let a = ffi::Bob::BOB_VALUE_2;
let b = ffi::give_bob();
assert!(a == b);
};
run_test(cxx, hdr, rs, &["Bob", "give_bob"], &[]);
}
#[test]
fn test_re_export() {
let cxx = indoc! {"
Bob give_bob() {
return Bob::BOB_VALUE_2;
}
"};
let hdr = indoc! {"
#include <cstdint>
enum Bob {
BOB_VALUE_1,
BOB_VALUE_2,
};
Bob give_bob();
"};
let rs = quote! {
let a = ffi::Bob::BOB_VALUE_2;
let b = ffi::give_bob();
assert!(a == b);
};
run_test_ex(
cxx,
hdr,
rs,
directives_from_lists(&["Bob", "give_bob"], &[], None),
None,
None,
Some(quote! { pub use ffi::Bob; }),
);
}
#[test]
fn test_enum_no_funcs() {
let cxx = indoc! {"
"};
let hdr = indoc! {"
enum Bob {
BOB_VALUE_1,
BOB_VALUE_2,
};
"};
let rs = quote! {
let a = ffi::Bob::BOB_VALUE_1;
let b = ffi::Bob::BOB_VALUE_2;
assert!(a != b);
};
run_test(cxx, hdr, rs, &["Bob"], &[]);
}
#[test]
fn test_enum_with_funcs_as_pod() {
let cxx = indoc! {"
Bob give_bob() {
return Bob::BOB_VALUE_2;
}
"};
let hdr = indoc! {"
#include <cstdint>
enum Bob {
BOB_VALUE_1,
BOB_VALUE_2,
};
Bob give_bob();
"};
let rs = quote! {
let a = ffi::Bob::BOB_VALUE_2;
let b = ffi::give_bob();
assert!(a == b);
};
run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]);
}
#[test] // works, but causes compile warnings
fn test_take_pod_class_by_value() {
let cxx = indoc! {"
uint32_t take_bob(Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
class Bob {
public:
uint32_t a;
uint32_t b;
};
uint32_t take_bob(Bob a);
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob(a), 12);
};
run_test(cxx, hdr, rs, &["take_bob"], &["Bob"]);
}
#[test]
fn test_pod_method() {
let cxx = indoc! {"
uint32_t Bob::get_bob() const {
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob() const;
};
"};
let rs = quote! {
let a = ffi::Bob { a: 12, b: 13 };
assert_eq!(a.get_bob(), 12);
};
run_test(cxx, hdr, rs, &[], &["Bob"]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/723
fn test_constructors_for_specialized_types() {
// bindgen sometimes makes such opaque types as type Bob = u32[2];
let hdr = indoc! {"
#include <cstdint>
template<typename T>
class A {
uint32_t foo() { return 12; };
private:
T a[2];
};
typedef A<uint32_t> B;
typedef B C;
"};
let rs = quote! {
let a = ffi::C::new().within_unique_ptr();
assert_eq!(a.foo(), 12);
};
run_test("", hdr, rs, &["C"], &[]);
}
#[test]
fn test_pod_mut_method() {
let cxx = indoc! {"
uint32_t Bob::get_bob() {
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob();
};
"};
let rs = quote! {
let mut a = Box::pin(ffi::Bob { a: 12, b: 13 });
assert_eq!(a.as_mut().get_bob(), 12);
};
run_test(cxx, hdr, rs, &[], &["Bob"]);
}
#[test]
fn test_define_int() {
let cxx = indoc! {"
"};
let hdr = indoc! {"
#define BOB 3
"};
let rs = quote! {
assert_eq!(ffi::BOB, 3);
};
run_test(cxx, hdr, rs, &["BOB"], &[]);
}
#[test]
fn test_define_str() {
let cxx = indoc! {"
"};
let hdr = indoc! {"
#define BOB \"foo\"
"};
let rs = quote! {
assert_eq!(std::str::from_utf8(ffi::BOB).unwrap().trim_end_matches(char::from(0)), "foo");
};
run_test(cxx, hdr, rs, &["BOB"], &[]);
}
#[test]
fn test_i32_const() {
let cxx = indoc! {"
"};
let hdr = indoc! {"
#include <cstdint>
const uint32_t BOB = 3;
"};
let rs = quote! {
assert_eq!(ffi::BOB, 3);
};
run_test(cxx, hdr, rs, &["BOB"], &[]);
}
#[test]
fn test_negative_rs_nonsense() {
// Really just testing the test infrastructure.
let cxx = indoc! {"
"};
let hdr = indoc! {"
#include <cstdint>
const uint32_t BOB = 3;
"};
let rs = quote! {
foo bar
};
run_test_expect_fail(cxx, hdr, rs, &["BOB"], &[]);
}
#[test]
fn test_negative_cpp_nonsense() {
// Really just testing the test infrastructure.
let cxx = indoc! {"
"};
let hdr = indoc! {"
#include <cstdint>
const uint32_t BOB = CAT;
"};
let rs = quote! {
assert_eq!(ffi::BOB, 3);
};
run_test_expect_fail(cxx, hdr, rs, &["BOB"], &[]);
}
#[test]
fn test_negative_make_nonpod() {
let cxx = indoc! {"
uint32_t take_bob(const Bob& a) {
return a.a;
}
std::unique_ptr<Bob> make_bob(uint32_t a) {
auto b = std::make_unique<Bob>();
b->a = a;
return b;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
std::unique_ptr<Bob> make_bob(uint32_t a);
uint32_t take_bob(const Bob& a);
"};
let rs = quote! {
ffi::Bob {};
};
let rs2 = quote! {
ffi::Bob { a: 12 };
};
let rs3 = quote! {
ffi::Bob { do_not_attempt_to_allocate_nonpod_types: [] };
};
run_test_expect_fail(cxx, hdr, rs, &["take_bob", "Bob", "make_bob"], &[]);
run_test_expect_fail(cxx, hdr, rs2, &["take_bob", "Bob", "make_bob"], &[]);
run_test_expect_fail(cxx, hdr, rs3, &["take_bob", "Bob", "make_bob"], &[]);
}
#[test]
fn test_method_pass_pod_by_value() {
let cxx = indoc! {"
uint32_t Bob::get_bob(Anna) const {
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Anna {
uint32_t a;
};
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(Anna a) const;
};
"};
let rs = quote! {
let a = ffi::Anna { a: 14 };
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a), 12);
};
run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]);
}
fn perform_asan_doom_test(into_raw: TokenStream, box_type: TokenStream) {
if std::env::var_os("AUTOCXX_ASAN").is_none() {
return;
}
// Testing that we get an asan fail when it's enabled.
// Really just testing our CI is working to spot ASAN mistakes.
let hdr = indoc! {"
#include <cstddef>
struct A {
int a;
};
inline size_t how_big_is_a() {
return sizeof(A);
}
"};
let rs = quote! {
let a = #box_type::emplace(ffi::A::new());
unsafe {
let a_raw = #into_raw;
// Intentional memory unsafety. Don't @ me.
let a_offset_into_doom = a_raw.offset(ffi::how_big_is_a().try_into().unwrap());
a_offset_into_doom.write_bytes(0x69, 1);
#box_type::from_raw(a_raw); // to delete. If we haven't yet crashed.
}
};
run_test_expect_fail("", hdr, rs, &["A", "how_big_is_a"], &[]);
}
#[test]
fn test_asan_working_as_expected_for_cpp_allocations() {
perform_asan_doom_test(quote! { a.into_raw() }, quote! { UniquePtr })
}
#[test]
fn test_asan_working_as_expected_for_rust_allocations() {
perform_asan_doom_test(
quote! { Box::into_raw(std::pin::Pin::into_inner_unchecked(a)) },
quote! { Box },
)
}
#[test]
fn test_inline_method() {
let hdr = indoc! {"
#include <cstdint>
struct Anna {
uint32_t a;
};
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(Anna) const {
return a;
}
};
"};
let rs = quote! {
let a = ffi::Anna { a: 14 };
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a), 12);
};
run_test("", hdr, rs, &[], &["Bob", "Anna"]);
}
#[test]
fn test_method_pass_pod_by_reference() {
let cxx = indoc! {"
uint32_t Bob::get_bob(const Anna&) const {
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Anna {
uint32_t a;
};
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(const Anna& a) const;
};
"};
let rs = quote! {
let a = ffi::Anna { a: 14 };
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(&a), 12);
};
run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]);
}
#[test]
fn test_method_pass_pod_by_mut_reference() {
let cxx = indoc! {"
uint32_t Bob::get_bob(Anna&) const {
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Anna {
uint32_t a;
};
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(Anna& a) const;
};
"};
let rs = quote! {
let mut a = Box::pin(ffi::Anna { a: 14 });
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a.as_mut()), 12);
};
run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]);
}
#[test]
fn test_method_pass_pod_by_up() {
let cxx = indoc! {"
uint32_t Bob::get_bob(std::unique_ptr<Anna>) const {
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Anna {
uint32_t a;
};
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(std::unique_ptr<Anna> z) const;
};
"};
let rs = quote! {
let a = ffi::Anna { a: 14 };
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(cxx::UniquePtr::new(a)), 12);
};
run_test(cxx, hdr, rs, &[], &["Bob", "Anna"]);
}
#[test]
fn test_method_pass_nonpod_by_value() {
let cxx = indoc! {"
uint32_t Bob::get_bob(Anna) const {
return a;
}
Anna give_anna() {
Anna a;
a.a = 10;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Anna {
uint32_t a;
std::string b;
};
Anna give_anna();
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(Anna a) const;
};
"};
let rs = quote! {
let a = ffi::give_anna().within_box();
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a), 12);
};
run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]);
}
#[test]
fn test_pass_two_nonpod_by_value() {
let cxx = indoc! {"
void take_a(A, A) {
}
"};
let hdr = indoc! {"
#include <string>
struct A {
std::string b;
};
void take_a(A, A);
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
let a2 = ffi::A::new().within_unique_ptr();
ffi::take_a(a, a2);
};
run_test(cxx, hdr, rs, &["A", "take_a"], &[]);
}
#[test]
fn test_issue_931() {
let cxx = "";
let hdr = indoc! {"
namespace a {
struct __cow_string {
__cow_string();
};
class b {
public:
__cow_string c;
};
class j {
public:
b d;
};
template <typename> class e;
} // namespace a
template <typename> struct f {};
namespace llvm {
template <class> class g {
union {
f<a::j> h;
};
};
class MemoryBuffer {
public:
g<a::e<MemoryBuffer>> i;
};
} // namespace llvm
"};
let rs = quote! {};
run_test(cxx, hdr, rs, &["llvm::MemoryBuffer"], &[]);
}
#[test]
fn test_issue_936() {
let cxx = "";
let hdr = indoc! {"
struct a;
class B {
public:
B(a &, bool);
};
"};
let rs = quote! {};
run_test(cxx, hdr, rs, &["B"], &[]);
}
#[test]
fn test_method_pass_nonpod_by_value_with_up() {
// Checks that existing UniquePtr params are not wrecked
// by the conversion we do here.
let cxx = indoc! {"
uint32_t Bob::get_bob(Anna, std::unique_ptr<Anna>) const {
return a;
}
Anna give_anna() {
Anna a;
a.a = 10;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
#include <memory>
struct Anna {
uint32_t a;
std::string b;
};
Anna give_anna();
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(Anna a, std::unique_ptr<Anna>) const;
};
"};
let rs = quote! {
let a = ffi::give_anna().within_unique_ptr();
let a2 = ffi::give_anna().within_unique_ptr();
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a, a2), 12);
};
run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]);
}
#[test]
fn test_issue_940() {
let cxx = "";
let hdr = indoc! {"
template <class> class b;
template <class = void> struct c;
struct identity;
template <class, class, class e, class> class f {
using g = e;
g h;
};
template <class i, class k = c<>, class l = b<i>>
using j = f<i, identity, k, l>;
class n;
class RenderFrameHost {
public:
virtual void o(const j<n> &);
virtual ~RenderFrameHost() {}
};
"};
let rs = quote! {};
run_test_ex(
cxx,
hdr,
rs,
directives_from_lists(&["RenderFrameHost"], &[], None),
make_cpp17_adder(),
None,
None,
);
}
#[test]
fn test_method_pass_nonpod_by_reference() {
let cxx = indoc! {"
uint32_t Bob::get_bob(const Anna&) const {
return a;
}
Anna give_anna() {
Anna a;
a.a = 10;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Anna {
uint32_t a;
std::string b;
};
Anna give_anna();
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(const Anna& a) const;
};
"};
let rs = quote! {
let a = ffi::give_anna().within_box();
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a.as_ref().get_ref()), 12);
};
run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]);
}
#[test]
fn test_method_pass_nonpod_by_mut_reference() {
let cxx = indoc! {"
uint32_t Bob::get_bob(Anna&) const {
return a;
}
Anna give_anna() {
Anna a;
a.a = 10;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Anna {
uint32_t a;
std::string b;
};
Anna give_anna();
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(Anna& a) const;
};
"};
let rs = quote! {
let mut a = ffi::give_anna().within_unique_ptr();
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a.as_mut().unwrap()), 12);
};
run_test(cxx, hdr, rs, &["Anna", "give_anna"], &["Bob"]);
}
#[test]
fn test_method_pass_nonpod_by_up() {
let cxx = indoc! {"
uint32_t Bob::get_bob(std::unique_ptr<Anna>) const {
return a;
}
Anna give_anna() {
Anna a;
a.a = 10;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
#include <string>
struct Anna {
uint32_t a;
std::string b;
};
Anna give_anna();
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t get_bob(std::unique_ptr<Anna> z) const;
};
"};
let rs = quote! {
let a = ffi::give_anna().within_unique_ptr();
let b = ffi::Bob { a: 12, b: 13 };
assert_eq!(b.get_bob(a), 12);
};
run_test(cxx, hdr, rs, &["give_anna"], &["Bob"]);
}
#[test]
fn test_method_return_nonpod_by_value() {
let cxx = indoc! {"
Anna Bob::get_anna() const {
Anna a;
a.a = 12;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Anna {
uint32_t a;
std::string b;
};
struct Bob {
public:
uint32_t a;
uint32_t b;
Anna get_anna() const;
};
"};
let rs = quote! {
let b = ffi::Bob { a: 12, b: 13 };
let a = b.get_anna().within_unique_ptr();
assert!(!a.is_null());
};
run_test(cxx, hdr, rs, &["Anna"], &["Bob"]);
}
#[test]
fn test_pass_string_by_value() {
let cxx = indoc! {"
uint32_t measure_string(std::string z) {
return z.length();
}
std::unique_ptr<std::string> get_msg() {
return std::make_unique<std::string>(\"hello\");
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
#include <memory>
uint32_t measure_string(std::string a);
std::unique_ptr<std::string> get_msg();
"};
let rs = quote! {
let a = ffi::get_msg();
let c = ffi::measure_string(a);
assert_eq!(c, 5);
};
run_test(cxx, hdr, rs, &["measure_string", "get_msg"], &[]);
}
#[test]
fn test_return_string_by_value() {
let cxx = indoc! {"
std::string get_msg() {
return \"hello\";
}
"};
let hdr = indoc! {"
#include <string>
std::string get_msg();
"};
let rs = quote! {
let a = ffi::get_msg();
assert!(a.as_ref().unwrap() == "hello");
};
run_test(cxx, hdr, rs, &["get_msg"], &[]);
}
#[test]
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
fn test_method_pass_string_by_value() {
let cxx = indoc! {"
uint32_t Bob::measure_string(std::string z) const {
return z.length();
}
std::unique_ptr<std::string> get_msg() {
return std::make_unique<std::string>(\"hello\");
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
#include <memory>
struct Bob {
public:
uint32_t a;
uint32_t b;
uint32_t measure_string(std::string a) const;
};
std::unique_ptr<std::string> get_msg();
"};
let rs = quote! {
let a = ffi::get_msg();
let b = ffi::Bob { a: 12, b: 13 };
let c = b.measure_string(a);
assert_eq!(c, 5);
};
run_test(cxx, hdr, rs, &["Bob", "get_msg"], &["Bob"]);
}
#[test]
fn test_method_return_string_by_value() {
let cxx = indoc! {"
std::string Bob::get_msg() const {
return \"hello\";
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Bob {
public:
uint32_t a;
uint32_t b;
std::string get_msg() const;
};
"};
let rs = quote! {
let b = ffi::Bob { a: 12, b: 13 };
let a = b.get_msg();
assert!(a.as_ref().unwrap() == "hello");
};
run_test(cxx, hdr, rs, &[], &["Bob"]);
}
#[test]
fn test_pass_rust_string_by_ref() {
let cxx = indoc! {"
uint32_t measure_string(const rust::String& z) {
return std::string(z).length();
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <cxx.h>
uint32_t measure_string(const rust::String& z);
"};
let rs = quote! {
let c = ffi::measure_string(&"hello".to_string());
assert_eq!(c, 5);
};
run_test(cxx, hdr, rs, &["measure_string"], &[]);
}
#[test]
fn test_pass_rust_string_by_value() {
let cxx = indoc! {"
uint32_t measure_string(rust::String z) {
return std::string(z).length();
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <cxx.h>
uint32_t measure_string(rust::String z);
"};
let rs = quote! {
let c = ffi::measure_string("hello".into());
assert_eq!(c, 5);
};
run_test(cxx, hdr, rs, &["measure_string"], &[]);
}
#[test]
fn test_pass_rust_str() {
// passing by value is the only legal option
let cxx = indoc! {"
uint32_t measure_string(rust::Str z) {
return std::string(z).length();
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <cxx.h>
uint32_t measure_string(rust::Str z);
"};
let rs = quote! {
let c = ffi::measure_string("hello");
assert_eq!(c, 5);
};
run_test(cxx, hdr, rs, &["measure_string"], &[]);
}
#[test]
fn test_multiple_classes_with_methods() {
let hdr = indoc! {"
#include <cstdint>
struct TrivialStruct {
uint32_t val = 0;
uint32_t get() const;
uint32_t inc();
};
TrivialStruct make_trivial_struct();
class TrivialClass {
public:
uint32_t get() const;
uint32_t inc();
private:
uint32_t val_ = 1;
};
TrivialClass make_trivial_class();
struct OpaqueStruct {
// ~OpaqueStruct();
uint32_t val = 2;
uint32_t get() const;
uint32_t inc();
};
OpaqueStruct make_opaque_struct();
class OpaqueClass {
public:
// ~OpaqueClass();
uint32_t get() const;
uint32_t inc();
private:
uint32_t val_ = 3;
};
OpaqueClass make_opaque_class();
"};
let cxx = indoc! {"
TrivialStruct make_trivial_struct() { return {}; }
TrivialClass make_trivial_class() { return {}; }
OpaqueStruct make_opaque_struct() { return {}; }
OpaqueClass make_opaque_class() { return {}; }
uint32_t TrivialStruct::get() const { return val;}
uint32_t TrivialClass::get() const { return val_; }
uint32_t OpaqueStruct::get() const { return val;}
uint32_t OpaqueClass::get() const { return val_; }
uint32_t TrivialStruct::inc() { return ++val; }
uint32_t TrivialClass::inc() { return ++val_; }
uint32_t OpaqueStruct::inc() { return ++val; }
uint32_t OpaqueClass::inc() { return ++val_; }
"};
let rs = quote! {
use ffi::*;
let mut ts = Box::pin(make_trivial_struct());
assert_eq!(ts.get(), 0);
assert_eq!(ts.as_mut().inc(), 1);
assert_eq!(ts.as_mut().inc(), 2);
let mut tc = Box::pin(make_trivial_class());
assert_eq!(tc.get(), 1);
assert_eq!(tc.as_mut().inc(), 2);
assert_eq!(tc.as_mut().inc(), 3);
let mut os= make_opaque_struct().within_unique_ptr();
assert_eq!(os.get(), 2);
assert_eq!(os.pin_mut().inc(), 3);
assert_eq!(os.pin_mut().inc(), 4);
let mut oc = make_opaque_class().within_unique_ptr();
assert_eq!(oc.get(), 3);
assert_eq!(oc.pin_mut().inc(), 4);
assert_eq!(oc.pin_mut().inc(), 5);
};
run_test(
cxx,
hdr,
rs,
&[
"make_trivial_struct",
"make_trivial_class",
"make_opaque_struct",
"make_opaque_class",
"OpaqueStruct",
"OpaqueClass",
],
&["TrivialStruct", "TrivialClass"],
);
}
#[test]
fn test_ns_return_struct() {
let cxx = indoc! {"
A::B::Bob give_bob() {
A::B::Bob a;
a.a = 3;
a.b = 4;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
namespace A {
namespace B {
struct Bob {
uint32_t a;
uint32_t b;
};
}
}
A::B::Bob give_bob();
"};
let rs = quote! {
assert_eq!(ffi::give_bob().b, 4);
};
run_test(cxx, hdr, rs, &["give_bob"], &["A::B::Bob"]);
}
#[test]
fn test_ns_take_struct() {
let cxx = indoc! {"
uint32_t take_bob(A::B::Bob a) {
return a.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
namespace A {
namespace B {
struct Bob {
uint32_t a;
uint32_t b;
};
}
}
uint32_t take_bob(A::B::Bob a);
"};
let rs = quote! {
let a = ffi::A::B::Bob { a: 12, b: 13 };
assert_eq!(ffi::take_bob(a), 12);
};
run_test(cxx, hdr, rs, &["take_bob"], &["A::B::Bob"]);
}
#[test]
fn test_ns_func() {
let cxx = indoc! {"
using namespace C;
A::B::Bob C::give_bob() {
A::B::Bob a;
a.a = 3;
a.b = 4;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
namespace A {
namespace B {
struct Bob {
uint32_t a;
uint32_t b;
};
}
}
namespace C {
::A::B::Bob give_bob();
}
"};
let rs = quote! {
assert_eq!(ffi::C::give_bob().b, 4);
};
run_test(cxx, hdr, rs, &["C::give_bob"], &["A::B::Bob"]);
}
#[test]
fn test_overload_constructors() {
let cxx = indoc! {"
Bob::Bob() {}
Bob::Bob(uint32_t _a) :a(_a) {}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
Bob();
Bob(uint32_t a);
uint32_t a;
uint32_t b;
};
"};
let rs = quote! {
ffi::Bob::new().within_unique_ptr();
ffi::Bob::new1(32).within_unique_ptr();
};
run_test(cxx, hdr, rs, &["Bob"], &[]);
}
#[test]
fn test_overload_functions() {
let cxx = indoc! {"
void daft(uint32_t) {}
void daft(uint8_t) {}
void daft(std::string) {}
void daft(Fred) {}
void daft(Norma) {}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Fred {
uint32_t a;
};
struct Norma {
Norma() {}
uint32_t a;
};
void daft(uint32_t);
void daft(uint8_t);
void daft(std::string);
void daft(Fred);
void daft(Norma);
"};
let rs = quote! {
use ffi::ToCppString;
ffi::daft(32);
ffi::daft1(8);
ffi::daft2("hello".into_cpp());
let b = ffi::Fred { a: 3 };
ffi::daft3(b);
let c = ffi::Norma::new().within_unique_ptr();
ffi::daft4(c);
};
run_test(
cxx,
hdr,
rs,
&["Norma", "daft", "daft1", "daft2", "daft3", "daft4"],
&["Fred"],
);
}
#[test]
#[ignore] // At present, bindgen generates two separate 'daft1'
// functions here, and there's not much we can do about that.
fn test_overload_numeric_functions() {
// Because bindgen deals with conflicting overloaded functions by
// appending a numeric suffix, let's see if we can cope.
let cxx = indoc! {"
void daft1(uint32_t) {}
void daft2(uint8_t) {}
void daft(std::string) {}
void daft(Fred) {}
void daft(Norma) {}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Fred {
uint32_t a;
};
struct Norma {
uint32_t a;
};
void daft1(uint32_t a);
void daft2(uint8_t a);
void daft(std::string a);
void daft(Fred a);
void daft(Norma a);
"};
let rs = quote! {
use ffi::ToCppString;
ffi::daft(32);
ffi::daft1(8);
ffi::daft2("hello".into_cpp());
let b = ffi::Fred { a: 3 };
ffi::daft3(b);
let c = ffi::Norma::new().within_unique_ptr();
ffi::daft4(c);
};
run_test(
cxx,
hdr,
rs,
&["Norma", "daft", "daft1", "daft2", "daft3", "daft4"],
&["Fred"],
);
}
#[test]
fn test_overload_methods() {
let cxx = indoc! {"
void Bob::daft(uint32_t) const {}
void Bob::daft(uint8_t) const {}
void Bob::daft(std::string) const {}
void Bob::daft(Fred) const {}
void Bob::daft(Norma) const {}
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Fred {
uint32_t a;
};
struct Norma {
Norma() {}
uint32_t a;
};
struct Bob {
uint32_t a;
void daft(uint32_t) const;
void daft(uint8_t) const;
void daft(std::string) const;
void daft(Fred) const;
void daft(Norma) const;
};
"};
let rs = quote! {
use ffi::ToCppString;
let a = ffi::Bob { a: 12 };
a.daft(32);
a.daft1(8);
a.daft2("hello".into_cpp());
let b = ffi::Fred { a: 3 };
a.daft3(b);
let c = ffi::Norma::new().within_unique_ptr();
a.daft4(c);
};
run_test(cxx, hdr, rs, &["Norma"], &["Fred", "Bob"]);
}
#[test]
fn test_ns_constructor() {
let cxx = indoc! {"
A::Bob::Bob() {}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
Bob();
uint32_t a;
uint32_t b;
};
}
"};
let rs = quote! {
ffi::A::Bob::new().within_unique_ptr();
};
run_test(cxx, hdr, rs, &["A::Bob"], &[]);
}
#[test]
fn test_ns_up_direct() {
let cxx = indoc! {"
std::unique_ptr<A::Bob> A::get_bob() {
A::Bob b;
b.a = 2;
b.b = 3;
return std::make_unique<A::Bob>(b);
}
uint32_t give_bob(A::Bob bob) {
return bob.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
uint32_t b;
};
std::unique_ptr<Bob> get_bob();
}
uint32_t give_bob(A::Bob bob);
"};
let rs = quote! {
assert_eq!(ffi::give_bob(ffi::A::get_bob()), 2);
};
run_test(cxx, hdr, rs, &["give_bob", "A::get_bob"], &[]);
}
#[test]
fn test_ns_up_wrappers() {
let cxx = indoc! {"
A::Bob get_bob() {
A::Bob b;
b.a = 2;
b.b = 3;
return b;
}
uint32_t give_bob(A::Bob bob) {
return bob.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
namespace A {
struct Bob {
uint32_t a;
uint32_t b;
};
}
A::Bob get_bob();
uint32_t give_bob(A::Bob bob);
"};
let rs = quote! {
assert_eq!(ffi::give_bob(as_new(ffi::get_bob())), 2);
};
run_test(cxx, hdr, rs, &["give_bob", "get_bob"], &[]);
}
#[test]
fn test_ns_up_wrappers_in_up() {
let cxx = indoc! {"
A::Bob A::get_bob() {
A::Bob b;
b.a = 2;
b.b = 3;
return b;
}
uint32_t give_bob(A::Bob bob) {
return bob.a;
}
"};
let hdr = indoc! {"
#include <cstdint>
namespace A {
struct Bob {
uint32_t a;
uint32_t b;
};
Bob get_bob();
}
uint32_t give_bob(A::Bob bob);
"};
let rs = quote! {
assert_eq!(ffi::give_bob(as_new(ffi::A::get_bob())), 2);
};
run_test(cxx, hdr, rs, &["give_bob", "A::get_bob"], &[]);
}
#[test]
fn test_return_reference() {
let cxx = indoc! {"
const Bob& give_bob(const Bob& input_bob) {
return input_bob;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
const Bob& give_bob(const Bob& input_bob);
"};
let rs = quote! {
let b = ffi::Bob { a: 3, b: 4 };
assert_eq!(ffi::give_bob(&b).b, 4);
};
run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]);
}
#[test]
fn test_return_reference_non_pod() {
let cxx = indoc! {"
const Bob& give_bob(const Bob& input_bob) {
return input_bob;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
namespace A {
void give_bob(); // force wrapper generation
}
const Bob& give_bob(const Bob& input_bob);
"};
let rs = quote! {};
run_test(cxx, hdr, rs, &["give_bob", "Bob", "A::give_bob"], &[]);
}
#[test]
fn test_return_reference_non_pod_string() {
let cxx = indoc! {"
const std::string& give_bob(const Bob& input_bob) {
return input_bob.a;
}
"};
let hdr = indoc! {"
#include <string>
struct Bob {
std::string a;
};
// namespace A {
// void give_bob(); // force wrapper generation
// }
const std::string& give_bob(const Bob& input_bob);
"};
let rs = quote! {};
run_test(cxx, hdr, rs, &["give_bob", "Bob"], &[]);
}
#[test]
fn test_member_return_reference() {
let hdr = indoc! {"
#include <string>
class A {
public:
virtual const std::string& get_str() { return a; }
virtual ~A() {}
std::string a;
};
"};
let rs = quote! {
let mut b = ffi::A::new().within_unique_ptr();
b.pin_mut().get_str();
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_destructor() {
let hdr = indoc! {"
struct WithDtor {
~WithDtor();
};
WithDtor make_with_dtor();
"};
let cxx = indoc! {"
WithDtor::~WithDtor() {}
WithDtor make_with_dtor() {
return {};
}
"};
let rs = quote! {
use ffi::*;
let with_dtor: cxx::UniquePtr<WithDtor> = make_with_dtor().within_unique_ptr();
drop(with_dtor);
};
run_test(cxx, hdr, rs, &["WithDtor", "make_with_dtor"], &[]);
}
#[test]
fn test_nested_with_destructor() {
// Regression test, naming the destructor in the generated C++ is a bit tricky.
let hdr = indoc! {"
struct A {
struct B {
B() = default;
~B() = default;
};
};
"};
let rs = quote! {
ffi::A_B::new().within_unique_ptr();
};
run_test("", hdr, rs, &["A", "A_B"], &[]);
}
// Even without a `safety!`, we still need to generate a safe `fn drop`.
#[test]
fn test_destructor_no_safety() {
let hdr = indoc! {"
struct WithDtor {
~WithDtor();
};
"};
let cxx = indoc! {"
WithDtor::~WithDtor() {}
"};
let hexathorpe = Token![#](Span::call_site());
let unexpanded_rust = quote! {
use autocxx::prelude::*;
include_cpp!(
#hexathorpe include "input.h"
generate!("WithDtor")
);
fn main() {}
};
do_run_test_manual(cxx, hdr, unexpanded_rust, None, None).unwrap();
}
#[test]
fn test_static_func() {
let hdr = indoc! {"
#include <cstdint>
struct WithStaticMethod {
static uint32_t call();
};
"};
let cxx = indoc! {"
uint32_t WithStaticMethod::call() {
return 42;
}
"};
let rs = quote! {
assert_eq!(ffi::WithStaticMethod::call(), 42);
};
run_test(cxx, hdr, rs, &["WithStaticMethod"], &[]);
}
#[test]
fn test_static_func_wrapper() {
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct A {
std::string a;
static A CreateA(std::string a, std::string) {
A c;
c.a = a;
return c;
}
};
"};
let rs = quote! {
use ffi::ToCppString;
ffi::A::CreateA("a".into_cpp(), "b".into_cpp());
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_give_pod_typedef_by_value() {
let cxx = indoc! {"
Horace give_bob() {
Horace a;
a.a = 3;
a.b = 4;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
using Horace = Bob;
Horace give_bob();
"};
let rs = quote! {
assert_eq!(ffi::give_bob().b, 4);
};
run_test(cxx, hdr, rs, &["give_bob"], &["Bob"]);
}
#[ignore] // because we need to put some aliases in the output ffi mod.
#[test]
fn test_use_pod_typedef() {
let cxx = indoc! {"
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
using Horace = Bob;
"};
let rs = quote! {
let h = Horace { a: 3, b: 4 };
assert_eq!(h.b, 4);
};
run_test(cxx, hdr, rs, &[], &["Bob"]);
}
#[test]
fn test_typedef_to_ns() {
let hdr = indoc! {"
#include <cstdint>
namespace A {
template<typename T>
struct C {
T* t;
};
typedef C<char> B;
}
"};
let rs = quote! {};
run_test("", hdr, rs, &["A::B"], &[]);
}
#[ignore] // we don't yet allow typedefs to be listed in allow_pod
#[test]
fn test_use_pod_typedef_with_allowpod() {
let cxx = indoc! {"
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
using Horace = Bob;
"};
let rs = quote! {
let h = Horace { a: 3, b: 4 };
assert_eq!(h.b, 4);
};
run_test(cxx, hdr, rs, &[], &["Horace"]);
}
#[test]
fn test_give_nonpod_typedef_by_value() {
let cxx = indoc! {"
Horace give_bob() {
Horace a;
a.a = 3;
a.b = 4;
return a;
}
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t b;
};
using Horace = Bob;
Horace give_bob();
inline uint32_t take_horace(const Horace& horace) { return horace.b; }
"};
let rs = quote! {
assert_eq!(ffi::take_horace(&moveit!(ffi::give_bob())), 4);
};
run_test(cxx, hdr, rs, &["give_bob", "take_horace"], &[]);
}
#[test]
fn test_conflicting_static_functions() {
let cxx = indoc! {"
Bob Bob::create() { Bob a; return a; }
Fred Fred::create() { Fred b; return b; }
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
Bob() : a(0) {}
uint32_t a;
static Bob create();
};
struct Fred {
Fred() : b(0) {}
uint32_t b;
static Fred create();
};
"};
let rs = quote! {
ffi::Bob::create();
ffi::Fred::create();
};
run_test(cxx, hdr, rs, &[], &["Bob", "Fred"]);
}
#[test]
fn test_conflicting_ns_up_functions() {
let cxx = indoc! {"
uint32_t A::create(C) { return 3; }
uint32_t B::create(C) { return 4; }
"};
let hdr = indoc! {"
#include <cstdint>
struct C {
C() {}
uint32_t a;
};
namespace A {
uint32_t create(C c);
};
namespace B {
uint32_t create(C c);
};
"};
let rs = quote! {
let c = ffi::C::new().within_unique_ptr();
let c2 = ffi::C::new().within_unique_ptr();
assert_eq!(ffi::A::create(c), 3);
assert_eq!(ffi::B::create(c2), 4);
};
run_test(cxx, hdr, rs, &["A::create", "B::create", "C"], &[]);
}
#[test]
fn test_conflicting_methods() {
let cxx = indoc! {"
uint32_t Bob::get() const { return a; }
uint32_t Fred::get() const { return b; }
"};
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
uint32_t get() const;
};
struct Fred {
uint32_t b;
uint32_t get() const;
};
"};
let rs = quote! {
let a = ffi::Bob { a: 10 };
let b = ffi::Fred { b: 20 };
assert_eq!(a.get(), 10);
assert_eq!(b.get(), 20);
};
run_test(cxx, hdr, rs, &[], &["Bob", "Fred"]);
}
#[test]
// There's a bindgen bug here. bindgen generates
// functions called 'get' and 'get1' but then generates impl
// blocks which call 'get' and 'get'. By luck, we currently
// should not be broken by this, but at some point we should take
// the time to create a minimal bindgen test case and submit it
// as a bindgen bug.
fn test_conflicting_up_wrapper_methods_not_in_ns() {
// Ensures the two names 'get' do not conflict in the flat
// cxx::bridge mod namespace.
let cxx = indoc! {"
Bob::Bob() : a(\"hello\") {}
Fred::Fred() : b(\"goodbye\") {}
std::string Bob::get() const { return a; }
std::string Fred::get() const { return b; }
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Bob {
Bob();
std::string a;
std::string get() const;
};
struct Fred {
Fred();
std::string b;
std::string get() const;
};
"};
let rs = quote! {
let a = ffi::Bob::new().within_unique_ptr();
let b = ffi::Fred::new().within_unique_ptr();
assert_eq!(a.get().as_ref().unwrap().to_str().unwrap(), "hello");
assert_eq!(b.get().as_ref().unwrap().to_str().unwrap(), "goodbye");
};
run_test(cxx, hdr, rs, &["Bob", "Fred"], &[]);
}
#[test]
fn test_conflicting_methods_in_ns() {
let cxx = indoc! {"
uint32_t A::Bob::get() const { return a; }
uint32_t B::Fred::get() const { return b; }
"};
let hdr = indoc! {"
#include <cstdint>
namespace A {
struct Bob {
uint32_t a;
uint32_t get() const;
};
}
namespace B {
struct Fred {
uint32_t b;
uint32_t get() const;
};
}
"};
let rs = quote! {
let a = ffi::A::Bob { a: 10 };
let b = ffi::B::Fred { b: 20 };
assert_eq!(a.get(), 10);
assert_eq!(b.get(), 20);
};
run_test(cxx, hdr, rs, &[], &["A::Bob", "B::Fred"]);
}
#[test]
fn test_conflicting_up_wrapper_methods_in_ns() {
let cxx = indoc! {"
A::Bob::Bob() : a(\"hello\") {}
B::Fred::Fred() : b(\"goodbye\") {}
std::string A::Bob::get() const { return a; }
std::string B::Fred::get() const { return b; }
"};
let hdr = indoc! {"
#include <cstdint>
#include <string>
namespace A {
struct Bob {
Bob();
std::string a;
std::string get() const;
};
}
namespace B {
struct Fred {
Fred();
std::string b;
std::string get() const;
};
}
"};
let rs = quote! {
let a = ffi::A::Bob::new().within_unique_ptr();
let b = ffi::B::Fred::new().within_unique_ptr();
assert_eq!(a.get().as_ref().unwrap().to_str().unwrap(), "hello");
assert_eq!(b.get().as_ref().unwrap().to_str().unwrap(), "goodbye");
};
run_test(cxx, hdr, rs, &["A::Bob", "B::Fred"], &[]);
}
#[test]
fn test_ns_struct_pod_request() {
let hdr = indoc! {"
#include <cstdint>
namespace A {
struct Bob {
uint32_t a;
};
}
"};
let rs = quote! {
ffi::A::Bob { a: 12 };
};
run_test("", hdr, rs, &[], &["A::Bob"]);
}
#[test]
fn test_conflicting_ns_funcs() {
let cxx = indoc! {"
uint32_t A::get() { return 10; }
uint32_t B::get() { return 20; }
"};
let hdr = indoc! {"
#include <cstdint>
namespace A {
uint32_t get();
}
namespace B {
uint32_t get();
}
"};
let rs = quote! {
assert_eq!(ffi::A::get(), 10);
assert_eq!(ffi::B::get(), 20);
};
run_test(cxx, hdr, rs, &["A::get", "B::get"], &[]);
}
#[ignore]
// because currently we feed a flat namespace to cxx
// This would be relatively easy to enable now that we have the facility
// to add aliases to the 'use' statements we generate, plus
// bridge_name_tracker to pick a unique name. TODO.
#[test]
fn test_conflicting_ns_structs() {
let hdr = indoc! {"
#include <cstdint>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
struct Bob {
uint32_t a;
};
}
"};
let rs = quote! {
ffi::A::Bob { a: 12 };
ffi::B::Bob { b: 12 };
};
run_test("", hdr, rs, &[], &["A::Bob", "B::Bob"]);
}
#[test]
fn test_make_string() {
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
};
"};
let rs = quote! {
use ffi::ToCppString;
let a = "hello".into_cpp();
assert_eq!(a.to_str().unwrap(), "hello");
};
run_test("", hdr, rs, &["Bob"], &[]);
}
#[test]
fn test_string_make_unique() {
let hdr = indoc! {"
#include <string>
inline void take_string(const std::string*) {};
"};
let rs = quote! {
let s = ffi::make_string("");
unsafe { ffi::take_string(s.as_ref().unwrap()) };
};
run_test("", hdr, rs, &["take_string"], &[]);
}
#[test]
fn test_string_constant() {
let hdr = indoc! {"
#include <cstdint>
const char* STRING = \"Foo\";
"};
let rs = quote! {
let a = std::str::from_utf8(ffi::STRING).unwrap().trim_end_matches(char::from(0));
assert_eq!(a, "Foo");
};
run_test("", hdr, rs, &["STRING"], &[]);
}
#[test]
fn test_string_let_cxx_string() {
let hdr = indoc! {"
#include <string>
inline void take_string(const std::string&) {};
"};
let rs = quote! {
autocxx::cxx::let_cxx_string!(s = "hello");
ffi::take_string(&s);
};
run_test("", hdr, rs, &["take_string"], &[]);
}
#[test]
fn test_pod_constant_harmless_inside_type() {
// Check that the presence of this constant doesn't break anything.
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
};
struct Anna {
uint32_t a;
const Bob BOB = Bob { 10 };
};
"};
let rs = quote! {};
run_test("", hdr, rs, &[], &["Anna"]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/93
fn test_pod_constant() {
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
};
const Bob BOB = Bob { 10 };
"};
let rs = quote! {
let a = &ffi::BOB;
assert_eq!(a.a, 10);
};
run_test("", hdr, rs, &["BOB"], &["Bob"]);
}
#[test]
fn test_pod_static_harmless_inside_type() {
// Check that the presence of this constant doesn't break anything.
// Remove this test when the following one is enabled.
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
};
struct Anna {
uint32_t a;
static Bob BOB;
};
Bob Anna::BOB = Bob { 10 };
"};
let rs = quote! {};
run_test("", hdr, rs, &[], &["Anna"]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/93
fn test_pod_static() {
let hdr = indoc! {"
#include <cstdint>
struct Bob {
uint32_t a;
};
static Bob BOB = Bob { 10 };
"};
let rs = quote! {
let a = &ffi::BOB;
assert_eq!(a.a, 10);
};
run_test("", hdr, rs, &["BOB"], &["Bob"]);
}
#[test]
#[ignore] // this probably requires code generation on the C++
// side. It's not at all clear how best to handle this.
fn test_non_pod_constant() {
let hdr = indoc! {"
#include <cstdint>
#include <string>
struct Bob {
std::string a;
std::string get() { return a };
};
const Bob BOB = Bob { \"hello\" };
"};
let rs = quote! {
let a = ffi::BOB;
// following line assumes that 'a' is a &Bob
// but who knows how we'll really do this.
assert_eq!(a.get().as_ref().unwrap().to_str().unwrap(), "hello");
};
run_test("", hdr, rs, &["BOB"], &[]);
}
#[test]
fn test_templated_typedef() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
template <typename STRING_TYPE> class BasicStringPiece {
public:
const STRING_TYPE* ptr_;
size_t length_;
};
typedef BasicStringPiece<uint8_t> StringPiece;
struct Origin {
Origin() {}
StringPiece host;
};
"};
let rs = quote! {
ffi::Origin::new().within_unique_ptr();
};
run_test("", hdr, rs, &["Origin"], &[]);
}
#[test]
fn test_struct_templated_typedef() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
struct Concrete {
uint8_t a;
};
template <typename STRING_TYPE> class BasicStringPiece {
public:
const STRING_TYPE* ptr_;
size_t length_;
};
typedef BasicStringPiece<Concrete> StringPiece;
struct Origin {
Origin() {}
StringPiece host;
};
"};
let rs = quote! {
ffi::Origin::new().within_unique_ptr();
};
run_test("", hdr, rs, &["Origin"], &[]);
}
#[test]
fn test_enum_typedef() {
let hdr = indoc! {"
enum ConstraintSolverParameters_TrailCompression : int {
ConstraintSolverParameters_TrailCompression_NO_COMPRESSION = 0,
ConstraintSolverParameters_TrailCompression_COMPRESS_WITH_ZLIB = 1
};
typedef ConstraintSolverParameters_TrailCompression TrailCompression;
"};
let rs = quote! {
let _ = ffi::TrailCompression::ConstraintSolverParameters_TrailCompression_NO_COMPRESSION;
};
run_test("", hdr, rs, &["TrailCompression"], &[]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/264
fn test_conflicting_usings() {
let hdr = indoc! {"
#include <cstdint>
#include <cstddef>
typedef size_t diff;
struct A {
using diff = diff;
diff a;
};
struct B {
using diff = diff;
diff a;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &[], &["A", "B"]);
}
#[test]
fn test_conflicting_usings_with_self_declaration1() {
let hdr = indoc! {"
#include <cstdint>
#include <cstddef>
struct common_params {
using difference_type = ptrdiff_t;
};
template <typename Params>
class btree_node {
public:
using difference_type = typename Params::difference_type;
Params params;
};
template <typename Tree>
class btree_container {
public:
using difference_type = typename Tree::difference_type;
void clear() {}
Tree b;
uint32_t a;
};
typedef btree_container<btree_node<common_params>> my_tree;
"};
let rs = quote! {};
run_test("", hdr, rs, &["my_tree"], &[]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/106
fn test_string_templated_typedef() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
template <typename STRING_TYPE> class BasicStringPiece {
public:
const STRING_TYPE* ptr_;
size_t length_;
};
typedef BasicStringPiece<std::string> StringPiece;
struct Origin {
Origin() {}
StringPiece host;
};
"};
let rs = quote! {
ffi::Origin::new().within_unique_ptr();
};
run_test("", hdr, rs, &["Origin"], &[]);
}
#[test]
fn test_associated_type_problem() {
// Regression test for a potential bindgen bug
let hdr = indoc! {"
namespace a {
template <typename> class b {};
} // namespace a
class bl {
public:
a::b<bl> bm;
};
struct B {
int a;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["B"], &[]);
}
#[test]
fn test_two_type_constructors() {
// https://github.com/google/autocxx/issues/877
let hdr = indoc! {"
struct A {
int a;
};
struct B {
int B;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[ignore] // https://github.com/rust-lang/rust-bindgen/issues/1924
#[test]
fn test_associated_type_templated_typedef_in_struct() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
template <typename STRING_TYPE> class BasicStringPiece {
public:
typedef size_t size_type;
typedef typename STRING_TYPE::value_type value_type;
const value_type* ptr_;
size_type length_;
};
typedef BasicStringPiece<std::string> StringPiece;
struct Origin {
// void SetHost(StringPiece host);
StringPiece host;
};
"};
let rs = quote! {
ffi::Origin::new().within_unique_ptr();
};
run_test("", hdr, rs, &["Origin"], &[]);
}
#[test]
fn test_associated_type_templated_typedef() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
template <typename STRING_TYPE> class BasicStringPiece {
public:
typedef size_t size_type;
typedef typename STRING_TYPE::value_type value_type;
const value_type* ptr_;
size_type length_;
};
typedef BasicStringPiece<std::string> StringPiece;
struct Container {
Container() {}
const StringPiece& get_string_piece() const { return sp; }
StringPiece sp;
};
inline void take_string_piece(const StringPiece&) {}
"};
let rs = quote! {
let sp = ffi::Container::new().within_box();
ffi::take_string_piece(sp.get_string_piece());
};
run_test("", hdr, rs, &["take_string_piece", "Container"], &[]);
}
#[test]
fn test_associated_type_templated_typedef_by_value_regular() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
template <typename STRING_TYPE> class BasicStringPiece {
public:
BasicStringPiece() : ptr_(nullptr), length_(0) {}
typedef size_t size_type;
typedef typename STRING_TYPE::value_type value_type;
const value_type* ptr_;
size_type length_;
};
typedef BasicStringPiece<std::string> StringPiece;
inline StringPiece give_string_piece() {
StringPiece s;
return s;
}
inline void take_string_piece(StringPiece) {}
"};
let rs = quote! {
let sp = ffi::give_string_piece();
ffi::take_string_piece(sp);
};
run_test_ex(
"",
hdr,
rs,
quote! {
generate!("take_string_piece")
generate!("give_string_piece")
instantiable!("StringPiece")
},
None,
None,
None,
);
}
#[test]
fn test_associated_type_templated_typedef_by_value_forward_declaration() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
template <typename STRING_TYPE> class BasicStringPiece;
typedef BasicStringPiece<std::string> StringPiece;
struct Container {
StringPiece give_string_piece() const;
void take_string_piece(StringPiece string_piece) const;
const StringPiece& get_string_piece() const;
uint32_t b;
};
inline void take_string_piece_by_ref(const StringPiece&) {}
"};
let cpp = indoc! {"
template <typename STRING_TYPE> class BasicStringPiece {
public:
BasicStringPiece() : ptr_(nullptr), length_(0) {}
typedef size_t size_type;
typedef typename STRING_TYPE::value_type value_type;
const value_type* ptr_;
size_type length_;
};
StringPiece Container::give_string_piece() const {
StringPiece s;
return s;
}
void Container::take_string_piece(StringPiece) const {}
StringPiece a;
const StringPiece& Container::get_string_piece() const {
return a;
}
"};
// As this template is forward declared we shouldn't be able to pass it by
// value, but we still want to be able to use it by reference.
let rs = quote! {
let cont = ffi::Container::new().within_box();
ffi::take_string_piece_by_ref(cont.as_ref().get_string_piece());
};
run_test(
cpp,
hdr,
rs,
&["take_string_piece_by_ref", "Container"],
&[],
);
}
#[test]
fn test_remove_cv_t_pathological() {
let hdr = indoc! {"
template <class _Ty>
struct remove_cv {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = _Fn<_Ty>;
};
template <class _Ty>
struct remove_cv<const _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = const _Fn<_Ty>;
};
template <class _Ty>
struct remove_cv<volatile _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = volatile _Fn<_Ty>;
};
template <class _Ty>
struct remove_cv<const volatile _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = const volatile _Fn<_Ty>;
};
template <class _Ty>
using remove_cv_t = typename remove_cv<_Ty>::type;
template <class _Ty>
struct remove_reference {
using type = _Ty;
using _Const_thru_ref_type = const _Ty;
};
template <class _Ty>
struct remove_reference<_Ty&> {
using type = _Ty;
using _Const_thru_ref_type = const _Ty&;
};
template <class _Ty>
struct remove_reference<_Ty&&> {
using type = _Ty;
using _Const_thru_ref_type = const _Ty&&;
};
template <class _Ty>
using remove_reference_t = typename remove_reference<_Ty>::type;
template <class _Ty>
using _Remove_cvref_t = remove_cv_t<remove_reference_t<_Ty>>;
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! {
generate_all!()
},
None,
None,
None,
);
}
#[test]
fn test_foreign_ns_func_arg_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
inline uint32_t daft(A::Bob a) { return a.a; }
}
"};
let rs = quote! {
let a = ffi::A::Bob { a: 12 };
assert_eq!(ffi::B::daft(a), 12);
};
run_test("", hdr, rs, &["B::daft"], &["A::Bob"]);
}
#[test]
fn test_foreign_ns_func_arg_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
Bob(uint32_t _a) :a(_a) {}
};
}
namespace B {
inline uint32_t daft(A::Bob a) { return a.a; }
}
"};
let rs = quote! {
let a = ffi::A::Bob::new(12).within_unique_ptr();
assert_eq!(ffi::B::daft(a), 12);
};
run_test("", hdr, rs, &["B::daft", "A::Bob"], &[]);
}
#[test]
fn test_foreign_ns_meth_arg_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
struct C {
uint32_t a;
uint32_t daft(A::Bob a) const { return a.a; }
};
}
"};
let rs = quote! {
let a = ffi::A::Bob { a: 12 };
let b = ffi::B::C { a: 12 };
assert_eq!(b.daft(a), 12);
};
run_test("", hdr, rs, &[], &["A::Bob", "B::C"]);
}
#[test]
fn test_foreign_ns_meth_arg_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
Bob(uint32_t _a) :a(_a) {}
};
}
namespace B {
struct C {
uint32_t a;
uint32_t daft(A::Bob a) const { return a.a; }
};
}
"};
let rs = quote! {
let a = ffi::A::Bob::new(12).within_unique_ptr();
let b = ffi::B::C { a: 12 };
assert_eq!(b.daft(a), 12);
};
run_test("", hdr, rs, &["A::Bob"], &["B::C"]);
}
#[test]
fn test_foreign_ns_cons_arg_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
struct C {
uint32_t a;
C(const A::Bob& input) : a(input.a) {}
};
}
"};
let rs = quote! {
let a = ffi::A::Bob { a: 12 };
let b = ffi::B::C::new(&a).within_unique_ptr();
assert_eq!(b.as_ref().unwrap().a, 12);
};
run_test("", hdr, rs, &[], &["B::C", "A::Bob"]);
}
#[test]
fn test_foreign_ns_cons_arg_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
Bob(uint32_t _a) :a(_a) {}
uint32_t a;
};
}
namespace B {
struct C {
uint32_t a;
C(const A::Bob& input) : a(input.a) {}
};
}
"};
let rs = quote! {
let a = ffi::A::Bob::new(12).within_unique_ptr();
let b = ffi::B::C::new(&a).within_unique_ptr();
assert_eq!(b.as_ref().unwrap().a, 12);
};
run_test("", hdr, rs, &["A::Bob"], &["B::C"]);
}
#[test]
fn test_foreign_ns_func_ret_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
inline A::Bob daft() { A::Bob bob; bob.a = 12; return bob; }
}
"};
let rs = quote! {
assert_eq!(ffi::B::daft().a, 12);
};
run_test("", hdr, rs, &["B::daft"], &["A::Bob"]);
}
#[test]
fn test_foreign_ns_func_ret_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
inline A::Bob daft() { A::Bob bob; bob.a = 12; return bob; }
}
"};
let rs = quote! {
ffi::B::daft().within_box().as_ref();
};
run_test("", hdr, rs, &["B::daft", "A::Bob"], &[]);
}
#[test]
fn test_foreign_ns_meth_ret_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
struct C {
uint32_t a;
A::Bob daft() const { A::Bob bob; bob.a = 12; return bob; }
};
}
"};
let rs = quote! {
let b = ffi::B::C { a: 12 };
assert_eq!(b.daft().a, 12);
};
run_test("", hdr, rs, &[], &["A::Bob", "B::C"]);
}
#[test]
fn test_foreign_ns_meth_ret_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
namespace A {
struct Bob {
uint32_t a;
};
}
namespace B {
struct C {
uint32_t a;
A::Bob daft() const { A::Bob bob; bob.a = 12; return bob; }
};
}
"};
let rs = quote! {
let b = ffi::B::C { a: 14 };
b.daft().within_unique_ptr().as_ref().unwrap();
};
run_test("", hdr, rs, &["A::Bob"], &["B::C"]);
}
#[test]
fn test_root_ns_func_arg_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
namespace B {
inline uint32_t daft(Bob a) { return a.a; }
}
"};
let rs = quote! {
let a = ffi::Bob { a: 12 };
assert_eq!(ffi::B::daft(a), 12);
};
run_test("", hdr, rs, &["B::daft"], &["Bob"]);
}
#[test]
fn test_root_ns_func_arg_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
Bob(uint32_t _a) :a(_a) {}
};
namespace B {
inline uint32_t daft(Bob a) { return a.a; }
}
"};
let rs = quote! {
let a = ffi::Bob::new(12).within_unique_ptr();
assert_eq!(ffi::B::daft(a), 12);
};
run_test("", hdr, rs, &["B::daft", "Bob"], &[]);
}
#[test]
fn test_root_ns_meth_arg_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
namespace B {
struct C {
uint32_t a;
uint32_t daft(Bob a) const { return a.a; }
};
}
"};
let rs = quote! {
let a = ffi::Bob { a: 12 };
let b = ffi::B::C { a: 12 };
assert_eq!(b.daft(a), 12);
};
run_test("", hdr, rs, &[], &["Bob", "B::C"]);
}
#[test]
fn test_root_ns_meth_arg_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
Bob(uint32_t _a) :a(_a) {}
};
namespace B {
struct C {
uint32_t a;
uint32_t daft(Bob a) const { return a.a; }
};
}
"};
let rs = quote! {
let a = ffi::Bob::new(12).within_unique_ptr();
let b = ffi::B::C { a: 12 };
assert_eq!(b.daft(a), 12);
};
run_test("", hdr, rs, &["Bob"], &["B::C"]);
}
#[test]
fn test_root_ns_cons_arg_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
namespace B {
struct C {
uint32_t a;
C(const Bob& input) : a(input.a) {}
};
}
"};
let rs = quote! {
let a = ffi::Bob { a: 12 };
let b = ffi::B::C::new(&a).within_unique_ptr();
assert_eq!(b.as_ref().unwrap().a, 12);
};
run_test("", hdr, rs, &[], &["B::C", "Bob"]);
}
#[test]
fn test_root_ns_cons_arg_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
Bob(uint32_t _a) :a(_a) {}
uint32_t a;
};
namespace B {
struct C {
uint32_t a;
C(const Bob& input) : a(input.a) {}
};
}
"};
let rs = quote! {
let a = ffi::Bob::new(12).within_unique_ptr();
let b = ffi::B::C::new(&a).within_unique_ptr();
assert_eq!(b.as_ref().unwrap().a, 12);
};
run_test("", hdr, rs, &["Bob"], &["B::C"]);
}
#[test]
fn test_root_ns_func_ret_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
namespace B {
inline Bob daft() { Bob bob; bob.a = 12; return bob; }
}
"};
let rs = quote! {
assert_eq!(ffi::B::daft().a, 12);
};
run_test("", hdr, rs, &["B::daft"], &["Bob"]);
}
#[test]
fn test_root_ns_func_ret_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
namespace B {
inline Bob daft() { Bob bob; bob.a = 12; return bob; }
}
"};
let rs = quote! {
ffi::B::daft().within_unique_ptr().as_ref().unwrap();
};
run_test("", hdr, rs, &["B::daft", "Bob"], &[]);
}
#[test]
fn test_root_ns_meth_ret_pod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
namespace B {
struct C {
uint32_t a;
Bob daft() const { Bob bob; bob.a = 12; return bob; }
};
}
"};
let rs = quote! {
let b = ffi::B::C { a: 12 };
assert_eq!(b.daft().a, 12);
};
run_test("", hdr, rs, &[], &["Bob", "B::C"]);
}
#[test]
fn test_root_ns_meth_ret_nonpod() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct Bob {
uint32_t a;
};
namespace B {
struct C {
uint32_t a;
Bob daft() const { Bob bob; bob.a = 12; return bob; }
};
}
"};
let rs = quote! {
let b = ffi::B::C { a: 12 };
b.daft().within_unique_ptr().as_ref().unwrap();
};
run_test("", hdr, rs, &["Bob"], &["B::C"]);
}
#[test]
fn test_forward_declaration() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
struct A;
struct B {
B() : a(0) {}
uint32_t a;
void daft(const A&) const {}
static B daft3(const A&) { B b; return b; }
A daft4();
std::unique_ptr<A> daft5();
const std::unique_ptr<A>& daft6();
};
A* get_a();
void delete_a(A*);
"};
let cpp = indoc! {"
struct A {
A() : a(0) {}
uint32_t a;
};
A* get_a() {
return new A();
}
void delete_a(A* a) {
delete a;
}
A B::daft4() {
A a;
return a;
}
std::unique_ptr<A> B::daft5() {
return std::make_unique<A>();
}
std::unique_ptr<A> fixed;
const std::unique_ptr<A>& B::daft6() {
return fixed;
}
"};
let rs = quote! {
let b = ffi::B::new().within_unique_ptr();
let a = ffi::get_a();
b.daft(unsafe { a.as_ref().unwrap() });
unsafe { ffi::delete_a(a) };
};
run_test(cpp, hdr, rs, &["B", "get_a", "delete_a"], &[]);
}
#[test]
fn test_ulong() {
let hdr = indoc! {"
inline unsigned long daft(unsigned long a) { return a; }
"};
let rs = quote! {
assert_eq!(ffi::daft(autocxx::c_ulong(34)), autocxx::c_ulong(34));
};
run_test("", hdr, rs, &["daft"], &[]);
}
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
#[cfg_attr(skip_windows_msvc_failing_tests, ignore)]
#[test]
fn test_typedef_to_ulong() {
let hdr = indoc! {"
typedef unsigned long fiddly;
inline fiddly daft(fiddly a) { return a; }
"};
let rs = quote! {
assert_eq!(ffi::daft(autocxx::c_ulong(34)), autocxx::c_ulong(34));
};
run_test("", hdr, rs, &["daft"], &[]);
}
#[test]
fn test_generate_typedef_to_ulong() {
let hdr = indoc! {"
#include <cstdint>
typedef uint32_t fish_t;
"};
let rs = quote! {
let _: ffi::fish_t;
};
run_test("", hdr, rs, &[], &["fish_t"]);
}
#[test]
fn test_ulong_method() {
let hdr = indoc! {"
class A {
public:
A() {};
unsigned long daft(unsigned long a) const { return a; }
};
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
assert_eq!(a.as_ref().unwrap().daft(autocxx::c_ulong(34)), autocxx::c_ulong(34));
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_ulong_wrapped_method() {
let hdr = indoc! {"
#include <cstdint>
struct B {
B() {};
uint32_t a;
};
class A {
public:
A() {};
unsigned long daft(unsigned long a, B) const { return a; }
};
"};
let rs = quote! {
let b = ffi::B::new().within_unique_ptr();
let a = ffi::A::new().within_unique_ptr();
assert_eq!(a.as_ref().unwrap().daft(autocxx::c_ulong(34), b), autocxx::c_ulong(34));
};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
fn test_reserved_name() {
let hdr = indoc! {"
#include <cstdint>
inline uint32_t async(uint32_t a) { return a; }
"};
let rs = quote! {
assert_eq!(ffi::async_(34), 34);
};
run_test("", hdr, rs, &["async_"], &[]);
}
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
#[cfg_attr(skip_windows_msvc_failing_tests, ignore)]
#[test]
fn test_nested_type() {
// Test that we can import APIs that use nested types.
// As a regression test, we also test that the nested type `A::B` doesn't conflict with the
// top-level type `B`. This used to cause compile errors.
let hdr = indoc! {"
struct A {
A() {}
struct B {
B() {}
};
enum C {};
using D = int;
};
struct B {
B() {}
void method_on_top_level_type() const {}
};
void take_A_B(A::B);
void take_A_C(A::C);
void take_A_D(A::D);
"};
let rs = quote! {
let _ = ffi::A::new().within_unique_ptr();
let b = ffi::B::new().within_unique_ptr();
b.as_ref().unwrap().method_on_top_level_type();
};
run_test("", hdr, rs, &["A", "B", "take_A_B", "take_A_C"], &[]);
}
#[test]
fn test_nested_type_in_namespace() {
// Test that we can import APIs that use nested types in a namespace.
// We can't make this part of the previous test as autocxx drops the
// namespace, so `A::B` and `N::A::B` would be imported as the same
// type.
let hdr = indoc! {"
namespace N {
struct A {
A() {}
struct B {
B() {}
};
};
};
void take_A_B(N::A::B);
"};
let rs = quote! {};
run_test("", hdr, rs, &["take_A_B"], &[]);
}
#[test]
fn test_nested_enum_in_namespace() {
let hdr = indoc! {"
namespace N {
struct A {
A() {}
enum B {
C,
D,
};
};
};
void take_A_B(N::A::B);
"};
let rs = quote! {};
run_test("", hdr, rs, &["take_A_B"], &[]);
}
#[test]
fn test_abstract_nested_type() {
let hdr = indoc! {"
namespace N {
class A {
public:
A() {}
class B {
private:
B() {}
public:
virtual ~B() {}
virtual void Foo() = 0;
};
};
};
void take_A_B(const N::A::B&);
"};
let rs = quote! {};
run_test("", hdr, rs, &["take_A_B", "N::A_B"], &[]);
}
#[test]
fn test_nested_type_constructor() {
let hdr = indoc! {"
#include <string>
class A {
public:
class B {
public:
B(const std::string&) {}
int b;
};
int a;
};
"};
let rs = quote! {
ffi::A_B::new(&ffi::make_string("Hello")).within_unique_ptr();
};
run_test("", hdr, rs, &["A_B"], &[]);
}
#[test]
fn test_generic_type() {
let hdr = indoc! {"
#include <cstdint>
#include <string>
template<typename TY>
struct Container {
Container(TY a_) : a(a_) {}
TY a;
};
struct Secondary {
Secondary() {}
void take_a(const Container<char>) const {}
void take_b(const Container<uint16_t>) const {}
uint16_t take_c(std::string a) const { return 10 + a.size(); }
};
"};
let rs = quote! {
use ffi::ToCppString;
let item = ffi::Secondary::new().within_unique_ptr();
assert_eq!(item.take_c("hello".into_cpp()), 15)
};
run_test("", hdr, rs, &["Secondary"], &[]);
}
#[test]
fn test_cycle_generic_type() {
let hdr = indoc! {"
#include <cstdint>
template<typename TY>
struct Container {
Container(TY a_) : a(a_) {}
TY a;
};
inline Container<char> make_thingy() {
Container<char> a('a');
return a;
}
typedef Container<char> Concrete;
inline uint32_t take_thingy(Concrete a) {
return a.a;
}
"};
let rs = quote! {
assert_eq!(ffi::take_thingy(ffi::make_thingy()), 'a' as u32)
};
run_test("", hdr, rs, &["take_thingy", "make_thingy"], &[]);
}
#[test]
fn test_virtual_fns() {
let hdr = indoc! {"
#include <cstdint>
class A {
public:
A(uint32_t num) : b(num) {}
virtual uint32_t foo(uint32_t a) { return a+1; };
virtual ~A() {}
uint32_t b;
};
class B: public A {
public:
B() : A(3), c(4) {}
virtual uint32_t foo(uint32_t a) { return a+2; };
uint32_t c;
};
"};
let rs = quote! {
let mut a = ffi::A::new(12).within_unique_ptr();
assert_eq!(a.pin_mut().foo(2), 3);
let mut b = ffi::B::new().within_unique_ptr();
assert_eq!(b.pin_mut().foo(2), 4);
};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
fn test_const_virtual_fns() {
let hdr = indoc! {"
#include <cstdint>
class A {
public:
A(uint32_t num) : b(num) {}
virtual uint32_t foo(uint32_t a) const { return a+1; };
virtual ~A() {}
uint32_t b;
};
class B: public A {
public:
B() : A(3), c(4) {}
virtual uint32_t foo(uint32_t a) const { return a+2; };
uint32_t c;
};
"};
let rs = quote! {
let a = ffi::A::new(12).within_unique_ptr();
assert_eq!(a.foo(2), 3);
let b = ffi::B::new().within_unique_ptr();
assert_eq!(b.foo(2), 4);
};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/197
fn test_virtual_fns_inheritance() {
let hdr = indoc! {"
#include <cstdint>
class A {
public:
A(uint32_t num) : b(num) {}
virtual uint32_t foo(uint32_t a) { return a+1; };
virtual ~A() {}
uint32_t b;
};
class B: public A {
public:
B() : A(3), c(4) {}
uint32_t c;
};
"};
let rs = quote! {
let mut b = ffi::B::new().within_unique_ptr();
assert_eq!(b.pin_mut().foo(2), 3);
};
run_test("", hdr, rs, &["B"], &[]);
}
#[test]
fn test_vector_cycle_up() {
let hdr = indoc! {"
#include <cstdint>
#include <vector>
#include <memory>
struct A {
uint32_t a;
};
inline uint32_t take_vec(std::unique_ptr<std::vector<A>> many_as) {
return many_as->size();
}
inline std::unique_ptr<std::vector<A>> get_vec() {
auto items = std::make_unique<std::vector<A>>();
items->push_back(A { 3 });
items->push_back(A { 4 });
return items;
}
"};
let rs = quote! {
let v = ffi::get_vec();
assert_eq!(v.as_ref().unwrap().is_empty(), false);
assert_eq!(ffi::take_vec(v), 2);
};
run_test("", hdr, rs, &["take_vec", "get_vec"], &[]);
}
#[test]
fn test_vector_cycle_bare() {
let hdr = indoc! {"
#include <cstdint>
#include <vector>
struct A {
uint32_t a;
};
inline uint32_t take_vec(std::vector<A> many_as) {
return many_as.size();
}
inline std::vector<A> get_vec() {
std::vector<A> items;
items.push_back(A { 3 });
items.push_back(A { 4 });
return items;
}
"};
let rs = quote! {
assert_eq!(ffi::take_vec(ffi::get_vec()), 2);
};
run_test("", hdr, rs, &["take_vec", "get_vec"], &[]);
}
#[test]
fn test_typedef_to_std() {
let hdr = indoc! {"
#include <string>
typedef std::string my_string;
inline uint32_t take_str(my_string a) {
return a.size();
}
"};
let rs = quote! {
use ffi::ToCppString;
assert_eq!(ffi::take_str("hello".into_cpp()), 5);
};
run_test("", hdr, rs, &["take_str"], &[]);
}
#[test]
fn test_typedef_to_up_in_fn_call() {
let hdr = indoc! {"
#include <string>
#include <memory>
typedef std::unique_ptr<std::string> my_string;
inline uint32_t take_str(my_string a) {
return a->size();
}
"};
let rs = quote! {
use ffi::ToCppString;
assert_eq!(ffi::take_str("hello".into_cpp()), 5);
};
run_test("", hdr, rs, &["take_str"], &[]);
}
#[test]
fn test_typedef_in_pod_struct() {
let hdr = indoc! {"
#include <string>
typedef uint32_t my_int;
struct A {
my_int a;
};
inline uint32_t take_a(A a) {
return a.a;
}
"};
let rs = quote! {
let a = ffi::A {
a: 32,
};
assert_eq!(ffi::take_a(a), 32);
};
run_test("", hdr, rs, &["take_a"], &["A"]);
}
#[test]
fn test_cint_in_pod_struct() {
let hdr = indoc! {"
#include <string>
struct A {
int a;
};
inline uint32_t take_a(A a) {
return a.a;
}
"};
let rs = quote! {
let a = ffi::A {
a: 32,
};
assert_eq!(ffi::take_a(a), 32);
};
run_test("", hdr, rs, &["take_a"], &["A"]);
}
#[test]
fn test_string_in_struct() {
let hdr = indoc! {"
#include <string>
#include <memory>
struct A {
std::string a;
};
inline A make_a(std::string b) {
A bob;
bob.a = b;
return bob;
}
inline uint32_t take_a(A a) {
return a.a.size();
}
"};
let rs = quote! {
use ffi::ToCppString;
assert_eq!(ffi::take_a(as_new(ffi::make_a("hello".into_cpp()))), 5);
};
run_test("", hdr, rs, &["make_a", "take_a"], &[]);
}
#[test]
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
fn test_up_in_struct() {
let hdr = indoc! {"
#include <string>
#include <memory>
struct A {
std::unique_ptr<std::string> a;
};
inline A make_a(std::string b) {
A bob;
bob.a = std::make_unique<std::string>(b);
return bob;
}
inline uint32_t take_a(A a) {
return a.a->size();
}
"};
let rs = quote! {
use ffi::ToCppString;
assert_eq!(ffi::take_a(as_new(ffi::make_a("hello".into_cpp()))), 5);
};
run_test("", hdr, rs, &["make_a", "take_a"], &[]);
}
#[test]
fn test_typedef_to_std_in_struct() {
let hdr = indoc! {"
#include <string>
typedef std::string my_string;
struct A {
my_string a;
};
inline A make_a(std::string b) {
A bob;
bob.a = b;
return bob;
}
inline uint32_t take_a(A a) {
return a.a.size();
}
"};
let rs = quote! {
use ffi::ToCppString;
assert_eq!(ffi::take_a(as_new(ffi::make_a("hello".into_cpp()))), 5);
};
run_test("", hdr, rs, &["make_a", "take_a"], &[]);
}
#[test]
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
fn test_typedef_to_up_in_struct() {
let hdr = indoc! {"
#include <string>
#include <memory>
typedef std::unique_ptr<std::string> my_string;
struct A {
my_string a;
};
inline A make_a(std::string b) {
A bob;
bob.a = std::make_unique<std::string>(b);
return bob;
}
inline uint32_t take_a(A a) {
return a.a->size();
}
"};
let rs = quote! {
use ffi::ToCppString;
assert_eq!(ffi::take_a(as_new(ffi::make_a("hello".into_cpp()))), 5);
};
run_test("", hdr, rs, &["make_a", "take_a"], &[]);
}
#[test]
fn test_float() {
let hdr = indoc! {"
inline float daft(float a) { return a; }
"};
let rs = quote! {
assert_eq!(ffi::daft(34.0f32), 34.0f32);
};
run_test("", hdr, rs, &["daft"], &[]);
}
#[test]
fn test_double() {
let hdr = indoc! {"
inline double daft(double a) { return a; }
"};
let rs = quote! {
assert_eq!(ffi::daft(34.0f64), 34.0f64);
};
run_test("", hdr, rs, &["daft"], &[]);
}
#[test]
fn test_issues_217_222() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
#include <cstddef>
template <typename STRING_TYPE> class BasicStringPiece {
public:
typedef size_t size_type;
typedef typename STRING_TYPE::traits_type traits_type;
typedef typename STRING_TYPE::value_type value_type;
typedef const value_type* pointer;
typedef const value_type& reference;
typedef const value_type& const_reference;
typedef ptrdiff_t difference_type;
typedef const value_type* const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
static const size_type npos;
};
template<typename CHAR>
class Replacements {
public:
Replacements() {
}
void SetScheme(const CHAR*) {
}
uint16_t a;
};
struct Component {
uint16_t a;
};
template <typename STR>
class StringPieceReplacements : public Replacements<typename STR::value_type> {
private:
using CharT = typename STR::value_type;
using StringPieceT = BasicStringPiece<STR>;
using ParentT = Replacements<CharT>;
using SetterFun = void (ParentT::*)(const CharT*, const Component&);
void SetImpl(SetterFun, StringPieceT) {
}
public:
void SetSchemeStr(const CharT* str) { SetImpl(&ParentT::SetScheme, str); }
};
class GURL {
public:
typedef StringPieceReplacements<std::string> UrlReplacements;
GURL() {}
GURL ReplaceComponents(const Replacements<char>&) const {
return GURL();
}
uint16_t a;
};
"};
let rs = quote! {
ffi::GURL::new().within_unique_ptr();
};
// The block! directives here are to avoid running into
// https://github.com/rust-lang/rust-bindgen/pull/1975
run_test_ex(
"",
hdr,
rs,
quote! { generate!("GURL") block!("StringPiece") block!("Replacements") },
None,
None,
None,
);
}
#[test]
#[ignore] // https://github.com/rust-lang/rust-bindgen/pull/1975, https://github.com/google/autocxx/issues/106
fn test_dependent_qualified_type() {
let hdr = indoc! {"
#include <stddef.h>
struct MyString {
typedef char value_type;
};
template<typename T> struct MyStringView {
typedef typename T::value_type view_value_type;
const view_value_type* start;
size_t length;
};
const char* HELLO = \"hello\";
inline MyStringView<MyString> make_string_view() {
MyStringView<MyString> r;
r.start = HELLO;
r.length = 2;
return r;
}
inline size_t take_string_view(const MyStringView<MyString>& bit) {
return bit.length;
}
"};
let rs = quote! {
let sv = ffi::make_string_view();
assert_eq!(ffi::take_string_view(sv.as_ref().unwrap()), 2);
};
run_test("", hdr, rs, &["take_string_view", "make_string_view"], &[]);
}
#[test]
fn test_simple_dependent_qualified_type() {
// bindgen seems to cope with this case just fine
let hdr = indoc! {"
#include <stddef.h>
#include <stdint.h>
struct MyString {
typedef char value_type;
};
template<typename T> struct MyStringView {
typedef typename T::value_type view_value_type;
const view_value_type* start;
size_t length;
};
typedef MyStringView<MyString>::view_value_type MyChar;
inline MyChar make_char() {
return 'a';
}
inline uint32_t take_char(MyChar c) {
return static_cast<unsigned char>(c);
}
"};
let rs = quote! {
let c = ffi::make_char();
assert_eq!(ffi::take_char(c), 97);
};
run_test("", hdr, rs, &["make_char", "take_char"], &[]);
}
#[test]
fn test_ignore_dependent_qualified_type() {
let hdr = indoc! {"
#include <stddef.h>
struct MyString {
typedef char value_type;
};
template<typename T> struct MyStringView {
typedef typename T::value_type view_value_type;
const view_value_type* start;
size_t length;
};
MyStringView<MyString> make_string_view();
struct B {
B() {}
inline size_t take_string_view(const MyStringView<MyString> bit) {
return bit.length;
}
};
"};
let cpp = indoc! {"
const char* HELLO = \"hello\";
MyStringView<MyString> make_string_view() {
MyStringView<MyString> r;
r.start = HELLO;
r.length = 2;
return r;
}
"};
let rs = quote! {
ffi::B::new().within_unique_ptr();
};
run_test(cpp, hdr, rs, &["B"], &[]);
}
#[test]
fn test_ignore_dependent_qualified_type_reference() {
let hdr = indoc! {"
#include <stddef.h>
struct MyString {
typedef char value_type;
};
template<typename T> struct MyStringView {
typedef typename T::value_type view_value_type;
const view_value_type* start;
size_t length;
};
MyStringView<MyString> make_string_view();
struct B {
B() {}
inline size_t take_string_view(const MyStringView<MyString>& bit) {
return bit.length;
}
};
"};
let cpp = indoc! {"
const char* HELLO = \"hello\";
MyStringView<MyString> make_string_view() {
MyStringView<MyString> r;
r.start = HELLO;
r.length = 2;
return r;
}
"};
let rs = quote! {
ffi::B::new().within_unique_ptr();
};
run_test(cpp, hdr, rs, &["B"], &[]);
}
#[test]
fn test_specialization() {
let hdr = indoc! {"
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <type_traits>
template <typename T, bool = std::is_trivially_destructible<T>::value>
struct OptionalStorageBase {
T value_;
};
template <typename T,
bool = std::is_trivially_copy_constructible<T>::value,
bool = std::is_trivially_move_constructible<T>::value>
struct OptionalStorage : OptionalStorageBase<T> {};
template <typename T>
struct OptionalStorage<T,
true /* trivially copy constructible */,
false /* trivially move constructible */>
: OptionalStorageBase<T> {
};
template <typename T>
struct OptionalStorage<T,
false /* trivially copy constructible */,
true /* trivially move constructible */>
: OptionalStorageBase<T> {
};
template <typename T>
struct OptionalStorage<T,
true /* trivially copy constructible */,
true /* trivially move constructible */>
: OptionalStorageBase<T> {
};
template <typename T>
class OptionalBase {
private:
OptionalStorage<T> storage_;
};
template <typename T>
class Optional : public OptionalBase<T> {
};
struct B {
B() {}
void take_optional(Optional<std::string>) {}
uint32_t a;
};
"};
let rs = quote! {
ffi::B::new().within_unique_ptr();
};
run_test("", hdr, rs, &["B"], &[]);
}
#[test]
fn test_private_constructor_make_unique() {
let hdr = indoc! {"
#include <stdint.h>
struct A {
private:
A() {};
public:
uint32_t a;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/266
fn test_take_array() {
let hdr = indoc! {"
#include <cstdint>
uint32_t take_array(const uint32_t a[4]) {
return a[0] + a[2];
}
"};
let rs = quote! {
let c: [u32; 4usize] = [ 10, 20, 30, 40 ];
let c = c as *const [_];
assert_eq!(ffi::take_array(&c), 40);
};
run_test("", hdr, rs, &["take_array"], &[]);
}
#[test]
fn test_take_array_in_struct() {
let hdr = indoc! {"
#include <cstdint>
struct data {
char a[4];
};
uint32_t take_array(const data a) {
return a.a[0] + a.a[2];
}
"};
let rs = quote! {
let c = ffi::data { a: [ 10, 20, 30, 40 ] };
assert_eq!(ffi::take_array(c), 40);
};
run_test("", hdr, rs, &["take_array"], &["data"]);
}
#[test]
fn test_union_ignored() {
let hdr = indoc! {"
#include <cstdint>
union A {
uint32_t a;
float b;
};
struct B {
B() :a(1) {}
uint32_t take_union(A) const {
return 3;
}
uint32_t get_a() const { return 2; }
uint32_t a;
};
"};
let rs = quote! {
let b = ffi::B::new().within_unique_ptr();
assert_eq!(b.get_a(), 2);
};
run_test("", hdr, rs, &["B"], &[]);
}
#[test]
fn test_double_underscores_ignored() {
let hdr = indoc! {"
#include <cstdint>
struct __FOO {
uint32_t a;
};
struct B {
B() :a(1) {}
uint32_t take_foo(__FOO) const {
return 3;
}
void do__something() const { }
uint32_t get_a() const { return 2; }
uint32_t a;
};
struct __default { __default() = default; };
struct __destructor { ~__destructor() = default; };
struct __copy { __copy(const __copy&) = default; };
struct __copy_operator { __copy_operator &operator=(const __copy_operator&) = default; };
struct __move { __move(__move&&) = default; };
struct __move_operator { __move_operator &operator=(const __move_operator&) = default; };
"};
let rs = quote! {
let b = ffi::B::new().within_unique_ptr();
assert_eq!(b.get_a(), 2);
};
run_test(
"",
hdr,
rs,
&[
"B",
"__default",
"__destructor",
"__copy",
"__copy_operator",
"__move",
"__move_operator",
],
&[],
);
}
// This test fails on Windows gnu but not on Windows msvc
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
#[test]
fn test_double_underscore_typedef_ignored() {
let hdr = indoc! {"
#include <cstdint>
typedef int __int32_t;
typedef __int32_t __darwin_pid_t;
typedef __darwin_pid_t pid_t;
struct B {
B() :a(1) {}
uint32_t take_foo(pid_t) const {
return 3;
}
uint32_t get_a() const { return 2; }
uint32_t a;
};
"};
let rs = quote! {
let b = ffi::B::new().within_unique_ptr();
assert_eq!(b.get_a(), 2);
};
run_test("", hdr, rs, &["B"], &[]);
}
#[test]
fn test_double_underscores_fn_namespace() {
let hdr = indoc! {"
namespace __B {
inline void a() {}
};
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_typedef_to_ptr_is_marked_unsafe() {
let hdr = indoc! {"
struct _xlocalefoo; /* forward reference */
typedef struct _xlocalefoo * locale_tfoo;
extern \"C\" {
locale_tfoo duplocalefoo(locale_tfoo);
}
"};
let rs = quote! {};
run_test("", hdr, rs, &["duplocalefoo"], &[]);
}
#[test]
fn test_issue_264() {
let hdr = indoc! {"
namespace a {
typedef int b;
//inline namespace c {}
template <typename> class aa;
inline namespace c {
template <typename d, typename = d, typename = aa<d>> class e;
}
typedef e<char> f;
template <typename g, typename, template <typename> typename> struct h {
using i = g;
};
template <typename g, template <typename> class k> using j = h<g, void, k>;
template <typename g, template <typename> class k>
using m = typename j<g, k>::i;
template <typename> struct l { typedef b ab; };
template <typename p> class aa {
public:
typedef p n;
};
struct r {
template <typename p> using o = typename p::c;
};
template <typename ad> struct u : r {
typedef typename ad::n n;
using ae = m<n, o>;
template <typename af, typename> struct v { using i = typename l<f>::ab; };
using ab = typename v<ad, ae>::i;
};
} // namespace a
namespace q {
template <typename ad> struct w : a::u<ad> {};
} // namespace q
namespace a {
inline namespace c {
template <typename, typename, typename ad> class e {
typedef q::w<ad> s;
public:
typedef typename s::ab ab;
};
} // namespace c
} // namespace a
namespace ag {
namespace ah {
typedef a::f::ab t;
class ai {
public:
t aj;
};
class al;
namespace am {
class an {
public:
void ao(ai);
};
} // namespace am
class ap {
public:
al aq();
};
class ar {
public:
am::an as;
};
class al {
public:
ar at;
};
struct au {
ap av;
};
} // namespace ah
} // namespace ag
namespace operations_research {
class aw {
public:
ag::ah::au ax;
};
class Solver {
public:
aw ay;
};
} // namespace operations_research
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
directives_from_lists(&["operations_research::Solver"], &[], None),
make_cpp17_adder(),
None,
None,
);
}
#[test]
fn test_unexpected_use() {
// https://github.com/google/autocxx/issues/303
let hdr = indoc! {"
typedef int a;
namespace b {
namespace c {
enum d : a;
}
} // namespace b
namespace {
using d = b::c::d;
}
namespace content {
class RenderFrameHost {
public:
RenderFrameHost() {}
d e;
};
} // namespace content
"};
let rs = quote! {
let _ = ffi::content::RenderFrameHost::new().within_unique_ptr();
};
run_test("", hdr, rs, &["content::RenderFrameHost"], &[]);
}
#[test]
fn test_get_pure_virtual() {
let hdr = indoc! {"
#include <cstdint>
class A {
public:
virtual ~A() {}
virtual uint32_t get_val() const = 0;
};
class B : public A {
public:
virtual uint32_t get_val() const { return 3; }
};
const B b;
inline const A* get_a() { return &b; };
"};
let rs = quote! {
let a = ffi::get_a();
let a_ref = unsafe { a.as_ref() }.unwrap();
assert_eq!(a_ref.get_val(), 3);
};
run_test("", hdr, rs, &["A", "get_a"], &[]);
}
#[test]
fn test_abstract_class_no_make_unique() {
// We shouldn't generate a new().within_unique_ptr() for abstract classes.
// The test is successful if the bindings compile, i.e. if autocxx doesn't
// attempt to instantiate the class.
let hdr = indoc! {"
class A {
public:
A() {}
virtual ~A() {}
virtual void foo() const = 0;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_derived_abstract_class_no_make_unique() {
let hdr = indoc! {"
class A {
public:
A();
virtual ~A() {}
virtual void foo() const = 0;
};
class B : public A {
public:
B();
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
fn test_recursive_derived_abstract_class_no_make_unique() {
let hdr = indoc! {"
class A {
public:
A() {}
virtual ~A() {}
virtual void foo() const = 0;
};
class B : public A {
public:
B() {};
};
class C : public B {
public:
C() {};
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A", "B", "C"], &[]);
}
#[test]
fn test_derived_abstract_class_with_no_allowlisting_no_make_unique() {
let hdr = indoc! {"
class A {
public:
A();
virtual ~A() {}
virtual void foo() const = 0;
};
class B : public A {
public:
B();
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["B"], &[]);
}
#[test]
fn test_vector_of_pointers() {
// Just ensures the troublesome API is ignored
let hdr = indoc! {"
#include <vector>
namespace operations_research {
class a;
class Solver {
public:
struct b c(std::vector<a *>);
};
class a {};
} // namespace operations_research
"};
let rs = quote! {};
run_test("", hdr, rs, &["operations_research::Solver"], &[]);
}
#[test]
fn test_vec_and_up_of_primitives() {
let hdr = indoc! {"
#include <vector>
#include <memory>
#include <cstdint>
class Value {
public:
Value(std::vector<uint32_t>) {} // OK
Value(std::unique_ptr<uint32_t>) {} // should be ignored
Value(std::vector<int>) {} // should be ignored
Value(std::unique_ptr<int>) {} // should be ignored
Value(std::vector<char>) {} // should be ignored
Value(std::unique_ptr<char>) {} // should be ignored
Value(std::vector<float>) {} // OK
Value(std::unique_ptr<float>) {} // should be ignored
Value(std::vector<bool>) {} // should be ignored
Value(std::unique_ptr<bool>) {} // should be ignored
Value(std::vector<size_t>) {} // OK
Value(std::unique_ptr<size_t>) {} // should be ignored
};
inline std::vector<uint32_t> make_vec_uint32_t() {
std::vector<uint32_t> a;
return a;
}
inline std::vector<float> make_vec_float() {
std::vector<float> a;
return a;
}
inline std::vector<size_t> make_vec_size_t() {
std::vector<size_t> a;
return a;
}
"};
let rs = quote! {
ffi::Value::new(ffi::make_vec_uint32_t()).within_box();
ffi::Value::new6(ffi::make_vec_float()).within_box();
ffi::Value::new10(ffi::make_vec_size_t()).within_box();
};
run_test(
"",
hdr,
rs,
&[
"Value",
"make_vec_uint32_t",
"make_vec_float",
"make_vec_size_t",
],
&[],
);
}
#[test]
fn test_pointer_to_pointer() {
// Just ensures the troublesome API is ignored
let hdr = indoc! {"
namespace operations_research {
class a;
class Solver {
public:
struct b c(a **);
};
class a {};
} // namespace operations_research
"};
let rs = quote! {};
run_test("", hdr, rs, &["operations_research::Solver"], &[]);
}
#[test]
fn test_defines_effective() {
let hdr = indoc! {"
#include <cstdint>
#ifdef FOO
inline uint32_t a() { return 4; }
#endif
"};
let rs = quote! {
ffi::a();
};
run_test_ex(
"",
hdr,
rs,
quote! { generate!("a") },
make_clang_arg_adder(&["-DFOO"]),
None,
None,
);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/227
fn test_function_pointer_template() {
let hdr = indoc! {"
typedef int a;
namespace std {
template <typename> class b;
}
typedef a c;
namespace operations_research {
class d;
class Solver {
public:
typedef std::b<c()> IndexEvaluator3;
d e(IndexEvaluator3);
};
class d {};
} // namespace operations_research
"};
let rs = quote! {};
run_test("", hdr, rs, &["operations_research::Solver"], &[]);
}
#[test]
fn test_cvoid() {
let hdr = indoc! {"
#include <memory>
#include <cstdint>
inline void* a() {
return static_cast<void*>(new int(3));
}
inline uint32_t b(void* p) {
int* p_int = static_cast<int*>(p);
auto val = *p_int;
delete p_int;
return val;
}
"};
let rs = quote! {
let ptr = ffi::a();
let res = unsafe { ffi::b(ptr) };
assert_eq!(res, 3);
};
run_test("", hdr, rs, &["a", "b"], &[]);
}
#[test]
fn test_c_schar() {
let hdr = indoc! {"
inline signed char a() {
return 8;
}
"};
let rs = quote! {
assert_eq!(ffi::a(), 8);
};
run_test("", hdr, rs, &["a"], &[]);
}
#[test]
fn test_c_uchar() {
let hdr = indoc! {"
inline unsigned char a() {
return 8;
}
"};
let rs = quote! {
assert_eq!(ffi::a(), 8);
};
run_test("", hdr, rs, &["a"], &[]);
}
#[test]
fn test_c_ulonglong() {
// We don't test all the different variable-length integer types which we populate.
// If one works, they probably all do. Hopefully.
let hdr = indoc! {"
inline unsigned long long a() {
return 8;
}
"};
let rs = quote! {
assert_eq!(ffi::a(), autocxx::c_ulonglong(8));
};
run_test("", hdr, rs, &["a"], &[]);
}
#[test]
fn test_string_transparent_function() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
inline uint32_t take_string(std::string a) { return a.size(); }
"};
let rs = quote! {
assert_eq!(ffi::take_string("hello"), 5);
};
run_test("", hdr, rs, &["take_string"], &[]);
}
#[test]
fn test_string_transparent_method() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
struct A {
A() {}
inline uint32_t take_string(std::string a) const { return a.size(); }
};
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
assert_eq!(a.take_string("hello"), 5);
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_string_transparent_static_method() {
let hdr = indoc! {"
#include <string>
#include <cstdint>
struct A {
A() {}
static inline uint32_t take_string(std::string a) { return a.size(); }
};
"};
let rs = quote! {
assert_eq!(ffi::A::take_string("hello"), 5);
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/490
fn test_issue_490() {
let hdr = indoc! {"
typedef int a;
typedef long unsigned size_t;
namespace std {
namespace {
using ::size_t;
template <class b, b c> struct g { static const b value = c; };
template <bool d> using e = g<bool, d>;
typedef e<true> true_type;
template <size_t, size_t> struct ag {};
template <class b> typename b ::h move();
template <class> class allocator;
template <class> class vector;
} // namespace
} // namespace std
void *operator new(size_t, void *);
namespace std {
namespace {
template <class> struct iterator;
template <class b, class> struct ay { using h = b *; };
template <class b> struct bj { b bk; };
template <class bm, class> class bn : bj<bm> {};
template <class b, class i = b> class unique_ptr {
typedef i bp;
typedef typename ay<b, bp>::h bh;
bn<bh, bp> bq;
public:
unique_ptr();
unique_ptr(bh);
bh get() const;
bh release();
};
template <class = void> struct bt;
} // namespace
} // namespace std
typedef a bv;
namespace absl {
template <typename ce> class cj {
public:
using bh = ce *;
using iterator = bh;
};
namespace j {
template <class ce> struct cp {
using k = ce;
using cq = std::bt<>;
};
template <class ce> using cr = typename cp<ce>::k;
template <class ce> using cs = typename cp<ce>::cq;
template <class, class, class, class> class ct {
public:
class iterator {};
class cu {
cu(iterator);
iterator cv;
};
};
template <typename> struct cw;
} // namespace j
template <class ce, class k = j::cr<ce>, class cq = j::cs<ce>,
class cx = std::allocator<ce>>
class cy : public j::ct<j::cw<ce>, k, cq, cx> {};
} // namespace absl
namespace cz {
template <typename da> class db { std::ag<sizeof(a), alignof(da)> c; };
} // namespace cz
namespace spanner {
class l;
class ColumnList {
public:
typedef absl::cj<l>::iterator iterator;
iterator begin();
};
class dd {
union {
cz::db<absl::cy<bv>::cu> e;
};
};
class Row {
public:
bool f(dd);
};
} // namespace spanner
"};
let rs = quote! {};
run_test("", hdr, rs, &["spanner::Row", "spanner::ColumnList"], &[]);
}
#[test]
fn test_immovable_object() {
let hdr = indoc! {"
class A {
public:
A();
A(A&&) = delete;
};
class B{
public:
B();
B(const B&) = delete;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
fn test_struct_with_reference() {
let hdr = indoc! {"
#include <cstdint>
#include <utility>
struct A {
uint32_t a;
};
struct B {
B(const A& param) : a(param) {}
const A& a;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
fn test_struct_with_rvalue() {
let hdr = indoc! {"
#include <cstdint>
#include <utility>
struct A {
uint32_t a;
};
struct B {
B(A&& param) : a(std::move(param)) {}
A&& a;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
fn test_immovable_nested_object() {
let hdr = indoc! {"
struct C {
class A {
public:
A();
A(A&&) = delete;
};
class B{
public:
B();
B(const B&) = delete;
};
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["C_A", "C_B"], &[]);
}
#[test]
fn test_type_called_type() {
let hdr = indoc! {"
namespace a {
template<int _Len>
struct b
{
union type
{
unsigned char __data[_Len];
struct foo {
int a;
};
};
};
}
inline void take_type(a::b<4>::type) {}
"};
let rs = quote! {};
run_test("", hdr, rs, &["take_type"], &[]);
}
#[test]
fn test_bridge_conflict_ty() {
let hdr = indoc! {"
namespace a {
struct Key { int a; };
}
namespace b {
struct Key { int a; };
}
"};
let rs = quote! {};
run_test("", hdr, rs, &["a::Key", "b::Key"], &[]);
}
#[test]
fn test_bridge_conflict_ty_fn() {
let hdr = indoc! {"
namespace a {
struct Key { int a; };
}
namespace b {
inline void Key() {}
}
"};
let rs = quote! {};
run_test("", hdr, rs, &["a::Key", "b::Key"], &[]);
}
#[test]
fn test_issue_506() {
let hdr = indoc! {"
namespace std {
template <class, class> class am;
typedef am<char, char> an;
} // namespace std
namespace be {
class bf {
virtual std::an bg() = 0;
};
class bh : bf {};
} // namespace be
namespace spanner {
class Database;
class Row {
public:
Row(be::bh *);
};
} // namespace spanner
"};
let rs = quote! {};
run_test("", hdr, rs, &["spanner::Database", "spanner::Row"], &[]);
}
#[test]
fn test_private_inheritance() {
let hdr = indoc! {"
class A {
public:
void foo() {}
int a;
};
class B : A {
public:
void bar() {}
int b;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A", "B"], &[]);
}
#[test]
fn test_error_generated_for_static_data() {
let hdr = indoc! {"
#include <cstdint>
struct A {
A() {}
uint32_t a;
};
static A FOO = A();
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! { generate!("FOO")},
None,
Some(make_error_finder("FOO")),
None,
);
}
#[test]
fn test_error_generated_for_array_dependent_function() {
let hdr = indoc! {"
#include <cstdint>
#include <functional>
inline void take_func(std::function<bool(const uint32_t number)>) {
}
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! { generate! ("take_func")},
None,
Some(make_error_finder("take_func")),
None,
);
}
#[test]
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
#[cfg_attr(skip_windows_msvc_failing_tests, ignore)]
fn test_error_generated_for_array_dependent_method() {
let hdr = indoc! {"
#include <cstdint>
#include <functional>
struct A {
void take_func(std::function<bool(const uint32_t number)>) {
}
};
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! { generate! ("A")},
None,
Some(make_string_finder(
["take_func", "couldn't be generated"]
.map(|s| s.to_string())
.to_vec(),
)),
None,
);
}
#[test]
fn test_error_generated_for_pod_with_nontrivial_destructor() {
// take_a is necessary here because cxx won't generate the required
// static assertions unless the type is actually used in some context
// where cxx needs to decide it's trivial or non-trivial.
let hdr = indoc! {"
#include <cstdint>
#include <functional>
struct A {
~A() {}
};
inline void take_a(A) {}
"};
let rs = quote! {};
run_test_expect_fail("", hdr, rs, &["take_a"], &["A"]);
}
#[test]
fn test_error_generated_for_pod_with_nontrivial_move_constructor() {
// take_a is necessary here because cxx won't generate the required
// static assertions unless the type is actually used in some context
// where cxx needs to decide it's trivial or non-trivial.
let hdr = indoc! {"
#include <cstdint>
#include <functional>
struct A {
A() = default;
A(A&&) {}
};
inline void take_a(A) {}
"};
let rs = quote! {};
run_test_expect_fail("", hdr, rs, &["take_a"], &["A"]);
}
#[test]
fn test_double_destruction() {
let hdr = indoc! {"
#include <stdio.h>
#include <stdlib.h>
// A simple type to let Rust verify the destructor is run.
struct NotTriviallyDestructible {
NotTriviallyDestructible() = default;
NotTriviallyDestructible(const NotTriviallyDestructible&) = default;
NotTriviallyDestructible(NotTriviallyDestructible&&) = default;
~NotTriviallyDestructible() {}
};
struct ExplicitlyDefaulted {
ExplicitlyDefaulted() = default;
~ExplicitlyDefaulted() = default;
NotTriviallyDestructible flag;
};
"};
let rs = quote! {
moveit! {
let mut moveit_t = ffi::ExplicitlyDefaulted::new();
}
};
match do_run_test(
"",
hdr,
rs,
directives_from_lists(
&[],
&["NotTriviallyDestructible", "ExplicitlyDefaulted"],
None,
),
None,
None,
None,
) {
Err(TestError::CppBuild(_)) => {} // be sure this fails due to a static_assert
// rather than some runtime problem
_ => panic!("Test didn't fail as expected"),
};
}
#[test]
fn test_keyword_function() {
let hdr = indoc! {"
inline void move(int) {};
"};
let rs = quote! {};
run_test("", hdr, rs, &["move_"], &[]);
}
#[test]
fn test_keyword_method() {
let hdr = indoc! {"
struct A {
int a;
inline void move() {};
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_doc_passthru() {
let hdr = indoc! {"
#include <cstdint>
/// Elephants!
struct A {
uint32_t a;
};
/// Giraffes!
struct B {
uint32_t a;
};
/// Rhinos!
inline uint32_t get_a() { return 3; }
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
directives_from_lists(&["A", "get_a"], &["B"], None),
None,
Some(make_string_finder(
["Giraffes", "Elephants", "Rhinos"]
.map(|s| s.to_string())
.to_vec(),
)),
None,
);
}
#[test]
fn test_closure() {
// Ensuring presence of this closure doesn't break other things
let hdr = indoc! {"
#include <functional>
#include <cstdint>
inline bool take_closure(std::function<bool(const uint32_t number)> fn) {
return fn(5);
}
inline uint32_t get_a() {
return 3;
}
"};
let rs = quote! {
assert_eq!(ffi::get_a(), 3);
};
run_test("", hdr, rs, &["get_a"], &[]);
}
#[test]
fn test_multiply_nested_inner_type() {
let hdr = indoc! {"
struct Turkey {
struct Duck {
struct Hen {
int wings;
};
struct HenWithDefault {
HenWithDefault() = default;
int wings;
};
struct HenWithDestructor {
~HenWithDestructor() = default;
int wings;
};
struct HenWithCopy {
HenWithCopy() = default;
HenWithCopy(const HenWithCopy&) = default;
int wings;
};
struct HenWithMove {
HenWithMove() = default;
HenWithMove(HenWithMove&&) = default;
int wings;
};
};
};
"};
let rs = quote! {
ffi::Turkey_Duck_Hen::new().within_unique_ptr();
ffi::Turkey_Duck_HenWithDefault::new().within_unique_ptr();
ffi::Turkey_Duck_HenWithDestructor::new().within_unique_ptr();
ffi::Turkey_Duck_HenWithCopy::new().within_unique_ptr();
ffi::Turkey_Duck_HenWithMove::new().within_unique_ptr();
moveit! {
let hen = ffi::Turkey_Duck_Hen::new();
let moved_hen = autocxx::moveit::new::mov(hen);
let _copied_hen = autocxx::moveit::new::copy(moved_hen);
let hen = ffi::Turkey_Duck_HenWithDefault::new();
let moved_hen = autocxx::moveit::new::mov(hen);
let _copied_hen = autocxx::moveit::new::copy(moved_hen);
let _hen = ffi::Turkey_Duck_HenWithDestructor::new();
let hen = ffi::Turkey_Duck_HenWithCopy::new();
let _copied_hen = autocxx::moveit::new::copy(hen);
let hen = ffi::Turkey_Duck_HenWithMove::new();
let _moved_hen = autocxx::moveit::new::mov(hen);
}
};
run_test(
"",
hdr,
rs,
&[],
&[
"Turkey_Duck_Hen",
"Turkey_Duck_HenWithDefault",
"Turkey_Duck_HenWithDestructor",
"Turkey_Duck_HenWithCopy",
"Turkey_Duck_HenWithMove",
],
);
}
#[test]
fn test_underscored_namespace_for_inner_type() {
let hdr = indoc! {"
namespace __foo {
struct daft {
struct bob {
int a;
};
int a;
};
}
inline void bar(__foo::daft::bob) {}
"};
let rs = quote! {};
run_test("", hdr, rs, &["bar"], &[]);
}
#[test]
fn test_blocklist_not_overly_broad() {
// This is a regression test. We used to block anything that starts with "rust" or "std",
// not just items in the "rust" and "std" namespaces. We therefore test that functions starting
// with "rust" or "std" get imported.
let hdr = indoc! {"
inline void rust_func() { }
inline void std_func() { }
"};
let rs = quote! {
ffi::rust_func();
ffi::std_func();
};
run_test("", hdr, rs, &["rust_func", "std_func"], &[]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/837
fn test_ref_qualified_method() {
let hdr = indoc! {"
struct A {
void foo() & {}
};
"};
let rs = quote! {
A::new().within_unique_ptr().foo();
};
run_test("", hdr, rs, &["A"], &[]);
}
#[cfg_attr(skip_windows_msvc_failing_tests, ignore)]
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
#[test]
fn test_stringview() {
// Test that APIs using std::string_view do not otherwise cause errors.
let hdr = indoc! {"
#include <string_view>
#include <string>
void take_string_view(std::string_view) {}
std::string_view return_string_view(const std::string& a) { return std::string_view(a); }
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
directives_from_lists(&["take_string_view", "return_string_view"], &[], None),
make_cpp17_adder(),
None,
None,
);
}
#[test]
fn test_include_cpp_alone() {
let hdr = indoc! {"
#include <cstdint>
inline uint32_t give_int() {
return 5;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::include_cpp;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("give_int")
}
fn main() {
assert_eq!(ffi::give_int(), 5);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
fn test_include_cpp_in_path() {
let hdr = indoc! {"
#include <cstdint>
inline uint32_t give_int() {
return 5;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
autocxx::include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("give_int")
}
fn main() {
assert_eq!(ffi::give_int(), 5);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
fn test_bitset() {
let hdr = indoc! {"
#include <cstddef>
template <size_t _N_words, size_t _Size>
class __bitset
{
public:
typedef size_t __storage_type;
__storage_type __first_[_N_words];
inline bool all() {
return false;
}
};
template <size_t _Size>
class bitset
: private __bitset<_Size == 0 ? 0 : (_Size - 1) / (sizeof(size_t) * 8) + 1, _Size>
{
public:
static const unsigned __n_words = _Size == 0 ? 0 : (_Size - 1) / (sizeof(size_t) * 8) + 1;
typedef __bitset<__n_words, _Size> base;
bool all() const noexcept;
};
typedef bitset<1> mybitset;
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! {
generate_all!()
},
None,
None,
None,
);
}
#[test]
fn test_cint_vector() {
let hdr = indoc! {"
#include <vector>
#include <cstdint>
inline std::vector<int32_t> give_vec() {
return std::vector<int32_t> {1,2};
}
"};
let rs = quote! {
assert_eq!(ffi::give_vec().as_ref().unwrap().as_slice(), &[1,2]);
};
run_test("", hdr, rs, &["give_vec"], &[]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/422
fn test_int_vector() {
let hdr = indoc! {"
#include <vector>
std::vector<int> give_vec() {
return std::vector<int> {1,2};
}
"};
let rs = quote! {
assert_eq!(ffi::give_vec().as_ref().unwrap().as_slice(), &[autocxx::c_int(1),autocxx::c_int(2)]);
};
run_test("", hdr, rs, &["give_vec"], &[]);
}
#[test]
fn test_size_t() {
let hdr = indoc! {"
#include <cstddef>
inline size_t get_count() { return 7; }
"};
let rs = quote! {
ffi::get_count();
};
run_test_ex(
"",
hdr,
rs,
directives_from_lists(&["get_count"], &[], None),
None,
Some(make_rust_code_finder(vec![
quote! {fn get_count() -> usize},
])),
None,
);
}
#[test]
fn test_deleted_function() {
// We shouldn't generate bindings for deleted functions.
// The test is successful if the bindings compile, i.e. if autocxx doesn't
// attempt to call the deleted function.
let hdr = indoc! {"
class A {
public:
void foo() = delete;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_ignore_move_constructor() {
let hdr = indoc! {"
class A {
public:
A() {}
A(A&&) {};
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_ignore_function_with_rvalue_ref() {
let hdr = indoc! {"
#include <string>
void moveme(std::string &&);
"};
let rs = quote! {};
run_test("", hdr, rs, &["moveme"], &[]);
}
#[test]
fn test_take_nonpod_rvalue_from_up() {
let hdr = indoc! {"
#include <string>
struct A {
std::string a;
};
inline void take_a(A&&) {};
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
ffi::take_a(a);
let a2 = ffi::A::new().within_box();
ffi::take_a(a2);
};
run_test("", hdr, rs, &["A", "take_a"], &[]);
}
#[test]
fn test_take_nonpod_rvalue_from_stack() {
let hdr = indoc! {"
#include <string>
struct A {
std::string a;
};
inline void take_a(A&&) {};
"};
let rs = quote! {
moveit! { let a = ffi::A::new() };
ffi::take_a(a);
};
run_test("", hdr, rs, &["A", "take_a"], &[]);
}
#[test]
fn test_overloaded_ignored_function() {
// When overloaded functions are ignored during import, the placeholder
// functions generated for them should have unique names, just as they
// would have if they had been imported successfully.
// The test is successful if the bindings compile.
let hdr = indoc! {"
struct Blocked {};
class A {
public:
void take_blocked(Blocked);
void take_blocked(Blocked, int);
};
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! {
generate!("A")
block!("Blocked")
},
None,
None,
None,
);
}
#[test]
fn test_namespaced_constant() {
let hdr = indoc! {"
namespace A {
const int kConstant = 3;
}
"};
let rs = quote! {
assert_eq!(ffi::A::kConstant, 3);
};
run_test("", hdr, rs, &["A::kConstant"], &[]);
}
#[test]
fn test_issue_470_492() {
let hdr = indoc! {"
namespace std {
template <bool, typename _Iftrue, typename _Iffalse> struct a;
}
template <typename> struct b;
template <typename d> struct c {
typedef std::a<b<d>::c, int, int> e;
};
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! {
generate_all!()
},
None,
None,
None,
);
}
#[test]
fn test_no_impl() {
let hdr = indoc! {"
struct A {
int a;
};
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! {
exclude_impls!()
exclude_utilities!()
generate!("A")
},
None,
None,
None,
);
}
#[test]
fn test_generate_all() {
let hdr = indoc! {"
#include <cstdint>
inline uint32_t give_int() {
return 5;
}
"};
let rs = quote! {
assert_eq!(ffi::give_int(), 5);
};
run_test_ex(
"",
hdr,
rs,
quote! {
generate_all!()
},
None,
None,
None,
);
}
#[test]
fn test_std_thing() {
let hdr = indoc! {"
#include <cstdint>
namespace std {
struct A {
uint8_t a;
};
}
typedef char daft;
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! {
generate_all!()
},
None,
None,
None,
);
}
#[test]
fn test_two_mods() {
let hdr = indoc! {"
#include <cstdint>
struct A {
uint32_t a;
};
inline A give_a() {
A a;
a.a = 5;
return a;
}
inline uint32_t get_a(A a) {
return a.a;
}
struct B {
uint32_t a;
};
inline B give_b() {
B a;
a.a = 8;
return a;
}
inline uint32_t get_b(B a) {
return a.a;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("give_a")
generate!("get_a")
}
include_cpp! {
#hexathorpe include "input.h"
name!(ffi2)
generate!("give_b")
generate!("get_b")
}
fn main() {
let a = ffi::give_a().within_unique_ptr();
assert_eq!(ffi::get_a(a), 5);
let b = unsafe { ffi2::give_b().within_unique_ptr() };
assert_eq!(unsafe { ffi2::get_b(b) }, 8);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
fn test_manual_bridge() {
let hdr = indoc! {"
#include <cstdint>
inline uint32_t give_int() {
return 5;
}
inline uint32_t give_int2() {
return 5;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
autocxx::include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("give_int")
}
#[cxx::bridge]
mod ffi2 {
unsafe extern "C++" {
include!("input.h");
fn give_int2() -> u32;
}
}
fn main() {
assert_eq!(ffi::give_int(), 5);
assert_eq!(ffi2::give_int2(), 5);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
fn test_manual_bridge_mixed_types() {
let hdr = indoc! {"
#include <memory>
struct A {
int a;
};
inline int take_A(const A& a) {
return a.a;
}
inline std::unique_ptr<A> give_A() {
auto a = std::make_unique<A>();
a->a = 5;
return a;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
autocxx::include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("take_A")
generate!("A")
}
#[cxx::bridge]
mod ffi2 {
unsafe extern "C++" {
include!("input.h");
type A = crate::ffi::A;
fn give_A() -> UniquePtr<A>;
}
}
fn main() {
let a = ffi2::give_A();
assert_eq!(ffi::take_A(&a), autocxx::c_int(5));
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
fn test_extern_cpp_type_cxx_bridge() {
let hdr = indoc! {"
#include <cstdint>
struct A {
A() : a(0) {}
int a;
};
inline void handle_a(const A&) {
}
inline A create_a() {
A a;
return a;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("handle_a")
generate!("create_a")
extern_cpp_opaque_type!("A", crate::ffi2::A)
}
#[cxx::bridge]
pub mod ffi2 {
unsafe extern "C++" {
include!("input.h");
type A;
}
impl UniquePtr<A> {}
}
fn main() {
let a = ffi::create_a();
ffi::handle_a(&a);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
fn test_extern_cpp_type_two_include_cpp() {
let hdr = indoc! {"
#include <cstdint>
struct A {
A() : a(0) {}
int a;
};
enum B {
VARIANT,
};
inline void handle_a(const A&) {
}
inline A create_a(B) {
A a;
return a;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
pub mod base {
autocxx::include_cpp! {
#hexathorpe include "input.h"
name!(ffi2)
safety!(unsafe_ffi)
generate!("A")
generate!("B")
}
pub use ffi2::*;
}
pub mod dependent {
autocxx::include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("handle_a")
generate!("create_a")
extern_cpp_type!("A", crate::base::A)
extern_cpp_type!("B", super::super::base::B)
pod!("B")
}
pub use ffi::*;
}
fn main() {
use autocxx::prelude::*;
let a = dependent::create_a(base::B::VARIANT).within_box();
dependent::handle_a(&a);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
#[ignore] // because we currently require UniquePtrTarget which this can't implement
fn test_extern_cpp_type_manual() {
let hdr = indoc! {"
#include <cstdint>
struct A {
int a;
};
inline void handle_a(const A& a) {
}
inline A create_a() {
A a;
return a;
}
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
autocxx::include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
generate!("handle_a")
generate!("create_a")
extern_cpp_type!("A", crate::ffi2::A)
}
pub mod ffi2 {
use autocxx::cxx::{type_id, ExternType};
#[repr(C)]
pub struct A {
a: std::os::raw::c_int
}
unsafe impl ExternType for A {
type Kind = autocxx::cxx::kind::Opaque;
type Id = type_id!("A");
}
}
fn main() {
let a = ffi2::A { a: 3 };
ffi::handle_a(&a);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}
#[test]
fn test_issue486() {
let hdr = indoc! {"
namespace a {
namespace spanner {
class Key;
}
} // namespace a
namespace spanner {
class Key {
public:
bool b(a::spanner::Key &);
};
} // namespace spanner
"};
let rs = quote! {};
run_test("", hdr, rs, &["spanner::Key"], &[]);
}
#[test]
#[ignore]
fn test_issue616() {
let hdr = indoc! {"
namespace N {
template <typename> class B{};
template <typename c> class C {
public:
using U = B<c>;
};
}
class A : N::C<A> {
U u;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_shared_ptr() {
let hdr = indoc! {"
#include <memory>
struct A {
int a;
};
inline std::shared_ptr<A> make_shared_int() {
return std::make_shared<A>(A { 3 });
}
inline int take_shared_int(std::shared_ptr<A> a) {
return a->a;
}
inline std::weak_ptr<A> shared_to_weak(std::shared_ptr<A> a) {
return std::weak_ptr<A>(a);
}
"};
let rs = quote! {
let a = ffi::make_shared_int();
assert_eq!(ffi::take_shared_int(a.clone()), autocxx::c_int(3));
ffi::shared_to_weak(a).upgrade();
};
run_test(
"",
hdr,
rs,
&["make_shared_int", "take_shared_int", "shared_to_weak"],
&[],
);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/799
fn test_shared_ptr_const() {
let hdr = indoc! {"
#include <memory>
inline std::shared_ptr<const int> make_shared_int() {
return std::make_shared<const int>(3);
}
inline int take_shared_int(std::shared_ptr<const int> a) {
return *a;
}
"};
let rs = quote! {
let a = ffi::make_shared_int();
assert_eq!(ffi::take_shared_int(a.clone()), autocxx::c_int(3));
};
run_test("", hdr, rs, &["make_shared_int", "take_shared_int"], &[]);
}
#[test]
fn test_rust_reference() {
let hdr = indoc! {"
#include <cstdint>
struct RustType;
inline uint32_t take_rust_reference(const RustType&) {
return 4;
}
"};
let rs = quote! {
let foo = RustType(3);
assert_eq!(ffi::take_rust_reference(&foo), 4);
};
run_test_ex(
"",
hdr,
rs,
quote! {
generate!("take_rust_reference")
extern_rust_type!(RustType)
},
None,
None,
Some(quote! {
pub struct RustType(i32);
}),
);
}
#[test]
fn test_rust_reference_autodiscover() {
let hdr = indoc! {"
#include <cstdint>
struct RustType;
inline uint32_t take_rust_reference(const RustType&) {
return 4;
}
"};
let rs = quote! {
let foo = RustType(3);
let result = ffi::take_rust_reference(&foo);
assert_eq!(result, 4);
};
run_test_ex(
"",
hdr,
rs,
quote! {},
Some(Box::new(EnableAutodiscover)),
None,
Some(quote! {
#[autocxx::extern_rust::extern_rust_type]
pub struct RustType(i32);
}),
);
}
#[test]
fn test_pass_thru_rust_reference() {
let hdr = indoc! {"
#include <cstdint>
struct RustType;
inline const RustType& pass_rust_reference(const RustType& a) {
return a;
}
"};
let rs = quote! {
let foo = RustType(3);
assert_eq!(ffi::pass_rust_reference(&foo).0, 3);
};
run_test_ex(
"",
hdr,
rs,
quote! {
generate!("pass_rust_reference")
extern_rust_type!(RustType)
},
None,
None,
Some(quote! {
pub struct RustType(i32);
}),
);
}
#[test]
fn test_extern_rust_method() {
let hdr = indoc! {"
#include <cstdint>
struct RustType;
uint32_t examine(const RustType& foo);
"};
let cxx = indoc! {"
uint32_t examine(const RustType& foo) {
return foo.get();
}"};
let rs = quote! {
let a = RustType(74);
assert_eq!(ffi::examine(&a), 74);
};
run_test_ex(
cxx,
hdr,
rs,
directives_from_lists(&["examine"], &[], None),
Some(Box::new(EnableAutodiscover)),
None,
Some(quote! {
#[autocxx::extern_rust::extern_rust_type]
pub struct RustType(i32);
impl RustType {
#[autocxx::extern_rust::extern_rust_function]
pub fn get(&self) -> i32 {
return self.0
}
}
}),
);
}
#[test]
fn test_rust_reference_no_autodiscover() {
let hdr = indoc! {"
#include <cstdint>
struct RustType;
inline uint32_t take_rust_reference(const RustType&) {
return 4;
}
"};
let rs = quote! {
let foo = RustType(3);
let result = ffi::take_rust_reference(&foo);
assert_eq!(result, 4);
};
run_test_ex(
"",
hdr,
rs,
directives_from_lists(&["take_rust_reference"], &[], None),
None,
None,
Some(quote! {
#[autocxx::extern_rust::extern_rust_type]
pub struct RustType(i32);
}),
);
}
#[test]
#[cfg_attr(skip_windows_msvc_failing_tests, ignore)]
// TODO - replace make_clang_arg_adder with something that knows how to add an MSVC-suitable
// directive for the cc build.
fn test_cpp17() {
let hdr = indoc! {"
static_assert(__cplusplus >= 201703L, \"This file expects a C++17 compatible compiler.\");
inline void foo() {}
"};
run_test_ex(
"",
hdr,
quote! {
ffi::foo();
},
quote! {
generate!("foo")
},
make_cpp17_adder(),
None,
None,
);
}
#[test]
fn test_box() {
let hdr = indoc! {"
#include <cxx.h>
struct Foo;
inline void take_box(rust::Box<Foo>) {
}
"};
run_test_ex(
"",
hdr,
quote! {
ffi::take_box(Box::new(Foo { a: "Hello".into() }))
},
quote! {
generate!("take_box")
extern_rust_type!(Foo)
},
None,
None,
Some(quote! {
pub struct Foo {
a: String,
}
}),
);
}
#[test]
fn test_box_via_extern_rust() {
let hdr = indoc! {"
#include <cxx.h>
struct Foo;
inline void take_box(rust::Box<Foo>) {
}
"};
run_test_ex(
"",
hdr,
quote! {
ffi::take_box(Box::new(Foo { a: "Hello".into() }))
},
quote! {},
Some(Box::new(EnableAutodiscover)),
None,
Some(quote! {
#[autocxx::extern_rust::extern_rust_type]
pub struct Foo {
a: String,
}
}),
);
}
#[test]
fn test_box_via_extern_rust_in_mod() {
let hdr = indoc! {"
#include <cxx.h>
struct Foo;
inline void take_box(rust::Box<Foo>) {
}
"};
run_test_ex(
"",
hdr,
quote! {
ffi::take_box(Box::new(bar::Foo { a: "Hello".into() }))
},
quote! {},
Some(Box::new(EnableAutodiscover)),
None,
Some(quote! {
mod bar {
#[autocxx::extern_rust::extern_rust_type]
pub struct Foo {
pub a: String,
}
}
}),
);
}
#[test]
fn test_extern_rust_fn_simple() {
let cpp = indoc! {"
void foo() {
my_rust_fun();
}
"};
let hdr = indoc! {"
#include <cxx.h>
inline void do_thing() {}
"};
run_test_ex(
cpp,
hdr,
quote! {
ffi::do_thing();
},
quote! {
generate!("do_thing")
},
Some(Box::new(EnableAutodiscover)),
None,
Some(quote! {
#[autocxx::extern_rust::extern_rust_function]
fn my_rust_fun() {
}
}),
);
}
#[test]
fn test_extern_rust_fn_in_mod() {
let hdr = indoc! {"
#include <cxx.h>
inline void do_thing() {}
"};
run_test_ex(
"",
hdr,
quote! {},
quote! {
generate!("do_thing")
},
Some(Box::new(EnableAutodiscover)),
None,
Some(quote! {
mod bar {
#[autocxx::extern_rust::extern_rust_function]
pub fn my_rust_fun() {
}
}
}),
);
}
#[test]
fn test_issue_956() {
let hdr = indoc! {"
#include <cstdint>
inline void take_int(int&) {}
inline void take_uin16(uint16_t&) {}
inline void take_char16(char16_t &) {}
"};
run_test(
"",
hdr,
quote! {},
&["take_int", "take_uin16", "take_char16"],
&[],
);
}
#[test]
fn test_extern_rust_fn_no_autodiscover() {
let hdr = indoc! {"
#include <cxx.h>
"};
let cpp = indoc! {"
void call_it() {
my_rust_fun();
}
"};
run_test_ex(
cpp,
hdr,
quote! {},
quote! {},
None,
None,
Some(quote! {
mod bar {
#[autocxx::extern_rust::extern_rust_function]
pub fn my_rust_fun() {
}
}
}),
);
}
#[test]
fn test_pv_subclass_mut() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual void foo() = 0;
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn foo(&mut self) {
}
}
}),
);
}
#[test]
fn test_pv_subclass_const() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual void foo() const = 0;
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn foo(&self) {
}
}
}),
);
}
#[test]
fn test_pv_subclass_calls_impossible() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual void foo() const = 0;
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_expect_fail_ex(
"",
hdr,
quote! {
MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn foo(&self) {
use ffi::Observer_supers;
self.foo_super()
}
}
}),
);
}
#[test]
fn test_pv_subclass_not_pub() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual void foo() const = 0;
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_expect_fail_ex(
"",
hdr,
quote! {
MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn foo(&self) {
}
}
}),
);
}
#[test]
fn test_pv_subclass_ptr_param() {
let hdr = indoc! {"
#include <cstdint>
struct A {
uint8_t a;
};
class Observer {
public:
Observer() {}
virtual void foo(const A*) const {};
virtual ~Observer() {}
};
"};
run_test_ex(
"",
hdr,
quote! {
MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
},
quote! {
generate!("A")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
unsafe fn foo(&self, a: *const ffi::A) {
use ffi::Observer_supers;
self.foo_super(a)
}
}
}),
);
}
#[test]
fn test_pv_subclass_return() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual uint32_t foo() const = 0;
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn foo(&self) -> u32 {
4
}
}
}),
);
}
#[test]
fn test_pv_subclass_passed_to_fn() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual uint32_t foo() const = 0;
virtual ~Observer() {}
};
inline void take_observer(const Observer&) {}
"};
run_test_ex(
"",
hdr,
quote! {
let o = MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
ffi::take_observer(o.borrow().as_ref());
},
quote! {
generate!("take_observer")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn foo(&self) -> u32 {
4
}
}
}),
);
}
#[test]
fn test_pv_subclass_derive_defaults() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual uint32_t foo() const = 0;
virtual ~Observer() {}
};
inline void take_observer(const Observer&) {}
"};
run_test_ex(
"",
hdr,
quote! {
use autocxx::subclass::CppSubclassDefault;
let o = MyObserver::default_rust_owned();
ffi::take_observer(o.borrow().as_ref());
},
quote! {
generate!("take_observer")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
#[autocxx::subclass::subclass]
#[derive(Default)]
pub struct MyObserver {
a: u32
}
impl ffi::Observer_methods for MyObserver {
fn foo(&self) -> u32 {
4
}
}
}),
);
}
#[test]
fn test_non_pv_subclass_simple() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual void foo() const {}
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
let obs = MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
obs.borrow().foo();
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
}
}),
);
}
#[test]
fn test_two_subclasses() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual void foo() const {}
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
let obs = MyObserverA::new_rust_owned(MyObserverA { a: 3, cpp_peer: Default::default() });
obs.borrow().foo();
let obs = MyObserverB::new_rust_owned(MyObserverB { a: 3, cpp_peer: Default::default() });
obs.borrow().foo();
},
quote! {
generate!("bar")
subclass!("Observer",MyObserverA)
subclass!("Observer",MyObserverB)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserverA {
a: u32
}
impl Observer_methods for MyObserverA {
}
#[autocxx::subclass::subclass]
pub struct MyObserverB {
a: u32
}
impl Observer_methods for MyObserverB {
}
}),
);
}
#[test]
fn test_two_superclasses_with_same_name_method() {
let hdr = indoc! {"
#include <cstdint>
class ObserverA {
public:
ObserverA() {}
virtual void foo() const {}
virtual ~ObserverA() {}
};
class ObserverB {
public:
ObserverB() {}
virtual void foo() const {}
virtual ~ObserverB() {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
let obs = MyObserverA::new_rust_owned(MyObserverA { a: 3, cpp_peer: Default::default() });
obs.borrow().foo();
let obs = MyObserverB::new_rust_owned(MyObserverB { a: 3, cpp_peer: Default::default() });
obs.borrow().foo();
},
quote! {
generate!("bar")
subclass!("ObserverA",MyObserverA)
subclass!("ObserverB",MyObserverB)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::ObserverA_methods;
use ffi::ObserverB_methods;
#[autocxx::subclass::subclass]
pub struct MyObserverA {
a: u32
}
impl ObserverA_methods for MyObserverA {
}
#[autocxx::subclass::subclass]
pub struct MyObserverB {
a: u32
}
impl ObserverB_methods for MyObserverB {
}
}),
);
}
#[test]
fn test_pv_protected_constructor() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
protected:
Observer() {}
public:
virtual void foo() const {}
virtual ~Observer() {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
let obs = MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
obs.borrow().foo();
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
}
}),
);
}
#[test]
fn test_pv_protected_method() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer() {}
virtual void foo() const {}
virtual ~Observer() {}
protected:
virtual void baz() const {}
};
inline void bar() {}
"};
run_test_ex(
"",
hdr,
quote! {
let obs = MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
obs.borrow().foo();
},
quote! {
generate!("bar")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn baz(&self) {
}
fn foo(&self) {
use ffi::Observer_supers;
self.baz_super()
}
}
}),
);
}
#[test]
fn test_pv_subclass_allocation_not_self_owned() {
let hdr = indoc! {"
#include <cstdint>
extern \"C\" void mark_freed() noexcept;
extern \"C\" void mark_allocated() noexcept;
class TestObserver {
public:
TestObserver() {
mark_allocated();
}
virtual void a() const = 0;
virtual ~TestObserver() {
mark_freed();
}
};
inline void TriggerTestObserverA(const TestObserver& obs) {
obs.a();
}
"};
run_test_ex(
"",
hdr,
quote! {
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
// Test when owned by C++
let obs = MyTestObserver::new_cpp_owned(
MyTestObserver::new()
);
assert!(Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
let obs_superclass = obs.as_ref().unwrap(); // &subclass
let obs_superclass = unsafe { std::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
ffi::TriggerTestObserverA(obs_superclass);
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
std::mem::drop(obs);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
// Test when owned by Rust
let obs = MyTestObserver::new_rust_owned(
MyTestObserver::new()
);
//let cpp_peer_ptr = unsafe { obs.borrow_mut().peer_mut().get_unchecked_mut() as *mut ffi::MyTestObserverCpp };
assert!(Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
ffi::TriggerTestObserverA(obs.as_ref().borrow().as_ref());
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
std::mem::drop(obs);
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
},
quote! {
generate!("TriggerTestObserverA")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use once_cell::sync::Lazy;
use std::sync::Mutex;
use autocxx::subclass::CppSubclass;
use ffi::TestObserver_methods;
#[autocxx::subclass::subclass]
pub struct MyTestObserver {
data: ExternalEngine,
}
impl TestObserver_methods for MyTestObserver {
fn a(&self) {
self.data.do_something();
}
}
impl MyTestObserver {
fn new() -> Self {
Self {
cpp_peer: Default::default(),
data: ExternalEngine::default(),
}
}
}
#[no_mangle]
pub fn mark_allocated() {
Lazy::force(&STATUS).lock().unwrap().cpp_allocated = true;
}
#[no_mangle]
pub fn mark_freed() {
Lazy::force(&STATUS).lock().unwrap().cpp_allocated = false;
}
#[derive(Default)]
struct Status {
cpp_allocated: bool,
rust_allocated: bool,
a_called: bool,
}
static STATUS: Lazy<Mutex<Status>> = Lazy::new(|| Mutex::new(Status::default()));
pub struct ExternalEngine;
impl ExternalEngine {
fn do_something(&self) {
Lazy::force(&STATUS).lock().unwrap().a_called = true;
}
}
impl Default for ExternalEngine {
fn default() -> Self {
Lazy::force(&STATUS).lock().unwrap().rust_allocated = true;
ExternalEngine
}
}
impl Drop for ExternalEngine {
fn drop(&mut self) {
Lazy::force(&STATUS).lock().unwrap().rust_allocated = false;
}
}
}),
);
}
#[test]
fn test_pv_subclass_allocation_self_owned() {
let hdr = indoc! {"
#include <cstdint>
extern \"C\" void mark_freed() noexcept;
extern \"C\" void mark_allocated() noexcept;
class TestObserver {
public:
TestObserver() {
mark_allocated();
}
virtual void a() const = 0;
virtual ~TestObserver() {
mark_freed();
}
};
inline void TriggerTestObserverA(const TestObserver& obs) {
const_cast<TestObserver&>(obs).a();
}
"};
run_test_ex(
"",
hdr,
quote! {
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
// Test when owned by C++
let obs = MyTestObserver::new_cpp_owned(
MyTestObserver::new(false)
);
assert!(Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
let obs_superclass = obs.as_ref().unwrap(); // &subclass
let obs_superclass = unsafe { std::mem::transmute::<&ffi::MyTestObserverCpp, &ffi::TestObserver>(obs_superclass) };
ffi::TriggerTestObserverA(obs_superclass);
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
std::mem::drop(obs);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
// Test when owned by Rust
let obs = MyTestObserver::new_rust_owned(
MyTestObserver::new(false)
);
assert!(Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
ffi::TriggerTestObserverA(obs.as_ref().borrow().as_ref());
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
Lazy::force(&STATUS).lock().unwrap().a_called = false;
std::mem::drop(obs);
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
// Test when self-owned
let obs = MyTestObserver::new_self_owned(
MyTestObserver::new(true)
);
let obs_superclass_ptr: *const ffi::TestObserver = obs.as_ref().borrow().as_ref();
// Retain just a pointer on the Rust side, so there is no Rust-side
// ownership.
std::mem::drop(obs);
assert!(Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
assert!(Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().a_called);
ffi::TriggerTestObserverA(unsafe { obs_superclass_ptr.as_ref().unwrap() });
assert!(Lazy::force(&STATUS).lock().unwrap().a_called);
assert!(!Lazy::force(&STATUS).lock().unwrap().rust_allocated);
assert!(!Lazy::force(&STATUS).lock().unwrap().cpp_allocated);
},
quote! {
generate!("TriggerTestObserverA")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use once_cell::sync::Lazy;
use std::sync::Mutex;
use autocxx::subclass::CppSubclass;
use autocxx::subclass::CppSubclassSelfOwned;
use ffi::TestObserver_methods;
#[autocxx::subclass::subclass(self_owned)]
pub struct MyTestObserver {
data: ExternalEngine,
self_owning: bool,
}
impl TestObserver_methods for MyTestObserver {
fn a(&self) {
self.data.do_something();
if self.self_owning {
self.delete_self();
}
}
}
impl MyTestObserver {
fn new(self_owning: bool) -> Self {
Self {
cpp_peer: Default::default(),
data: ExternalEngine::default(),
self_owning,
}
}
}
#[no_mangle]
pub fn mark_allocated() {
Lazy::force(&STATUS).lock().unwrap().cpp_allocated = true;
}
#[no_mangle]
pub fn mark_freed() {
Lazy::force(&STATUS).lock().unwrap().cpp_allocated = false;
}
#[derive(Default)]
struct Status {
cpp_allocated: bool,
rust_allocated: bool,
a_called: bool,
}
static STATUS: Lazy<Mutex<Status>> = Lazy::new(|| Mutex::new(Status::default()));
pub struct ExternalEngine;
impl ExternalEngine {
fn do_something(&self) {
Lazy::force(&STATUS).lock().unwrap().a_called = true;
}
}
impl Default for ExternalEngine {
fn default() -> Self {
Lazy::force(&STATUS).lock().unwrap().rust_allocated = true;
ExternalEngine
}
}
impl Drop for ExternalEngine {
fn drop(&mut self) {
Lazy::force(&STATUS).lock().unwrap().rust_allocated = false;
}
}
}),
);
}
#[test]
fn test_pv_subclass_calls() {
let hdr = indoc! {"
#include <cstdint>
extern \"C\" void mark_c_called() noexcept;
extern \"C\" void mark_d_called() noexcept;
extern \"C\" void mark_e_called() noexcept;
extern \"C\" void mark_f_called() noexcept;
extern \"C\" void mark_g_called() noexcept;
extern \"C\" void mark_h_called() noexcept;
class TestObserver {
public:
TestObserver() {}
virtual uint32_t a(uint32_t) const = 0;
virtual uint32_t b(uint32_t) = 0;
virtual uint32_t c(uint32_t) const { mark_c_called(); return 0; };
virtual uint32_t d(uint32_t) { mark_d_called(); return 0; };
virtual uint32_t e(uint32_t) const { mark_e_called(); return 0; };
virtual uint32_t f(uint32_t) { mark_f_called(); return 0; };
virtual uint32_t g(uint32_t) const { mark_g_called(); return 0; };
virtual uint32_t h(uint32_t) { mark_h_called(); return 0; };
virtual ~TestObserver() {}
};
extern TestObserver* obs;
inline void register_observer(TestObserver& a) {
obs = &a;
}
inline uint32_t call_a(uint32_t param) {
return obs->a(param);
}
inline uint32_t call_b(uint32_t param) {
return obs->b(param);
}
inline uint32_t call_c(uint32_t param) {
return obs->c(param);
}
inline uint32_t call_d(uint32_t param) {
return obs->d(param);
}
inline uint32_t call_e(uint32_t param) {
return obs->e(param);
}
inline uint32_t call_f(uint32_t param) {
return obs->f(param);
}
inline uint32_t call_g(uint32_t param) {
return obs->g(param);
}
inline uint32_t call_h(uint32_t param) {
return obs->h(param);
}
"};
run_test_ex(
"TestObserver* obs;",
hdr,
quote! {
let obs = MyTestObserver::new_rust_owned(
MyTestObserver::default()
);
ffi::register_observer(obs.as_ref().borrow_mut().pin_mut());
assert_eq!(ffi::call_a(1), 2);
assert!(Lazy::force(&STATUS).lock().unwrap().sub_a_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
assert_eq!(ffi::call_b(1), 3);
assert!(Lazy::force(&STATUS).lock().unwrap().sub_b_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
assert_eq!(ffi::call_c(1), 4);
assert!(Lazy::force(&STATUS).lock().unwrap().sub_c_called);
assert!(!Lazy::force(&STATUS).lock().unwrap().super_c_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
assert_eq!(ffi::call_d(1), 5);
assert!(Lazy::force(&STATUS).lock().unwrap().sub_d_called);
assert!(!Lazy::force(&STATUS).lock().unwrap().super_d_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
assert_eq!(ffi::call_e(1), 0);
assert!(Lazy::force(&STATUS).lock().unwrap().sub_e_called);
assert!(Lazy::force(&STATUS).lock().unwrap().super_e_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
assert_eq!(ffi::call_f(1), 0);
assert!(Lazy::force(&STATUS).lock().unwrap().sub_f_called);
assert!(Lazy::force(&STATUS).lock().unwrap().super_f_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
assert_eq!(ffi::call_g(1), 0);
assert!(Lazy::force(&STATUS).lock().unwrap().super_g_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
assert_eq!(ffi::call_h(1), 0);
assert!(Lazy::force(&STATUS).lock().unwrap().super_h_called);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
},
quote! {
generate!("register_observer")
generate!("call_a")
generate!("call_b")
generate!("call_c")
generate!("call_d")
generate!("call_e")
generate!("call_f")
generate!("call_g")
generate!("call_h")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use once_cell::sync::Lazy;
use std::sync::Mutex;
use autocxx::subclass::CppSubclass;
use ffi::TestObserver_methods;
#[autocxx::subclass::subclass]
#[derive(Default)]
pub struct MyTestObserver {
}
impl TestObserver_methods for MyTestObserver {
// a and b are pure virtual
fn a(&self, param: u32) -> u32 {
Lazy::force(&STATUS).lock().unwrap().sub_a_called = true;
param + 1
}
fn b(&mut self, param: u32) -> u32 {
Lazy::force(&STATUS).lock().unwrap().sub_b_called = true;
param + 2
}
// c and d we override the superclass
fn c(&self, param: u32) -> u32 {
Lazy::force(&STATUS).lock().unwrap().sub_c_called = true;
param + 3
}
fn d(&mut self, param: u32) -> u32 {
Lazy::force(&STATUS).lock().unwrap().sub_d_called = true;
param + 4
}
// e and f we call through to the superclass
fn e(&self, param: u32) -> u32 {
Lazy::force(&STATUS).lock().unwrap().sub_e_called = true;
self.peer().e_super(param)
}
fn f(&mut self, param: u32) -> u32 {
Lazy::force(&STATUS).lock().unwrap().sub_f_called = true;
self.peer_mut().f_super(param)
}
// g and h we do not do anything, so calls should only call
// the superclass
}
#[no_mangle]
pub fn mark_c_called() {
Lazy::force(&STATUS).lock().unwrap().super_c_called = true;
}
#[no_mangle]
pub fn mark_d_called() {
Lazy::force(&STATUS).lock().unwrap().super_d_called = true;
}
#[no_mangle]
pub fn mark_e_called() {
Lazy::force(&STATUS).lock().unwrap().super_e_called = true;
}
#[no_mangle]
pub fn mark_f_called() {
Lazy::force(&STATUS).lock().unwrap().super_f_called = true;
}
#[no_mangle]
pub fn mark_g_called() {
Lazy::force(&STATUS).lock().unwrap().super_g_called = true;
}
#[no_mangle]
pub fn mark_h_called() {
Lazy::force(&STATUS).lock().unwrap().super_h_called = true;
}
#[derive(Default)]
struct Status {
super_c_called: bool,
super_d_called: bool,
super_e_called: bool,
super_f_called: bool,
super_g_called: bool,
super_h_called: bool,
sub_a_called: bool,
sub_b_called: bool,
sub_c_called: bool,
sub_d_called: bool,
sub_e_called: bool,
sub_f_called: bool,
}
static STATUS: Lazy<Mutex<Status>> = Lazy::new(|| Mutex::new(Status::default()));
}),
);
}
#[test]
fn test_cycle_nonpod_simple() {
let hdr = indoc! {"
#include <string>
struct NonPod {
std::string a;
};
inline NonPod make_non_pod(std::string a) {
NonPod p;
p.a = a;
return p;
}
inline NonPod call_n(NonPod param) {
return param;
}
"};
let rs = quote! {
let nonpod = ffi::make_non_pod("hello").within_unique_ptr();
ffi::call_n(nonpod).within_unique_ptr();
};
run_test("", hdr, rs, &["NonPod", "make_non_pod", "call_n"], &[])
}
#[test]
fn test_pv_subclass_types() {
let hdr = indoc! {"
#include <cstdint>
#include <string>
#include <vector>
struct Fwd;
struct Pod {
uint32_t a;
};
struct NonPod {
std::string a;
};
class TestObserver {
public:
TestObserver() {}
virtual std::string s(std::string p) const { return p; }
virtual Pod p(Pod p) const { return p; }
virtual NonPod n(NonPod p) const { return p; }
virtual void f(const Fwd&) const { }
virtual std::vector<NonPod> v(std::vector<NonPod> v) const { return v; }
virtual const std::vector<NonPod>& vr(const std::vector<NonPod>& vr) const { return vr; }
virtual const std::vector<Fwd>& vfr(const std::vector<Fwd>& vfr) const { return vfr; }
virtual ~TestObserver() {}
};
extern TestObserver* obs;
inline void register_observer(TestObserver& a) {
obs = &a;
}
inline std::string call_s(std::string param) {
return obs->s(param);
}
inline Pod call_p(Pod param) {
return obs->p(param);
}
inline NonPod call_n(NonPod param) {
return obs->n(param);
}
inline NonPod make_non_pod(std::string a) {
NonPod p;
p.a = a;
return p;
}
"};
run_test_ex(
"TestObserver* obs;",
hdr,
quote! {
let obs = MyTestObserver::new_rust_owned(
MyTestObserver::default()
);
ffi::register_observer(obs.as_ref().borrow_mut().pin_mut());
ffi::call_p(ffi::Pod { a: 3 });
ffi::call_s("hello");
ffi::call_n(ffi::make_non_pod("goodbye").within_unique_ptr());
},
quote! {
generate!("register_observer")
generate!("call_s")
generate!("call_n")
generate!("call_p")
generate!("NonPod")
generate!("make_non_pod")
generate_pod!("Pod")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::TestObserver_methods;
#[autocxx::subclass::subclass]
#[derive(Default)]
pub struct MyTestObserver {
}
impl TestObserver_methods for MyTestObserver {
fn s(&self, p: cxx::UniquePtr<cxx::CxxString>) -> cxx::UniquePtr<cxx::CxxString> {
self.peer().s_super(p)
}
fn p(&self, p: ffi::Pod) -> ffi::Pod {
self.peer().p_super(p)
}
fn n(&self, p: cxx::UniquePtr<ffi::NonPod>) -> cxx::UniquePtr<ffi::NonPod> {
self.peer().n_super(p)
}
}
}),
);
}
#[test]
fn test_pv_subclass_constructors() {
// Also tests a Rust-side subclass type which is an empty struct
let hdr = indoc! {"
#include <cstdint>
#include <string>
class TestObserver {
public:
TestObserver() {}
TestObserver(uint8_t) {}
TestObserver(std::string) {}
virtual void call() const { }
virtual ~TestObserver() {}
};
extern TestObserver* obs;
inline void register_observer(TestObserver& a) {
obs = &a;
}
inline void do_a_thing() {
return obs->call();
}
"};
run_test_ex(
"TestObserver* obs;",
hdr,
quote! {
let obs = MyTestObserver::new_rust_owned(
MyTestObserver::default()
);
ffi::register_observer(obs.as_ref().borrow_mut().pin_mut());
ffi::do_a_thing();
},
quote! {
generate!("register_observer")
generate!("do_a_thing")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::prelude::*;
#[subclass]
#[derive(Default)]
pub struct MyTestObserver;
impl ffi::TestObserver_methods for MyTestObserver {
fn call(&self) {
self.peer().call_super()
}
}
impl CppPeerConstructor<ffi::MyTestObserverCpp> for MyTestObserver {
fn make_peer(&mut self, peer_holder: CppSubclassRustPeerHolder<Self>) -> cxx::UniquePtr<ffi::MyTestObserverCpp> {
ffi::MyTestObserverCpp::new1(peer_holder, 3u8).within_unique_ptr()
}
}
}),
);
}
#[test]
fn test_pv_subclass_fancy_constructor() {
let hdr = indoc! {"
#include <cstdint>
class Observer {
public:
Observer(uint8_t) {}
virtual uint32_t foo() const = 0;
virtual ~Observer() {}
};
inline void take_observer(const Observer&) {}
"};
run_test_expect_fail_ex(
"",
hdr,
quote! {
let o = MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() }, ffi::MyObserverCpp::make_unique);
ffi::take_observer(o.borrow().as_ref());
},
quote! {
generate!("take_observer")
subclass!("Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
use ffi::Observer_methods;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl Observer_methods for MyObserver {
fn foo(&self) -> u32 {
4
}
}
}),
);
}
#[test]
fn test_non_pv_subclass_overloads() {
let hdr = indoc! {"
#include <cstdint>
#include <string>
class TestObserver {
public:
TestObserver() {}
virtual void call(uint8_t) const {}
virtual void call(std::string) const {}
virtual ~TestObserver() {}
};
extern TestObserver* obs;
inline void register_observer(TestObserver& a) {
obs = &a;
}
inline void do_a_thing() {
return obs->call(8);
}
"};
run_test_ex(
"TestObserver* obs;",
hdr,
quote! {
let obs = MyTestObserver::new_rust_owned(
MyTestObserver::default()
);
ffi::register_observer(obs.as_ref().borrow_mut().pin_mut());
ffi::do_a_thing();
},
quote! {
generate!("register_observer")
generate!("do_a_thing")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::prelude::*;
#[subclass]
#[derive(Default)]
pub struct MyTestObserver;
impl ffi::TestObserver_methods for MyTestObserver {
fn call(&self, a: u8) {
self.peer().call_super(a)
}
fn call1(&self, a: cxx::UniquePtr<cxx::CxxString>) {
self.peer().call1_super(a)
}
}
}),
);
}
#[test]
fn test_pv_subclass_overrides() {
let hdr = indoc! {"
#include <cstdint>
#include <string>
class TestObserver {
public:
TestObserver() {}
virtual void call(uint8_t) const = 0;
virtual void call(std::string) const = 0;
virtual ~TestObserver() {}
};
extern TestObserver* obs;
inline void register_observer(TestObserver& a) {
obs = &a;
}
inline void do_a_thing() {
return obs->call(8);
}
"};
run_test_ex(
"TestObserver* obs;",
hdr,
quote! {
let obs = MyTestObserver::new_rust_owned(
MyTestObserver::default()
);
ffi::register_observer(obs.as_ref().borrow_mut().pin_mut());
ffi::do_a_thing();
},
quote! {
generate!("register_observer")
generate!("do_a_thing")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::prelude::*;
#[subclass]
#[derive(Default)]
pub struct MyTestObserver;
impl ffi::TestObserver_methods for MyTestObserver {
fn call(&self, _a: u8) {
}
fn call1(&self, _a: cxx::UniquePtr<cxx::CxxString>) {
}
}
}),
);
}
#[test]
fn test_pv_subclass_namespaced_superclass() {
let hdr = indoc! {"
#include <cstdint>
namespace a {
class Observer {
public:
Observer() {}
virtual uint32_t foo() const = 0;
virtual ~Observer() {}
};
}
inline void take_observer(const a::Observer&) {}
"};
run_test_ex(
"",
hdr,
quote! {
let o = MyObserver::new_rust_owned(MyObserver { a: 3, cpp_peer: Default::default() });
ffi::take_observer(o.borrow().as_ref());
},
quote! {
generate!("take_observer")
subclass!("a::Observer",MyObserver)
},
None,
None,
Some(quote! {
use autocxx::subclass::CppSubclass;
#[autocxx::subclass::subclass]
pub struct MyObserver {
a: u32
}
impl ffi::a::Observer_methods for MyObserver {
fn foo(&self) -> u32 {
4
}
}
}),
);
}
#[test]
fn test_no_constructor_make_unique() {
let hdr = indoc! {"
#include <stdint.h>
struct A {
uint32_t a;
};
"};
let rs = quote! {
ffi::A::new().within_unique_ptr();
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_constructor_moveit() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() {}
void set(uint32_t val) { a = val; }
uint32_t get() const { return a; }
uint32_t a;
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {
moveit! {
let mut stack_obj = ffi::A::new();
}
stack_obj.as_mut().set(42);
assert_eq!(stack_obj.get(), 42);
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_move_out_of_uniqueptr() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() {}
std::string so_we_are_non_trivial;
};
inline A get_a() {
A a;
return a;
}
"};
let rs = quote! {
let a = ffi::get_a().within_unique_ptr();
moveit! {
let _stack_obj = autocxx::moveit::new::mov(a);
}
};
run_test("", hdr, rs, &["A", "get_a"], &[]);
}
#[test]
fn test_implicit_constructor_with_typedef_field() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct B {
uint32_t b;
};
typedef struct B C;
struct A {
B field;
uint32_t a;
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {
moveit! {
let mut stack_obj = ffi::A::new();
}
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_implicit_constructor_with_array_field() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
uint32_t a[3];
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {
moveit! {
let mut _stack_obj = ffi::A::new();
}
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_implicit_constructor_moveit() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
void set(uint32_t val) { a = val; }
uint32_t get() const { return a; }
uint32_t a;
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {
moveit! {
let mut stack_obj = ffi::A::new();
}
stack_obj.as_mut().set(42);
assert_eq!(stack_obj.get(), 42);
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_pass_by_value_moveit() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
void set(uint32_t val) { a = val; }
uint32_t a;
std::string so_we_are_non_trivial;
};
inline void take_a(A) {}
struct B {
B() {}
B(const B&) {}
B(B&&) {}
std::string so_we_are_non_trivial;
};
inline void take_b(B) {}
"};
let rs = quote! {
moveit! {
let mut stack_obj = ffi::A::new();
}
stack_obj.as_mut().set(42);
ffi::take_a(&*stack_obj);
ffi::take_a(as_copy(stack_obj.as_ref()));
ffi::take_a(as_copy(stack_obj.as_ref()));
// A has no move constructor so we can't consume it.
let heap_obj = ffi::A::new().within_unique_ptr();
ffi::take_a(heap_obj.as_ref().unwrap());
ffi::take_a(&heap_obj);
ffi::take_a(autocxx::as_copy(heap_obj.as_ref().unwrap()));
ffi::take_a(heap_obj); // consume
let heap_obj2 = ffi::A::new().within_box();
ffi::take_a(heap_obj2.as_ref().get_ref());
ffi::take_a(&heap_obj2);
ffi::take_a(autocxx::as_copy(heap_obj2.as_ref().get_ref()));
ffi::take_a(heap_obj2); // consume
moveit! {
let mut stack_obj = ffi::B::new();
}
ffi::take_b(&*stack_obj);
ffi::take_b(as_copy(stack_obj.as_ref()));
ffi::take_b(as_copy(stack_obj.as_ref()));
ffi::take_b(as_mov(stack_obj)); // due to move constructor
// Test direct-from-New-to-param.
ffi::take_b(as_new(ffi::B::new()));
};
run_test("", hdr, rs, &["A", "take_a", "B", "take_b"], &[]);
}
#[test]
fn test_nonconst_reference_parameter() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
// Force generating a wrapper for the second `take_a`.
struct NOP { void take_a() {}; };
struct A {
std::string so_we_are_non_trivial;
};
inline void take_a(A&) {}
"};
let rs = quote! {
let mut heap_obj = ffi::A::new().within_unique_ptr();
ffi::take_a(heap_obj.pin_mut());
};
run_test("", hdr, rs, &["NOP", "A", "take_a"], &[]);
}
#[test]
fn test_nonconst_reference_method_parameter() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
// Force generating a wrapper for the second `take_a`.
struct NOP { void take_a() {}; };
struct A {
std::string so_we_are_non_trivial;
};
struct B {
inline void take_a(A&) const {}
};
"};
let rs = quote! {
let mut a = ffi::A::new().within_unique_ptr();
let b = ffi::B::new().within_unique_ptr();
b.take_a(a.pin_mut());
};
run_test("", hdr, rs, &["NOP", "A", "B"], &[]);
}
fn destruction_test(ident: proc_macro2::Ident, extra_bit: Option<TokenStream>) {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
extern bool gConstructed;
struct A {
A() { gConstructed = true; }
virtual ~A() { gConstructed = false; }
void set(uint32_t val) { a = val; }
uint32_t get() const { return a; }
uint32_t a;
std::string so_we_are_non_trivial;
};
inline bool is_constructed() { return gConstructed; }
struct B: public A {
uint32_t b;
};
"};
let cpp = indoc! {"
bool gConstructed = false;
"};
let rs = quote! {
assert!(!ffi::is_constructed());
{
moveit! {
let mut _stack_obj = ffi::#ident::new();
}
assert!(ffi::is_constructed());
#extra_bit
}
assert!(!ffi::is_constructed());
};
run_test(cpp, hdr, rs, &[&ident.to_string(), "is_constructed"], &[]);
}
#[test]
fn test_destructor_moveit() {
destruction_test(
parse_quote! { A },
Some(quote! {
_stack_obj.as_mut().set(42);
assert_eq!(_stack_obj.get(), 42);
}),
);
}
#[test]
fn test_destructor_derived_moveit() {
destruction_test(parse_quote! { B }, None);
}
#[test]
fn test_copy_and_move_constructor_moveit() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() {}
A(const A& other) : a(other.a+1) {}
A(A&& other) : a(other.a+2) { other.a = 666; }
void set(uint32_t val) { a = val; }
uint32_t get() const { return a; }
uint32_t a;
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {
moveit! {
let mut stack_obj = ffi::A::new();
}
stack_obj.as_mut().set(42);
moveit! {
let stack_obj2 = autocxx::moveit::new::copy(stack_obj.as_ref());
}
assert_eq!(stack_obj2.get(), 43);
assert_eq!(stack_obj.get(), 42);
moveit! {
let stack_obj3 = autocxx::moveit::new::mov(stack_obj);
}
assert_eq!(stack_obj3.get(), 44);
// Following line prevented by moveit, even though it would
// be possible in C++.
// assert_eq!(stack_obj.get(), 666);
};
run_test("", hdr, rs, &["A"], &[]);
}
// This test fails on Windows gnu but not on Windows msvc
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
#[test]
fn test_uniqueptr_moveit() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() {}
void set(uint32_t val) { a = val; }
uint32_t get() const { return a; }
uint32_t a;
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {
use autocxx::moveit::EmplaceUnpinned;
let mut up_obj = cxx::UniquePtr::emplace(ffi::A::new());
up_obj.as_mut().unwrap().set(42);
assert_eq!(up_obj.get(), 42);
};
run_test("", hdr, rs, &["A"], &[]);
}
// This test fails on Windows gnu but not on Windows msvc
#[cfg_attr(skip_windows_gnu_failing_tests, ignore)]
#[test]
fn test_various_emplacement() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() {}
void set(uint32_t val) { a = val; }
uint32_t get() const { return a; }
uint32_t a;
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {
use autocxx::moveit::EmplaceUnpinned;
use autocxx::moveit::Emplace;
let mut up_obj = cxx::UniquePtr::emplace(ffi::A::new());
up_obj.pin_mut().set(666);
// Can't current move out of a UniquePtr
let mut box_obj = Box::emplace(ffi::A::new());
box_obj.as_mut().set(667);
let box_obj2 = Box::emplace(autocxx::moveit::new::mov(box_obj));
moveit! { let back_on_stack = autocxx::moveit::new::mov(box_obj2); }
assert_eq!(back_on_stack.get(), 667);
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_emplace_uses_overridden_new_and_delete() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() {}
void* operator new(size_t count);
void operator delete(void* ptr) noexcept;
void* operator new(size_t count, void* ptr);
std::string so_we_are_non_trivial;
};
void reset_flags();
bool was_new_called();
bool was_delete_called();
"};
let cxx = indoc! {"
bool new_called;
bool delete_called;
void reset_flags() {
new_called = false;
delete_called = false;
}
void* A::operator new(size_t count) {
new_called = true;
return ::operator new(count);
}
void* A::operator new(size_t count, void* ptr) {
return ::operator new(count, ptr);
}
void A::operator delete(void* ptr) noexcept {
delete_called = true;
::operator delete(ptr);
}
bool was_new_called() {
return new_called;
}
bool was_delete_called() {
return delete_called;
}
"};
let rs = quote! {
ffi::reset_flags();
{
let _ = ffi::A::new().within_unique_ptr();
assert!(ffi::was_new_called());
}
assert!(ffi::was_delete_called());
ffi::reset_flags();
{
use autocxx::moveit::EmplaceUnpinned;
let _ = cxx::UniquePtr::emplace(ffi::A::new());
}
assert!(ffi::was_delete_called());
};
run_test(
cxx,
hdr,
rs,
&["A", "reset_flags", "was_new_called", "was_delete_called"],
&[],
);
}
#[test]
fn test_pass_by_reference_to_value_param() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() : count(0) {}
std::string so_we_are_non_trivial;
uint32_t count;
};
void take_a(A a) {
a.count++;
}
uint32_t report_on_a(const A& a) {
return a.count;
}
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
ffi::take_a(a.as_ref().unwrap());
ffi::take_a(&a); // syntactic sugar
assert_eq!(ffi::report_on_a(&a), 0); // should have acted upon copies
};
run_test("", hdr, rs, &["A", "take_a", "report_on_a"], &[]);
}
#[test]
fn test_explicit_everything() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
struct A {
A() {} // default constructor
A(A&&) {} // move constructor
A(const A&) {} // copy constructor
A& operator=(const A&) { return *this; } // copy assignment operator
A& operator=(A&&) { return *this; } // move assignment operator
~A() {} // destructor
void set(uint32_t val) { a = val; }
uint32_t get() const { return a; }
uint32_t a;
std::string so_we_are_non_trivial;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_generate_ns() {
let hdr = indoc! {"
namespace A {
inline void foo() {}
inline void bar() {}
}
namespace B {
inline void baz() {}
}
"};
let rs = quote! {
ffi::A::foo();
};
run_test_ex(
"",
hdr,
rs,
quote! {
generate_ns!("A")
safety!(unsafe_ffi)
},
None,
None,
None,
);
}
#[test]
fn test_no_constructor_make_unique_ns() {
let hdr = indoc! {"
#include <stdint.h>
namespace B {
struct A {
uint32_t a;
};
}
"};
let rs = quote! {
ffi::B::A::new().within_unique_ptr();
};
run_test("", hdr, rs, &["B::A"], &[]);
}
#[test]
fn test_no_constructor_pod_make_unique() {
let hdr = indoc! {"
#include <stdint.h>
struct A {
uint32_t a;
};
"};
let rs = quote! {
ffi::A::new().within_unique_ptr();
};
run_test("", hdr, rs, &[], &["A"]);
}
#[test]
fn test_no_constructor_pv() {
let hdr = indoc! {"
#include <stdint.h>
class A {
public:
virtual ~A() {}
virtual void foo() = 0;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_suppress_system_includes() {
let hdr = indoc! {"
#include <stdint.h>
#include <string>
inline void a() {};
"};
let rs = quote! {};
run_test_ex(
"",
hdr,
rs,
quote! { generate("a")},
Some(Box::new(SetSuppressSystemHeaders)),
Some(Box::new(NoSystemHeadersChecker)),
None,
);
}
#[test]
fn test_no_rvo_move() {
let hdr = indoc! {"
#include <memory>
class A {
public:
static std::unique_ptr<A> create() { return std::make_unique<A>(); }
};
"};
let rs = quote! {
ffi::A::create();
};
run_test_ex(
"",
hdr,
rs,
quote! { generate!("A") },
None,
Some(Box::new(CppMatcher::new(
&["return A::create();"],
&["return std::move(A::create());"],
))),
None,
);
}
#[test]
fn test_abstract_up() {
let hdr = indoc! {"
#include <memory>
class A {
public:
virtual void foo() const = 0;
virtual ~A() {}
};
class B : public A {
public:
void foo() const {}
};
inline std::unique_ptr<A> get_a() { return std::make_unique<B>(); }
"};
let rs = quote! {
let a = ffi::get_a();
a.foo();
};
run_test("", hdr, rs, &["A", "get_a"], &[]);
}
#[test]
fn test_abstract_private() {
let hdr = indoc! {"
#include <memory>
class A {
virtual void foo() const = 0;
public:
virtual ~A() {}
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_abstract_issue_979() {
let hdr = indoc! {"
class Test {
virtual ~Test() {}
virtual void TestBody() = 0;
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["Test"], &[]);
}
#[test]
fn test_class_having_protected_method() {
let hdr = indoc! {"
#include <cstdint>
class A {
protected:
inline uint32_t protected_method() { return 0; }
};
"};
let rs = quote! {};
run_test("", hdr, rs, &[], &["A"]);
}
#[test]
fn test_protected_inner_class() {
let hdr = indoc! {"
#include <cstdint>
inline uint32_t DoMath(uint32_t a) {
return a * 3;
}
class A {
protected:
inline uint32_t protected_method() { return 0; }
struct B {
int x;
};
inline B protected_method_2() {
return { 0 };
}
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_private_inner_class() {
let hdr = indoc! {"
#include <cstdint>
inline uint32_t DoMath(uint32_t a) {
return a * 3;
}
class A {
protected:
inline uint32_t protected_method() { return 0; }
private:
struct B {
int x;
};
inline B private_method_2() {
return { 0 };
}
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_class_having_private_method() {
let hdr = indoc! {"
#include <cstdint>
class A {
private:
inline uint32_t private_method() { return 0; }
};
"};
let rs = quote! {};
run_test("", hdr, rs, &[], &["A"]);
}
#[test]
#[ignore] // https://github.com/google/autocxx/issues/787
fn test_chrono_problem() {
let hdr = indoc! {"
#include <chrono>
struct Clock {
typedef std::chrono::nanoseconds duration;
};
struct Class {
int a() { return 42; }
std::chrono::time_point<Clock> b();
};
"};
let rs = quote! {};
run_test("", hdr, rs, &[], &["Class"]);
}
fn size_and_alignment_test(pod: bool) {
static TYPES: [(&str, &str); 6] = [
("A", "struct A { uint8_t a; };"),
("B", "struct B { uint32_t a; };"),
("C", "struct C { uint64_t a; };"),
("D", "enum D { Z, X };"),
("E", "struct E { uint8_t a; uint32_t b; };"),
("F", "struct F { uint32_t a; uint8_t b; };"),
];
let type_definitions = TYPES.iter().map(|(_, def)| *def).join("\n");
let function_definitions = TYPES.iter().map(|(name, _)| format!("inline size_t get_sizeof_{}() {{ return sizeof({}); }}\ninline size_t get_alignof_{}() {{ return alignof({}); }}\n",
name, name, name, name)).join("\n");
let hdr = format!(
indoc! {"
#include <cstdint>
#include <cstddef>
{}
{}
"},
type_definitions, function_definitions
);
#[allow(clippy::unnecessary_to_owned)] // wrongly triggers on into_iter() below
let allowlist_fns: Vec<String> = TYPES
.iter()
.flat_map(|(name, _)| {
[
format!("get_sizeof_{}", name),
format!("get_alignof_{}", name),
]
.to_vec()
.into_iter()
})
.collect_vec();
let allowlist_types: Vec<String> = TYPES.iter().map(|(name, _)| name.to_string()).collect_vec();
let allowlist_both = allowlist_types
.iter()
.cloned()
.chain(allowlist_fns.iter().cloned())
.collect_vec();
let allowlist_types: Vec<&str> = allowlist_types.iter().map(AsRef::as_ref).collect_vec();
let allowlist_fns: Vec<&str> = allowlist_fns.iter().map(AsRef::as_ref).collect_vec();
let allowlist_both: Vec<&str> = allowlist_both.iter().map(AsRef::as_ref).collect_vec();
let rs = TYPES.iter().fold(quote! {}, |mut accumulator, (name, _)| {
let get_align_symbol =
proc_macro2::Ident::new(&format!("get_alignof_{}", name), Span::call_site());
let get_size_symbol =
proc_macro2::Ident::new(&format!("get_sizeof_{}", name), Span::call_site());
let type_symbol = proc_macro2::Ident::new(name, Span::call_site());
accumulator.extend(quote! {
let c_size = ffi::#get_size_symbol();
let c_align = ffi::#get_align_symbol();
assert_eq!(std::mem::size_of::<ffi::#type_symbol>(), c_size);
assert_eq!(std::mem::align_of::<ffi::#type_symbol>(), c_align);
});
accumulator
});
if pod {
run_test("", &hdr, rs, &allowlist_fns, &allowlist_types);
} else {
run_test("", &hdr, rs, &allowlist_both, &[]);
}
}
#[test]
fn test_sizes_and_alignment_nonpod() {
size_and_alignment_test(false)
}
#[test]
fn test_sizes_and_alignment_pod() {
size_and_alignment_test(true)
}
#[test]
fn test_nested_class_methods() {
let hdr = indoc! {"
#include <cstdint>
class A {
public:
virtual ~A() {}
struct B {
virtual void b() const {}
};
virtual void a() const {}
struct C {
virtual void b() const {}
};
virtual void c() const {}
struct D {
virtual void b() const {}
};
};
"};
let rs = quote! {
let a = ffi::A::new().within_unique_ptr();
a.a();
a.c();
};
run_test("", hdr, rs, &["A"], &[]);
}
#[test]
fn test_call_superclass() {
let hdr = indoc! {"
#include <memory>
class A {
public:
virtual void foo() const {};
virtual ~A() {}
};
class B : public A {
public:
void bar() const {}
};
inline std::unique_ptr<B> get_b() { return std::make_unique<B>(); }
"};
let rs = quote! {
let b = ffi::get_b();
b.as_ref().unwrap().as_ref().foo();
};
run_test("", hdr, rs, &["A", "B", "get_b"], &[]);
}
#[test]
fn test_pass_superclass() {
let hdr = indoc! {"
#include <memory>
class A {
public:
virtual void foo() const {};
virtual ~A() {}
};
class B : public A {
public:
void bar() const {}
};
inline std::unique_ptr<B> get_b() { return std::make_unique<B>(); }
inline void take_a(const A&) {}
"};
let rs = quote! {
let b = ffi::get_b();
ffi::take_a(b.as_ref().unwrap().as_ref());
};
run_test("", hdr, rs, &["A", "B", "get_b", "take_a"], &[]);
}
#[test]
fn test_issue486_multi_types() {
let hdr = indoc! {"
namespace a {
namespace spanner {
struct Key {};
}
} // namespace a
namespace b {
namespace spanner {
typedef int Key;
}
} // namespace b
namespace c {
namespace spanner {
enum Key { A, B };
}
} // namespace c
namespace spanner {
class Key {
public:
bool a(a::spanner::Key &);
bool b(b::spanner::Key &);
bool c(c::spanner::Key &);
};
} // namespace spanner
"};
let rs = quote! {};
run_test(
"",
hdr,
rs,
&["spanner::Key", "a::spanner::Key", "b::spanner::Key"],
&[],
);
}
#[test]
/// Tests types with various forms of copy, move, and default constructors. Calls the things which
/// should be generated, and will produce C++ compile failures if other wrappers are generated.
///
/// Specifically, we can have the cross product of any of these:
/// * Explicitly deleted
/// * Implicitly defaulted
/// * User declared
/// * Explicitly defaulted
/// Not handled yet: https://github.com/google/autocxx/issues/815.
/// Once this is handled, add equivalents of all the implicitly defaulted cases, at all
/// visibility levels.
/// applied to each of these:
/// * Default constructor
/// * Copy constructor
/// * Move constructor
/// in any of these:
/// * The class itself
/// * A base class
/// * A field of the class
/// * A field of a base class
/// with any of these access modifiers:
/// * private (impossible for implicitly defaulted)
/// * protected (impossible for implicitly defaulted)
/// * public
///
/// Various combinations of these lead to the default versions being deleted. The move and copy
/// ones also interact with each other in various ways.
///
/// TODO: Remove all the `int x` members after https://github.com/google/autocxx/issues/832 is
/// fixed.
fn test_implicit_constructor_rules() {
let cxx = "";
let hdr = indoc! {"
struct AllImplicitlyDefaulted {
void a() const {}
};
struct PublicDeleted {
PublicDeleted() = delete;
PublicDeleted(const PublicDeleted&) = delete;
PublicDeleted(PublicDeleted&&) = delete;
void a() const {}
int x;
};
struct PublicDeletedDefault {
PublicDeletedDefault() = delete;
void a() const {}
int x;
};
struct PublicDeletedCopy {
PublicDeletedCopy() = default;
PublicDeletedCopy(const PublicDeletedCopy&) = delete;
void a() const {}
int x;
};
struct PublicDeletedCopyNoDefault {
PublicDeletedCopyNoDefault(const PublicDeletedCopyNoDefault&) = delete;
void a() const {}
int x;
};
struct PublicMoveDeletedCopy {
PublicMoveDeletedCopy() = default;
PublicMoveDeletedCopy(const PublicMoveDeletedCopy&) = delete;
PublicMoveDeletedCopy(PublicMoveDeletedCopy&&) = default;
void a() const {}
int x;
};
struct PublicDeletedMove {
PublicDeletedMove() = default;
PublicDeletedMove(PublicDeletedMove&&) = delete;
void a() const {}
int x;
};
struct PublicDeletedDestructor {
PublicDeletedDestructor() = default;
~PublicDeletedDestructor() = delete;
void a() const {}
int x;
};
struct PublicDestructor {
PublicDestructor() = default;
~PublicDestructor() = default;
void a() const {}
int x;
};
struct ProtectedDeleted {
void a() const {}
int x;
protected:
ProtectedDeleted() = delete;
ProtectedDeleted(const ProtectedDeleted&) = delete;
ProtectedDeleted(ProtectedDeleted&&) = delete;
};
struct ProtectedDeletedDefault {
void a() const {}
int x;
protected:
ProtectedDeletedDefault() = delete;
};
struct ProtectedDeletedCopy {
ProtectedDeletedCopy() = default;
void a() const {}
int x;
protected:
ProtectedDeletedCopy(const ProtectedDeletedCopy&) = delete;
};
struct ProtectedDeletedCopyNoDefault {
void a() const {}
int x;
protected:
ProtectedDeletedCopyNoDefault(const ProtectedDeletedCopyNoDefault&) = delete;
};
struct ProtectedMoveDeletedCopy {
ProtectedMoveDeletedCopy() = default;
void a() const {}
int x;
protected:
ProtectedMoveDeletedCopy(const ProtectedMoveDeletedCopy&) = delete;
ProtectedMoveDeletedCopy(ProtectedMoveDeletedCopy&&) = default;
};
struct ProtectedDeletedMove {
ProtectedDeletedMove() = default;
void a() const {}
int x;
protected:
ProtectedDeletedMove(ProtectedDeletedMove&&) = delete;
};
struct ProtectedDeletedDestructor {
ProtectedDeletedDestructor() = default;
void a() const {}
int x;
protected:
~ProtectedDeletedDestructor() = delete;
};
struct ProtectedDestructor {
ProtectedDestructor() = default;
void a() const {}
int x;
protected:
~ProtectedDestructor() = default;
};
struct PrivateDeleted {
void a() const {}
int x;
private:
PrivateDeleted() = delete;
PrivateDeleted(const PrivateDeleted&) = delete;
PrivateDeleted(PrivateDeleted&&) = delete;
};
struct PrivateDeletedDefault {
void a() const {}
int x;
private:
PrivateDeletedDefault() = delete;
};
struct PrivateDeletedCopy {
PrivateDeletedCopy() = default;
void a() const {}
int x;
private:
PrivateDeletedCopy(const PrivateDeletedCopy&) = delete;
};
struct PrivateDeletedCopyNoDefault {
void a() const {}
int x;
private:
PrivateDeletedCopyNoDefault(const PrivateDeletedCopyNoDefault&) = delete;
};
struct PrivateMoveDeletedCopy {
PrivateMoveDeletedCopy() = default;
void a() const {}
int x;
private:
PrivateMoveDeletedCopy(const PrivateMoveDeletedCopy&) = delete;
PrivateMoveDeletedCopy(PrivateMoveDeletedCopy&&) = default;
};
struct PrivateDeletedMove {
PrivateDeletedMove() = default;
void a() const {}
int x;
private:
PrivateDeletedMove(PrivateDeletedMove&&) = delete;
};
struct PrivateDeletedDestructor {
PrivateDeletedDestructor() = default;
void a() const {}
int x;
private:
~PrivateDeletedDestructor() = delete;
};
struct PrivateDestructor {
PrivateDestructor() = default;
void a() const {}
int x;
private:
~PrivateDestructor() = default;
};
struct NonConstCopy {
NonConstCopy() = default;
NonConstCopy(NonConstCopy&) {}
NonConstCopy(NonConstCopy&&) = default;
void a() const {}
};
struct TwoCopy {
TwoCopy() = default;
TwoCopy(TwoCopy&) {}
TwoCopy(const TwoCopy&) {}
TwoCopy(TwoCopy&&) = default;
void a() const {}
};
struct MemberPointerDeleted {
PublicDeleted *x;
void a() const {}
};
struct MemberConstPointerDeleted {
PublicDeleted *const x;
void a() const {}
};
struct MemberConst {
const int x;
void a() const {}
};
struct MemberReferenceDeleted {
PublicDeleted &x;
void a() const {}
};
struct MemberConstReferenceDeleted {
const PublicDeleted &x;
void a() const {}
};
struct MemberReference {
int &x;
void a() const {}
};
struct MemberConstReference {
const int &x;
void a() const {}
};
struct MemberRvalueReferenceDeleted {
PublicDeleted &&x;
void a() const {}
};
struct MemberRvalueReference {
int &&x;
void a() const {}
};
struct BasePublicDeleted : public PublicDeleted {};
struct BasePublicDeletedDefault : public PublicDeletedDefault {};
struct BasePublicDeletedCopy : public PublicDeletedCopy {};
struct BasePublicDeletedCopyNoDefault : public PublicDeletedCopyNoDefault { };
struct BasePublicMoveDeletedCopy : public PublicMoveDeletedCopy {};
struct BasePublicDeletedMove : public PublicDeletedMove {};
struct BasePublicDeletedDestructor : public PublicDeletedDestructor {};
struct BasePublicDestructor : public PublicDestructor {};
struct MemberPublicDeleted {
void a() const {}
PublicDeleted member;
};
struct MemberPublicDeletedDefault {
void a() const {}
PublicDeletedDefault member;
};
struct MemberPublicDeletedCopy {
void a() const {}
PublicDeletedCopy member;
};
struct MemberPublicDeletedCopyNoDefault {
void a() const {}
PublicDeletedCopyNoDefault member;
};
struct MemberPublicMoveDeletedCopy {
void a() const {}
PublicMoveDeletedCopy member;
};
struct MemberPublicDeletedMove {
void a() const {}
PublicDeletedMove member;
};
struct MemberPublicDeletedDestructor {
void a() const {}
PublicDeletedDestructor member;
};
struct MemberPublicDestructor {
void a() const {}
PublicDestructor member;
};
struct BaseMemberPublicDeleted : public MemberPublicDeleted {};
struct BaseMemberPublicDeletedDefault : public MemberPublicDeletedDefault {};
struct BaseMemberPublicDeletedCopy : public MemberPublicDeletedCopy {};
struct BaseMemberPublicDeletedCopyNoDefault : public MemberPublicDeletedCopyNoDefault {};
struct BaseMemberPublicMoveDeletedCopy : public MemberPublicMoveDeletedCopy {};
struct BaseMemberPublicDeletedMove : public MemberPublicDeletedMove {};
struct BaseMemberPublicDeletedDestructor : public MemberPublicDeletedDestructor {};
struct BaseMemberPublicDestructor : public MemberPublicDestructor {};
struct BaseProtectedDeleted : public ProtectedDeleted {};
struct BaseProtectedDeletedDefault : public ProtectedDeletedDefault {};
struct BaseProtectedDeletedCopy : public ProtectedDeletedCopy {};
struct BaseProtectedDeletedCopyNoDefault : public ProtectedDeletedCopyNoDefault {};
struct BaseProtectedMoveDeletedCopy : public ProtectedMoveDeletedCopy {};
struct BaseProtectedDeletedMove : public ProtectedDeletedMove {};
struct BaseProtectedDeletedDestructor : public ProtectedDeletedDestructor {};
struct BaseProtectedDestructor : public ProtectedDestructor {};
struct MemberProtectedDeleted {
void a() const {}
ProtectedDeleted member;
};
struct MemberProtectedDeletedDefault {
void a() const {}
ProtectedDeletedDefault member;
};
struct MemberProtectedDeletedCopy {
void a() const {}
ProtectedDeletedCopy member;
};
struct MemberProtectedDeletedCopyNoDefault {
void a() const {}
ProtectedDeletedCopyNoDefault member;
};
struct MemberProtectedMoveDeletedCopy {
void a() const {}
ProtectedMoveDeletedCopy member;
};
struct MemberProtectedDeletedMove {
void a() const {}
ProtectedDeletedMove member;
};
struct MemberProtectedDeletedDestructor {
void a() const {}
ProtectedDeletedDestructor member;
};
struct MemberProtectedDestructor {
void a() const {}
ProtectedDestructor member;
};
struct BaseMemberProtectedDeleted : public MemberProtectedDeleted {};
struct BaseMemberProtectedDeletedDefault : public MemberProtectedDeletedDefault {};
struct BaseMemberProtectedDeletedCopy : public MemberProtectedDeletedCopy {};
struct BaseMemberProtectedDeletedCopyNoDefault : public MemberProtectedDeletedCopyNoDefault {};
struct BaseMemberProtectedMoveDeletedCopy : public MemberProtectedMoveDeletedCopy {};
struct BaseMemberProtectedDeletedMove : public MemberProtectedDeletedMove {};
struct BaseMemberProtectedDeletedDestructor : public MemberProtectedDeletedDestructor {};
struct BaseMemberProtectedDestructor : public MemberProtectedDestructor {};
struct BasePrivateDeleted : public PrivateDeleted {};
struct BasePrivateDeletedDefault : public PrivateDeletedDefault {};
struct BasePrivateDeletedCopy : public PrivateDeletedCopy {};
struct BasePrivateDeletedCopyNoDefault : public PrivateDeletedCopyNoDefault {};
struct BasePrivateMoveDeletedCopy : public PrivateMoveDeletedCopy {};
struct BasePrivateDeletedMove : public PrivateDeletedMove {};
struct BasePrivateDeletedDestructor : public PrivateDeletedDestructor {};
struct BasePrivateDestructor : public PrivateDestructor {};
struct MemberPrivateDeleted {
void a() const {}
PrivateDeleted member;
};
struct MemberPrivateDeletedDefault {
void a() const {}
PrivateDeletedDefault member;
};
struct MemberPrivateDeletedCopy {
void a() const {}
PrivateDeletedCopy member;
};
struct MemberPrivateDeletedCopyNoDefault {
void a() const {}
PrivateDeletedCopyNoDefault member;
};
struct MemberPrivateMoveDeletedCopy {
void a() const {}
PrivateMoveDeletedCopy member;
};
struct MemberPrivateDeletedMove {
void a() const {}
PrivateDeletedMove member;
};
struct MemberPrivateDeletedDestructor {
void a() const {}
PrivateDeletedDestructor member;
};
struct MemberPrivateDestructor {
void a() const {}
PrivateDestructor member;
};
struct BaseMemberPrivateDeleted : public MemberPrivateDeleted {};
struct BaseMemberPrivateDeletedDefault : public MemberPrivateDeletedDefault {};
struct BaseMemberPrivateDeletedCopy : public MemberPrivateDeletedCopy {};
struct BaseMemberPrivateDeletedCopyNoDefault : public MemberPrivateDeletedCopyNoDefault {};
struct BaseMemberPrivateMoveDeletedCopy : public MemberPrivateMoveDeletedCopy {};
struct BaseMemberPrivateDeletedMove : public MemberPrivateDeletedMove {};
struct BaseMemberPrivateDeletedDestructor : public MemberPrivateDeletedDestructor {};
struct BaseMemberPrivateDestructor : public MemberPrivateDestructor {};
"};
let rs = quote! {
// Some macros to test various operations on our types. Note that some of them define
// functions which take arguments that the APIs defined in this test have no way to
// produce, because we have C++ types which can't be constructed (for example). In a real
// program, there might be other C++ APIs which can instantiate these types.
// TODO: https://github.com/google/autocxx/issues/829: Should this be merged with
// `test_make_unique`? Currently types where the Rust wrappers permit this but not that
// aren't running C++ destructors.
macro_rules! test_constructible {
[$t:ty] => {
moveit! {
let _moveit_t = <$t>::new();
}
}
}
macro_rules! test_make_unique {
[$t:ty] => {
let _unique_t = <$t>::new().within_unique_ptr();
}
}
macro_rules! test_copyable {
[$t:ty] => {
{
fn test_copyable(moveit_t: impl autocxx::moveit::new::New<Output = $t>) {
moveit! {
let moveit_t = moveit_t;
let _copied_t = autocxx::moveit::new::copy(moveit_t);
}
}
}
}
}
macro_rules! test_movable {
[$t:ty] => {
{
fn test_movable(moveit_t: impl autocxx::moveit::new::New<Output = $t>) {
moveit! {
let moveit_t = moveit_t;
let _moved_t = autocxx::moveit::new::mov(moveit_t);
}
}
}
}
}
macro_rules! test_call_a {
[$t:ty] => {
{
fn test_call_a(t: &$t) {
t.a();
}
}
}
}
macro_rules! test_call_a_as {
[$t:ty, $parent:ty] => {
{
fn test_call_a(t: &$t) {
let t: &$parent = t.as_ref();
t.a();
}
}
}
}
test_constructible![ffi::AllImplicitlyDefaulted];
test_make_unique![ffi::AllImplicitlyDefaulted];
test_copyable![ffi::AllImplicitlyDefaulted];
test_movable![ffi::AllImplicitlyDefaulted];
test_call_a![ffi::AllImplicitlyDefaulted];
test_call_a![ffi::PublicDeleted];
test_copyable![ffi::PublicDeletedDefault];
test_movable![ffi::PublicDeletedDefault];
test_call_a![ffi::PublicDeletedDefault];
test_constructible![ffi::PublicDeletedCopy];
test_make_unique![ffi::PublicDeletedCopy];
test_call_a![ffi::PublicDeletedCopy];
test_call_a![ffi::PublicDeletedCopyNoDefault];
test_constructible![ffi::PublicMoveDeletedCopy];
test_make_unique![ffi::PublicMoveDeletedCopy];
test_movable![ffi::PublicMoveDeletedCopy];
test_call_a![ffi::PublicMoveDeletedCopy];
test_constructible![ffi::PublicDeletedMove];
test_make_unique![ffi::PublicDeletedMove];
test_call_a![ffi::PublicDeletedMove];
test_constructible![ffi::PublicDeletedDestructor];
test_copyable![ffi::PublicDeletedDestructor];
test_call_a![ffi::PublicDeletedDestructor];
test_constructible![ffi::PublicDestructor];
test_make_unique![ffi::PublicDestructor];
test_copyable![ffi::PublicDestructor];
test_call_a![ffi::PublicDestructor];
test_call_a![ffi::ProtectedDeleted];
test_copyable![ffi::ProtectedDeletedDefault];
test_movable![ffi::ProtectedDeletedDefault];
test_call_a![ffi::ProtectedDeletedDefault];
test_constructible![ffi::ProtectedDeletedCopy];
test_make_unique![ffi::ProtectedDeletedCopy];
test_call_a![ffi::ProtectedDeletedCopy];
test_call_a![ffi::ProtectedDeletedCopyNoDefault];
test_constructible![ffi::ProtectedMoveDeletedCopy];
test_make_unique![ffi::ProtectedMoveDeletedCopy];
test_call_a![ffi::ProtectedMoveDeletedCopy];
test_constructible![ffi::ProtectedDeletedMove];
test_make_unique![ffi::ProtectedDeletedMove];
test_call_a![ffi::ProtectedDeletedMove];
test_constructible![ffi::ProtectedDeletedDestructor];
test_copyable![ffi::ProtectedDeletedDestructor];
test_call_a![ffi::ProtectedDeletedDestructor];
test_constructible![ffi::ProtectedDestructor];
test_copyable![ffi::ProtectedDestructor];
test_call_a![ffi::ProtectedDestructor];
test_call_a![ffi::PrivateDeleted];
test_copyable![ffi::PrivateDeletedDefault];
test_movable![ffi::PrivateDeletedDefault];
test_call_a![ffi::PrivateDeletedDefault];
test_constructible![ffi::PrivateDeletedCopy];
test_make_unique![ffi::PrivateDeletedCopy];
test_call_a![ffi::PrivateDeletedCopy];
test_call_a![ffi::PrivateDeletedCopyNoDefault];
test_constructible![ffi::PrivateMoveDeletedCopy];
test_make_unique![ffi::PrivateMoveDeletedCopy];
test_call_a![ffi::PrivateMoveDeletedCopy];
test_constructible![ffi::PrivateDeletedMove];
test_make_unique![ffi::PrivateDeletedMove];
test_call_a![ffi::PrivateDeletedMove];
test_constructible![ffi::PrivateDeletedDestructor];
test_copyable![ffi::PrivateDeletedDestructor];
test_call_a![ffi::PrivateDeletedDestructor];
test_constructible![ffi::PrivateDestructor];
test_copyable![ffi::PrivateDestructor];
test_call_a![ffi::PrivateDestructor];
test_constructible![ffi::NonConstCopy];
test_make_unique![ffi::NonConstCopy];
test_movable![ffi::NonConstCopy];
test_call_a![ffi::NonConstCopy];
test_constructible![ffi::TwoCopy];
test_make_unique![ffi::TwoCopy];
test_copyable![ffi::TwoCopy];
test_movable![ffi::TwoCopy];
test_call_a![ffi::TwoCopy];
// TODO: https://github.com/google/autocxx/issues/865
// Treat pointers and references differently so this has a default constructor.
//test_constructible![ffi::MemberPointerDeleted];
//test_make_unique![ffi::MemberPointerDeleted];
test_copyable![ffi::MemberPointerDeleted];
test_movable![ffi::MemberPointerDeleted];
test_call_a![ffi::MemberPointerDeleted];
test_copyable![ffi::MemberConstPointerDeleted];
test_movable![ffi::MemberConstPointerDeleted];
test_call_a![ffi::MemberConstPointerDeleted];
//test_copyable![ffi::MemberConst];
//test_movable![ffi::MemberConst];
//test_call_a![ffi::MemberConst];
test_copyable![ffi::MemberReferenceDeleted];
test_movable![ffi::MemberReferenceDeleted];
test_call_a![ffi::MemberReferenceDeleted];
test_copyable![ffi::MemberConstReferenceDeleted];
test_movable![ffi::MemberConstReferenceDeleted];
test_call_a![ffi::MemberConstReferenceDeleted];
test_copyable![ffi::MemberReference];
test_movable![ffi::MemberReference];
test_call_a![ffi::MemberReference];
test_copyable![ffi::MemberConstReference];
test_movable![ffi::MemberConstReference];
test_call_a![ffi::MemberConstReference];
test_movable![ffi::MemberRvalueReferenceDeleted];
test_call_a![ffi::MemberRvalueReferenceDeleted];
test_movable![ffi::MemberRvalueReference];
test_call_a![ffi::MemberRvalueReference];
test_call_a_as![ffi::BasePublicDeleted, ffi::PublicDeleted];
test_copyable![ffi::BasePublicDeletedDefault];
test_movable![ffi::BasePublicDeletedDefault];
test_call_a_as![ffi::BasePublicDeletedDefault, ffi::PublicDeletedDefault];
test_constructible![ffi::BasePublicDeletedCopy];
test_make_unique![ffi::BasePublicDeletedCopy];
test_call_a_as![ffi::BasePublicDeletedCopy, ffi::PublicDeletedCopy];
test_call_a_as![ffi::BasePublicDeletedCopyNoDefault, ffi::PublicDeletedCopyNoDefault];
test_constructible![ffi::BasePublicMoveDeletedCopy];
test_make_unique![ffi::BasePublicMoveDeletedCopy];
test_movable![ffi::BasePublicMoveDeletedCopy];
test_call_a_as![ffi::BasePublicMoveDeletedCopy, ffi::PublicMoveDeletedCopy];
test_constructible![ffi::BasePublicDeletedMove];
test_make_unique![ffi::BasePublicDeletedMove];
test_call_a_as![ffi::BasePublicDeletedMove, ffi::PublicDeletedMove];
test_call_a_as![ffi::BasePublicDeletedDestructor, ffi::PublicDeletedDestructor];
test_constructible![ffi::BasePublicDestructor];
test_make_unique![ffi::BasePublicDestructor];
test_copyable![ffi::BasePublicDestructor];
test_call_a_as![ffi::BasePublicDestructor, ffi::PublicDestructor];
test_call_a![ffi::MemberPublicDeleted];
test_copyable![ffi::MemberPublicDeletedDefault];
test_movable![ffi::MemberPublicDeletedDefault];
test_call_a![ffi::MemberPublicDeletedDefault];
test_constructible![ffi::MemberPublicDeletedCopy];
test_make_unique![ffi::MemberPublicDeletedCopy];
test_call_a![ffi::MemberPublicDeletedCopy];
test_call_a![ffi::MemberPublicDeletedCopyNoDefault];
test_constructible![ffi::MemberPublicMoveDeletedCopy];
test_make_unique![ffi::MemberPublicMoveDeletedCopy];
test_movable![ffi::MemberPublicMoveDeletedCopy];
test_call_a![ffi::MemberPublicMoveDeletedCopy];
test_constructible![ffi::MemberPublicDeletedMove];
test_make_unique![ffi::MemberPublicDeletedMove];
test_call_a![ffi::MemberPublicDeletedMove];
test_call_a![ffi::MemberPublicDeletedDestructor];
test_constructible![ffi::MemberPublicDestructor];
test_make_unique![ffi::MemberPublicDestructor];
test_copyable![ffi::MemberPublicDestructor];
test_call_a![ffi::MemberPublicDestructor];
test_call_a_as![ffi::BaseMemberPublicDeleted, ffi::MemberPublicDeleted];
test_copyable![ffi::BaseMemberPublicDeletedDefault];
test_movable![ffi::BaseMemberPublicDeletedDefault];
test_call_a_as![ffi::BaseMemberPublicDeletedDefault, ffi::MemberPublicDeletedDefault];
test_constructible![ffi::BaseMemberPublicDeletedCopy];
test_make_unique![ffi::BaseMemberPublicDeletedCopy];
test_call_a_as![ffi::BaseMemberPublicDeletedCopy, ffi::MemberPublicDeletedCopy];
test_call_a_as![ffi::BaseMemberPublicDeletedCopyNoDefault, ffi::MemberPublicDeletedCopyNoDefault];
test_constructible![ffi::BaseMemberPublicMoveDeletedCopy];
test_make_unique![ffi::BaseMemberPublicMoveDeletedCopy];
test_movable![ffi::BaseMemberPublicMoveDeletedCopy];
test_call_a_as![ffi::BaseMemberPublicMoveDeletedCopy, ffi::MemberPublicMoveDeletedCopy];
test_constructible![ffi::BaseMemberPublicDeletedMove];
test_make_unique![ffi::BaseMemberPublicDeletedMove];
test_call_a_as![ffi::BaseMemberPublicDeletedMove, ffi::MemberPublicDeletedMove];
test_call_a_as![ffi::BaseMemberPublicDeletedDestructor, ffi::MemberPublicDeletedDestructor];
test_constructible![ffi::BaseMemberPublicDestructor];
test_make_unique![ffi::BaseMemberPublicDestructor];
test_copyable![ffi::BaseMemberPublicDestructor];
test_call_a_as![ffi::BaseMemberPublicDestructor, ffi::MemberPublicDestructor];
test_call_a_as![ffi::BaseProtectedDeleted, ffi::ProtectedDeleted];
test_copyable![ffi::BaseProtectedDeletedDefault];
test_movable![ffi::BaseProtectedDeletedDefault];
test_call_a_as![ffi::BaseProtectedDeletedDefault, ffi::ProtectedDeletedDefault];
test_constructible![ffi::BaseProtectedDeletedCopy];
test_make_unique![ffi::BaseProtectedDeletedCopy];
test_call_a_as![ffi::BaseProtectedDeletedCopy, ffi::ProtectedDeletedCopy];
test_call_a_as![ffi::BaseProtectedDeletedCopyNoDefault, ffi::ProtectedDeletedCopyNoDefault];
test_constructible![ffi::BaseProtectedMoveDeletedCopy];
test_make_unique![ffi::BaseProtectedMoveDeletedCopy];
test_movable![ffi::BaseProtectedMoveDeletedCopy];
test_call_a_as![ffi::BaseProtectedMoveDeletedCopy, ffi::ProtectedMoveDeletedCopy];
test_constructible![ffi::BaseProtectedDeletedMove];
test_make_unique![ffi::BaseProtectedDeletedMove];
test_call_a_as![ffi::BaseProtectedDeletedMove, ffi::ProtectedDeletedMove];
test_call_a_as![ffi::BaseProtectedDeletedDestructor, ffi::ProtectedDeletedDestructor];
test_constructible![ffi::BaseProtectedDestructor];
test_make_unique![ffi::BaseProtectedDestructor];
test_copyable![ffi::BaseProtectedDestructor];
test_call_a_as![ffi::BaseProtectedDestructor, ffi::ProtectedDestructor];
test_call_a![ffi::MemberProtectedDeleted];
test_copyable![ffi::MemberProtectedDeletedDefault];
test_movable![ffi::MemberProtectedDeletedDefault];
test_call_a![ffi::MemberProtectedDeletedDefault];
test_constructible![ffi::MemberProtectedDeletedCopy];
test_make_unique![ffi::MemberProtectedDeletedCopy];
test_call_a![ffi::MemberProtectedDeletedCopy];
test_call_a![ffi::MemberProtectedDeletedCopyNoDefault];
test_constructible![ffi::MemberProtectedMoveDeletedCopy];
test_make_unique![ffi::MemberProtectedMoveDeletedCopy];
test_call_a![ffi::MemberProtectedMoveDeletedCopy];
test_constructible![ffi::MemberProtectedDeletedMove];
test_make_unique![ffi::MemberProtectedDeletedMove];
test_call_a![ffi::MemberProtectedDeletedMove];
test_call_a![ffi::MemberProtectedDeletedDestructor];
test_call_a![ffi::MemberProtectedDestructor];
test_call_a_as![ffi::BaseMemberProtectedDeleted, ffi::MemberProtectedDeleted];
test_copyable![ffi::BaseMemberProtectedDeletedDefault];
test_movable![ffi::BaseMemberProtectedDeletedDefault];
test_call_a_as![ffi::BaseMemberProtectedDeletedDefault, ffi::MemberProtectedDeletedDefault];
test_constructible![ffi::BaseMemberProtectedDeletedCopy];
test_make_unique![ffi::BaseMemberProtectedDeletedCopy];
test_call_a_as![ffi::BaseMemberProtectedDeletedCopy, ffi::MemberProtectedDeletedCopy];
test_call_a_as![ffi::BaseMemberProtectedDeletedCopyNoDefault, ffi::MemberProtectedDeletedCopyNoDefault];
test_constructible![ffi::BaseMemberProtectedMoveDeletedCopy];
test_make_unique![ffi::BaseMemberProtectedMoveDeletedCopy];
test_call_a_as![ffi::BaseMemberProtectedMoveDeletedCopy, ffi::MemberProtectedMoveDeletedCopy];
test_constructible![ffi::BaseMemberProtectedDeletedMove];
test_make_unique![ffi::BaseMemberProtectedDeletedMove];
test_call_a_as![ffi::BaseMemberProtectedDeletedMove, ffi::MemberProtectedDeletedMove];
test_call_a_as![ffi::BaseMemberProtectedDeletedDestructor, ffi::MemberProtectedDeletedDestructor];
test_call_a_as![ffi::BaseMemberProtectedDestructor, ffi::MemberProtectedDestructor];
test_call_a_as![ffi::BasePrivateDeleted, ffi::PrivateDeleted];
test_copyable![ffi::BasePrivateDeletedDefault];
test_movable![ffi::BasePrivateDeletedDefault];
test_call_a_as![ffi::BasePrivateDeletedDefault, ffi::PrivateDeletedDefault];
test_constructible![ffi::BasePrivateDeletedCopy];
test_make_unique![ffi::BasePrivateDeletedCopy];
test_call_a_as![ffi::BasePrivateDeletedCopy, ffi::PrivateDeletedCopy];
test_call_a_as![ffi::BasePrivateDeletedCopyNoDefault, ffi::PrivateDeletedCopyNoDefault];
test_constructible![ffi::BasePrivateMoveDeletedCopy];
test_make_unique![ffi::BasePrivateMoveDeletedCopy];
test_call_a_as![ffi::BasePrivateMoveDeletedCopy, ffi::PrivateMoveDeletedCopy];
test_constructible![ffi::BasePrivateDeletedMove];
test_make_unique![ffi::BasePrivateDeletedMove];
test_call_a_as![ffi::BasePrivateDeletedMove, ffi::PrivateDeletedMove];
test_call_a_as![ffi::BasePrivateDeletedDestructor, ffi::PrivateDeletedDestructor];
test_call_a_as![ffi::BasePrivateDestructor, ffi::PrivateDestructor];
test_call_a![ffi::MemberPrivateDeleted];
test_copyable![ffi::MemberPrivateDeletedDefault];
test_movable![ffi::MemberPrivateDeletedDefault];
test_call_a![ffi::MemberPrivateDeletedDefault];
test_constructible![ffi::MemberPrivateDeletedCopy];
test_make_unique![ffi::MemberPrivateDeletedCopy];
test_call_a![ffi::MemberPrivateDeletedCopy];
test_call_a![ffi::MemberPrivateDeletedCopyNoDefault];
test_constructible![ffi::MemberPrivateMoveDeletedCopy];
test_make_unique![ffi::MemberPrivateMoveDeletedCopy];
test_call_a![ffi::MemberPrivateMoveDeletedCopy];
test_constructible![ffi::MemberPrivateDeletedMove];
test_make_unique![ffi::MemberPrivateDeletedMove];
test_call_a![ffi::MemberPrivateDeletedMove];
test_call_a![ffi::MemberPrivateDeletedDestructor];
test_call_a![ffi::MemberPrivateDestructor];
test_call_a_as![ffi::BaseMemberPrivateDeleted, ffi::MemberPrivateDeleted];
test_copyable![ffi::BaseMemberPrivateDeletedDefault];
test_movable![ffi::BaseMemberPrivateDeletedDefault];
test_call_a_as![ffi::BaseMemberPrivateDeletedDefault, ffi::MemberPrivateDeletedDefault];
test_constructible![ffi::BaseMemberPrivateDeletedCopy];
test_make_unique![ffi::BaseMemberPrivateDeletedCopy];
test_call_a_as![ffi::BaseMemberPrivateDeletedCopy, ffi::MemberPrivateDeletedCopy];
test_call_a_as![ffi::BaseMemberPrivateDeletedCopyNoDefault, ffi::MemberPrivateDeletedCopyNoDefault];
test_constructible![ffi::BaseMemberPrivateMoveDeletedCopy];
test_make_unique![ffi::BaseMemberPrivateMoveDeletedCopy];
test_call_a_as![ffi::BaseMemberPrivateMoveDeletedCopy, ffi::MemberPrivateMoveDeletedCopy];
test_constructible![ffi::BaseMemberPrivateDeletedMove];
test_make_unique![ffi::BaseMemberPrivateDeletedMove];
test_call_a_as![ffi::BaseMemberPrivateDeletedMove, ffi::MemberPrivateDeletedMove];
test_call_a_as![ffi::BaseMemberPrivateDeletedDestructor, ffi::MemberPrivateDeletedDestructor];
test_call_a_as![ffi::BaseMemberPrivateDestructor, ffi::MemberPrivateDestructor];
};
run_test(
cxx,
hdr,
rs,
&[
"AllImplicitlyDefaulted",
"PublicDeleted",
"PublicDeletedDefault",
"PublicDeletedCopy",
"PublicDeletedCopyNoDefault",
"PublicMoveDeletedCopy",
"PublicDeletedMove",
"PublicDeletedDestructor",
"PublicDestructor",
"ProtectedDeleted",
"ProtectedDeletedDefault",
"ProtectedDeletedCopy",
"ProtectedDeletedCopyNoDefault",
"ProtectedMoveDeletedCopy",
"ProtectedDeletedMove",
"ProtectedDeletedDestructor",
"ProtectedDestructor",
"PrivateDeleted",
"PrivateDeletedDefault",
"PrivateDeletedCopy",
"PrivateDeletedCopyNoDefault",
"PrivateMoveDeletedCopy",
"PrivateDeletedMove",
"PrivateDeletedDestructor",
"PrivateDestructor",
"NonConstCopy",
"TwoCopy",
"MemberPointerDeleted",
"MemberConstPointerDeleted",
// TODO: Handle top-level const on C++ members correctly.
//"MemberConst",
"MemberReferenceDeleted",
"MemberConstReferenceDeleted",
"MemberReference",
"MemberConstReference",
"MemberRvalueReferenceDeleted",
"MemberRvalueReference",
"BasePublicDeleted",
"BasePublicDeletedDefault",
"BasePublicDeletedCopy",
"BasePublicDeletedCopyNoDefault",
"BasePublicMoveDeletedCopy",
"BasePublicDeletedMove",
"BasePublicDeletedDestructor",
"BasePublicDestructor",
"MemberPublicDeleted",
"MemberPublicDeletedDefault",
"MemberPublicDeletedCopy",
"MemberPublicDeletedCopyNoDefault",
"MemberPublicMoveDeletedCopy",
"MemberPublicDeletedMove",
"MemberPublicDeletedDestructor",
"MemberPublicDestructor",
"BaseMemberPublicDeleted",
"BaseMemberPublicDeletedDefault",
"BaseMemberPublicDeletedCopy",
"BaseMemberPublicDeletedCopyNoDefault",
"BaseMemberPublicMoveDeletedCopy",
"BaseMemberPublicDeletedMove",
"BaseMemberPublicDeletedDestructor",
"BaseMemberPublicDestructor",
"BaseProtectedDeleted",
"BaseProtectedDeletedDefault",
"BaseProtectedDeletedCopy",
"BaseProtectedDeletedCopyNoDefault",
"BaseProtectedMoveDeletedCopy",
"BaseProtectedDeletedMove",
"BaseProtectedDeletedDestructor",
"BaseProtectedDestructor",
"MemberProtectedDeleted",
"MemberProtectedDeletedDefault",
"MemberProtectedDeletedCopy",
"MemberProtectedDeletedCopyNoDefault",
"MemberProtectedMoveDeletedCopy",
"MemberProtectedDeletedMove",
"MemberProtectedDeletedDestructor",
"MemberProtectedDestructor",
"BaseMemberProtectedDeleted",
"BaseMemberProtectedDeletedDefault",
"BaseMemberProtectedDeletedCopy",
"BaseMemberProtectedDeletedCopyNoDefault",
"BaseMemberProtectedMoveDeletedCopy",
"BaseMemberProtectedDeletedMove",
"BaseMemberProtectedDeletedDestructor",
"BaseMemberProtectedDestructor",
"BasePrivateDeleted",
"BasePrivateDeletedDefault",
"BasePrivateDeletedCopy",
"BasePrivateDeletedCopyNoDefault",
"BasePrivateMoveDeletedCopy",
"BasePrivateDeletedMove",
"BasePrivateDeletedDestructor",
"BasePrivateDestructor",
"MemberPrivateDeleted",
"MemberPrivateDeletedDefault",
"MemberPrivateDeletedCopy",
"MemberPrivateDeletedCopyNoDefault",
"MemberPrivateMoveDeletedCopy",
"MemberPrivateDeletedMove",
"MemberPrivateDeletedDestructor",
"MemberPrivateDestructor",
"BaseMemberPrivateDeleted",
"BaseMemberPrivateDeletedDefault",
"BaseMemberPrivateDeletedCopy",
"BaseMemberPrivateDeletedCopyNoDefault",
"BaseMemberPrivateMoveDeletedCopy",
"BaseMemberPrivateDeletedMove",
"BaseMemberPrivateDeletedDestructor",
"BaseMemberPrivateDestructor",
],
&[],
);
}
#[test]
/// Test that destructors hidden in various places are correctly called.
///
/// Some types are excluded because we know they behave poorly due to
/// https://github.com/google/autocxx/issues/829.
fn test_tricky_destructors() {
let cxx = "";
let hdr = indoc! {"
#include <stdio.h>
#include <stdlib.h>
// A simple type to let Rust verify the destructor is run.
struct DestructorFlag {
DestructorFlag() = default;
DestructorFlag(const DestructorFlag&) = default;
DestructorFlag(DestructorFlag&&) = default;
~DestructorFlag() {
if (!flag) return;
if (*flag) {
fprintf(stderr, \"DestructorFlag is already set\\n\");
abort();
}
*flag = true;
// Note we deliberately do NOT clear the value of `flag`, to catch Rust calling
// this destructor twice.
}
bool *flag = nullptr;
};
struct ImplicitlyDefaulted {
DestructorFlag flag;
void set_flag(bool *flag_pointer) { flag.flag = flag_pointer; }
};
struct ExplicitlyDefaulted {
ExplicitlyDefaulted() = default;
~ExplicitlyDefaulted() = default;
DestructorFlag flag;
void set_flag(bool *flag_pointer) { flag.flag = flag_pointer; }
};
struct Explicit {
Explicit() = default;
~Explicit() {}
DestructorFlag flag;
void set_flag(bool *flag_pointer) { flag.flag = flag_pointer; }
};
struct BaseImplicitlyDefaulted : public ImplicitlyDefaulted {
void set_flag(bool *flag_pointer) { ImplicitlyDefaulted::set_flag(flag_pointer); }
};
struct BaseExplicitlyDefaulted : public ExplicitlyDefaulted {
void set_flag(bool *flag_pointer) { ExplicitlyDefaulted::set_flag(flag_pointer); }
};
struct BaseExplicit : public Explicit {
void set_flag(bool *flag_pointer) { Explicit::set_flag(flag_pointer); }
};
struct MemberImplicitlyDefaulted {
ImplicitlyDefaulted member;
void set_flag(bool *flag_pointer) { member.set_flag(flag_pointer); }
};
struct MemberExplicitlyDefaulted {
ExplicitlyDefaulted member;
void set_flag(bool *flag_pointer) { member.set_flag(flag_pointer); }
};
struct MemberExplicit {
Explicit member;
void set_flag(bool *flag_pointer) { member.set_flag(flag_pointer); }
};
struct BaseMemberImplicitlyDefaulted : public MemberImplicitlyDefaulted {
void set_flag(bool *flag_pointer) { MemberImplicitlyDefaulted::set_flag(flag_pointer); }
};
struct BaseMemberExplicitlyDefaulted : public MemberExplicitlyDefaulted {
void set_flag(bool *flag_pointer) { MemberExplicitlyDefaulted::set_flag(flag_pointer); }
};
struct BaseMemberExplicit : public MemberExplicit {
void set_flag(bool *flag_pointer) { MemberExplicit::set_flag(flag_pointer); }
};
"};
let rs = quote! {
macro_rules! test_type {
[$t:ty] => {
let mut unique_t = <$t>::new().within_unique_ptr();
let mut destructor_flag = false;
unsafe {
unique_t.pin_mut().set_flag(&mut destructor_flag);
}
std::mem::drop(unique_t);
assert!(destructor_flag, "Destructor did not run with make_unique for {}", quote::quote!{$t});
moveit! {
let mut moveit_t = <$t>::new();
}
let mut destructor_flag = false;
unsafe {
moveit_t.as_mut().set_flag(&mut destructor_flag);
}
std::mem::drop(moveit_t);
assert!(destructor_flag, "Destructor did not run with moveit for {}", quote::quote!{$t});
}
}
test_type![ffi::ImplicitlyDefaulted];
test_type![ffi::ExplicitlyDefaulted];
test_type![ffi::Explicit];
test_type![ffi::BaseImplicitlyDefaulted];
test_type![ffi::BaseExplicitlyDefaulted];
test_type![ffi::BaseExplicit];
test_type![ffi::MemberImplicitlyDefaulted];
test_type![ffi::MemberExplicitlyDefaulted];
test_type![ffi::MemberExplicit];
test_type![ffi::BaseMemberImplicitlyDefaulted];
test_type![ffi::BaseMemberExplicitlyDefaulted];
test_type![ffi::BaseMemberExplicit];
};
run_test(
cxx,
hdr,
rs,
&[
"DestructorFlag",
"ImplicitlyDefaulted",
"ExplicitlyDefaulted",
"Explicit",
"BaseImplicitlyDefaulted",
"BaseExplicitlyDefaulted",
"BaseExplicit",
"MemberImplicitlyDefaulted",
"MemberExplicitlyDefaulted",
"MemberExplicit",
"BaseMemberImplicitlyDefaulted",
"BaseMemberExplicitlyDefaulted",
"BaseMemberExplicit",
],
&[],
);
}
#[test]
fn test_concretize() {
let hdr = indoc! {"
#include <string>
template<typename CONTENTS>
class Container {
private:
CONTENTS* contents;
};
struct B {
std::string a;
};
"};
run_test_ex(
"",
hdr,
quote! {},
quote! {
concrete!("Container<B>", ContainerOfB)
generate!("B")
},
None,
None,
Some(quote! {
struct HasAField {
contents: ffi::ContainerOfB
}
}),
);
}
#[test]
fn test_doc_comments_survive() {
let hdr = indoc! {"
#include <cstdint>
/// Struct line A
/// Struct line B
struct A { int b; };
/// POD struct line A
/// POD struct line B
struct B {
/// Field line A
/// Field line B
uint32_t b;
/// Method line A
/// Method line B
void foo() {}
};
/// Enum line A
/// Enum line B
enum C {
/// Variant line A
/// Variant line B
VARIANT,
};
/// Function line A
/// Function line B
inline void D() {}
"};
let expected_messages = [
"Struct",
"POD struct",
"Field",
"Method",
"Enum",
"Variant",
"Function",
]
.into_iter()
.flat_map(|l| [format!("{} line A", l), format!("{} line B", l)])
.collect_vec();
run_test_ex(
"",
hdr,
quote! {},
directives_from_lists(&["A", "C", "D"], &["B"], None),
None,
Some(make_string_finder(expected_messages)),
None,
);
}
#[test]
fn optional_param_in_copy_constructor() {
let hdr = indoc! {"
struct A {
A(const A &other, bool optional_arg = false);
};
"};
run_test("", hdr, quote! {}, &["A"], &[]);
}
#[test]
fn param_in_copy_constructor() {
let hdr = indoc! {"
struct A {
A(const A &other, bool arg);
};
"};
run_test("", hdr, quote! {}, &["A"], &[]);
}
#[test]
fn test_variadic() {
let hdr = indoc! {"
class SomeClass{
public:
inline void foo(int, ... ) {}
};
"};
run_test("", hdr, quote! {}, &["SomeClass"], &[]);
}
#[test]
fn test_typedef_to_enum() {
let hdr = indoc! {"
enum b {};
class c {
public:
typedef b d;
d e();
};
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_typedef_to_ns_enum() {
let hdr = indoc! {"
namespace a {
enum b {};
class c {
public:
typedef b d;
d e();
};
} // namespace
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_typedef_unsupported_type_pub() {
let hdr = indoc! {"
#include <set>
namespace NS{
class cls{
public:
typedef std::set<int> InnerType;
};
}
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_ns!("NS") },
None,
None,
None,
);
}
#[test]
fn test_typedef_unsupported_type_pri() {
let hdr = indoc! {"
#include <set>
namespace NS{
class cls{
private:
typedef std::set<int> InnerType;
};
}
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_ns!("NS") },
None,
None,
None,
);
}
#[test]
fn test_array_trouble1() {
let hdr = indoc! {"
namespace a {
template <typename b> struct array {
typedef b c;
typedef c d;
};
} // namespace a
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_array_trouble2() {
let hdr = indoc! {"
template <typename b> struct array {
typedef b c;
typedef c d;
};
"};
run_test("", hdr, quote! {}, &["array_d"], &[]);
}
#[test]
fn test_issue_1087a() {
let hdr = indoc! {"
template <typename _CharT> class a {
_CharT b;
};
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_issue_1087b() {
let hdr = indoc! {"
template <typename _CharT> class a {
typedef _CharT b;
b c;
};
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_issue_1087c() {
let hdr = indoc! {"
namespace {
namespace {
template <typename _CharT> class a {
typedef _CharT b;
b c;
};
}
}
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_issue_1089() {
let hdr = indoc! {"
namespace a {
template <typename c, c> struct d;
template <bool, typename, typename> struct ab;
inline namespace {
namespace ac {
template <typename, template <typename> class, typename> struct bh;
template <template <typename> class ad, typename... bi>
using bj = bh<void, ad, bi...>;
template <typename ad> using bk = typename ad::b;
template <typename> struct bm;
} // namespace ac
template <typename ad>
struct b : ab<ac::bj<ac::bk, ad>::e, ac::bm<ad>, d<bool, ad ::e>>::bg {};
} // namespace
} // namespace a
"};
run_test_ex(
"",
hdr,
quote! {},
quote! { generate_all!() },
None,
None,
None,
);
}
#[test]
fn test_pass_rust_str_and_return_struct() {
let cxx = indoc! {"
A take_str_return_struct(rust::Str) {
A a;
return a;
}
"};
let hdr = indoc! {"
#include <cxx.h>
struct A {};
A take_str_return_struct(rust::Str);
"};
let rs = quote! {
ffi::take_str_return_struct("hi");
};
run_test(cxx, hdr, rs, &["take_str_return_struct"], &[]);
}
#[test]
fn test_issue_1065a() {
let hdr = indoc! {"
#include <memory>
#include <vector>
template <typename at> class au {
std::unique_ptr<at> aw;
};
class bb;
using bc = au<bb>;
class RenderFrameHost {
public:
virtual std::vector<bc> &bd() = 0;
virtual ~RenderFrameHost() {}
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["RenderFrameHost"], &[]);
}
#[test]
fn test_issue_1065b() {
let hdr = indoc! {"
#include <memory>
#include <vector>
class bb;
using bc = std::unique_ptr<bb>;
class RenderFrameHost {
public:
virtual std::vector<bc> &bd() = 0;
virtual ~RenderFrameHost() {}
};
"};
let rs = quote! {};
run_test("", hdr, rs, &["RenderFrameHost"], &[]);
}
// Yet to test:
// - Ifdef
// - Out param pointers
// - ExcludeUtilities
// - Struct fields which are typedefs
// Negative tests:
// - Private methods
// - Private fields