string CodeGen::generateUnionType()

in lang/c++/impl/avrogencpp.cc [365:456]


string CodeGen::generateUnionType(const NodePtr &n) {
    size_t c = n->leaves();
    vector<string> types;
    vector<string> names;

    auto it = doing.find(n);
    if (it != doing.end()) {
        for (size_t i = 0; i < c; ++i) {
            const NodePtr &nn = n->leafAt(i);
            types.push_back(generateDeclaration(nn));
            names.push_back(cppNameOf(nn));
        }
    } else {
        doing.insert(n);
        for (size_t i = 0; i < c; ++i) {
            const NodePtr &nn = n->leafAt(i);
            types.push_back(generateType(nn));
            names.push_back(cppNameOf(nn));
        }
        doing.erase(n);
    }
    if (done.find(n) != done.end()) {
        return done[n];
    }

    // re-use existing union types that have the exact same branches
    if (const auto existingName = unionTracker_.getExistingUnionName(types); existingName.has_value()) {
        return existingName.value();
    }
    const std::string result = unionTracker_.generateNewUnionName(types);

    os_ << "struct " << result << " {\n"
        << "private:\n"
        << "    size_t idx_;\n"
        << "    std::any value_;\n"
        << "public:\n";

    os_ << "    /** enum representing union branches as returned by the idx() function */\n"
        << "    enum class Branch: size_t {\n";

    // generate a enum that maps the branch name to the corresponding index (as returned by idx())
    std::set<std::string> used_branch_names;
    for (size_t i = 0; i < c; ++i) {
        // escape reserved literals for c++
        auto branch_name = decorate(names[i]);
        // avoid rare collisions, e.g. someone might name their struct int_
        if (used_branch_names.find(branch_name) != used_branch_names.end()) {
            size_t postfix = 2;
            std::string escaped_name = branch_name + "_" + std::to_string(postfix);
            while (used_branch_names.find(escaped_name) != used_branch_names.end()) {
                ++postfix;
                escaped_name = branch_name + "_" + std::to_string(postfix);
            }
            branch_name = escaped_name;
        }
        os_ << "        " << branch_name << " = " << i << ",\n";
        used_branch_names.insert(branch_name);
    }
    os_ << "    };\n";

    os_ << "    size_t idx() const { return idx_; }\n";
    os_ << "    Branch branch() const { return static_cast<Branch>(idx_); }\n";

    for (size_t i = 0; i < c; ++i) {
        const NodePtr &nn = n->leafAt(i);
        if (nn->type() == avro::AVRO_NULL) {
            os_ << "    bool is_null() const {\n"
                << "        return (idx_ == " << i << ");\n"
                << "    }\n"
                << "    void set_null() {\n"
                << "        idx_ = " << i << ";\n"
                << "        value_ = std::any();\n"
                << "    }\n";
        } else {
            const string &type = types[i];
            const string &name = names[i];
            os_ << "    "
                << "const " << type << "& get_" << name << "() const;\n"
                << "    " << type << "& get_" << name << "();\n"
                << "    void set_" << name << "(const " << type << "& v);\n"
                << "    void set_" << name << "(" << type << "&& v);\n";
            pendingGettersAndSetters.emplace_back(result, type, name, i);
        }
    }

    os_ << "    " << result << "();\n";
    pendingConstructors.emplace_back(result, types[0],
                                     n->leafAt(0)->type() != avro::AVRO_NULL);
    os_ << "};\n\n";

    return result;
}