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