Skip to content

Commit 6db0c29

Browse files
committed
support variadic arguments through tuples
1 parent 0d805d7 commit 6db0c29

File tree

5 files changed

+184
-14
lines changed

5 files changed

+184
-14
lines changed

src/codegen/mod.rs

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,15 @@ struct CodegenResult<'a> {
238238
/// function name to the number of overloads we have already codegen'd for
239239
/// that name. This lets us give each overload a unique suffix.
240240
overload_counters: HashMap<String, u32>,
241+
242+
variadic_methods: HashMap<
243+
Ident,
244+
(
245+
Vec<proc_macro2::TokenStream>,
246+
proc_macro2::TokenStream,
247+
Vec<proc_macro2::TokenStream>,
248+
),
249+
>,
241250
}
242251

243252
impl<'a> CodegenResult<'a> {
@@ -255,6 +264,7 @@ impl<'a> CodegenResult<'a> {
255264
functions_seen: Default::default(),
256265
vars_seen: Default::default(),
257266
overload_counters: Default::default(),
267+
variadic_methods: Default::default(),
258268
}
259269
}
260270

@@ -2445,12 +2455,6 @@ impl MethodCodegen for Method {
24452455
return;
24462456
}
24472457

2448-
// Do not generate variadic methods, since rust does not allow
2449-
// implementing them, and we don't do a good job at it anyway.
2450-
if signature.is_variadic() {
2451-
return;
2452-
}
2453-
24542458
let count = {
24552459
let count = method_names.entry(name.clone()).or_insert(0);
24562460
*count += 1;
@@ -2468,13 +2472,10 @@ impl MethodCodegen for Method {
24682472
let function_name = ctx.rust_ident(function_name);
24692473
let mut args = utils::fnsig_arguments(ctx, signature);
24702474
let mut ret = utils::fnsig_return_ty(ctx, signature);
2475+
let is_variadic = signature.is_variadic();
24712476

2472-
if !self.is_static() && !self.is_constructor() {
2473-
args[0] = if self.is_const() {
2474-
quote! { &self }
2475-
} else {
2476-
quote! { &mut self }
2477-
};
2477+
if is_variadic & ctx.options().tuple_varargs_len.is_none() {
2478+
return;
24782479
}
24792480

24802481
// If it's a constructor, we always return `Self`, and we inject the
@@ -2490,6 +2491,24 @@ impl MethodCodegen for Method {
24902491
let mut exprs =
24912492
helpers::ast_ty::arguments_from_signature(signature, ctx);
24922493

2494+
if is_variadic {
2495+
let (last_arg, args) = args.split_last_mut().unwrap();
2496+
// FIXME (pvdrz): what if this identifier is already being used?
2497+
*last_arg = quote!(var_args: impl VarArgs);
2498+
result.variadic_methods.insert(
2499+
function_name.clone(),
2500+
(args.to_owned(), ret.clone(), exprs.clone()),
2501+
);
2502+
}
2503+
2504+
if !self.is_static() && !self.is_constructor() {
2505+
args[0] = if self.is_const() {
2506+
quote! { &self }
2507+
} else {
2508+
quote! { &mut self }
2509+
};
2510+
}
2511+
24932512
let mut stmts = vec![];
24942513

24952514
// If it's a constructor, we need to insert an extra parameter with a
@@ -2523,8 +2542,15 @@ impl MethodCodegen for Method {
25232542
};
25242543
};
25252544

2526-
let call = quote! {
2527-
#function_name (#( #exprs ),* )
2545+
let call = if is_variadic {
2546+
let function_name = quote::format_ident!("call_{}", function_name);
2547+
quote! {
2548+
var_args.#function_name(#( #exprs ),* )
2549+
}
2550+
} else {
2551+
quote! {
2552+
#function_name (#( #exprs ),* )
2553+
}
25282554
};
25292555

25302556
stmts.push(call);
@@ -4404,6 +4430,51 @@ pub(crate) fn codegen(
44044430
result.push(dynamic_items_tokens);
44054431
}
44064432

4433+
if let Some(max_len) = context.options().tuple_varargs_len {
4434+
let mut methods = Vec::with_capacity(result.variadic_methods.len());
4435+
let mut impls = (0..=max_len)
4436+
.map(|len| {
4437+
(
4438+
(0..len)
4439+
.map(|len| quote::format_ident!("t{}", len))
4440+
.collect::<Vec<_>>(),
4441+
vec![],
4442+
)
4443+
})
4444+
.collect::<Vec<_>>();
4445+
for (name, (args, ret, exprs)) in result.variadic_methods.drain() {
4446+
let call_name = quote::format_ident!("call_{}", name);
4447+
methods.push(quote! {
4448+
unsafe fn #call_name(self, #(#args),*) #ret
4449+
});
4450+
4451+
for (fields, methods) in &mut impls {
4452+
methods.push(quote! {
4453+
unsafe fn #call_name(self, #(#args),*) #ret {
4454+
let (#(#fields,)*) = self;
4455+
#name(#(#exprs),*, #(#fields),*)
4456+
}
4457+
});
4458+
}
4459+
}
4460+
result.items.push(quote! {
4461+
pub trait VarArgs {
4462+
#(#methods;)*
4463+
}
4464+
});
4465+
4466+
for (fields, methods) in impls {
4467+
let params = (0..fields.len())
4468+
.map(|len| quote::format_ident!("T{}", len))
4469+
.collect::<Vec<_>>();
4470+
result.items.push(quote! {
4471+
impl<#(#params),*> VarArgs for (#(#params,)*) {
4472+
#(#methods)*
4473+
}
4474+
});
4475+
}
4476+
}
4477+
44074478
result.items
44084479
})
44094480
}

src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,10 @@ impl Builder {
601601
output_vector.push("--sort-semantically".into());
602602
}
603603

604+
if let Some(len) = self.options.tuple_varargs_len {
605+
output_vector.push(format!("--tuple-varargs-len={}", len));
606+
}
607+
604608
// Add clang arguments
605609

606610
output_vector.push("--".into());
@@ -1510,6 +1514,12 @@ impl Builder {
15101514
self
15111515
}
15121516

1517+
/// Sets the maximum tuple length that can be used to emulate variadic arguments,
1518+
pub fn tuple_varargs_len(mut self, len: usize) -> Self {
1519+
self.options.tuple_varargs_len = Some(len);
1520+
self
1521+
}
1522+
15131523
/// Generate the Rust bindings using the options built up thus far.
15141524
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
15151525
// Add any extra arguments from the environment to the clang command line.
@@ -2045,6 +2055,9 @@ struct BindgenOptions {
20452055

20462056
/// Sort the code generation
20472057
sort_semantically: bool,
2058+
2059+
/// The maximum tuple length that can be used to emulate variadic arguments.
2060+
tuple_varargs_len: Option<usize>,
20482061
}
20492062

20502063
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -2201,6 +2214,7 @@ impl Default for BindgenOptions {
22012214
force_explicit_padding: false,
22022215
vtable_generation: false,
22032216
sort_semantically: false,
2217+
tuple_varargs_len: Default::default(),
22042218
}
22052219
}
22062220
}

src/options.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ where
524524
Arg::new("sort-semantically")
525525
.long("sort-semantically")
526526
.help("Enables sorting of code generation in a predefined manner."),
527+
Arg::new("tuple-varargs-len").long("tuple-varargs-len").takes_value(true).help("Enables using tuples to emulate variadic arguments up to a certain tuple length"),
527528
Arg::new("V")
528529
.long("version")
529530
.help("Prints the version, and exits"),
@@ -1019,5 +1020,19 @@ where
10191020
builder = builder.sort_semantically(true);
10201021
}
10211022

1023+
if let Some(len) = matches.value_of("tuple-varargs-len") {
1024+
let len = match len.parse() {
1025+
Ok(len) => len,
1026+
Err(err) => {
1027+
return Err(io::Error::new(
1028+
io::ErrorKind::Other,
1029+
format!("invalid length: {}", err),
1030+
))
1031+
}
1032+
};
1033+
1034+
builder = builder.tuple_varargs_len(len)
1035+
}
1036+
10221037
Ok((builder, output, verbose))
10231038
}

tests/expectations/tests/variadic-method.rs

Lines changed: 69 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/headers/variadic-method.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// bindgen-flags: --tuple-varargs-len 5
12

23
void foo(const char* fmt, ...);
34

0 commit comments

Comments
 (0)