// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.config.codegen; import java.io.FileWriter; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Map; import java.util.Arrays; import java.util.StringTokenizer; import java.util.stream.Collectors; /** * This class autogenerates C++ code for the C++ config, based on a CNode tree given. */ public class CppClassBuilder implements ClassBuilder { private final CNode root; private final NormalizedDefinition nd; private final File rootDir; private final String relativePathUnderRoot; private static final Map vectorTypeDefs = Map.of( "bool", "::config::BoolVector", "int32_t", "::config::IntVector", "int64_t", "::config::LongVector", "double", "::config::DoubleVector", "vespalib::string", "::config::StringVector" ); private static final Map mapTypeDefs = Map.of( "bool", "::config::BoolMap", "int32_t", "::config::IntMap", "int64_t", "::config::LongMap", "double", "::config::DoubleMap", "vespalib::string", "::config::StringMap" ); private static final Map slimeTypeMap = Map.of( "bool", "Bool", "int", "Long", "long", "Long", "double", "Double", "string", "String", "enum", "String", "file", "String", "reference", "String" ); public CppClassBuilder(CNode root, NormalizedDefinition nd, File rootDir, String relativePathUnderRoot) { this.root = root; this.nd = nd; this.rootDir = rootDir; this.relativePathUnderRoot = relativePathUnderRoot; } public void createConfigClasses() { generateConfig(root, nd); } void writeFile(File f, String content) throws IOException { FileWriter fw = new FileWriter(f); fw.write(content); fw.close(); } void generateConfig(CNode root, NormalizedDefinition nd) { try{ StringWriter headerWriter = new StringWriter(); StringWriter bodyWriter = new StringWriter(); writeHeaderFile(headerWriter, root); writeBodyFile(bodyWriter, root, relativePathUnderRoot, nd); String newHeader = headerWriter.toString(); String newBody = bodyWriter.toString(); String prefix = ""; if (relativePathUnderRoot != null) { prefix = relativePathUnderRoot + "/"; } File headerFile = new File(rootDir, prefix + getFileName(root, "h")); File bodyFile = new File(rootDir, prefix + getFileName(root, "cpp")); writeFile(headerFile, newHeader); writeFile(bodyFile, newBody); } catch (IOException e) { e.printStackTrace(); } } String getFileName(CNode node, String extension) { return "config-" + node.getName() + "." + extension; } static String removeDashesAndUpperCaseAllFirstChars(String source, boolean capitalizeFirst) { // Create upper case chars after each dash String [] parts = source.split("[-_]"); StringBuilder sb = new StringBuilder(); for (String s : parts) { sb.append(s.substring(0, 1).toUpperCase()).append(s.substring(1)); } String result = sb.toString(); if (!capitalizeFirst) { result = result.substring(0,1).toLowerCase() + result.substring(1); } return result; } /** Convert name of type to the name we want to use in macro ifdefs in file. */ String getDefineName(String name) { return name.toUpperCase().replace("-", ""); } /** Convert name of type to the name we want to use as type name in the generated code. */ static String getTypeName(String name) { return removeDashesAndUpperCaseAllFirstChars(name, true); } /** Convert name of an identifier from value in def file to name to use in C++ file. */ String getIdentifier(String name) { return removeDashesAndUpperCaseAllFirstChars(name, false); } /** * Class to generate noexcept specifier if default constructor, copy constructor or copy assignment is non-throwing */ private static class NoExceptSpecifier { private enum Variant { COPY, MOVE, DEFAULT_CONSTRUCTOR } private final boolean copyEnabled; private final boolean moveEnabled; private final boolean defaultConstructorEnabled; public NoExceptSpecifier(CNode node) { copyEnabled = checkNode(node, Variant.COPY); moveEnabled = checkNode(node, Variant.MOVE); defaultConstructorEnabled = checkNode(node, Variant.DEFAULT_CONSTRUCTOR); } private static boolean checkNode(CNode node, Variant variant) { if (node instanceof InnerCNode) { for (CNode child: node.getChildren()) { if ((child.isArray || child.isMap) && variant != Variant.MOVE) { return false; } if (!checkNode(child, variant)) { return false; } } } return true; } private static String qualifier(boolean enabled) { if (enabled) { return " noexcept"; } else { return ""; } } public String copyQualifier() { return qualifier(copyEnabled); } public String moveQualifier() { return qualifier(moveEnabled); } public String defaultConstructorQualifier() { return qualifier(defaultConstructorEnabled); } public String toString() { return copyQualifier(); } } void writeHeaderFile(Writer w, CNode root) throws IOException { writeHeaderHeader(w, root); writeHeaderPublic(w, root); writeHeaderFooter(w, root); } void writeHeaderPublic(Writer w, CNode root) throws IOException { w.write("public:\n"); writeHeaderTypeDefs(w, root, " "); writeTypeDeclarations(w, root, " "); writeHeaderFunctionDeclarations(w, getTypeName(root, false), root, " "); writeStaticMemberDeclarations(w, " "); writeMembers(w, root, " "); } String [] generateCppNameSpace(CNode root) { String namespace = root.getNamespace(); if (namespace.contains(".")) { return namespace.split("\\."); } return new String[]{namespace}; } String generateCppNameSpaceString(String[] namespaceList) { StringBuilder str = new StringBuilder(); for (int i = 0; i < namespaceList.length - 1; i++) { str.append(namespaceList[i]); str.append("::"); } str.append(namespaceList[namespaceList.length - 1]); return str.toString(); } String generateCppNameSpaceDefine(String[] namespaceList) { StringBuilder str = new StringBuilder(); for (int i = 0; i < namespaceList.length - 1; i++) { str.append(namespaceList[i].toUpperCase()); str.append("_"); } str.append(namespaceList[namespaceList.length - 1].toUpperCase()); return str.toString(); } void writeNameSpaceBegin(Writer w, String [] namespaceList) throws IOException { w.write("namespace "); w.write(getNestedNameSpace(namespaceList)); w.write(" {\n"); } String getNestedNameSpace(String [] namespaceList) { return Arrays.stream(namespaceList).map(String::toString).collect(Collectors.joining("::")); } void writeNameSpaceEnd(Writer w, String [] namespaceList) throws IOException { w.write("} // namespace "); w.write(getNestedNameSpace(namespaceList)); w.write("\n"); } void writeHeaderHeader(Writer w, CNode root) throws IOException { String [] namespaceList = generateCppNameSpace(root); String namespacePrint = generateCppNameSpaceString(namespaceList); String namespaceDefine = generateCppNameSpaceDefine(namespaceList); String className = getTypeName(root, false); String defineName = namespaceDefine + "_" + getDefineName(className); w.write("" + "/**\n" + " * @class " + namespacePrint + "::" + className + "\n" + " * @ingroup config\n" + " *\n" + " * @brief This is an autogenerated class for handling VESPA config.\n" + " *\n" + " * This class is autogenerated by vespa from a config definition file.\n" + " * To subscribe to config, you need to include the config/config.h header, \n" + " * and create a ConfigSubscriber in order to subscribe for config.\n" ); if (root.getComment().length() > 0) { w.write(" *\n"); StringTokenizer st = new StringTokenizer(root.getComment(), "\n"); while (st.hasMoreTokens()) { w.write(" * " + st.nextToken() + "\n"); } } w.write("" + " */\n" + "#ifndef CLOUD_CONFIG_" + defineName + "_H\n" + "#define CLOUD_CONFIG_" + defineName + "_H\n" + "\n" + "#include \n" + "#include \n" + "\n"); w.write("namespace config {\n"); w.write(" class ConfigValue;\n"); w.write(" class ConfigPayload;\n"); w.write("}\n\n"); w.write("namespace vespalib::slime {\n"); w.write(" struct Inspector;\n"); w.write(" struct Cursor;\n"); w.write("}\n\n"); writeNameSpaceBegin(w, namespaceList); w.write("\nnamespace internal {\n\n"); w.write("" + "/**\n" + " * This class contains the config. DO NOT USE THIS CLASS DIRECTLY. Use the typedeffed\n" + " * versions after this class declaration.\n" + " */\n" + "class Internal" + className + "Type : public ::config::ConfigInstance\n" + "{\n" ); } void writeTypeDeclarations(Writer w, CNode node, String indent) throws IOException { java.util.Set 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) { w.write(indent + "enum class " + typeName + " { "); LeafCNode.EnumLeaf leaf = (LeafCNode.EnumLeaf) child; for (int i=0; i " + typeName + "Vector;" + "\n" + indent + "typedef std::map " + typeName + "Map;" + "\n" + indent + "static " + typeName + " get" + typeName + "(const vespalib::string&);\n" + indent + "static vespalib::string get" + typeName + "Name(" + typeName + " e);\n" + "\n" ); w.write(indent + "struct Internal" + typeName + "Converter {\n"); w.write(indent + " " + typeName + " operator()(const ::vespalib::string & __fieldName, const ::vespalib::slime::Inspector & __inspector);\n"); w.write(indent + " " + typeName + " operator()(const ::vespalib::slime::Inspector & __inspector);\n"); w.write(indent + " " + typeName + " operator()(const ::vespalib::slime::Inspector & __inspector, " + typeName + " __eDefault);\n"); w.write(indent + "};\n"); } else { w.write(indent + "class " + typeName + " {\n"); w.write(indent + "public:\n"); writeTypeDeclarations(w, child, indent + " "); writeStructFunctionDeclarations(w, getTypeName(child, false), child, indent + " "); writeMembers(w, child, indent + " "); w.write(indent + "};\n"); w.write(indent + "typedef std::vector<" + typeName + "> " + typeName + "Vector;\n\n"); w.write(indent + "typedef std::map " + typeName + "Map;\n\n"); } } } } void writeHeaderFunctionDeclarations(Writer w, String className, CNode node, String indent) throws IOException { w.write("" + indent + "const vespalib::string & defName() const override { return CONFIG_DEF_NAME; }\n" + indent + "const vespalib::string & defMd5() const override { return CONFIG_DEF_MD5; }\n" + indent + "const vespalib::string & defNamespace() const override { return CONFIG_DEF_NAMESPACE; }\n" + indent + "void serialize(::config::ConfigDataBuffer & __buffer) const override;\n"); writeConfigClassFunctionDeclarations(w, "Internal" + className + "Type", node, indent); } void writeConfigClassFunctionDeclarations(Writer w, String className, CNode node, String indent) throws IOException { w.write(indent + className + "(const ::config::ConfigValue & __value);\n"); w.write(indent + className + "(const ::config::ConfigDataBuffer & __value);\n"); w.write(indent + className + "(const ::config::ConfigPayload & __payload);\n"); writeCommonFunctionDeclarations(w, className, node, indent); } void writeStructFunctionDeclarations(Writer w, String className, CNode node, String indent) throws IOException { w.write(indent + className + "(const " + vectorTypeDefs.get("vespalib::string") + " & __lines);\n"); w.write(indent + className + "(const vespalib::slime::Inspector & __inspector);\n"); w.write(indent + className + "(const ::config::ConfigPayload & __payload);\n"); writeCommonFunctionDeclarations(w, className, node, indent); w.write(indent + "void serialize(vespalib::slime::Cursor & __cursor) const;\n"); } void writeClassCopyConstructorDeclaration(Writer w, String className, NoExceptSpecifier noexcept, String indent) throws IOException { w.write(indent + className + "(const " + className + " & __rhs)" + noexcept.copyQualifier() + ";\n"); } void writeClassAssignmentOperatorDeclaration(Writer w, String className, NoExceptSpecifier noexcept, String indent) throws IOException { w.write(indent + className + " & operator = (const " + className + " & __rhs)" + noexcept.copyQualifier() + ";\n"); } void writeClassMoveConstructorDeclaration(Writer w, String className, NoExceptSpecifier noexcept, String indent) throws IOException { w.write(indent + className + "(" + className + " && __rhs)" + noexcept.moveQualifier() + ";\n"); } void writeClassMoveOperatorDeclaration(Writer w, String className, NoExceptSpecifier noexcept, String indent) throws IOException { w.write(indent + className + " & operator = (" + className + " && __rhs)" + noexcept.moveQualifier() + ";\n"); } void writeConfigClassCopyConstructorDefinition(Writer w, String parent, String className, NoExceptSpecifier noexcept) throws IOException { w.write(parent + "::" + className + "(const " + className + " & __rhs)" + noexcept.copyQualifier() + " = default;\n"); } void writeConfigClassAssignmentOperatorDefinition(Writer w, String parent, String className, NoExceptSpecifier noexcept) throws IOException { w.write(parent + " & " + parent + "::" + "operator =(const " + className + " & __rhs)" + noexcept.copyQualifier() + " = default;\n"); } void writeConfigClassMoveConstructorDefinition(Writer w, String parent, String className, NoExceptSpecifier noexcept) throws IOException { w.write(parent + "::" + className + "(" + className + " && __rhs)" + noexcept.moveQualifier() + " = default;\n"); } void writeConfigClassMoveOperatorDefinition(Writer w, String parent, String className, NoExceptSpecifier noexcept) throws IOException { w.write(parent + " & " + parent + "::" + "operator =(" + className + " && __rhs)" + noexcept.moveQualifier() + " = default;\n"); } void writeClassCopyConstructorDefinition(Writer w, String parent, CNode node) throws IOException { String typeName = getTypeName(node, false); NoExceptSpecifier noexcept = new NoExceptSpecifier(node); w.write(parent + "::" + typeName + "(const " + typeName + " & __rhs)" + noexcept.copyQualifier() + " = default;\n"); } void writeClassMoveConstructorDefinition(Writer w, String parent, CNode node) throws IOException { String typeName = getTypeName(node, false); NoExceptSpecifier noexcept = new NoExceptSpecifier(node); w.write(parent + "::" + typeName + "(" + typeName + " && __rhs)" + noexcept.moveQualifier() + " = default;\n"); } void writeClassAssignmentOperatorDefinition(Writer w, String parent, CNode node) throws IOException { String typeName = getTypeName(node, false); NoExceptSpecifier noexcept = new NoExceptSpecifier(node); // Write empty constructor w.write(parent + " & " + parent + "::" + "operator = (const " + typeName + " & __rhs)" + noexcept.copyQualifier() + " = default;\n"); } void writeClassMoveOperatorDefinition(Writer w, String parent, CNode node) throws IOException { String typeName = getTypeName(node, false); NoExceptSpecifier noexcept = new NoExceptSpecifier(node); // Write empty constructor w.write(parent + " & " + parent + "::" + "operator = (" + typeName + " && __rhs)" + noexcept.moveQualifier() + " = default;\n"); } void writeDestructor(Writer w, String parent, String className) throws IOException { w.write(parent + "~" + className + "() = default; \n"); } void writeCommonFunctionDeclarations(Writer w, String className, CNode node, String indent) throws IOException { NoExceptSpecifier noexcept = new NoExceptSpecifier(node); w.write("" + indent + className + "() " + noexcept.defaultConstructorQualifier() + ";\n"); writeClassCopyConstructorDeclaration(w, className, noexcept, indent); writeClassAssignmentOperatorDeclaration(w, className, noexcept, indent); writeClassMoveConstructorDeclaration(w, className, noexcept, indent); writeClassMoveOperatorDeclaration(w, className, noexcept, indent); w.write("" + indent + "~" + className + "();\n"); w.write("\n" + indent + "bool operator==(const " + className + "& __rhs) const noexcept;\n" + indent + "bool operator!=(const " + className + "& __rhs) const noexcept;\n" + "\n" ); } static String getTypeName(CNode node, boolean includeArray) { String type = null; if (node instanceof InnerCNode innerNode) { type = getTypeName(innerNode.getName()); } else if (node instanceof LeafCNode leaf) { if (leaf.getType().equals("bool")) { type = "bool"; } else if (leaf.getType().equals("int")) { type = "int32_t"; } else if (leaf.getType().equals("long")) { type = "int64_t"; } else if (leaf.getType().equals("double")) { type = "double"; } else if (leaf.getType().equals("enum")) { type = getTypeName(node.getName()); } else if (leaf.getType().equals("string")) { type = "vespalib::string"; } else if (leaf.getType().equals("reference")) { type = "vespalib::string"; } else if (leaf.getType().equals("file")) { type = "vespalib::string"; } else { throw new IllegalArgumentException("Unknown leaf datatype " + leaf.getType()); } } if (type == null) { throw new IllegalArgumentException("Unknown node " + node); } if (node.isArray && includeArray) { if (vectorTypeDefs.containsKey(type)) { type = vectorTypeDefs.get(type); } else { type = type + "Vector"; } } else if (node.isMap && includeArray) { if (mapTypeDefs.containsKey(type)) { type = mapTypeDefs.get(type); } else { type = type + "Map"; } } return type; } void writeStaticMemberDeclarations(Writer w, String indent) throws IOException { w.write("" + indent + "static const vespalib::string CONFIG_DEF_MD5;\n" + indent + "static const vespalib::string CONFIG_DEF_NAME;\n" + indent + "static const vespalib::string CONFIG_DEF_NAMESPACE;\n" + indent + "static const ::config::StringVector CONFIG_DEF_SCHEMA;\n" + indent + "static const int64_t CONFIG_DEF_SERIALIZE_VERSION;\n" + "\n" ); } void writeComment(Writer w, String indent, String comment, boolean javadoc) throws IOException { // If simple one liner comment, write on one line. if (javadoc && comment.indexOf('\n') == -1 && comment.length() <= 80 - (indent.length() + 7)) { w.write(indent + "/** " + comment + " */\n"); return; } else if (!javadoc && comment.indexOf('\n') == -1 && comment.length() <= 80 - (indent.length() + 3)) { w.write(indent + "// " + comment + "\n"); return; } // If not we need to write multi line comment. int maxLineLen = 80 - (indent.length() + 3); if (javadoc) w.write(indent + "/**\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 + " */\n"); } void writeMembers(Writer w, CNode node, String indent) throws IOException { for (CNode child : node.getChildren()) { String typeName = getTypeName(child, true); if (child.getComment().length() > 0) { String comment = child.getComment(); int index; do { index = comment.indexOf("\n\n"); if (index == -1) break; String next = comment.substring(0, index); comment = comment.substring(index + 2); w.write("\n"); writeComment(w, indent, next, false); } while (true); w.write("\n"); writeComment(w, indent, comment, true); } w.write(indent + typeName + " " + getIdentifier(child.getName()) + ";"); if (child instanceof LeafCNode) { LeafCNode leaf = (LeafCNode) child; DefaultValue value = leaf.getDefaultValue(); if (value != null) { w.write(" // Default: " + value.getStringRepresentation()); } } w.write("\n"); } } void writeHeaderTypeDefs(Writer w, CNode root, String indent) throws IOException { w.write(indent + "typedef std::unique_ptr UP;\n"); } private static String getInternalClassName(CNode root) { return "Internal" + getTypeName(root, false) + "Type"; } void writeHeaderFooter(Writer w, CNode root) throws IOException { String [] namespaceList = generateCppNameSpace(root); String namespaceDefine = generateCppNameSpaceDefine(namespaceList); String className = getTypeName(root, false); String defineName = namespaceDefine + "_" + getDefineName(className); w.write("" + "};\n" + "\n" + "} // namespace internal\n\n"); w.write("typedef internal::" + getInternalClassName(root) + " " + className + "ConfigBuilder;\n"); w.write("typedef const internal::" + getInternalClassName(root) + " " + className + "Config;\n"); w.write("\n"); writeNameSpaceEnd(w, namespaceList); w.write("#endif // VESPA_config_" + defineName + "_H\n"); } void writeBodyFile(Writer w, CNode root, String subdir, NormalizedDefinition nd) throws IOException { writeBodyHeader(w, root, subdir); writeStaticMemberDefinitions(w, root, nd); writeDefinition(w, root, null); writeBodyFooter(w, root); } void writeBodyHeader(Writer w, CNode root, String subdir) throws IOException { if (subdir == null) { w.write("#include \"" + getFileName(root, "h") + "\""); } else { w.write("#include <" + subdir + "/" + getFileName(root, "h") + ">"); } w.write("\n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("#include \n"); w.write("\n"); writeNameSpaceBegin(w, generateCppNameSpace(root)); w.write("\nnamespace internal {\n\n"); w.write("using ::config::ConfigParser;\n"); w.write("using ::config::InvalidConfigException;\n"); w.write("using ::config::ConfigInstance;\n"); w.write("using ::config::ConfigValue;\n"); w.write("using namespace vespalib::slime::convenience;\n"); w.write("\n"); } void writeStaticMemberDefinitions(Writer w, CNode root, NormalizedDefinition nd) throws IOException { String typeName = getInternalClassName(root); w.write("const vespalib::string " + typeName + "::CONFIG_DEF_MD5(\"" + root.defMd5 + "\");\n" + "const vespalib::string " + typeName + "::CONFIG_DEF_NAME(\"" + root.defName + "\");\n" + "const vespalib::string " + typeName + "::CONFIG_DEF_NAMESPACE(\"" + root.getNamespace() + "\");\n" + "const int64_t " + typeName + "::CONFIG_DEF_SERIALIZE_VERSION(1);\n"); w.write("const static vespalib::string __internalDefSchema[] = {\n"); for (String line : nd.getNormalizedContent()) { w.write("\"" + line.replace("\"", "\\\"") + "\",\n"); } w.write("};\n"); w.write("const ::config::StringVector " + typeName + "::CONFIG_DEF_SCHEMA(__internalDefSchema,\n"); w.write(" __internalDefSchema + (sizeof(__internalDefSchema) / \n"); w.write(" sizeof(__internalDefSchema[0])));\n"); w.write("\n"); } 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 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 vespalib::string& name)\n" + "{\n" ); for (int i=0; i(t) << \")\";\n" + " return ost.str();\n" + " }\n" + " }\n" + "}\n" + "\n" ); w.write(parent + typeName + " " + parent + "Internal" + typeName + "Converter::operator()(const ::vespalib::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 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("vespalib::string") + " & __lines(__value.getLines());\n"); } else { w.write(parent + typeName + "(const " + vectorTypeDefs.get("vespalib::string") +" & __lines)\n" + "{\n"); } w.write(indent + "std::set __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 " + childName + "__ValueMap(\n "); } else { w.write(indent + childName + " = get" + childType + "("); } childType = "vespalib::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 " + inserterName + "(" + childName + ");\n"); w.write(indent + childInspector + ".traverse(" + inserterName + ");\n"); } else if (child.isMap) { String inserterName = "__" + childName + "Inserter"; w.write(indent + "::config::internal::MapInserter<" + childType); if (child instanceof LeafCNode.EnumLeaf) { w.write(", Internal" + childType + "Converter"); } w.write("> " + inserterName + "(" + childName + ");\n"); w.write(indent + childInspector + ".traverse(" + inserterName + ");\n"); } else { w.write(indent + childName + " = "); if (child instanceof LeafCNode.EnumLeaf) { w.write("Internal" + childType + "Converter"); } else { w.write("::config::internal::ValueConverter<" + childType + ">"); } if (child instanceof LeafCNode && ((LeafCNode) child).getDefaultValue() != null) { LeafCNode leaf = (LeafCNode) child; String defaultValue = getDefaultValue(leaf); if (leaf.getType().equals("enum")) { defaultValue = getTypeName(leaf, false) + "::" + defaultValue; } w.write("()(" + childInspector + ", " + defaultValue + ");\n"); } else if (child instanceof InnerCNode) { w.write("()(" + childInspector + ");\n"); } else { w.write("()(\"" + child.getName() + "\", " + childInspector + ");\n"); } } } w.write("}\n\n"); } void writeBodyFooter(Writer w, CNode root) throws IOException { w.write("} // namespace internal\n\n"); writeNameSpaceEnd(w, generateCppNameSpace(root)); } String getDefaultValue(LeafCNode leaf) { String defaultVal = leaf.getDefaultValue().getStringRepresentation(); if (leaf.getType().equals("string") && defaultVal.equals("null")) throw new CodegenRuntimeException("Default value null not allowed for C++ config"); if (leaf.getType().equals("long") && "-9223372036854775808".equals(defaultVal)) { return "LONG_MIN"; } else if (leaf.getType().equals("int") && "-2147483648".equals(defaultVal)) { return "INT_MIN"; } else { return defaultVal; } } }