Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix C++ static wrappers generation #3165

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "tests/headers/wrap-static-fns-cxx.hpp"

// Static wrappers

int foo__extern(void) { return foo(); }
int bar__extern(void) { return bar(); }
int takes_ptr__extern(int *arg) { return takes_ptr(arg); }
int takes_fn_ptr__extern(int (*f) (int)) { return takes_fn_ptr(f); }
int takes_fn__extern(int (f) (int)) { return takes_fn(f); }
int takes_alias__extern(func f) { return takes_alias(f); }
int takes_qualified__extern(const int *const *arg) { return takes_qualified(arg); }
enum foo takes_enum__extern(const enum foo f) { return takes_enum(f); }
void nevermore__extern(void) { nevermore(); }
int takes_fn_with_no_args__extern(int (f) (void)) { return takes_fn_with_no_args(f); }
void no_extra_argument__extern(__builtin_va_list va) { no_extra_argument(va); }
int many_va_list__extern(int i, __builtin_va_list va1, __builtin_va_list va2) { return many_va_list(i, va1, va2); }
int wrap_as_variadic_fn1__extern(int i, ...) {
int ret;
va_list ap;

va_start(ap, i);
ret = wrap_as_variadic_fn1(i, ap);
va_end(ap);
return ret;
}
void wrap_as_variadic_fn2__extern(int i, ...) {
va_list ap;

va_start(ap, i);
wrap_as_variadic_fn2(i, ap);
va_end(ap);
}
int qux_foo__extern(void) { return qux::foo(); }
95 changes: 95 additions & 0 deletions bindgen-tests/tests/expectations/tests/wrap-static-fns-cxx.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 90 additions & 0 deletions bindgen-tests/tests/headers/wrap-static-fns-cxx.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// bindgen-flags: --wrap-static-fns
// bindgen-parse-callbacks: wrap-as-variadic-fn

// to avoid polluting the expectation tests we put the stdarg.h behind a conditional
// variable only used in bindgen-integration
#ifdef USE_VA_HEADER
#include <stdarg.h>
#endif

static inline int foo() {
return 11;
}
static int bar();
static int bar() {
return 1;
}
inline int baz() {
return 2;
}

static inline int takes_ptr(int* arg) {
return *arg + 1;
}

static inline int takes_fn_ptr(int (*f)(int)) {
return f(1);
}

static inline int takes_fn(int (f)(int)) {
return f(2);
}

typedef int (func)(int);

static inline int takes_alias(func f) {
return f(3);
}

static inline int takes_qualified(const int *const *arg) {
return **arg;
}

enum foo {
BAR = 0x0,
};

static inline enum foo takes_enum(const enum foo f) {
return f;
}

static inline void nevermore() {
while (1) { }
}

static inline int takes_fn_with_no_args(int(f)(void)) {
return f();
}

static inline int variadic(int x, ...) {
return x;
}

static inline void no_extra_argument(__builtin_va_list va) {}

static inline int many_va_list(int i, __builtin_va_list va1, __builtin_va_list va2) {
return i;
}

#ifndef USE_VA_HEADER
static inline int wrap_as_variadic_fn1(int i, __builtin_va_list va) {
return i;
}

static inline void wrap_as_variadic_fn2(int i, __builtin_va_list va) {}
#else
static inline int wrap_as_variadic_fn1(int i, va_list va) {
int res = 0;

for (int j = 0; j < i; j++)
res += (int) va_arg(va, int);

return res;
}

static inline void wrap_as_variadic_fn2(int i, va_list va) {}
#endif

namespace qux {
static int foo();
}
38 changes: 38 additions & 0 deletions bindgen-tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,3 +757,41 @@ fn test_wrap_static_fns() {
.unwrap();
}
}

#[test]
fn test_wrap_static_fns_cxx() {
// This test is for testing diffs of the generated C source and header files
// TODO: If another such feature is added, convert this test into a more generic
// test that looks at `tests/headers/generated` directory.
let expect_path = PathBuf::from("tests/expectations/tests/generated")
.join("wrap_static_fns_cxx");
println!("In path is ::: {}", expect_path.display());

let generated_path =
PathBuf::from(env::var("OUT_DIR").unwrap()).join("wrap_static_fns_cxx");
println!("Out path is ::: {}", generated_path.display());

let _bindings = Builder::default()
.header("tests/headers/wrap-static-fns-cxx.hpp")
.wrap_static_fns(true)
.wrap_static_fns_path(generated_path.display().to_string())
.parse_callbacks(Box::new(parse_callbacks::WrapAsVariadicFn))
.generate()
.expect("Failed to generate bindings");

let expected_cpp = fs::read_to_string(expect_path.with_extension("cpp"))
.expect("Could not read generated wrap_static_fns_cxx.cpp");

let actual_cpp = fs::read_to_string(generated_path.with_extension("cpp"))
.expect("Could not read actual wrap_static_fns_cxx.cpp");

if expected_cpp != actual_cpp {
error_diff_mismatch(
&actual_cpp,
&expected_cpp,
None,
&expect_path.with_extension("cpp"),
)
.unwrap();
}
}
21 changes: 12 additions & 9 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4677,7 +4677,7 @@ impl CodeGenerator for Function {

let should_wrap = is_internal &&
ctx.options().wrap_static_fns &&
link_name_attr.is_none();
(link_name_attr.is_none() || utils::is_cpp(ctx));

if should_wrap {
let name = canonical_name.clone() + ctx.wrap_static_fns_suffix();
Expand Down Expand Up @@ -5201,6 +5201,15 @@ pub(crate) mod utils {
use std::path::PathBuf;
use std::str::FromStr;

pub(super) fn is_cpp(context: &BindgenContext) -> bool {
args_are_cpp(&context.options().clang_args) ||
context
.options()
.input_headers
.iter()
.any(|h| file_is_cpp(h))
}

pub(super) fn serialize_items(
result: &CodegenResult,
context: &BindgenContext,
Expand All @@ -5220,14 +5229,8 @@ pub(crate) mod utils {
std::fs::create_dir_all(dir)?;
}

let is_cpp = args_are_cpp(&context.options().clang_args) ||
context
.options()
.input_headers
.iter()
.any(|h| file_is_cpp(h));

let source_path = path.with_extension(if is_cpp { "cpp" } else { "c" });
let source_path =
path.with_extension(if is_cpp(context) { "cpp" } else { "c" });

let mut code = Vec::new();

Expand Down
17 changes: 15 additions & 2 deletions bindgen/codegen/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,16 @@ impl<'a> CSerialize<'a> for Function {

assert!(!signature.is_variadic());

let name = self.name();
// Get the function name + namespace
let name = {
let path = item.path_for_allowlisting(ctx).clone();
if path.get(0).is_some_and(|part| part == "root") {
&path[1..]
} else {
&path[..]
}
.join("::")
};

// Function arguments stored as `(name, type_id)` tuples.
let args = {
Expand Down Expand Up @@ -114,7 +123,11 @@ impl<'a> CSerialize<'a> for Function {
};

// The name used for the wrapper self.
let wrap_name = format!("{name}{}", ctx.wrap_static_fns_suffix());
let wrap_name = format!(
"{}{}",
item.canonical_name(ctx),
ctx.wrap_static_fns_suffix()
);

// The function's return type
let (ret_item, ret_ty) = {
Expand Down
Loading