diff --git a/VulkanHppGenerator.cpp b/VulkanHppGenerator.cpp index 4c09c38..1b029d6 100644 --- a/VulkanHppGenerator.cpp +++ b/VulkanHppGenerator.cpp @@ -29,9 +29,9 @@ void appendTypesafeStuff(std::string &str, std::string const& typesafeCheck); void appendVersionCheck(std::string & str, std::string const& version); bool beginsWith(std::string const& text, std::string const& prefix); bool endsWith(std::string const& text, std::string const& postfix); -template void checkAlias(std::map const& data, std::string const& name, int line); +void check(bool condition, int line, std::string const& message); void checkAttributes(std::map const& attributes, int line, std::map> const& required, std::map> const& optional); -void checkElements(std::vector const& elements, std::set const& values); +void checkElements(int line, std::vector const& elements, std::set const& required, std::set const& optional = {}); void checkEmptyElement(tinyxml2::XMLElement const* element); void checkOrderedElements(std::vector const& elements, std::vector const& values); void cleanup(std::stringstream & ss); @@ -62,6 +62,7 @@ std::string toUpperCase(std::string const& name); std::vector tokenize(std::string tokenString, char separator); std::string trim(std::string const& input); std::string trimEnd(std::string const& input); +void warn(bool condition, int line, std::string const& message); const std::set nonConstSTypeStructs = { "VkBaseInStructure", "VkBaseOutStructure" }; @@ -159,12 +160,11 @@ bool endsWith(std::string const& text, std::string const& postfix) return !postfix.empty() && (postfix.length() <= text.length()) && (text.substr(text.length() - postfix.length()) == postfix); } -template -void checkAlias(std::map const& data, std::string const& name, int line) +void check(bool condition, int line, std::string const& message) { - if (data.find(name) == data.end()) + if (!condition) { - throw std::runtime_error("Spec error on line " + std::to_string(line) + ": missing alias <" + name + ">"); + throw std::runtime_error("Spec error on line " + std::to_string(line) + ": " + message); } } @@ -179,14 +179,8 @@ void checkAttributes(std::map const& attributes, int l for (auto const& r : required) { auto attributesIt = attributes.find(r.first); - if (attributesIt == attributes.end()) - { - throw std::runtime_error("Spec error on line " + std::to_string(line) + ": missing attribute <" + r.first + ">"); - } - if (!r.second.empty() && (r.second.find(attributesIt->second) == r.second.end())) - { - throw std::runtime_error("Spec error on line " + std::to_string(line) + ": unexpected attribute value <" + attributesIt->second + "> in attribute <" + r.first + ">"); - } + check(attributesIt != attributes.end(), line, "missing attribute <" + r.first + ">"); + check(r.second.empty() || (r.second.find(attributesIt->second) != r.second.end()), line, "unexpected attribute value <" + attributesIt->second + "> in attribute <" + r.first + ">"); } // check if all not required attributes or optional, and if there is a set of allowed values, check if the actual value is part of that set for (auto const& a : attributes) @@ -196,7 +190,7 @@ void checkAttributes(std::map const& attributes, int l auto optionalIt = optional.find(a.first); if (optionalIt == optional.end()) { - std::cerr << "warning: " << "Unknown attribute " + a.first + " in line " + std::to_string(line) + "!" << std::endl; + warn(false, line, "unknown attribute <" + a.first + ">"); continue; } if (!optionalIt->second.empty()) @@ -204,45 +198,41 @@ void checkAttributes(std::map const& attributes, int l std::vector values = tokenize(a.second, ','); for (auto const& v : values) { - if (optionalIt->second.find(v) == optionalIt->second.end()) - { - throw std::runtime_error("Spec error on line " + std::to_string(line) + ": unexpected attribute value <" + v + "> in attribute <" + a.first + ">"); - } + check(optionalIt->second.find(v) != optionalIt->second.end(), line, "unexpected attribute value <" + v + "> in attribute <" + a.first + ">"); } } } } } -void checkElements(std::vector const& elements, std::set const& values) +void checkElements(int line, std::vector const& elements, std::set const& required, std::set const& optional) { - for (auto e : elements) + std::set encountered; + for (auto const& e : elements) { - if (values.find(e->Value()) == values.end()) - { - std::cerr << "warning: Unknown element in spec on line: " << std::to_string(e->GetLineNum()) << " " << e->Value() << "!" << std::endl; - } + std::string value = e->Value(); + encountered.insert(value); + check((required.find(value) != required.end()) || (optional.find(value) != optional.end()), e->GetLineNum(), "unknown element <" + value + ">"); + } + for (auto const& r : required) + { + check(encountered.find(r) != encountered.end(), line, "missing required element <" + r + ">"); } } void checkEmptyElement(tinyxml2::XMLElement const* element) { - checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); - checkElements(getChildElements(element), {}); + int line = element->GetLineNum(); + checkAttributes(getAttributes(element), line, {}, {}); + checkElements(line, getChildElements(element), {}); } void checkOrderedElements(std::vector const& elements, std::vector const& values) { for (size_t i = 0; i < elements.size(); i++) { - if (values.size() <= i) - { - throw std::runtime_error("Spec error on line " + std::to_string(elements[i]->GetLineNum()) + ": unexpected surplus element <" + elements[i]->Value() + ">"); - } - if (values[i] != elements[i]->Value()) - { - throw std::runtime_error("Spec error on line " + std::to_string(elements[i]->GetLineNum()) + ": unexpected element <" + elements[i]->Value() + ">, expected <" + values[i] + ">"); - } + check(i < values.size(), elements[i]->GetLineNum(), std::string("unexpected surplus element <") + elements[i]->Value() + ">"); + check(values[i] == elements[i]->Value(), elements[i]->GetLineNum(), std::string("unexpected element <") + elements[i]->Value() + ">, expected <" + values[i] + ">"); } } @@ -691,6 +681,14 @@ std::string trimEnd(std::string const& input) return result; } +void warn(bool condition, int line, std::string const& message) +{ + if (!condition) + { + std::cerr << "Spec warning on line " << std::to_string(line) << " " << message << "!" << std::endl; + } +} + VulkanHppGenerator::VulkanHppGenerator() { m_handles.insert(std::make_pair("", HandleData())); // insert the default "handle" without class (for createInstance, and such) @@ -842,7 +840,7 @@ void VulkanHppGenerator::appendBaseTypes(std::string & str) const { if (baseType.first != "VkFlags") // filter out VkFlags { - str += " using " + stripPrefix(baseType.first, "Vk") + " = " + baseType.second + ";\n"; + str += " using " + stripPrefix(baseType.first, "Vk") + " = " + baseType.second.type + ";\n"; } } } @@ -853,10 +851,7 @@ void VulkanHppGenerator::appendBitmasks(std::string & str) const { auto bitmaskBits = m_enums.find(bitmask.second.requirement); bool hasBits = (bitmaskBits != m_enums.end()); - if (!bitmask.second.requirement.empty() && !hasBits) - { - throw std::runtime_error(bitmask.first + " references the undefined requirement '" + bitmask.second.requirement + "'"); - } + check(bitmask.second.requirement.empty() || hasBits, bitmask.second.xmlLine, "bitmask <" + bitmask.first + "> references the undefined requirement <" + bitmask.second.requirement + ">"); std::string strippedBitmaskName = stripPrefix(bitmask.first, "Vk"); std::string strippedEnumName = hasBits ? stripPrefix(bitmaskBits->first, "Vk") : ""; @@ -3008,17 +3003,15 @@ void VulkanHppGenerator::appendStructureChainValidation(std::string & str) if (itExtend == m_structures.end()) { std::string errorString; - errorString = extendName + " does not specify a struct in structextends field."; + errorString = "<" + extendName + "> does not specify a struct in structextends field."; // check if symbol name is an alias to a struct auto itAlias = std::find_if(m_structures.begin(), m_structures.end(), [&extendName](std::pair const &it) -> bool {return std::find(it.second.aliases.begin(), it.second.aliases.end(), extendName) != it.second.aliases.end(); }); if (itAlias != m_structures.end()) { - errorString += " The symbol is an alias and maps to " + itAlias->first + "."; + errorString += " The symbol is an alias and maps to <" + itAlias->first + ">."; } - - errorString += "\n"; - throw std::runtime_error(errorString); + check(true, structure.second.xmlLine, errorString); } if (structure.second.platform != itExtend->second.platform) { @@ -3241,15 +3234,17 @@ bool VulkanHppGenerator::containsUnion(std::string const& type) const void VulkanHppGenerator::checkCorrectness() { + check(!m_vulkanLicenseHeader.empty(), -1, "missing license header"); + for (auto const& baseType : m_baseTypes) + { + check(m_types.find(baseType.second.type) != m_types.end(), baseType.second.xmlLine, "basetype type <" + baseType.second.type + "> not specified"); + } for (auto const& handle : m_handles) { for (auto const& command: handle.second.commands) { // check that functions returning a VkResult specify successcodes - if ((command.second.returnType == "VkResult") && command.second.successCodes.empty()) - { - throw std::runtime_error("Spec error on command Vk" + startUpperCase(command.first) + " : missing successcodes on command returning VkResult!"); - } + check((command.second.returnType != "VkResult") || !command.second.successCodes.empty(), command.second.xmlLine, "missing successcodes on command <" + command.first + "> returning VkResult!"); } } } @@ -3456,7 +3451,7 @@ void VulkanHppGenerator::linkCommandToHandle(std::string const& name, CommandDat // put the command into the handle's list of commands assert(handleIt->second.commands.find(name) == handleIt->second.commands.end()); - handleIt->second.commands[name] = commandData; + handleIt->second.commands.insert(std::make_pair(name, commandData)); // and store the handle in the command-to-handle map assert(m_commandToHandle.find(name) == m_commandToHandle.end()); @@ -3465,20 +3460,29 @@ void VulkanHppGenerator::linkCommandToHandle(std::string const& name, CommandDat void VulkanHppGenerator::readBaseType(tinyxml2::XMLElement const* element, std::map const& attributes) { - checkAttributes(attributes, element->GetLineNum(), { { "category",{ "basetype" } } }, {}); + int line = element->GetLineNum(); + checkAttributes(attributes, line, { { "category",{ "basetype" } } }, {}); std::vector children = getChildElements(element); - checkOrderedElements(children, { "type", "name" }); - checkEmptyElement(children[0]); - checkEmptyElement(children[1]); + checkElements(line, children, { "name", "type" }); - std::string type = children[0]->GetText(); - std::string name = children[1]->GetText(); -#if !defined(NDEBUG) - assert((type == "uint32_t") || (type == "uint64_t")); -#endif - - assert(m_baseTypes.find(name) == m_baseTypes.end()); - m_baseTypes[name] = type; + std::string name, type; + for (auto child : children) + { + std::string value = child->Value(); + if (value == "name") + { + name = child->GetText(); + check(!name.empty(), child->GetLineNum(), "basetype name is empty"); + } + else + { + assert(value == "type"); + type = child->GetText(); + check(!type.empty(), child->GetLineNum(), "basetype type is empty"); + } + } + assert(!name.empty() && !type.empty()); + check(m_baseTypes.insert(std::make_pair(name, BaseTypeData(type, element->GetLineNum()))).second, line, "basetype <" + name + "> already specified"); } void VulkanHppGenerator::readBitmask(tinyxml2::XMLElement const* element, std::map const& attributes) @@ -3508,16 +3512,16 @@ void VulkanHppGenerator::readBitmask(tinyxml2::XMLElement const* element, std::m requirement = requiresIt->second; } - m_bitmasks.insert(std::make_pair(name, BitmaskData(requirement))); + m_bitmasks.insert(std::make_pair(name, BitmaskData(requirement, element->GetLineNum()))); } } void VulkanHppGenerator::readBitmaskAlias(int lineNum, std::string const& alias, std::map const& attributes, std::vector const& children) { checkAttributes(attributes, lineNum, { { "alias",{} },{ "category",{ "bitmask" } },{ "name",{} } }, {}); // re-check on alias type! - checkElements(children, {}); + checkElements(lineNum, children, {}); - checkAlias(m_bitmasks, alias, lineNum); + check(m_bitmasks.find(alias) != m_bitmasks.end(), lineNum, "missing alias <" + alias + ">."); auto bitmasksIt = m_bitmasks.find(alias); assert((bitmasksIt != m_bitmasks.end()) && bitmasksIt->second.alias.empty()); @@ -3542,7 +3546,7 @@ void VulkanHppGenerator::readCommand(tinyxml2::XMLElement const* element) std::vector children = getChildElements(element); std::string name; - CommandData commandData; + CommandData commandData(element->GetLineNum()); auto aliasIt = attributes.find("alias"); if (aliasIt != attributes.end()) { @@ -3550,7 +3554,7 @@ void VulkanHppGenerator::readCommand(tinyxml2::XMLElement const* element) } else { - checkElements(children, { "implicitexternsyncparams", "param", "proto" }); + checkElements(element->GetLineNum(), children, { "param", "proto" }, { "implicitexternsyncparams" }); commandData.successCodes = readCommandSuccessCodes(attributes); @@ -3576,10 +3580,10 @@ void VulkanHppGenerator::readCommandAlias(int lineNum, std::string const& alias, { // for command aliases, create a copy of the aliased command checkAttributes(attributes, lineNum, { { "alias",{} },{ "name",{} } }, {}); // re-check on alias type! - checkElements(children, {}); + checkElements(lineNum, children, {}); std::string name = attributes.find("name")->second; - checkAlias(m_commandToHandle, alias, lineNum); + check(m_commandToHandle.find(alias) != m_commandToHandle.end(), lineNum, "missing alias <" + alias + ">."); auto handleIt = m_handles.find(m_commandToHandle.find(alias)->second); assert(handleIt != m_handles.end()); auto commandsIt = handleIt->second.commands.find(alias); @@ -3596,7 +3600,7 @@ VulkanHppGenerator::ParamData VulkanHppGenerator::readCommandParam(tinyxml2::XML std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "externsync",{} },{ "len",{} },{ "noautovalidity",{ "true" } },{ "optional",{ "false", "true" } } }); std::vector children = getChildElements(element); - checkElements(children, { "name", "type" }); + checkElements(element->GetLineNum(), children, { "name", "type" }); ParamData paramData; for (auto attribute : attributes) @@ -3652,7 +3656,7 @@ void VulkanHppGenerator::readCommands(tinyxml2::XMLElement const* element) std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } }); std::vector children = getChildElements(element); - checkElements(children, { "command" }); + checkElements(element->GetLineNum(), children, { "command" }); for (auto child : children) { @@ -3680,14 +3684,15 @@ std::vector VulkanHppGenerator::readCommandSuccessCodes(std::mapGetLineNum(), {}, {}); - checkElements(getChildElements(element), {}); + int line = element->GetLineNum(); + checkAttributes(getAttributes(element), line, {}, {}); + checkElements(line, getChildElements(element), {}); assert(element->GetText()); std::string text = element->GetText(); if (text.find("\nCopyright") == 0) { - assert(m_vulkanLicenseHeader.empty()); + check(m_vulkanLicenseHeader.empty(), line, "second encounter of a Copyright comment"); m_vulkanLicenseHeader = text; // replace any '\n' with "\n// " @@ -3704,39 +3709,43 @@ void VulkanHppGenerator::readComment(tinyxml2::XMLElement const* element) void VulkanHppGenerator::readDefine(tinyxml2::XMLElement const* element, std::map const& attributes) { - checkAttributes(attributes, element->GetLineNum(), { { "category",{ "define" } } }, { { "name",{} } }); + int line = element->GetLineNum(); + checkAttributes(attributes, line, { { "category",{ "define" } } }, { { "name",{} } }); auto nameIt = attributes.find("name"); if (nameIt != attributes.end()) { - assert(!element->FirstChildElement()); - assert(nameIt->second == "VK_DEFINE_NON_DISPATCHABLE_HANDLE"); + check(!element->FirstChildElement(), line, "unknown formatting of type category=define name <" + nameIt->second + ">"); + check(nameIt->second == "VK_DEFINE_NON_DISPATCHABLE_HANDLE", line, "unknown type category=define name <" + nameIt->second + ">"); + check(element->LastChild() && element->LastChild()->ToText() && element->LastChild()->ToText()->Value(), line, "unknown formatting of type category=define named <" + nameIt->second + ">"); // filter out the check for the different types of VK_DEFINE_NON_DISPATCHABLE_HANDLE std::string text = element->LastChild()->ToText()->Value(); size_t start = text.find("#if defined(__LP64__)"); + check(start != std::string::npos, line, "unexpected text in type category=define named <" + nameIt->second + ">"); size_t end = text.find_first_of("\r\n", start + 1); + check(end != std::string::npos, line, "unexpected text in type category=define named <" + nameIt->second + ">"); m_typesafeCheck = text.substr(start, end - start); } else if (element->GetText() && (trim(element->GetText()) == "struct")) { -#if !defined(NDEBUG) + // here are a couple of structs as defines, which really are types! tinyxml2::XMLElement const* child = element->FirstChildElement(); - assert(child && (strcmp(child->Value(), "name") == 0) && child->GetText()); - m_defines.insert(child->GetText()); -#endif + check(child && (strcmp(child->Value(), "name") == 0) && child->GetText(), line, "unexpected formatting of type category=define"); + std::string text = child->GetText(); + check(m_types.insert(text).second, line, "type defined has already been speficied"); } else { tinyxml2::XMLElement const* child = element->FirstChildElement(); - assert(child && !child->FirstAttribute() && (strcmp(child->Value(), "name") == 0) && child->GetText()); + check(child && !child->FirstAttribute() && (strcmp(child->Value(), "name") == 0) && child->GetText(), line, "unknown formatting of type category define"); std::string text = trim(child->GetText()); if (text == "VK_HEADER_VERSION") { m_version = trimEnd(element->LastChild()->ToText()->Value()); } // ignore all the other defines - assert(!child->NextSiblingElement() || (child->NextSiblingElement() && !child->NextSiblingElement()->FirstAttribute() && (strcmp(child->NextSiblingElement()->Value(), "type") == 0) && !child->NextSiblingElement()->NextSiblingElement())); + warn(!child->NextSiblingElement() || (child->NextSiblingElement() && !child->NextSiblingElement()->FirstAttribute() && (strcmp(child->NextSiblingElement()->Value(), "type") == 0) && !child->NextSiblingElement()->NextSiblingElement()), line, "unknown formatting of type category define"); } } @@ -3745,7 +3754,7 @@ void VulkanHppGenerator::readEnum(tinyxml2::XMLElement const* element, EnumData std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "alias",{} },{ "bitpos",{} },{ "comment",{} },{ "value",{} } }); assert((attributes.find("alias") != attributes.end()) + (attributes.find("bitpos") != attributes.end()) + (attributes.find("value") != attributes.end()) == 1); - checkElements(getChildElements(element), {}); + checkElements(element->GetLineNum(), getChildElements(element), {}); std::string name = attributes.find("name")->second; std::string tag = findTag(m_tags, name, postfix); @@ -3768,7 +3777,7 @@ void VulkanHppGenerator::readEnums(tinyxml2::XMLElement const* element) std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "comment",{} },{ "type",{ "bitmask", "enum" } } }); std::vector children = getChildElements(element); - checkElements(children, { "comment", "enum", "unused" }); + checkElements(element->GetLineNum(), children, {}, { "comment", "enum", "unused" }); std::string name = attributes.find("name")->second; @@ -3834,7 +3843,7 @@ void VulkanHppGenerator::readExtension(tinyxml2::XMLElement const* element) { "type",{ "device", "instance" } } }); std::vector children = getChildElements(element); - checkElements(children, { "require" }); + checkElements(element->GetLineNum(), children, {}, { "require" }); if (attributes.find("supported")->second == "disabled") { @@ -3873,11 +3882,11 @@ void VulkanHppGenerator::readExtensionDisabledRequire(tinyxml2::XMLElement const { checkAttributes(getAttributes(element), element->GetLineNum(), {}, {}); std::vector children = getChildElements(element); - checkElements(children, { "command", "comment", "enum", "type" }); + checkElements(element->GetLineNum(), children, { "enum" }, { "command", "comment", "type" }); for (auto child : children) { - checkElements(getChildElements(child), {}); + checkElements(child->GetLineNum(), getChildElements(child), {}); std::string value = child->Value(); if ((value == "command") || (value == "type")) @@ -3926,7 +3935,7 @@ void VulkanHppGenerator::readExtensionRequire(tinyxml2::XMLElement const* elemen std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "extension",{} },{ "feature",{} } }); std::vector children = getChildElements(element); - checkElements(children, { "command", "comment", "enum", "type" }); + checkElements(element->GetLineNum(), children, {}, { "command", "comment", "enum", "type" }); for (auto child : children) { @@ -3958,7 +3967,7 @@ void VulkanHppGenerator::readExtensionRequireCommand(tinyxml2::XMLElement const* { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); - checkElements(getChildElements(element), {}); + checkElements(element->GetLineNum(), getChildElements(element), {}); // just add the protect string to the CommandData if (!platform.empty()) @@ -3978,7 +3987,7 @@ void VulkanHppGenerator::readExtensionRequireType(tinyxml2::XMLElement const* el { std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); - checkElements(getChildElements(element), {}); + checkElements(element->GetLineNum(), getChildElements(element), {}); // add the protect-string to the appropriate type: enum, flag, handle, scalar, or struct std::string name = attributes.find("name")->second; @@ -3990,10 +3999,8 @@ void VulkanHppGenerator::readExtensionRequireType(tinyxml2::XMLElement const* el auto objectTypeIt = m_enums.find("VkObjectType"); assert(objectTypeIt != m_enums.end()); std::string objectTypeName = "e" + stripPrefix(handleIt->first, "Vk"); - if (std::find_if(objectTypeIt->second.values.begin(), objectTypeIt->second.values.end(), [objectTypeName](EnumValueData const& evd) {return evd.vkValue == objectTypeName; }) == objectTypeIt->second.values.end()) - { - throw std::runtime_error("Spec error on line " + std::to_string(element->GetLineNum()) + ": missing entry in VkObjectType enum for handle <" + name + ">."); - } + auto valueIt = std::find_if(objectTypeIt->second.values.begin(), objectTypeIt->second.values.end(), [objectTypeName](EnumValueData const& evd) {return evd.vkValue == objectTypeName; }); + check(valueIt != objectTypeIt->second.values.end(), element->GetLineNum(), "missing entry in VkObjectType enum for handle <" + name + ">."); } if (!platform.empty()) @@ -4024,7 +4031,7 @@ void VulkanHppGenerator::readExtensionRequireType(tinyxml2::XMLElement const* el } else { - assert((m_defines.find(name) != m_defines.end())); + assert((m_types.find(name) != m_types.end())); } } } @@ -4036,7 +4043,7 @@ void VulkanHppGenerator::readExtensions(tinyxml2::XMLElement const* element) std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); - checkElements(children, { "extension" }); + checkElements(element->GetLineNum(), children, { "extension" }); for (auto child : children) { @@ -4049,7 +4056,7 @@ void VulkanHppGenerator::readFeature(tinyxml2::XMLElement const* element) std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), { { "api",{ "vulkan" } },{ "comment",{} },{ "name",{} },{ "number",{} } }, {}); std::vector children = getChildElements(element); - checkElements(children, { "require" }); + checkElements(element->GetLineNum(), children, { "require" }); for (auto child : children) { @@ -4062,7 +4069,7 @@ void VulkanHppGenerator::readFeatureRequire(tinyxml2::XMLElement const* element) std::map attributes = getAttributes(element); checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } }); std::vector children = getChildElements(element); - checkElements(children, { "command", "comment", "enum", "type" }); + checkElements(element->GetLineNum(), children, {}, { "command", "comment", "enum", "type" }); for (auto child : children) { @@ -4078,7 +4085,7 @@ void VulkanHppGenerator::readFuncpointer(tinyxml2::XMLElement const* element, st { checkAttributes(attributes, element->GetLineNum(), { { "category",{ "funcpointer" } } }, { { "requires",{} } }); std::vector children = getChildElements(element); - checkElements(children, { "name", "type" }); + checkElements(element->GetLineNum(), children, { "name" }, { "type" }); assert(!children.empty()); checkEmptyElement(children[0]); @@ -4100,8 +4107,8 @@ void VulkanHppGenerator::readHandle(tinyxml2::XMLElement const* element, std::ma if (aliasIt != attributes.end()) { checkAttributes(attributes, element->GetLineNum(), { { "alias",{} },{ "category",{ "handle" } },{ "name",{} } }, {}); // re-check on alias type! - checkElements(children, {}); - checkAlias(m_handles, aliasIt->second, element->GetLineNum()); + checkElements(element->GetLineNum(), children, {}); + check(m_handles.find(aliasIt->second) != m_handles.end(), element->GetLineNum(), "missing alias <" + aliasIt->second + ">."); auto handlesIt = m_handles.find(aliasIt->second); assert((handlesIt != m_handles.end()) && handlesIt->second.alias.empty()); @@ -4127,9 +4134,10 @@ void VulkanHppGenerator::readHandle(tinyxml2::XMLElement const* element, std::ma void VulkanHppGenerator::readPlatform(tinyxml2::XMLElement const* element) { + int line = element->GetLineNum(); std::map attributes = getAttributes(element); - checkAttributes(attributes, element->GetLineNum(), { { "comment",{} },{ "name",{} },{ "protect",{} } }, {}); - checkElements(getChildElements(element), {}); + checkAttributes(attributes, line, { { "comment",{} },{ "name",{} },{ "protect",{} } }, {}); + checkElements(line, getChildElements(element), {}); std::string name, protect; for (auto const& attribute : attributes) @@ -4137,18 +4145,17 @@ void VulkanHppGenerator::readPlatform(tinyxml2::XMLElement const* element) if (attribute.first == "name") { name = attribute.second; + check(!name.empty(), line, "attribute is empty"); } else if (attribute.first == "protect") { protect = attribute.second; - } - else - { - assert(attribute.first == "comment"); + check(!protect.empty(), line, "attribute is empty"); } } assert(!name.empty() && !protect.empty()); - assert(m_platforms.find(name) == m_platforms.end()); + check(m_platforms.find(name) == m_platforms.end(), line, "platform name <" + name + "> already specified"); + check(std::find_if(m_platforms.begin(), m_platforms.end(), [&protect](std::pair const& p) { return p.second == protect; }) == m_platforms.end(), line, "platform protect <" + protect + "> already specified"); m_platforms[name] = protect; } @@ -4156,11 +4163,7 @@ void VulkanHppGenerator::readPlatforms(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); - checkElements(children, { "platform" }); - - // init m_platforms with "" -> "" - assert(m_platforms.empty()); - m_platforms[""] = ""; + checkElements(element->GetLineNum(), children, { "platform" }); for (auto child : children) { @@ -4185,7 +4188,7 @@ void VulkanHppGenerator::readRequireEnum(tinyxml2::XMLElement const* element, st { "offset",{} }, { "value",{} } }); - checkElements(getChildElements(element), {}); + checkElements(element->GetLineNum(), getChildElements(element), {}); // TODO process enums which don't extend existing enums auto extendsIt = attributes.find("extends"); @@ -4231,16 +4234,25 @@ void VulkanHppGenerator::readRequireEnum(tinyxml2::XMLElement const* element, st } } -#if !defined(NDEBUG) void VulkanHppGenerator::readRequires(tinyxml2::XMLElement const* element, std::map const& attributes) { - checkAttributes(attributes, element->GetLineNum(), { {"name", {}}, { "requires", {}} }, {}); - checkElements(getChildElements(element), {}); + int line = element->GetLineNum(); + checkAttributes(attributes, line, { {"name", {}}, { "requires", {}} }, {}); + checkElements(line, getChildElements(element), {}); - auto nameIt = attributes.find("name"); - assert(nameIt != attributes.end()); + for (auto attribute : attributes) + { + if (attribute.first == "name") + { + check(m_types.insert(attribute.second).second, line, "type named <" + attribute.second + "> already specified"); + } + else + { + assert(attribute.first == "requires"); + check(m_includes.find(attribute.second) != m_includes.end(), line, "type requires unknown include <" + attribute.second + ">"); + } + } } -#endif void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map const& attributes) { @@ -4256,7 +4268,7 @@ void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool is { "structextends",{} } }); std::vector children = getChildElements(element); - checkElements(children, { "comment", "member" }); + checkElements(element->GetLineNum(), children, {}, { "member", "comment" }); std::string name = attributes.find("name")->second; @@ -4268,7 +4280,7 @@ void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool is else { assert(m_structures.find(name) == m_structures.end()); - std::map::iterator it = m_structures.insert(std::make_pair(name, StructureData())).first; + std::map::iterator it = m_structures.insert(std::make_pair(name, StructureData(element->GetLineNum()))).first; it->second.returnedOnly = (attributes.find("returnedonly") != attributes.end()); it->second.isUnion = isUnion; @@ -4281,7 +4293,7 @@ void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool is void VulkanHppGenerator::readStructAlias(int lineNum, std::string const& name, std::string const& alias, std::map const& attributes) { checkAttributes(attributes, lineNum, { { "alias",{} },{ "category",{ "struct" } },{ "name",{} } }, {}); // re-check on alias type! - checkAlias(m_structures, alias, lineNum); + check(m_structures.find(alias) != m_structures.end(), lineNum, "missing alias <" + alias + ">."); auto structsIt = m_structures.find(alias); assert((structsIt != m_structures.end()) && (std::find(structsIt->second.aliases.begin(), structsIt->second.aliases.end(), name) == structsIt->second.aliases.end())); @@ -4304,7 +4316,7 @@ VulkanHppGenerator::MemberData VulkanHppGenerator::readStructMember(tinyxml2::XM { "values",{} } }); std::vector children = getChildElements(element); - checkElements(children, { "comment", "enum", "name", "type" }); + checkElements(element->GetLineNum(), children, { "name", "type" }, { "comment", "enum" }); MemberData memberData; auto valuesIt = attributes.find("values"); @@ -4367,7 +4379,7 @@ void VulkanHppGenerator::readTags(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); - checkElements(children, { "tag" }); + checkElements(element->GetLineNum(), children, { "tag" }); for (auto child : children) { @@ -4377,28 +4389,28 @@ void VulkanHppGenerator::readTags(tinyxml2::XMLElement const* element) void VulkanHppGenerator::readTag(tinyxml2::XMLElement const* element) { + int line = element->GetLineNum(); std::map attributes = getAttributes(element); - checkAttributes(attributes, element->GetLineNum(), { { "author",{} },{ "contact",{} },{ "name",{} } }, {}); - checkElements(getChildElements(element), {}); + checkAttributes(attributes, line, { { "author",{} },{ "contact",{} },{ "name",{} } }, {}); + checkElements(line, getChildElements(element), {}); for (auto const& attribute : attributes) { - std::string name = attribute.first; - if (name == "name") + if (attribute.first == "name") { - std::string value = attribute.second; - assert(m_tags.find(value) == m_tags.end()); - m_tags.insert(value); + check(m_tags.find(attribute.second) == m_tags.end(), line, "tag named <" + attribute.second + "> has already been specified"); + m_tags.insert(attribute.second); } else { - assert((name == "author") || (name == "contact")); + check((attribute.first == "author") || (attribute.first == "contact"), line, "unknown attribute <" + attribute.first + ">"); } } } void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element) { + int line = element->GetLineNum(); std::map attributes = getAttributes(element); auto categoryIt = attributes.find("category"); @@ -4428,20 +4440,20 @@ void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element) { readHandle(element, attributes); } + else if (categoryIt->second == "include") + { + readTypeInclude(element, attributes); + } else if (categoryIt->second == "struct") { readStruct(element, false, attributes); } - else if (categoryIt->second == "union") + else { + check(categoryIt->second == "union", element->GetLineNum(), "unknown type category <" + categoryIt->second + ">"); readStruct(element, true, attributes); } - else if ( categoryIt->second != "include") - { - throw std::runtime_error("Spec error on line " + std::to_string(element->GetLineNum()) + ": unknown category <" + categoryIt->second + ">"); - } } -#if !defined(NDEBUG) else { auto requiresIt = attributes.find("requires"); @@ -4451,10 +4463,10 @@ void VulkanHppGenerator::readType(tinyxml2::XMLElement const* element) } else { - assert((attributes.size() == 1) && (attributes.begin()->first == "name") && (attributes.begin()->second == "int")); + check((attributes.size() == 1) && (attributes.begin()->first == "name") && (attributes.begin()->second == "int"), line, "unknown type"); + check(m_types.insert(attributes.begin()->second).second, line, "type <" + attributes.begin()->second + "> already specified"); } } -#endif } void VulkanHppGenerator::readTypeEnum(tinyxml2::XMLElement const* element, std::map const& attributes) @@ -4478,11 +4490,20 @@ void VulkanHppGenerator::readTypeEnum(tinyxml2::XMLElement const* element, std:: } } +void VulkanHppGenerator::readTypeInclude(tinyxml2::XMLElement const* element, std::map const& attributes) +{ + checkAttributes(attributes, element->GetLineNum(), { { "category", { "include" } }, { "name",{} } }, {}); + checkElements(element->GetLineNum(), getChildElements(element), {}); + + std::string name = attributes.find("name")->second; + check(m_includes.insert(name).second, element->GetLineNum(), "include named <" + name + "> already specified"); +} + void VulkanHppGenerator::readTypes(tinyxml2::XMLElement const* element) { checkAttributes(getAttributes(element), element->GetLineNum(), { { "comment",{} } }, {}); std::vector children = getChildElements(element); - checkElements(children, { "comment", "type" }); + checkElements(element->GetLineNum(), children, { "comment", "type" }); for (auto child : children) { @@ -4491,13 +4512,10 @@ void VulkanHppGenerator::readTypes(tinyxml2::XMLElement const* element) { readType(child); } -#if !defined(NDEBUG) else { - assert(value == "comment"); - checkEmptyElement(child); + readComment(child); } -#endif } } @@ -4550,6 +4568,34 @@ std::string VulkanHppGenerator::TypeData::compose() const return prefix + (prefix.empty() ? "" : " ") + ((type.substr(0, 2) == "Vk") ? "VULKAN_HPP_NAMESPACE::" : "") + stripPrefix(type, "Vk") + postfix; } +std::string to_string(tinyxml2::XMLError error) +{ + switch (error) + { + case tinyxml2::XML_SUCCESS : return "XML_SUCCESS"; + case tinyxml2::XML_NO_ATTRIBUTE : return "XML_NO_ATTRIBUTE"; + case tinyxml2::XML_WRONG_ATTRIBUTE_TYPE : return "XML_WRONG_ATTRIBUTE_TYPE"; + case tinyxml2::XML_ERROR_FILE_NOT_FOUND : return "XML_ERROR_FILE_NOT_FOUND"; + case tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED : return "XML_ERROR_FILE_COULD_NOT_BE_OPENED"; + case tinyxml2::XML_ERROR_FILE_READ_ERROR : return "XML_ERROR_FILE_READ_ERROR"; + case tinyxml2::UNUSED_XML_ERROR_ELEMENT_MISMATCH : return "UNUSED_XML_ERROR_ELEMENT_MISMATCH"; + case tinyxml2::XML_ERROR_PARSING_ELEMENT : return "XML_ERROR_PARSING_ELEMENT"; + case tinyxml2::XML_ERROR_PARSING_ATTRIBUTE : return "XML_ERROR_PARSING_ATTRIBUTE"; + case tinyxml2::UNUSED_XML_ERROR_IDENTIFYING_TAG : return "UNUSED_XML_ERROR_IDENTIFYING_TAG"; + case tinyxml2::XML_ERROR_PARSING_TEXT : return "XML_ERROR_PARSING_TEXT"; + case tinyxml2::XML_ERROR_PARSING_CDATA : return "XML_ERROR_PARSING_CDATA"; + case tinyxml2::XML_ERROR_PARSING_COMMENT : return "XML_ERROR_PARSING_COMMENT"; + case tinyxml2::XML_ERROR_PARSING_DECLARATION : return "XML_ERROR_PARSING_DECLARATION"; + case tinyxml2::XML_ERROR_PARSING_UNKNOWN : return "XML_ERROR_PARSING_UNKNOWN"; + case tinyxml2::XML_ERROR_EMPTY_DOCUMENT : return "XML_ERROR_EMPTY_DOCUMENT"; + case tinyxml2::XML_ERROR_MISMATCHED_ELEMENT : return "XML_ERROR_MISMATCHED_ELEMENT"; + case tinyxml2::XML_ERROR_PARSING : return "XML_ERROR_PARSING"; + case tinyxml2::XML_CAN_NOT_CONVERT_TEXT : return "XML_CAN_NOT_CONVERT_TEXT"; + case tinyxml2::XML_NO_TEXT_NODE : return "XML_NO_TEXT_NODE"; + default : return "unknown error code <" + std::to_string(error) + ">"; + } +} + int main(int argc, char **argv) { static const std::string classArrayProxy = R"( @@ -5674,19 +5720,24 @@ namespace std tinyxml2::XMLError error = doc.LoadFile(filename.c_str()); if (error != tinyxml2::XML_SUCCESS) { - std::cout << "VkGenerate: failed to load file " << filename << " . Error code: " << error << std::endl; + std::cout << "VkGenerate: failed to load file " << filename << " with error <" << to_string(error) << ">" << std::endl; return -1; } VulkanHppGenerator generator; - tinyxml2::XMLElement const* registryElement = doc.FirstChildElement(); + tinyxml2::XMLElement const* registryElement = nullptr; + for (tinyxml2::XMLElement const* element = doc.FirstChildElement(); element; element = doc.NextSiblingElement()) + { + std::string value = element->Value(); + check(value == "registry", element->GetLineNum(), "unknown top level element <" + value + ">"); + registryElement = element; + } + assert(registryElement); checkAttributes(getAttributes(registryElement), registryElement->GetLineNum(), {}, {}); - assert(strcmp(registryElement->Value(), "registry") == 0); - assert(!registryElement->NextSiblingElement()); std::vector children = getChildElements(registryElement); - checkElements(children, { "commands", "comment", "enums", "extensions", "feature", "tags", "types", "vendorids", "platforms" }); + checkElements(registryElement->GetLineNum(), children, { "commands", "comment", "enums", "extensions", "feature", "tags", "types", "platforms" }); for (auto child : children) { const std::string value = child->Value(); diff --git a/VulkanHppGenerator.hpp b/VulkanHppGenerator.hpp index d89e9e0..852303a 100644 --- a/VulkanHppGenerator.hpp +++ b/VulkanHppGenerator.hpp @@ -52,15 +52,28 @@ class VulkanHppGenerator void readTypes(tinyxml2::XMLElement const* element); private: + struct BaseTypeData + { + BaseTypeData(std::string const& type_, int line) + : type(type_) + , xmlLine(line) + {} + + std::string type; + int xmlLine; + }; + struct BitmaskData { - BitmaskData(std::string const& r) + BitmaskData(std::string const& r, int line) : requirement(r) + , xmlLine(line) {} std::string requirement; // original vulkan name: VK*FlagBits std::string platform; std::string alias; // original vulkan name + int xmlLine; }; struct TypeData @@ -92,8 +105,9 @@ class VulkanHppGenerator struct CommandData { - CommandData() + CommandData(int line) : isAlias(false) + , xmlLine(line) {} std::vector params; @@ -101,6 +115,7 @@ class VulkanHppGenerator std::string returnType; std::vector successCodes; bool isAlias; + int xmlLine; }; struct EnumValueData @@ -147,9 +162,10 @@ class VulkanHppGenerator struct StructureData { - StructureData() + StructureData(int line) : returnedOnly(false) , isUnion(false) + , xmlLine(line) {} bool returnedOnly; @@ -159,6 +175,7 @@ class VulkanHppGenerator std::vector structExtends; std::vector aliases; std::string subStruct; + int xmlLine; }; private: @@ -237,6 +254,7 @@ class VulkanHppGenerator void readHandle(tinyxml2::XMLElement const* element, std::map const& attributes); void readPlatform(tinyxml2::XMLElement const* element); void readRequireEnum(tinyxml2::XMLElement const* element, std::string const& tag); + void readRequires(tinyxml2::XMLElement const* element, std::map const& attributes); void readStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map const& attributes); void readStructAlias(int lineNum, std::string const& name, std::string const& alias, std::map const& attributes); MemberData readStructMember(tinyxml2::XMLElement const* element); @@ -244,29 +262,26 @@ class VulkanHppGenerator void readTag(tinyxml2::XMLElement const* element); void readType(tinyxml2::XMLElement const* element); void readTypeEnum(tinyxml2::XMLElement const* element, std::map const& attributes); + void readTypeInclude(tinyxml2::XMLElement const* element, std::map const& attributes); void registerDeleter(std::string const& name, std::pair const& commandData); void unlinkCommandFromHandle(std::string const& name); -#if !defined(NDEBUG) - void readRequires(tinyxml2::XMLElement const* element, std::map const& attributes); -#endif private: - std::map m_baseTypes; + std::map m_baseTypes; std::map m_bitmasks; std::map m_commandToHandle; std::map m_enums; std::set m_extendedStructs; // structs which are referenced by the structextends tag std::map m_handles; + std::set m_includes; std::map m_platforms; std::map m_structureAliases; std::map m_structures; std::set m_tags; + std::set m_types; std::string m_typesafeCheck; std::string m_version; std::string m_vulkanLicenseHeader; -#if !defined(NDEBUG) - std::set m_defines; // just used for verfication in readExtensionType -#endif }; const size_t INVALID_INDEX = (size_t)~0;