void writeDefinition(Writer w, CNode node, String fullClassName) throws IOException {
        boolean root = false;
        if (fullClassName == null) {
            fullClassName =  getInternalClassName(node);
            root = true;
        }
        final String parent = fullClassName + "::";
        java.util.Set<String> declaredTypes = new java.util.HashSet<>();
        for (CNode child : node.getChildren()) {
            boolean complexType = (child instanceof InnerCNode || child instanceof LeafCNode.EnumLeaf);
            if (complexType && !declaredTypes.contains(child.getName())) {
                String typeName = getTypeName(child, false);
                declaredTypes.add(child.getName());
                if (child instanceof LeafCNode.EnumLeaf) {
                    LeafCNode.EnumLeaf leaf = (LeafCNode.EnumLeaf) child;
                    // Definition of getType(string)
                    w.write(parent + typeName + "\n"
                            + parent + "get" + typeName + "(const std::string& name)\n"
                            + "{\n"
                            );
                    for (int i=0; i<leaf.getLegalValues().length; ++i) {
                        w.write("    " + (i != 0 ? "} else " : ""));
                        w.write("if (name == \"" + leaf.getLegalValues()[i] + "\") {\n"
                                + "        return " + typeName + "::" + leaf.getLegalValues()[i] + ";\n");
                    }
                    w.write("    } else {\n"
                            + "        throw InvalidConfigException(\"Illegal enum value '\" + name + \"'\");\n"
                            + "    }\n"
                            + "}\n"
                            + "\n"
                            );
                    // Definition of getTypeName(enum)
                    w.write("std::string\n"
                            + parent + "get" + typeName + "Name(" + typeName + " t)\n"
                            + "{\n"
                            + "    switch (t) {\n"
                            );
                    for (int i=0; i<leaf.getLegalValues().length; ++i) {
                        w.write("        case " + typeName + "::" + leaf.getLegalValues()[i]  + ": return \"" + leaf.getLegalValues()[i] + "\";\n");
                    }
                    w.write("        default:\n"
                            + "        {\n"
                            + "            vespalib::asciistream ost;\n"
                            + "            ost << \"UNKNOWN(\" << static_cast<int>(t) << \")\";\n"
                            + "            return ost.str();\n"
                            + "        }\n"
                            + "    }\n"
                            + "}\n"
                            + "\n"
                            );
                    w.write(parent + typeName + " " + parent + "Internal" + typeName + "Converter::operator()(const ::std::string & __fieldName, const ::vespalib::slime::Inspector & __inspector) {\n");
                    w.write("    if (__inspector.valid()) {\n");
                    w.write("        return " + parent + "get" + typeName + "(__inspector.asString().make_string());\n");
                    w.write("    }\n");
                    w.write("    throw InvalidConfigException(\"Value for '\" + __fieldName + \"' required but not found\");\n");
                    w.write("}\n");
                    w.write(parent + typeName + " " + parent + "Internal" + typeName + "Converter::operator()(const ::vespalib::slime::Inspector & __inspector) {\n");
                    w.write("    return " + parent + "get" + typeName + "(__inspector.asString().make_string());\n");
                    w.write("}\n");
                    w.write(parent + typeName + " " + parent + "Internal" + typeName + "Converter::operator()(const ::vespalib::slime::Inspector & __inspector, " + typeName + " __eDefault) {\n");
                    w.write("    if (__inspector.valid()) {\n");
                    w.write("        return " + parent + "get" + typeName + "(__inspector.asString().make_string());\n");
                    w.write("    }\n");
                    w.write("    return __eDefault;\n");
                    w.write("}\n\n");
                } else {
                    writeDefinition(w, child, parent + typeName);
                }
            }
        }
        String tmpName = getTypeName(node, false);
        String typeName = root ? getInternalClassName(node) : tmpName;
        NoExceptSpecifier noexcept = new NoExceptSpecifier(node);
        // Write empty constructor
        w.write(parent + typeName + "()" + noexcept.defaultConstructorQualifier() + "\n");
        for (int i=0; i<node.getChildren().length; ++i) {
            CNode child = node.getChildren()[i];
            String childName = getIdentifier(child.getName());
            if (i == 0) {
                w.write("    : " + childName + "(");
            } else {
                w.write("),\n      " + childName + "(");
            }
            if (child.isArray || child.isMap) {
                // Default array for empty constructor is empty array.
            } else if (child instanceof LeafCNode) { // If we have a default value, use that..
                LeafCNode leaf = (LeafCNode) child;
                if (leaf.getDefaultValue() != null) {
                    if (leaf.getType().equals("enum")) {
                        w.write(getTypeName(leaf, false) + "::");
                    }
                    w.write(getDefaultValue(leaf));
                } else {
                    // Defines empty constructor defaults for primitives without default set
                    if (leaf.getType().equals("bool")) {
                        w.write("false");
                    } else if (leaf.getType().equals("int")) {
                        w.write("0");
                    } else if (leaf.getType().equals("double")) {
                        w.write("0");
                    } else if (leaf.getType().equals("string")) {
                    } else if (leaf.getType().equals("enum")) {
                        LeafCNode.EnumLeaf enumNode = (LeafCNode.EnumLeaf) leaf;
                        w.write(getTypeName(leaf, false) + "::" + enumNode.getLegalValues()[0]);
                    } else if (leaf.getType().equals("reference")) {
                    } else if (leaf.getType().equals("file")) {
                    }
                }
            }
            // If we hit neither else, we're an inner node, thus special type that has its own empty constructor
        }
        if (node.getChildren().length > 0)
            w.write(")\n");
        w.write(""
                + "{\n"
                + "}\n"
                + "\n"
                );
        // Write copy constructor
        if (root) {
            writeConfigClassCopyConstructorDefinition(w, fullClassName, typeName, noexcept);
            writeConfigClassAssignmentOperatorDefinition(w, fullClassName, typeName, noexcept);
            writeConfigClassMoveConstructorDefinition(w, fullClassName, typeName, noexcept);
            writeConfigClassMoveOperatorDefinition(w, fullClassName, typeName, noexcept);
        } else {
            writeClassCopyConstructorDefinition(w, fullClassName, node);
            writeClassAssignmentOperatorDefinition(w, fullClassName, node);
            writeClassMoveConstructorDefinition(w, fullClassName, node);
            writeClassMoveOperatorDefinition(w, fullClassName, node);
        }
        writeDestructor(w, parent, typeName);

        // Write parsing constructor
        String indent = "    ";
        if (root) {
            w.write(typeName + "::" + typeName + "(const ConfigValue & __value)\n"
                    + "{\n"
                    + indent + "try {\n");
            indent = "        ";
            w.write(indent + "const " + vectorTypeDefs.get("std::string") + " & __lines(__value.getLines());\n");
        } else {
            w.write(parent + typeName + "(const " + vectorTypeDefs.get("std::string") +" & __lines)\n"
                    + "{\n");
        }
        w.write(indent + "std::set<std::string> __remainingValuesToParse = ConfigParser::getUniqueNonWhiteSpaceLines(__lines);\n");
        for (CNode child : node.getChildren()) {
            String childType = getTypeName(child, false);
            String childName = getIdentifier(child.getName());
            String childVectorType = null;
            if (child instanceof LeafCNode.EnumLeaf) {
                if (child.isArray) {
                    childVectorType = "::config::StringVector";
                    w.write(indent + childVectorType + " " + childName + "__ValueList(\n            ");
                } else if (child.isMap) {
                    w.write(indent + "std::map<std::string, std::string> " + childName + "__ValueMap(\n            ");
                } else {
                    w.write(indent + childName + " = get" + childType + "(");
                }
                childType = "std::string";
            } else {
                w.write(indent + childName + " = ");
            }
            if (child.isArray) {
                if (childVectorType == null) {
                    childVectorType = getTypeName(child, true);
                }
                w.write("ConfigParser::parseArray<" + childVectorType + ">(\"" + child.getName() + "\", __lines)");
            } else if (child.isMap) {
                w.write("ConfigParser::parseMap<" + childType + ">(\"" + child.getName() + "\", __lines)");
            } else {
                if (child instanceof LeafCNode) {
                    w.write("ConfigParser::parse<" + childType + ">(\"" + child.getName() + "\", __lines");
                } else {
                    w.write("ConfigParser::parseStruct<" + childType + ">(\"" + child.getName() + "\", __lines");
                }
                if (child instanceof LeafCNode leaf && leaf.getDefaultValue() != null) {
                    if (leaf.getDefaultValue().getValue() != null) {
                        String defaultVal = getDefaultValue(leaf);
                        if (leaf instanceof LeafCNode.EnumLeaf) {
                            defaultVal = '"' + defaultVal + '"';
                        }
                        w.write(", " + defaultVal);
                    }
                }
                w.write(")");
            }
            if (child instanceof LeafCNode.EnumLeaf) {
                childType = getTypeName(child, false);
                w.write(");\n");
                if (child.isArray) {
                    w.write(indent + childName + ".reserve(" + childName + "__ValueList.size());\n"
                            + indent + "for (const auto & item : " + childName + "__ValueList) {\n"
                            + indent + "    " + childName + ".push_back(get" + childType + "(item));\n"
                            + indent + "}\n"
                            );
                } else if (child.isMap) {
                    w.write(indent + "for (const auto & entry : " + childName + "__ValueMap) {\n"
                                   + "    " + childName + "[entry.first] = get" + childType + "(entry.second);\n"
                                   + "}\n"
                    );
                }
            } else {
                w.write(";\n");
            }
            w.write(indent + "ConfigParser::stripLinesForKey(\"" + child.getName() + "\", " + "__remainingValuesToParse);\n");
        }
        if (root) {
            indent = "    ";
            w.write(indent + "} catch (InvalidConfigException & __ice) {\n");
            w.write(indent + "    throw InvalidConfigException(\"Error parsing config '\" + CONFIG_DEF_NAME + \"' in namespace '\" + CONFIG_DEF_NAMESPACE + \"'"
                    + ": \" + __ice.getMessage());\n"
                    + indent + "}\n");
        }
        w.write("}\n"
                + "\n"
                );
        // Write operator==
        w.write("bool\n"
                + parent + lineBreak(parent, typeName) + "operator==(const " + typeName + "& __rhs) const noexcept\n"
                + "{\n"
                + "    return ("
                );
        for (int i = 0; i<node.getChildren().length; ++i) {
            CNode child = node.getChildren()[i];
            String childName = getIdentifier(child.getName());
            if (i != 0) {
                w.write(" &&\n            ");
            }
            w.write(childName + " == __rhs." + childName);
        }
        w.write(");\n"
                + "}\n"
                + "\n"
                );
        // Write operator!=
        w.write("bool\n"
                + parent + lineBreak(parent, typeName) + "operator!=(const " + typeName + "& __rhs) const noexcept\n"
                + "{\n"
                + "    return !(operator==(__rhs));\n"
                + "}\n"
                + "\n"
                );
        writeSlimeEncoder(w, node, parent, root);
        writeSlimeDecoder(w, node, parent, root);
        writeSlimeConstructor(w, node, parent, root);
    }
\n");
        do {
            String current;
            // Extract first line to write
            int newLine = comment.indexOf('\n');
            if (newLine == -1) {
                current = comment;
                comment = "";
            } else {
                current = comment.substring(0, newLine);
                comment = comment.substring(newLine + 1);
            }
            // If line too long, cut it in two
            if (current.length() > maxLineLen) {
                int spaceIndex = current.lastIndexOf(' ', maxLineLen);
                if (spaceIndex >= maxLineLen - 15) {
                    comment = current.substring(spaceIndex + 1)
                              + "\n" + comment;
                    current = current.substring(0, spaceIndex);
                } else {
                    comment = current.substring(maxLineLen) + "\n" + comment;
                    current = current.substring(0, maxLineLen) + "-";
                }
            }
            w.write(indent + (javadoc ? " * " : "// ") + current + "\n");
        } while (comment.length() > 0);
        if (javadoc) w.write(indent + "
configgen/src/main/java/com/yahoo/config/codegen/CppClassBuilder.java