From a277ea4604c549810774c65c70097a4d9006293b Mon Sep 17 00:00:00 2001 From: Joe Ellis Date: Mon, 27 Jul 2020 10:26:38 +0100 Subject: [PATCH] Add dynamic loading support --- src/codegen/dynamic.rs | 0 src/codegen/mod.rs | 195 ++++++++++++++++++ src/ir/context.rs | 82 +++++++- src/ir/item.rs | 9 + src/lib.rs | 36 ++++ src/options.rs | 15 +- .../tests/dynamic_loading_simple.rs | 36 ++++ .../tests/dynamic_loading_with_blacklist.rs | 83 ++++++++ .../tests/dynamic_loading_with_class.rs | 78 +++++++ .../tests/dynamic_loading_with_whitelist.rs | 32 +++ tests/headers/dynamic_loading_simple.h | 5 + .../dynamic_loading_with_blacklist.hpp | 15 ++ tests/headers/dynamic_loading_with_class.hpp | 15 ++ .../dynamic_loading_with_whitelist.hpp | 15 ++ 14 files changed, 614 insertions(+), 2 deletions(-) create mode 100644 src/codegen/dynamic.rs create mode 100644 tests/expectations/tests/dynamic_loading_simple.rs create mode 100644 tests/expectations/tests/dynamic_loading_with_blacklist.rs create mode 100644 tests/expectations/tests/dynamic_loading_with_class.rs create mode 100644 tests/expectations/tests/dynamic_loading_with_whitelist.rs create mode 100644 tests/headers/dynamic_loading_simple.h create mode 100644 tests/headers/dynamic_loading_with_blacklist.hpp create mode 100644 tests/headers/dynamic_loading_with_class.hpp create mode 100644 tests/headers/dynamic_loading_with_whitelist.hpp diff --git a/src/codegen/dynamic.rs b/src/codegen/dynamic.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index c68eb6da95..367540b2c3 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -182,6 +182,7 @@ impl From for Vec<&'static str> { } } +#[derive(Clone)] struct CodegenResult<'a> { items: Vec, @@ -435,6 +436,15 @@ impl CodeGenerator for Item { return; } + if self.is_dynamic(ctx) { + debug!( + "Item is to be dynamically generated; ignoring it for now. \ + self = {:?}", + self + ); + return; + } + if self.is_blacklisted(ctx) || result.seen(self.id()) { debug!( "::codegen: Ignoring hidden or seen: \ @@ -3913,6 +3923,122 @@ impl CodeGenerator for ObjCInterface { } } +trait DynamicBindingGenerator { + /// Extra information from the caller. + type Extra; + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + struct_result: &mut CodegenResult<'a>, + impl_result: &mut CodegenResult<'a>, + extra: &Self::Extra, + ); +} + +impl DynamicBindingGenerator for Item { + type Extra = (); + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + struct_result: &mut CodegenResult<'a>, + impl_result: &mut CodegenResult<'a>, + _extra: &(), + ) { + assert!(self.is_dynamic(ctx)); + if !self.is_enabled_for_codegen(ctx) { + return; + } + + // If this item is blacklisted, or we've already seen it, nothing to do. + if self.is_blacklisted(ctx) || + struct_result.seen(self.id()) || + impl_result.seen(self.id()) + { + debug!( + "::codegen: Ignoring hidden or seen: \ + self = {:?}", + self + ); + return; + } + + debug!( + "::dyngen: self = {:?}", + self + ); + + if !ctx.codegen_items().contains(&self.id()) { + warn!("Found non-whitelisted item in dynamic binding generation: {:?}", self); + } + + struct_result.set_seen(self.id()); + impl_result.set_seen(self.id()); + + match *self.kind() { + ItemKind::Function(ref fun) => { + assert!(fun.kind() == FunctionKind::Function); + fun.dyngen(ctx, struct_result, impl_result, self); + } + _ => panic!( + "Unexpected item type when doing dynamic bindings generation." + ), + } + } +} + +impl DynamicBindingGenerator for Function { + type Extra = Item; + + fn dyngen<'a>( + &self, + ctx: &BindgenContext, + struct_result: &mut CodegenResult<'a>, + impl_result: &mut CodegenResult<'a>, + item: &Item, + ) { + let signature_item = ctx.resolve_item(self.signature()); + let signature = signature_item.kind().expect_type().canonical_type(ctx); + let signature = match *signature.kind() { + TypeKind::Function(ref sig) => sig, + _ => panic!("Signature kind is not a Function: {:?}", signature), + }; + + let canonical_name = item.canonical_name(ctx); + let abi = match signature.abi() { + Abi::ThisCall if !ctx.options().rust_features().thiscall_abi => { + warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target"); + return; + } + Abi::Win64 if signature.is_variadic() => { + warn!("Skipping variadic function with Win64 ABI that isn't supported"); + return; + } + Abi::Unknown(unknown_abi) => { + panic!( + "Invalid or unknown abi {:?} for function {:?} ({:?})", + unknown_abi, canonical_name, self + ); + } + abi => abi, + }; + + let args = utils::fnsig_arguments(ctx, signature); + let ret = utils::fnsig_return_ty(ctx, signature); + + let ident = ctx.rust_ident(&canonical_name); + + struct_result.push(quote! { + #ident: libloading::Symbol<'a, unsafe extern #abi fn ( #( #args ),* ) #ret>, + }); + + impl_result.push(quote! { + #ident: lib.get(#canonical_name.as_bytes()).unwrap(), + }); + } +} + pub(crate) fn codegen( context: BindgenContext, ) -> (Vec, BindgenOptions) { @@ -3948,6 +4074,75 @@ pub(crate) fn codegen( &(), ); + // If the set of items to generate dynamic bindings for is nonempty... + if !context.dyngen_items().is_empty() { + let _t = context.timer("dyngen"); + debug!("dyngen: {:?}", context.options()); + + // `struct_result` tracks the tokens that will appears inside the library struct, e.g.: + // ``` + // struct Lib { + // x: libloading::Symbol<'a, ...>, // <- tracks these! + // } + // ``` + // + // `impl_result` tracks the tokens that will appear inside the library struct's + // implementation, e.g.: + // + // ``` + // impl<'a> Lib<'a> { + // pub fn new(lib: &libloading::Library) -> Lib { + // unsafe { + // x: lib.get(...), // <- tracks these! + // } + // } + // } + // ``` + // + // We clone the CodeGenerator object used for normal code generation, but set its items + // to the empty vector. We do this so that we can keep track of which items we have + // seen, etc. + let mut struct_result = result.clone(); + struct_result.items = vec![]; + let mut impl_result = result.clone(); + impl_result.items = vec![]; + + // Run dynamic binding generation for each of the required items. + for item in context.dyngen_items() { + context.resolve_item(*item).dyngen( + context, + &mut struct_result, + &mut impl_result, + &(), + ); + } + + let lib_ident = context.rust_ident( + context.options().dynamic_library_name.as_ref().unwrap(), + ); + + let struct_items = struct_result.items; + result.push(quote! { + extern crate libloading; + pub struct #lib_ident { + #(#struct_items)* + } + }); + + let impl_items = impl_result.items; + result.push(quote! { + impl<'a> #lib_ident<'a> { + pub fn new(lib: &libloading::Library) -> #lib_ident { + unsafe { + #lib_ident { + #(#impl_items)* + } + } + } + } + }); + } + result.items }) } diff --git a/src/ir/context.rs b/src/ir/context.rs index 38d73b7e64..2d853c217b 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -11,7 +11,7 @@ use super::derive::{ CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd, }; -use super::function::Function; +use super::function::{Function, FunctionKind}; use super::int::IntKind; use super::item::{IsOpaque, Item, ItemAncestors, ItemSet}; use super::item_kind::ItemKind; @@ -386,6 +386,11 @@ pub struct BindgenContext { /// It's computed right after computing the whitelisted items. codegen_items: Option, + /// The set of `ItemId`s that we will perform dynamic binding generation for. + /// + /// It's computed following the codegen/whitelisted items. + dyngen_items: Option, + /// Map from an item's id to the set of template parameter items that it /// uses. See `ir::named` for more details. Always `Some` during the codegen /// phase. @@ -619,6 +624,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" generated_bindgen_complex: Cell::new(false), whitelisted: None, codegen_items: None, + dyngen_items: None, used_template_parameters: None, need_bitfield_allocation: Default::default(), cannot_derive_debug: None, @@ -1189,6 +1195,9 @@ If you encounter an error missing from this list, please file an issue or a PR!" // graph, and their completion means that the IR graph is now frozen. self.compute_whitelisted_and_codegen_items(); + // Compute the items for dynamic generation. + self.compute_dynamic_generation_items(); + // Make sure to do this after processing replacements, since that messes // with the parentage and module children, and we want to assert that it // messes with them correctly. @@ -2262,6 +2271,13 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.codegen_items.as_ref().unwrap() } + /// Get a reference to the set of dynamic bindings we should generate. + pub fn dyngen_items(&self) -> &ItemSet { + assert!(self.in_codegen_phase()); + assert!(self.current_module == self.root_module); + self.dyngen_items.as_ref().unwrap() + } + /// Compute the whitelisted items set and populate `self.whitelisted`. fn compute_whitelisted_and_codegen_items(&mut self) { assert!(self.in_codegen_phase()); @@ -2412,6 +2428,70 @@ If you encounter an error missing from this list, please file an issue or a PR!" } } + /// Compute the items for dynamic generation. + fn compute_dynamic_generation_items(&mut self) { + assert!(self.in_codegen_phase()); + assert!(self.current_module == self.root_module); + let _t = self.timer("compute_dynamic_generation_items"); + + let dyngen_items = self + .items() + // Only consider items that are enabled for codegen. + .filter(|&(_, item)| item.is_enabled_for_codegen(self)) + .filter(|&(_, item)| { + // If the user has not chosen to do dynamic loading, then we have nothing to + // do. + if !self.options().dynamic_loading { + return false; + } + + let name = item.path_for_whitelisting(self)[1..].join("::"); + + // If there is a whitelist and this function is not on it, don't add it. + if !self.options().whitelisted_functions.is_empty() && + !self.options().whitelisted_functions.matches(&name) + { + return false; + } + + // If there is a blacklist and this function is on it, don't add it. + if !self.options().blacklisted_functions.is_empty() && + self.options.blacklisted_functions.matches(&name) + { + return false; + } + + // We don't want to include the root module in the list of items to generate + // dynamic bindings for. + if item.id() == self.root_module { + return false; + } + + // We only want to generate bindings for the stuff that we have at the toplevel. + if item.parent_id() != self.root_module { + return false; + } + + // If we get to here: + // - The user wants to generate dynamic bindings. + // - The item being considered is at the toplevel (but is not the root itself). + // - The item is permitted by the {white,black}list. + + // Finally, only generate dynamic bindings for functions (for now! this could + // change in the future to support variables). + match item.kind() { + ItemKind::Function(ref f) => { + f.kind() == FunctionKind::Function + } + _ => false, + } + }) + .map(|(id, _)| id) + .collect::(); + + self.dyngen_items = Some(dyngen_items); + } + /// Convenient method for getting the prefix to use for most traits in /// codegen depending on the `use_core` option. pub fn trait_prefix(&self) -> Ident { diff --git a/src/ir/item.rs b/src/ir/item.rs index 0f94f02091..620ed754a8 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -624,6 +624,15 @@ impl Item { &self.annotations } + /// Whether this item should have dynamic bindings generated, rather than normal bindings. + pub fn is_dynamic(&self, ctx: &BindgenContext) -> bool { + debug_assert!( + ctx.in_codegen_phase(), + "You're not supposed to call this yet" + ); + ctx.dyngen_items().contains(&self.id) + } + /// Whether this item should be blacklisted. /// /// This may be due to either annotations or to other kind of configuration. diff --git a/src/lib.rs b/src/lib.rs index 5a86364e6d..ed3e6d7a01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -503,6 +503,16 @@ impl Builder { output_vector.push(path.into()); } + if self.options.dynamic_loading { + output_vector.push("--dynamic-loading".into()); + if let Some(dynamic_library_name) = + &self.options.dynamic_library_name + { + output_vector.push("--dynamic-library-name".into()); + output_vector.push(dynamic_library_name.clone()); + } + } + // Add clang arguments output_vector.push("--".into()); @@ -1431,6 +1441,24 @@ impl Builder { self.options.wasm_import_module_name = Some(import_name.into()); self } + + /// Specify whether dynamic loading will be used with these bindings. + pub fn dynamic_loading(mut self, dynamic_loading: bool) -> Self { + self.options.dynamic_loading = dynamic_loading; + if self.options.dynamic_library_name.is_none() { + self.options.dynamic_library_name = Some("Lib".to_string()); + } + self + } + + /// Specify the dynamic library name if we are generating bindings for a shared library. + pub fn dynamic_library_name>( + mut self, + dynamic_library_name: T, + ) -> Self { + self.options.dynamic_library_name = Some(dynamic_library_name.into()); + self + } } /// Configuration options for generated bindings. @@ -1699,6 +1727,12 @@ struct BindgenOptions { /// Wasm import module name. wasm_import_module_name: Option, + + /// Whether or not we are generating bindings for a shared library. + dynamic_loading: bool, + + /// The name of the dynamic library if we are generating bindings for a shared library. + dynamic_library_name: Option, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1827,6 +1861,8 @@ impl Default for BindgenOptions { no_hash_types: Default::default(), array_pointers_in_arguments: false, wasm_import_module_name: None, + dynamic_loading: false, + dynamic_library_name: None, } } } diff --git a/src/options.rs b/src/options.rs index 0add7b4cbe..ba49fa1305 100644 --- a/src/options.rs +++ b/src/options.rs @@ -450,7 +450,14 @@ where .long("wasm-import-module-name") .value_name("name") .takes_value(true) - .help("The name to be used in a #[link(wasm_import_module = ...)] statement") + .help("The name to be used in a #[link(wasm_import_module = ...)] statement"), + Arg::with_name("dynamic-loading") + .long("dynamic-loading") + .help("Use dynamic loading mode."), + Arg::with_name("dynamic-library-name") + .long("dynamic-library-name") + .takes_value(true) + .help("Set the name of dynamic library. Ignored if --dynamic-loading is not used.") ]) // .args() .get_matches_from(args); @@ -837,6 +844,12 @@ where } } + builder = builder.dynamic_loading(matches.is_present("dynamic-loading")); + if let Some(dynamic_library_name) = matches.value_of("dynamic-library-name") + { + builder = builder.dynamic_library_name(dynamic_library_name); + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose)) diff --git a/tests/expectations/tests/dynamic_loading_simple.rs b/tests/expectations/tests/dynamic_loading_simple.rs new file mode 100644 index 0000000000..3bf9fb1ec5 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_simple.rs @@ -0,0 +1,36 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +extern crate libloading; +pub struct TestLib { + foo: libloading::Symbol< + 'a, + unsafe extern "C" fn( + x: ::std::os::raw::c_int, + y: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, + bar: libloading::Symbol< + 'a, + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + baz: + libloading::Symbol<'a, unsafe extern "C" fn() -> ::std::os::raw::c_int>, +} +impl<'a> TestLib<'a> { + pub fn new(lib: &libloading::Library) -> TestLib { + unsafe { + TestLib { + foo: lib.get("foo".as_bytes()).unwrap(), + bar: lib.get("bar".as_bytes()).unwrap(), + baz: lib.get("baz".as_bytes()).unwrap(), + } + } + } +} diff --git a/tests/expectations/tests/dynamic_loading_with_blacklist.rs b/tests/expectations/tests/dynamic_loading_with_blacklist.rs new file mode 100644 index 0000000000..62ab9f271b --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_blacklist.rs @@ -0,0 +1,83 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct X { + pub _x: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_X() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(X)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(X)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::()))._x as *const _ as usize }, + 0usize, + concat!("Offset of field: ", stringify!(X), "::", stringify!(_x)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN1X13some_functionEv"] + pub fn X_some_function(this: *mut X); +} +extern "C" { + #[link_name = "\u{1}_ZN1X19some_other_functionEv"] + pub fn X_some_other_function(this: *mut X); +} +extern "C" { + #[link_name = "\u{1}_ZN1XC1Ei"] + pub fn X_X(this: *mut X, x: ::std::os::raw::c_int); +} +impl X { + #[inline] + pub unsafe fn some_function(&mut self) { + X_some_function(self) + } + #[inline] + pub unsafe fn some_other_function(&mut self) { + X_some_other_function(self) + } + #[inline] + pub unsafe fn new(x: ::std::os::raw::c_int) -> Self { + let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); + X_X(__bindgen_tmp.as_mut_ptr(), x); + __bindgen_tmp.assume_init() + } +} +extern crate libloading; +pub struct TestLib { + foo: libloading::Symbol< + 'a, + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + bar: libloading::Symbol< + 'a, + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, +} +impl<'a> TestLib<'a> { + pub fn new(lib: &libloading::Library) -> TestLib { + unsafe { + TestLib { + foo: lib.get("foo".as_bytes()).unwrap(), + bar: lib.get("bar".as_bytes()).unwrap(), + } + } + } +} diff --git a/tests/expectations/tests/dynamic_loading_with_class.rs b/tests/expectations/tests/dynamic_loading_with_class.rs new file mode 100644 index 0000000000..3be902fd03 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_class.rs @@ -0,0 +1,78 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct A { + pub _x: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_A() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(A)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(A)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::()))._x as *const _ as usize }, + 0usize, + concat!("Offset of field: ", stringify!(A), "::", stringify!(_x)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN1A13some_functionEv"] + pub fn A_some_function(this: *mut A); +} +extern "C" { + #[link_name = "\u{1}_ZN1A19some_other_functionEv"] + pub fn A_some_other_function(this: *mut A); +} +extern "C" { + #[link_name = "\u{1}_ZN1AC1Ei"] + pub fn A_A(this: *mut A, x: ::std::os::raw::c_int); +} +impl A { + #[inline] + pub unsafe fn some_function(&mut self) { + A_some_function(self) + } + #[inline] + pub unsafe fn some_other_function(&mut self) { + A_some_other_function(self) + } + #[inline] + pub unsafe fn new(x: ::std::os::raw::c_int) -> Self { + let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); + A_A(__bindgen_tmp.as_mut_ptr(), x); + __bindgen_tmp.assume_init() + } +} +extern crate libloading; +pub struct TestLib { + foo: libloading::Symbol< + 'a, + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + bar: libloading::Symbol<'a, unsafe extern "C" fn()>, +} +impl<'a> TestLib<'a> { + pub fn new(lib: &libloading::Library) -> TestLib { + unsafe { + TestLib { + foo: lib.get("foo".as_bytes()).unwrap(), + bar: lib.get("bar".as_bytes()).unwrap(), + } + } + } +} diff --git a/tests/expectations/tests/dynamic_loading_with_whitelist.rs b/tests/expectations/tests/dynamic_loading_with_whitelist.rs new file mode 100644 index 0000000000..ef16b307e6 --- /dev/null +++ b/tests/expectations/tests/dynamic_loading_with_whitelist.rs @@ -0,0 +1,32 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +extern crate libloading; +pub struct TestLib { + foo: libloading::Symbol< + 'a, + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + baz: libloading::Symbol< + 'a, + unsafe extern "C" fn( + x: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, +} +impl<'a> TestLib<'a> { + pub fn new(lib: &libloading::Library) -> TestLib { + unsafe { + TestLib { + foo: lib.get("foo".as_bytes()).unwrap(), + baz: lib.get("baz".as_bytes()).unwrap(), + } + } + } +} diff --git a/tests/headers/dynamic_loading_simple.h b/tests/headers/dynamic_loading_simple.h new file mode 100644 index 0000000000..bbe9e0743d --- /dev/null +++ b/tests/headers/dynamic_loading_simple.h @@ -0,0 +1,5 @@ +// bindgen-flags: --dynamic-loading --dynamic-library-name TestLib + +int foo(int x, int y); +int bar(void *x); +int baz(); diff --git a/tests/headers/dynamic_loading_with_blacklist.hpp b/tests/headers/dynamic_loading_with_blacklist.hpp new file mode 100644 index 0000000000..17157fb906 --- /dev/null +++ b/tests/headers/dynamic_loading_with_blacklist.hpp @@ -0,0 +1,15 @@ +// bindgen-flags: --dynamic-loading --dynamic-library-name TestLib --blacklist-function baz + +class X { + int _x; + + public: + X(int x); + + void some_function(); + void some_other_function(); +}; + +int foo(void *x); +int bar(void *x); +int baz(void *x); diff --git a/tests/headers/dynamic_loading_with_class.hpp b/tests/headers/dynamic_loading_with_class.hpp new file mode 100644 index 0000000000..4e079791e4 --- /dev/null +++ b/tests/headers/dynamic_loading_with_class.hpp @@ -0,0 +1,15 @@ +// bindgen-flags: --dynamic-loading --dynamic-library-name TestLib + +int foo(void *x); + +class A { + int _x; + + public: + A(int x); + + void some_function(); + void some_other_function(); +}; + +void bar(); diff --git a/tests/headers/dynamic_loading_with_whitelist.hpp b/tests/headers/dynamic_loading_with_whitelist.hpp new file mode 100644 index 0000000000..52343fa5f9 --- /dev/null +++ b/tests/headers/dynamic_loading_with_whitelist.hpp @@ -0,0 +1,15 @@ +// bindgen-flags: --dynamic-loading --dynamic-library-name TestLib --whitelist-function baz --whitelist-function foo + +class X { + int _x; + + public: + X(int x); + + void some_function(); + void some_other_function(); +}; + +int foo(void *x); +int bar(void *x); +int baz(void *x);