in src/bindgen/ir/enumeration.rs [944:1097]
fn write_derived_functions_enum<F: Write, LB: LanguageBackend>(
&self,
config: &Config,
language_backend: &mut LB,
out: &mut SourceWriter<F>,
) {
let has_data = self.tag.is_some();
let tag_name = self.tag_name();
if config.language != Language::Cxx {
return;
}
// Emit an ostream function if required.
if config.enumeration.derive_ostream(&self.annotations) {
// For enums without data, this emits the serializer function for the
// enum. For enums with data, this emits the serializer function for
// the tag enum. In the latter case we need a couple of minor changes
// due to the function living inside the top-level struct or enum.
let stream = config
.function
.rename_args
.apply("stream", IdentifierType::FunctionArg);
let instance = config
.function
.rename_args
.apply("instance", IdentifierType::FunctionArg);
out.new_line();
out.new_line();
// For enums without data, we mark the function inline because the
// header might get included into multiple compilation units that
// get linked together, and not marking it inline would result in
// multiply-defined symbol errors. For enums with data we don't have
// the same problem, but mark it as a friend function of the
// containing union/struct.
// Note also that for enums with data, the case labels for switch
// statements apparently need to be qualified to the top-level
// generated struct or union. This is why the generated case labels
// below use the A::B::C format for enums with data, with A being
// self.export_name(). Failure to have that qualification results
// in a surprising compilation failure for the generated header.
write!(
out,
"{} std::ostream& operator<<(std::ostream& {}, const {}& {})",
if has_data { "friend" } else { "inline" },
stream,
tag_name,
instance,
);
out.open_brace();
if has_data {
// C++ name resolution rules are weird.
write!(
out,
"using {} = {}::{};",
tag_name,
self.export_name(),
tag_name
);
out.new_line();
}
write!(out, "switch ({})", instance);
out.open_brace();
let vec: Vec<_> = self
.variants
.iter()
.map(|x| {
format!(
"case {}::{}: {} << \"{}\"; break;",
tag_name, x.export_name, stream, x.export_name
)
})
.collect();
out.write_vertical_source_list(
language_backend,
&vec[..],
ListType::Join(""),
|_, out, s| write!(out, "{}", s),
);
out.close_brace(false);
out.new_line();
write!(out, "return {};", stream);
out.close_brace(false);
if has_data {
// For enums with data, this emits the serializer function for
// the top-level union or struct.
out.new_line();
out.new_line();
write!(
out,
"friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
stream,
self.export_name(),
instance,
);
out.open_brace();
// C++ name resolution rules are weird.
write!(
out,
"using {} = {}::{};",
tag_name,
self.export_name(),
tag_name
);
out.new_line();
write!(out, "switch ({}.tag)", instance);
out.open_brace();
let vec: Vec<_> = self
.variants
.iter()
.map(|x| {
let tag_str = format!("\"{}\"", x.export_name);
if let VariantBody::Body {
ref name, ref body, ..
} = x.body
{
format!(
"case {}::{}: {} << {}{}{}.{}; break;",
tag_name,
x.export_name,
stream,
if body.has_tag_field { "" } else { &tag_str },
if body.has_tag_field { "" } else { " << " },
instance,
name,
)
} else {
format!(
"case {}::{}: {} << {}; break;",
tag_name, x.export_name, stream, tag_str,
)
}
})
.collect();
out.write_vertical_source_list(
language_backend,
&vec[..],
ListType::Join(""),
|_, out, s| write!(out, "{}", s),
);
out.close_brace(false);
out.new_line();
write!(out, "return {};", stream);
out.close_brace(false);
}
}
}