fn write_derived_functions_enum()

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);
            }
        }
    }