Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions engine/src/conversion/analysis/fun/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2049,6 +2049,7 @@ impl<'a> FnAnalyzer<'a> {
/// Also fills out the [`PodAndConstructorAnalysis::constructors`] fields with information useful
/// for further analysis phases.
fn add_constructors_present(&mut self, mut apis: ApiVec<FnPrePhase1>) -> ApiVec<FnPrePhase2> {
let enums = self.config.get_enums();
let all_items_found = find_constructors_present(&apis);
for (self_ty, items_found) in all_items_found.iter() {
if self.config.exclude_impls {
Expand All @@ -2057,10 +2058,8 @@ impl<'a> FnAnalyzer<'a> {
// messy, see the comment on this function for why.
continue;
}
if self
.config
.is_on_constructor_blocklist(&self_ty.to_cpp_name())
{
let cpp_name = self_ty.to_cpp_name();
if self.config.is_on_constructor_blocklist(&cpp_name) || enums.contains(&cpp_name) {
continue;
}
let path = self_ty.to_type_path();
Expand Down
16 changes: 16 additions & 0 deletions engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,22 @@ impl IncludeCppEngine {
.allowlist_var(&a);
}
}

for (style, enums) in &self.config.enum_styles.0 {
use autocxx_parser::EnumStyle::*;
let apply: fn(bindgen::Builder, &String) -> bindgen::Builder = match style {
BitfieldEnum => |b, e| b.bitfield_enum(e),
NewtypeEnum => |b, e| b.newtype_enum(e),
// NewtypeGlobalEnum => |b, e| b.newtype_global_enum(e),
RustifiedEnum => |b, e| b.rustified_enum(e),
RustifiedNonExhaustiveEnum => |b, e| b.rustified_non_exhaustive_enum(e),
// ConstifiedEnumModule => |b, e| b.constified_enum_module(e),
// ConstifiedEnum => |b, e| b.constified_enum(e),
};
for name in enums {
builder = apply(builder, name);
}
}

for item in &self.config.opaquelist {
builder = builder.opaque_type(item);
Expand Down
173 changes: 173 additions & 0 deletions integration-tests/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,179 @@ fn test_enum_with_funcs() {
run_test(cxx, hdr, rs, &["Bob", "give_bob"], &[]);
}

#[test]
fn test_bitfield_enum() {
let hdr = indoc! {"
#include <cstdint>
enum SomeFlags : int {
FLAG_A = 1 << 0, // 0x1
FLAG_B = 1 << 2, // 0x4
FLAG_C = FLAG_A | FLAG_B, // 0x5
};
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
enum_style!(BitfieldEnum, "SomeFlags")
generate_pod!("SomeFlags")
}

fn main() {
let a = ffi::SomeFlags::FLAG_A;
let b = ffi::SomeFlags::FLAG_B;
let c = ffi::SomeFlags::FLAG_C;
assert_eq!(a.0, 0x1);
assert_eq!(b.0, 0x4);
assert_eq!(c.0, 0x5);

let aob = ffi::SomeFlags::FLAG_A | ffi::SomeFlags::FLAG_B;
assert_eq!(aob.0, 0x5);
assert_eq!(aob.0, ffi::SomeFlags::FLAG_C.0);

let anb = ffi::SomeFlags::FLAG_A & ffi::SomeFlags::FLAG_B;
assert_eq!(anb.0, 0x0);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}

#[test]
fn test_newtype_enum() {
let hdr = indoc! {"
#include <cstdint>
enum SomeFlags : int {
FLAG_A = 1 << 0, // 0x1
FLAG_B = 1 << 2, // 0x4
FLAG_C = FLAG_A | FLAG_B, // 0x5
};
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
enum_style!(NewtypeEnum, "SomeFlags")
generate_pod!("SomeFlags")
}

fn main() {
let a = ffi::SomeFlags::FLAG_A;
let b = ffi::SomeFlags::FLAG_B;
let c = ffi::SomeFlags::FLAG_C;
assert_eq!(a.0, 0x1);
assert_eq!(b.0, 0x4);
assert_eq!(c.0, 0x5);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}

#[test]
fn test_rustified_enum() {
let hdr = indoc! {"
enum Bob {
BOB_VALUE_1,
BOB_VALUE_2,
};
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
enum_style!(RustifiedEnum, "Bob")
generate_pod!("Bob")
}

fn main() {
let a = ffi::Bob::BOB_VALUE_1;
let b = ffi::Bob::BOB_VALUE_2;
assert!(a != b);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}

#[test]
fn test_rustified_nonexhaustive_enum() {
let hdr = indoc! {"
enum Bob {
BOB_VALUE_1,
BOB_VALUE_2,
};
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
enum_style!(RustifiedEnum, "Bob")
generate_pod!("Bob")
}

fn main() {
let a = ffi::Bob::BOB_VALUE_1;
let b = ffi::Bob::BOB_VALUE_2;
assert!(a != b);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}

#[test]
fn test_several_enums() {
let hdr = indoc! {"
enum First : int {
FIRST_A = 5,
FIRST_B = 6
};
enum Second {
SECOND_A,
SECOND_B
};
enum Default : int {
DEFAULT_A = 1 << 1,
DEFAULT_B = 1 << 3
};
enum Newtype {
NEWTYPE_A,
NEWTYPE_B
};
"};
let hexathorpe = Token![#](Span::call_site());
let rs = quote! {
use autocxx::prelude::*;
include_cpp! {
#hexathorpe include "input.h"
safety!(unsafe_ffi)
enum_style!(BitfieldEnum, "First", "Second")
enum_style!(NewtypeEnum, "Newtype")
generate_pod!("First")
generate_pod!("Second")
generate_pod!("Newtype")
generate!("Default")
}

fn main() {
let first_a = ffi::First::FIRST_A;
let first_b = ffi::First::FIRST_B;
assert_eq!((first_a & first_b).0, 0x4);
let second_a = ffi::Second::SECOND_A;
let second_b = ffi::Second::SECOND_B;
assert_eq!((second_a | second_b).0, 0x1);
assert!(ffi::Default::DEFAULT_A != ffi::Default::DEFAULT_B);
assert!(ffi::Newtype::NEWTYPE_A != ffi::Newtype::NEWTYPE_B);
}
};
do_run_test_manual("", hdr, rs, None, None).unwrap();
}

#[test]
fn test_re_export() {
let cxx = indoc! {"
Expand Down
6 changes: 6 additions & 0 deletions parser/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use syn::{
use syn::{Ident, Result as ParseResult};
use thiserror::Error;

use crate::enum_style::EnumStyleMap;
use crate::{directives::get_directives, RustPath};

use quote::quote;
Expand Down Expand Up @@ -228,6 +229,7 @@ pub struct IncludeCppConfig {
pub concretes: ConcretesMap,
pub externs: ExternCppTypeMap,
pub opaquelist: Vec<String>,
pub enum_styles: EnumStyleMap,
}

impl Parse for IncludeCppConfig {
Expand Down Expand Up @@ -384,6 +386,10 @@ impl IncludeCppConfig {
self.constructor_blocklist.contains(&cpp_name.to_string())
}

pub fn get_enums(&self) -> HashSet<&String> {
self.enum_styles.get_enum_names()
}

pub fn get_blocklist(&self) -> impl Iterator<Item = &String> {
self.blocklist.iter()
}
Expand Down
56 changes: 56 additions & 0 deletions parser/src/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::config::AllowlistErr;
use crate::config::Allowlist;

use crate::directive_names::{EXTERN_RUST_FUN, EXTERN_RUST_TYPE, SUBCLASS};
use crate::enum_style::EnumStyleMap;
use crate::{AllowlistEntry, IncludeCppConfig};
use crate::{ParseResult, RustFun, RustPath};

Expand Down Expand Up @@ -114,6 +115,13 @@ pub(crate) fn get_directives() -> &'static DirectivesMap {
"extern_cpp_opaque_type".into(),
Box::new(ExternCppType { opaque: true }),
);
need_exclamation.insert(
"enum_style".into(),
Box::new(EnumStyle(
|config| &mut config.enum_styles,
|config| &config.enum_styles,
)),
);

DirectivesMap {
need_hexathorpe,
Expand Down Expand Up @@ -567,3 +575,51 @@ impl Directive for ExternCppType {
)
}
}

struct EnumStyle<SET, GET>(SET, GET)
where
SET: Fn(&mut IncludeCppConfig) -> &mut EnumStyleMap,
GET: Fn(&IncludeCppConfig) -> &EnumStyleMap;

impl<SET, GET> Directive for EnumStyle<SET, GET>
where
SET: Fn(&mut IncludeCppConfig) -> &mut EnumStyleMap + Sync + Send,
GET: Fn(&IncludeCppConfig) -> &EnumStyleMap + Sync + Send,
{
fn parse(
&self,
args: ParseStream,
config: &mut IncludeCppConfig,
_ident_span: &Span,
) -> ParseResult<()> {
let style: crate::EnumStyle = args.parse()?;
args.parse::<syn::token::Comma>()?;
let litstrs: syn::punctuated::Punctuated<syn::LitStr, syn::token::Comma> =
syn::punctuated::Punctuated::parse_separated_nonempty(args)?;
let enums = litstrs.into_iter().map(|s| s.value());

config
.enum_styles
.0
.entry(style)
.or_insert_with(Vec::new)
.extend(enums);
Ok(())
}

#[cfg(feature = "reproduction_case")]
fn output<'a>(
&self,
config: &'a IncludeCppConfig,
) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
Box::new(config.enum_styles.0.iter().map(|(style, enums)| {
let lits: Vec<syn::LitStr> = enums
.iter()
.map(|s| syn::LitStr::new(s, Span::call_site()))
.collect();
quote! {
#(#lits),* , #style
}
}))
}
}
Loading