Add option to generate C headers with C++ compatibility

By default C++ compatibility is disabled.
This commit is contained in:
Wodann 2019-05-31 23:56:04 +09:00
parent 767fce25d1
commit bbeff3e3aa
5 changed files with 90 additions and 14 deletions

View File

@ -208,12 +208,24 @@ impl Bindings {
}
if !self.functions.is_empty() || !self.globals.is_empty() {
if self.config.language == Language::Cxx {
if self.config.language == Language::C && self.config.cpp_compat {
out.new_line_if_not_start();
out.write("#ifdef __cplusplus");
out.new_line();
}
if self.config.language == Language::Cxx || self.config.cpp_compat {
out.new_line_if_not_start();
out.write("extern \"C\" {");
out.new_line();
}
if self.config.language == Language::C && self.config.cpp_compat {
out.new_line();
out.write("#endif // __cplusplus");
out.new_line();
}
for global in &self.globals {
out.new_line_if_not_start();
global.write(&self.config, &mut out);
@ -226,11 +238,23 @@ impl Bindings {
out.new_line();
}
if self.config.language == Language::Cxx {
out.new_line_if_not_start();
if self.config.language == Language::C && self.config.cpp_compat {
out.new_line();
out.write("#ifdef __cplusplus");
out.new_line();
}
if self.config.language == Language::Cxx || self.config.cpp_compat {
out.new_line();
out.write("} // extern \"C\"");
out.new_line();
}
if self.config.language == Language::C && self.config.cpp_compat {
out.new_line_if_not_start();
out.write("#endif // __cplusplus");
out.new_line();
}
}
if self.config.language == Language::Cxx {

View File

@ -613,6 +613,8 @@ pub struct Config {
pub tab_width: usize,
/// The language to output bindings for
pub language: Language,
/// Include preprocessor defines in C bindings to ensure C++ compatibility
pub cpp_compat: bool,
/// The style to declare structs, enums and unions in for C
pub style: Style,
/// The configuration options for parsing
@ -658,6 +660,7 @@ impl Default for Config {
line_length: 100,
tab_width: 2,
language: Language::Cxx,
cpp_compat: false,
style: Style::Type,
macro_expansion: Default::default(),
parse: ParseConfig::default(),

View File

@ -540,6 +540,18 @@ impl Source for Enum {
if !size.is_none() || config.style.generate_tag() {
write!(out, " {}", enum_name);
}
if config.cpp_compat {
if let Some(prim) = size {
out.new_line();
out.write("#ifdef __cplusplus");
out.new_line();
write!(out, " : {}", prim);
out.new_line();
out.write("#endif // __cplusplus");
out.new_line();
}
}
} else {
out.write("enum class");
@ -575,10 +587,22 @@ impl Source for Enum {
}
if config.language == Language::C {
if config.cpp_compat {
out.new_line_if_not_start();
out.write("#ifndef __cplusplus");
out.new_line();
}
if let Some(prim) = size {
out.new_line();
write!(out, "typedef {} {};", prim, enum_name);
}
if config.cpp_compat {
out.new_line_if_not_start();
out.write("#endif // __cplusplus");
out.new_line();
}
}
// Done emitting the enum

View File

@ -42,6 +42,10 @@ fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
};
}
if matches.is_present("cpp-compat") {
config.cpp_compat = true;
}
if let Some(style) = matches.value_of("style") {
config.style = match style {
"Both" => Style::Both,
@ -141,6 +145,11 @@ fn main() {
.help("Specify the language to output bindings in")
.possible_values(&["c++", "C++", "c", "C"]),
)
.arg(
Arg::with_name("cpp-compat")
.long("cpp-compat")
.help("Whether to add C++ compatibility to generated C bindings")
)
.arg(
Arg::with_name("style")
.short("s")

View File

@ -10,6 +10,7 @@ fn run_cbindgen(
path: &Path,
output: &Path,
language: Language,
cpp_compat: bool,
style: Option<Style>,
) {
let program = Path::new(cbindgen_path);
@ -18,6 +19,10 @@ fn run_cbindgen(
Language::Cxx => {}
Language::C => {
command.arg("--lang").arg("c");
if cpp_compat {
command.arg("--cpp-compat");
}
}
}
@ -86,6 +91,7 @@ fn run_compile_test(
name: &'static str,
path: &Path,
language: Language,
cpp_compat: bool,
style: Option<Style>,
) {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
@ -101,25 +107,35 @@ fn run_compile_test(
Style::Type => {}
}
}
match language {
Language::Cxx => {
output.push(format!("{}.cpp", name));
}
Language::C => {
output.push(format!("{}.c", name));
}
}
run_cbindgen(cbindgen_path, path, &output, language, style);
let ext = match language {
Language::Cxx => "cpp",
Language::C => {
if cpp_compat {
"compat.c"
} else {
"c"
}
}
};
output.push(format!("{}.{}", name, ext));
run_cbindgen(cbindgen_path, path, &output, language, cpp_compat, style);
compile(&output, language);
if language == Language::C && cpp_compat {
compile(&output, Language::Cxx)
}
}
fn test_file(cbindgen_path: &'static str, name: &'static str, filename: &'static str) {
let test = Path::new(filename);
for style in &[Style::Type, Style::Tag, Style::Both] {
run_compile_test(cbindgen_path, name, &test, Language::C, Some(*style));
run_compile_test(cbindgen_path, name, &test, Language::C, true, Some(*style));
run_compile_test(cbindgen_path, name, &test, Language::C, false, Some(*style));
}
run_compile_test(cbindgen_path, name, &test, Language::Cxx, None);
run_compile_test(cbindgen_path, name, &test, Language::Cxx, false, None);
}
macro_rules! test_file {