Introduce helper function check() to ease throwing on spec error detection. (#502)

* Introduce helper function check() to ease throwing on spec error detection.

* Use some more checks

* Some more checks
This commit is contained in:
Andreas Süßenbach 2020-02-04 10:35:33 +01:00 committed by GitHub
parent 4d53dac22b
commit cf39cd4f0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 228 additions and 162 deletions

View File

@ -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 <typename T> void checkAlias(std::map<std::string, T> const& data, std::string const& name, int line);
void check(bool condition, int line, std::string const& message);
void checkAttributes(std::map<std::string, std::string> const& attributes, int line, std::map<std::string, std::set<std::string>> const& required, std::map<std::string, std::set<std::string>> const& optional);
void checkElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& values);
void checkElements(int line, std::vector<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& required, std::set<std::string> const& optional = {});
void checkEmptyElement(tinyxml2::XMLElement const* element);
void checkOrderedElements(std::vector<tinyxml2::XMLElement const*> const& elements, std::vector<std::string> const& values);
void cleanup(std::stringstream & ss);
@ -62,6 +62,7 @@ std::string toUpperCase(std::string const& name);
std::vector<std::string> 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<std::string> 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 <typename T>
void checkAlias(std::map<std::string, T> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> const& attributes, int l
std::vector<std::string> 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<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& values)
void checkElements(int line, std::vector<tinyxml2::XMLElement const*> const& elements, std::set<std::string> const& required, std::set<std::string> const& optional)
{
for (auto e : elements)
std::set<std::string> 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<tinyxml2::XMLElement const*> const& elements, std::vector<std::string> 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<std::string, StructureData> 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<std::string, std::string> const& attributes)
{
checkAttributes(attributes, element->GetLineNum(), { { "category",{ "basetype" } } }, {});
int line = element->GetLineNum();
checkAttributes(attributes, line, { { "category",{ "basetype" } } }, {});
std::vector<tinyxml2::XMLElement const*> 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<std::string, std::string> 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<std::string, std::string> const& attributes, std::vector<tinyxml2::XMLElement const*> 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<tinyxml2::XMLElement const*> 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<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "externsync",{} },{ "len",{} },{ "noautovalidity",{ "true" } },{ "optional",{ "false", "true" } } });
std::vector<tinyxml2::XMLElement const*> 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<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } });
std::vector<tinyxml2::XMLElement const*> children = getChildElements(element);
checkElements(children, { "command" });
checkElements(element->GetLineNum(), children, { "command" });
for (auto child : children)
{
@ -3680,14 +3684,15 @@ std::vector<std::string> VulkanHppGenerator::readCommandSuccessCodes(std::map<st
void VulkanHppGenerator::readComment(tinyxml2::XMLElement const* element)
{
checkAttributes(getAttributes(element), element->GetLineNum(), {}, {});
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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, { { "comment",{} },{ "type",{ "bitmask", "enum" } } });
std::vector<tinyxml2::XMLElement const*> 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<tinyxml2::XMLElement const*> 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<tinyxml2::XMLElement const*> 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<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "extension",{} },{ "feature",{} } });
std::vector<tinyxml2::XMLElement const*> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "comment",{} } }, {});
std::vector<tinyxml2::XMLElement const*> 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<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), { { "api",{ "vulkan" } },{ "comment",{} },{ "name",{} },{ "number",{} } }, {});
std::vector<tinyxml2::XMLElement const*> 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<std::string, std::string> attributes = getAttributes(element);
checkAttributes(attributes, element->GetLineNum(), {}, { { "comment",{} } });
std::vector<tinyxml2::XMLElement const*> 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<tinyxml2::XMLElement const*> 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<std::string, std::string> 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 <name> is empty");
}
else if (attribute.first == "protect")
{
protect = attribute.second;
}
else
{
assert(attribute.first == "comment");
check(!protect.empty(), line, "attribute <protect> 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<std::string, std::string> 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<tinyxml2::XMLElement const*> 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<std::string, std::string> 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<std::string, std::string> const& attributes)
{
@ -4256,7 +4268,7 @@ void VulkanHppGenerator::readStruct(tinyxml2::XMLElement const* element, bool is
{ "structextends",{} }
});
std::vector<tinyxml2::XMLElement const*> 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<std::string, StructureData>::iterator it = m_structures.insert(std::make_pair(name, StructureData())).first;
std::map<std::string, StructureData>::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<std::string, std::string> 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<tinyxml2::XMLElement const*> 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<tinyxml2::XMLElement const*> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> const& attributes)
@ -4478,11 +4490,20 @@ void VulkanHppGenerator::readTypeEnum(tinyxml2::XMLElement const* element, std::
}
}
void VulkanHppGenerator::readTypeInclude(tinyxml2::XMLElement const* element, std::map<std::string, std::string> 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<tinyxml2::XMLElement const*> 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<tinyxml2::XMLElement const*> 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();

View File

@ -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<ParamData> params;
@ -101,6 +115,7 @@ class VulkanHppGenerator
std::string returnType;
std::vector<std::string> 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<std::string> structExtends;
std::vector<std::string> aliases;
std::string subStruct;
int xmlLine;
};
private:
@ -237,6 +254,7 @@ class VulkanHppGenerator
void readHandle(tinyxml2::XMLElement const* element, std::map<std::string, std::string> 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<std::string, std::string> const& attributes);
void readStruct(tinyxml2::XMLElement const* element, bool isUnion, std::map<std::string, std::string> const& attributes);
void readStructAlias(int lineNum, std::string const& name, std::string const& alias, std::map<std::string, std::string> 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<std::string, std::string> const& attributes);
void readTypeInclude(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes);
void registerDeleter(std::string const& name, std::pair<std::string, CommandData> const& commandData);
void unlinkCommandFromHandle(std::string const& name);
#if !defined(NDEBUG)
void readRequires(tinyxml2::XMLElement const* element, std::map<std::string, std::string> const& attributes);
#endif
private:
std::map<std::string, std::string> m_baseTypes;
std::map<std::string, BaseTypeData> m_baseTypes;
std::map<std::string, BitmaskData> m_bitmasks;
std::map<std::string, std::string> m_commandToHandle;
std::map<std::string, EnumData> m_enums;
std::set<std::string> m_extendedStructs; // structs which are referenced by the structextends tag
std::map<std::string, HandleData> m_handles;
std::set<std::string> m_includes;
std::map<std::string, std::string> m_platforms;
std::map<std::string, std::string> m_structureAliases;
std::map<std::string, StructureData> m_structures;
std::set<std::string> m_tags;
std::set<std::string> m_types;
std::string m_typesafeCheck;
std::string m_version;
std::string m_vulkanLicenseHeader;
#if !defined(NDEBUG)
std::set<std::string> m_defines; // just used for verfication in readExtensionType
#endif
};
const size_t INVALID_INDEX = (size_t)~0;