diff options
author | Stephen Kelly <steveire@gmail.com> | 2017-01-14 22:13:49 +0000 |
---|---|---|
committer | Stephen Kelly <steveire@gmail.com> | 2017-01-15 11:18:50 +0000 |
commit | 636d6acc7adf8bf7169d38833340161dc42d3484 (patch) | |
tree | 2ff1bbefa5808aec21e902b10c9547e359067604 /find-modules | |
parent | 8c347c61abafa68e247ff4664ae658cfa15af932 (diff) | |
download | extra-cmake-modules-636d6acc7adf8bf7169d38833340161dc42d3484.tar.gz extra-cmake-modules-636d6acc7adf8bf7169d38833340161dc42d3484.tar.bz2 |
Bindings: Fix handling of forward declarations
It is not appropriate to decorate each forward declaration with the SIP
attribute /External/. That is only needed for forward declarations of
types which are defined in a different module.
Local forward declarations can be omitted from the sip code. In sip
code, a forward declaration followed later by a full class definition is
an error.
Omit forward declarations unless they are decorated with the external
attribute. Introduce a rules database for consumers to decorate them
with the attribute as required.
Diffstat (limited to 'find-modules')
-rw-r--r-- | find-modules/Qt5Ruleset.py | 7 | ||||
-rwxr-xr-x | find-modules/rules_engine.py | 77 | ||||
-rw-r--r-- | find-modules/sip_generator.py | 82 |
3 files changed, 131 insertions, 35 deletions
diff --git a/find-modules/Qt5Ruleset.py b/find-modules/Qt5Ruleset.py index 9392ae18..2d8cfaac 100644 --- a/find-modules/Qt5Ruleset.py +++ b/find-modules/Qt5Ruleset.py @@ -107,6 +107,9 @@ def variable_rules(): def typedef_rules(): return [] +def forward_declaration_rules(): + return [] + class RuleSet(rules_engine.RuleSet): """ SIP file generator rules. This is a set of (short, non-public) functions @@ -114,6 +117,7 @@ class RuleSet(rules_engine.RuleSet): """ def __init__(self): self._container_db = rules_engine.ContainerRuleDb(container_rules) + self._forward_declaration_db = rules_engine.ForwardDeclarationRuleDb(forward_declaration_rules) self._fn_db = rules_engine.FunctionRuleDb(function_rules) self._param_db = rules_engine.ParameterRuleDb(parameter_rules) self._typedef_db = rules_engine.TypedefRuleDb(typedef_rules) @@ -124,6 +128,9 @@ class RuleSet(rules_engine.RuleSet): def container_rules(self): return self._container_db + def forward_declaration_rules(self): + return self._forward_declaration_db + def function_rules(self): return self._fn_db diff --git a/find-modules/rules_engine.py b/find-modules/rules_engine.py index 34bd15dc..897d0433 100755 --- a/find-modules/rules_engine.py +++ b/find-modules/rules_engine.py @@ -235,6 +235,71 @@ class ContainerRuleDb(AbstractCompiledRuleDb): return None +class ForwardDeclarationRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR FORWARD DECLARATIONS. + + These are used to customise the behaviour of the SIP generator by allowing + the forward declaration for any container (class, struct, union) to be + customised, for example to add SIP compiler annotations. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the container. + + 1. A regular expression which matches the container name. + + 2. A regular expression which matches any template parameters. + + 3. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def declaration_xxx(container, sip, matcher): + ''' + Return a modified declaration for the given container. + + :param container: The clang.cindex.Cursor for the container. + :param sip: A dict with the following keys: + + name The name of the container. + template_parameters Any template parameters. + annotations Any SIP annotations. + + :param matcher: The re.Match object. This contains named + groups corresponding to the key names above + EXCEPT body and annotations. + + :return: An updated set of sip.xxx values. Setting sip.name to the + empty string will cause the container to be suppressed. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(ForwardDeclarationRuleDb, self).__init__(db, ["parents", "container", "template_parameters"]) + + def apply(self, container, sip): + """ + Walk over the rules database for containers, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = _parents(container) + matcher, rule = self._match(parents, sip["name"], + ", ".join(sip["template_parameters"])) + if matcher: + before = deepcopy(sip) + rule.fn(container, sip, matcher) + return rule.trace_result(parents, container, before, sip) + return None + + class FunctionRuleDb(AbstractCompiledRuleDb): """ THE RULES FOR FUNCTIONS. @@ -774,6 +839,15 @@ class RuleSet(object): raise NotImplemented(_("Missing subclass implementation")) @abstractmethod + def forward_declaration_rules(self): + """ + Return a compiled list of rules for containers. + + :return: A ForwardDeclarationRuleDb instance + """ + raise NotImplemented(_("Missing subclass implementation")) + + @abstractmethod def function_rules(self): """ Return a compiled list of rules for functions. @@ -888,6 +962,9 @@ def typedef_discard(container, typedef, sip, matcher): def discard_QSharedData_base(container, sip, matcher): sip["base_specifiers"].remove("QSharedData") +def mark_forward_declaration_external(container, sip, matcher): + sip["annotations"].add("External") + def rules(project_rules): """ Constructor. diff --git a/find-modules/sip_generator.py b/find-modules/sip_generator.py index 41dd6369..7df9de3d 100644 --- a/find-modules/sip_generator.py +++ b/find-modules/sip_generator.py @@ -313,30 +313,51 @@ class SipGenerator(object): pad = " " * ((level + 1) * 4) body += pad + "// {}\n".format(SipGenerator.describe(member)) body += decl + + + if container.kind == CursorKind.TRANSLATION_UNIT: + return body + + if container.kind == CursorKind.NAMESPACE: + container_type = "namespace " + name + elif container.kind in [CursorKind.CLASS_DECL, CursorKind.CLASS_TEMPLATE, + CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION]: + container_type = "class " + name + elif container.kind == CursorKind.STRUCT_DECL: + container_type = "struct " + name + elif container.kind == CursorKind.UNION_DECL: + container_type = "union " + name + else: + raise AssertionError( + _("Unexpected container {}: {}[{}]").format(container.kind, name, container.extent.start.line)) + + sip["decl"] = container_type + sip["template_parameters"] = template_type_parameters + + pad = " " * (level * 4) + # # Empty containers are still useful if they provide namespaces or forward declarations. # - if not body and level >= 0: - body = "\n" + if not body: text = self._read_source(container.extent) if not text.endswith("}"): # # Forward declaration. # - sip["annotations"].add("External") - if body and level >= 0: - if container.kind == CursorKind.NAMESPACE: - container_type = "namespace " + name - elif container.kind in [CursorKind.CLASS_DECL, CursorKind.CLASS_TEMPLATE, - CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION]: - container_type = "class " + name - elif container.kind == CursorKind.STRUCT_DECL: - container_type = "struct " + name - elif container.kind == CursorKind.UNION_DECL: - container_type = "union " + name - else: - raise AssertionError( - _("Unexpected container {}: {}[{}]").format(container.kind, name, container.extent.start.line)) + modifying_rule = self.rules.forward_declaration_rules().apply(container, sip) + if sip["name"]: + if modifying_rule: + body += "// Modified {} (by {}):\n".format(SipGenerator.describe(container), modifying_rule) + if "External" in sip["annotations"]: + body += pad + sip["decl"] + body += " /External/;\n" + else: + body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), "default forward declaration handling") + + else: + body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), modifying_rule) + else: # # Generate private copy constructor for non-copyable types. # @@ -345,33 +366,24 @@ class SipGenerator(object): # # Flesh out the SIP context for the rules engine. # - sip["template_parameters"] = template_type_parameters - sip["decl"] = container_type sip["base_specifiers"] = base_specifiers sip["body"] = body modifying_rule = self.rules.container_rules().apply(container, sip) - pad = " " * (level * 4) if sip["name"]: decl = "" if modifying_rule: decl += "// Modified {} (by {}):\n".format(SipGenerator.describe(container), modifying_rule) decl += pad + sip["decl"] - if "External" in sip["annotations"]: - # - # SIP /External/ does not seem to work as one might wish. Suppress. - # - body = decl + " /External/;\n" - body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), "/External/ handling") - else: - if sip["base_specifiers"]: - decl += ": " + ", ".join(sip["base_specifiers"]) - if sip["annotations"]: - decl += " /" + ",".join(sip["annotations"]) + "/" - if sip["template_parameters"]: - decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl - decl += "\n" + pad + "{\n" - decl += "%TypeHeaderCode\n#include <{}>\n%End\n".format(include_filename) - body = decl + sip["body"] + pad + "};\n" + + if sip["base_specifiers"]: + decl += ": " + ", ".join(sip["base_specifiers"]) + if sip["annotations"]: + decl += " /" + ",".join(sip["annotations"]) + "/" + if sip["template_parameters"]: + decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl + decl += "\n" + pad + "{\n" + decl += "%TypeHeaderCode\n#include <{}>\n%End\n".format(include_filename) + body = decl + sip["body"] + pad + "};\n" else: body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), modifying_rule) return body |