Skip to content

Commit 522ab91

Browse files
committed
support variadic arguments through tuples
1 parent 73ce4bc commit 522ab91

File tree

5 files changed

+228
-14
lines changed

5 files changed

+228
-14
lines changed

bindgen-cli/options.rs

+15
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ where
568568
Arg::new("merge-extern-blocks")
569569
.long("merge-extern-blocks")
570570
.help("Deduplicates extern blocks."),
571+
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"),
571572
Arg::new("V")
572573
.long("version")
573574
.help("Prints the version, and exits"),
@@ -1088,5 +1089,19 @@ where
10881089
builder = builder.merge_extern_blocks(true);
10891090
}
10901091

1092+
if let Some(len) = matches.value_of("tuple-varargs-len") {
1093+
let len = match len.parse() {
1094+
Ok(len) => len,
1095+
Err(err) => {
1096+
return Err(io::Error::new(
1097+
io::ErrorKind::Other,
1098+
format!("invalid length: {}", err),
1099+
))
1100+
}
1101+
};
1102+
1103+
builder = builder.tuple_varargs_len(len);
1104+
}
1105+
10911106
Ok((builder, output, verbose))
10921107
}

bindgen-tests/tests/expectations/tests/variadic-method.rs

+77
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/headers/variadic-method.hpp

+1
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

bindgen/codegen/mod.rs

+121-14
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use quote::TokenStreamExt;
5151
use crate::{Entry, HashMap, HashSet};
5252
use std::borrow::Cow;
5353
use std::cell::Cell;
54+
use std::collections::BTreeMap;
5455
use std::collections::VecDeque;
5556
use std::fmt::Write;
5657
use std::iter;
@@ -192,6 +193,12 @@ impl From<DerivableTraits> for Vec<&'static str> {
192193
}
193194
}
194195

196+
struct VariadicMethodInfo {
197+
args: Vec<proc_macro2::TokenStream>,
198+
ret: proc_macro2::TokenStream,
199+
exprs: Vec<proc_macro2::TokenStream>,
200+
}
201+
195202
struct CodegenResult<'a> {
196203
items: Vec<proc_macro2::TokenStream>,
197204
dynamic_items: DynamicItems,
@@ -239,6 +246,8 @@ struct CodegenResult<'a> {
239246
/// function name to the number of overloads we have already codegen'd for
240247
/// that name. This lets us give each overload a unique suffix.
241248
overload_counters: HashMap<String, u32>,
249+
250+
variadic_methods: BTreeMap<Ident, VariadicMethodInfo>,
242251
}
243252

244253
impl<'a> CodegenResult<'a> {
@@ -256,6 +265,7 @@ impl<'a> CodegenResult<'a> {
256265
functions_seen: Default::default(),
257266
vars_seen: Default::default(),
258267
overload_counters: Default::default(),
268+
variadic_methods: Default::default(),
259269
}
260270
}
261271

@@ -2485,12 +2495,6 @@ impl MethodCodegen for Method {
24852495
return;
24862496
}
24872497

2488-
// Do not generate variadic methods, since rust does not allow
2489-
// implementing them, and we don't do a good job at it anyway.
2490-
if signature.is_variadic() {
2491-
return;
2492-
}
2493-
24942498
let count = {
24952499
let count = method_names.entry(name.clone()).or_insert(0);
24962500
*count += 1;
@@ -2508,13 +2512,10 @@ impl MethodCodegen for Method {
25082512
let function_name = ctx.rust_ident(function_name);
25092513
let mut args = utils::fnsig_arguments(ctx, signature);
25102514
let mut ret = utils::fnsig_return_ty(ctx, signature);
2515+
let is_variadic = signature.is_variadic();
25112516

2512-
if !self.is_static() && !self.is_constructor() {
2513-
args[0] = if self.is_const() {
2514-
quote! { &self }
2515-
} else {
2516-
quote! { &mut self }
2517-
};
2517+
if is_variadic && ctx.options().tuple_varargs_len.is_none() {
2518+
return;
25182519
}
25192520

25202521
// If it's a constructor, we always return `Self`, and we inject the
@@ -2530,6 +2531,28 @@ impl MethodCodegen for Method {
25302531
let mut exprs =
25312532
helpers::ast_ty::arguments_from_signature(signature, ctx);
25322533

2534+
if is_variadic {
2535+
let (last_arg, args) = args.split_last_mut().unwrap();
2536+
// FIXME (pvdrz): what if this identifier is already being used?
2537+
*last_arg = quote!(var_args: impl VarArgs);
2538+
result.variadic_methods.insert(
2539+
function_name.clone(),
2540+
VariadicMethodInfo {
2541+
args: args.to_owned(),
2542+
ret: ret.clone(),
2543+
exprs: exprs.clone(),
2544+
},
2545+
);
2546+
}
2547+
2548+
if !self.is_static() && !self.is_constructor() {
2549+
args[0] = if self.is_const() {
2550+
quote! { &self }
2551+
} else {
2552+
quote! { &mut self }
2553+
};
2554+
}
2555+
25332556
let mut stmts = vec![];
25342557

25352558
// If it's a constructor, we need to insert an extra parameter with a
@@ -2563,8 +2586,15 @@ impl MethodCodegen for Method {
25632586
};
25642587
};
25652588

2566-
let call = quote! {
2567-
#function_name (#( #exprs ),* )
2589+
let call = if is_variadic {
2590+
let function_name = quote::format_ident!("call_{}", function_name);
2591+
quote! {
2592+
var_args.#function_name(#( #exprs ),* )
2593+
}
2594+
} else {
2595+
quote! {
2596+
#function_name (#( #exprs ),* )
2597+
}
25682598
};
25692599

25702600
stmts.push(call);
@@ -4508,6 +4538,10 @@ pub(crate) fn codegen(
45084538
result.push(dynamic_items_tokens);
45094539
}
45104540

4541+
if let Some(max_len) = context.options().tuple_varargs_len {
4542+
utils::generate_varargs_trait(max_len, &mut result);
4543+
}
4544+
45114545
postprocessing::postprocessing(result.items, context.options())
45124546
})
45134547
}
@@ -5047,4 +5081,77 @@ pub mod utils {
50475081

50485082
true
50495083
}
5084+
5085+
pub(super) fn generate_varargs_trait(
5086+
max_len: usize,
5087+
result: &mut super::CodegenResult,
5088+
) {
5089+
// This will hold the identifiers to be used for the fields of the tuples `t0, ..., tn` as
5090+
// well as the identifiers of the type parameters for each field type of the tuples `T0, ..., TN`.
5091+
// FIXME (pvdrz): what if these identifiers are already in use?
5092+
let (fields, params): (Vec<_>, Vec<_>) = (0..max_len)
5093+
.map(|len| {
5094+
(
5095+
quote::format_ident!("t{}", len),
5096+
quote::format_ident!("T{}", len),
5097+
)
5098+
})
5099+
.unzip();
5100+
5101+
// This will hold the methods to be declared in the `VarArgs` trait as well as the
5102+
// bodies of the implementations of such methods for each tuple length.
5103+
let (trait_method_decls, trait_method_impl_fns): (Vec<_>, Vec<_>) =
5104+
std::mem::take(&mut result.variadic_methods)
5105+
.into_iter()
5106+
.map(|(name, info)| {
5107+
let super::VariadicMethodInfo { args, ret, exprs } = info;
5108+
5109+
// The name of the `VarArgs` trait method associated with this method.
5110+
// FIXME (pvdrz): what these identifiers are already in use?
5111+
let trait_method_name =
5112+
quote::format_ident!("call_{}", name);
5113+
5114+
// The declaration of the `VarArgs` trait method associated with this method.
5115+
let trait_method_decl = quote! {
5116+
unsafe fn #trait_method_name(self, #(#args),*) #ret
5117+
};
5118+
5119+
// The implementations of the `VarArgs` trait method associated with this
5120+
// method for each tuple length.
5121+
let trait_method_impls = (0..=fields.len())
5122+
.map(|index| {
5123+
let fields = &fields[..index];
5124+
quote! {
5125+
#trait_method_decl {
5126+
let (#(#fields,)*) = self;
5127+
#name(#(#exprs),*, #(#fields),*)
5128+
}
5129+
}
5130+
})
5131+
.collect::<Vec<_>>();
5132+
5133+
(trait_method_decl, trait_method_impls)
5134+
})
5135+
.unzip();
5136+
5137+
// Push the trait with the method declarations.
5138+
result.items.push(quote! {
5139+
pub trait VarArgs {
5140+
#(#trait_method_decls;)*
5141+
}
5142+
});
5143+
5144+
for index in 0..=params.len() {
5145+
let params = &params[..index];
5146+
let methods =
5147+
trait_method_impl_fns.iter().map(|impls| &impls[index]);
5148+
5149+
// Push the implementation the trait for each tuple.
5150+
result.items.push(quote! {
5151+
impl<#(#params),*> VarArgs for (#(#params,)*) {
5152+
#(#methods)*
5153+
}
5154+
});
5155+
}
5156+
}
50505157
}

bindgen/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,10 @@ impl Builder {
619619
output_vector.push("--merge-extern-blocks".into());
620620
}
621621

622+
if let Some(len) = self.options.tuple_varargs_len {
623+
output_vector.push(format!("--tuple-varargs-len={}", len));
624+
}
625+
622626
// Add clang arguments
623627

624628
output_vector.push("--".into());
@@ -1560,6 +1564,12 @@ impl Builder {
15601564
self
15611565
}
15621566

1567+
/// Sets the maximum tuple length that can be used to emulate variadic arguments,
1568+
pub fn tuple_varargs_len(mut self, len: usize) -> Self {
1569+
self.options.tuple_varargs_len = Some(len);
1570+
self
1571+
}
1572+
15631573
/// Generate the Rust bindings using the options built up thus far.
15641574
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
15651575
// Add any extra arguments from the environment to the clang command line.
@@ -2105,6 +2115,9 @@ struct BindgenOptions {
21052115

21062116
/// Deduplicate `extern` blocks.
21072117
merge_extern_blocks: bool,
2118+
2119+
/// The maximum tuple length that can be used to emulate variadic arguments.
2120+
tuple_varargs_len: Option<usize>,
21082121
}
21092122

21102123
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -2261,6 +2274,7 @@ impl Default for BindgenOptions {
22612274
vtable_generation: false,
22622275
sort_semantically: false,
22632276
merge_extern_blocks: false,
2277+
tuple_varargs_len: Default::default(),
22642278
}
22652279
}
22662280
}

0 commit comments

Comments
 (0)