Skip to content

Commit 51d29a0

Browse files
committed
Support multiple template declarations on a class or function
- Fixes #20
1 parent 1758155 commit 51d29a0

File tree

3 files changed

+180
-9
lines changed

3 files changed

+180
-9
lines changed

cxxheaderparser/parser.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
Reference,
4545
TemplateArgument,
4646
TemplateDecl,
47+
TemplateDeclTypeVar,
4748
TemplateInst,
4849
TemplateNonTypeParam,
4950
TemplateParam,
@@ -615,7 +616,18 @@ def _parse_template(self, tok: LexToken, doxygen: typing.Optional[str]) -> None:
615616

616617
template = self._parse_template_decl()
617618

619+
# Check for multiple specializations
618620
tok = self.lex.token()
621+
if tok.type == "template":
622+
templates = [template]
623+
while tok.type == "template":
624+
templates.append(self._parse_template_decl())
625+
tok = self.lex.token()
626+
627+
# Can only be followed by declarations
628+
self._parse_declarations(tok, doxygen, templates)
629+
return
630+
619631
if tok.type == "using":
620632
self._parse_using(tok, doxygen, template)
621633
elif tok.type == "friend":
@@ -1094,7 +1106,7 @@ def _parse_class_decl(
10941106
typename: PQName,
10951107
tok: LexToken,
10961108
doxygen: typing.Optional[str],
1097-
template: typing.Optional[TemplateDecl],
1109+
template: TemplateDeclTypeVar,
10981110
typedef: bool,
10991111
location: Location,
11001112
mods: ParsedTypeModifiers,
@@ -1795,7 +1807,7 @@ def _parse_function(
17951807
return_type: typing.Optional[DecoratedType],
17961808
pqname: PQName,
17971809
op: typing.Optional[str],
1798-
template: typing.Optional[TemplateDecl],
1810+
template: TemplateDeclTypeVar,
17991811
doxygen: typing.Optional[str],
18001812
location: Location,
18011813
constructor: bool,
@@ -2168,7 +2180,7 @@ def _parse_decl(
21682180
mods: ParsedTypeModifiers,
21692181
location: Location,
21702182
doxygen: typing.Optional[str],
2171-
template: typing.Optional[TemplateDecl],
2183+
template: TemplateDeclTypeVar,
21722184
is_typedef: bool,
21732185
is_friend: bool,
21742186
) -> bool:
@@ -2295,6 +2307,9 @@ def _parse_decl(
22952307
if not dtype:
22962308
raise CxxParseError("appear to be parsing a field without a type")
22972309

2310+
if isinstance(template, list):
2311+
raise CxxParseError("multiple template declarations on a field")
2312+
22982313
self._parse_field(mods, dtype, pqname, template, doxygen, location, is_typedef)
22992314
return False
23002315

@@ -2303,7 +2318,7 @@ def _parse_operator_conversion(
23032318
mods: ParsedTypeModifiers,
23042319
location: Location,
23052320
doxygen: typing.Optional[str],
2306-
template: typing.Optional[TemplateDecl],
2321+
template: TemplateDeclTypeVar,
23072322
is_typedef: bool,
23082323
is_friend: bool,
23092324
) -> None:
@@ -2356,7 +2371,7 @@ def _parse_declarations(
23562371
self,
23572372
tok: LexToken,
23582373
doxygen: typing.Optional[str],
2359-
template: typing.Optional[TemplateDecl] = None,
2374+
template: TemplateDeclTypeVar = None,
23602375
is_typedef: bool = False,
23612376
is_friend: bool = False,
23622377
) -> None:
@@ -2426,7 +2441,7 @@ def _maybe_parse_class_enum_decl(
24262441
parsed_type: Type,
24272442
mods: ParsedTypeModifiers,
24282443
doxygen: typing.Optional[str],
2429-
template: typing.Optional[TemplateDecl],
2444+
template: TemplateDeclTypeVar,
24302445
is_typedef: bool,
24312446
is_friend: bool,
24322447
location: Location,

cxxheaderparser/types.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,26 @@ class Foo {};
514514
params: typing.List[TemplateParam] = field(default_factory=list)
515515

516516

517+
#: If no template, this is None. This is a TemplateDecl if this there is a single
518+
#: declaration:
519+
#:
520+
#: .. code-block:: c++
521+
#:
522+
#: template <typename T>
523+
#: struct C {};
524+
#:
525+
#: If there are multiple template declarations, then this is a list of
526+
#: declarations in the order that they're encountered:
527+
#:
528+
#: .. code-block:: c++
529+
#:
530+
#: template<>
531+
#: template<class U>
532+
#: struct A<char>::C {};
533+
#:
534+
TemplateDeclTypeVar = typing.Union[None, TemplateDecl, typing.List[TemplateDecl]]
535+
536+
517537
@dataclass
518538
class TemplateInst:
519539
"""
@@ -538,7 +558,7 @@ class ForwardDecl:
538558
"""
539559

540560
typename: PQName
541-
template: typing.Optional[TemplateDecl] = None
561+
template: TemplateDeclTypeVar = None
542562
doxygen: typing.Optional[str] = None
543563

544564
#: Set if this is a forward declaration of an enum and it has a base
@@ -576,7 +596,7 @@ class ClassDecl:
576596
typename: PQName
577597

578598
bases: typing.List[BaseClass] = field(default_factory=list)
579-
template: typing.Optional[TemplateDecl] = None
599+
template: TemplateDeclTypeVar = None
580600

581601
explicit: bool = False
582602
final: bool = False
@@ -642,7 +662,7 @@ class Function:
642662
#: whatever the trailing return type was
643663
has_trailing_return: bool = False
644664

645-
template: typing.Optional[TemplateDecl] = None
665+
template: TemplateDeclTypeVar = None
646666

647667
#: Value of any throw specification for this function. The value omits the
648668
#: outer parentheses.

tests/test_template.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,3 +2027,139 @@ def test_fwd_declared_method() -> None:
20272027
]
20282028
)
20292029
)
2030+
2031+
2032+
def test_multiple_explicit_member_specialization() -> None:
2033+
content = """
2034+
template <>
2035+
template <>
2036+
inline Standard_CString
2037+
StdObjMgt_Attribute<TDF_TagSource>::Simple<Standard_Integer>::PName() const {
2038+
return "PDF_TagSource";
2039+
}
2040+
"""
2041+
data = parse_string(content, cleandoc=True)
2042+
2043+
assert data == ParsedData(
2044+
namespace=NamespaceScope(
2045+
method_impls=[
2046+
Method(
2047+
return_type=Type(
2048+
typename=PQName(
2049+
segments=[NameSpecifier(name="Standard_CString")]
2050+
)
2051+
),
2052+
name=PQName(
2053+
segments=[
2054+
NameSpecifier(
2055+
name="StdObjMgt_Attribute",
2056+
specialization=TemplateSpecialization(
2057+
args=[
2058+
TemplateArgument(
2059+
arg=Type(
2060+
typename=PQName(
2061+
segments=[
2062+
NameSpecifier(
2063+
name="TDF_TagSource"
2064+
)
2065+
]
2066+
)
2067+
)
2068+
)
2069+
]
2070+
),
2071+
),
2072+
NameSpecifier(
2073+
name="Simple",
2074+
specialization=TemplateSpecialization(
2075+
args=[
2076+
TemplateArgument(
2077+
arg=Type(
2078+
typename=PQName(
2079+
segments=[
2080+
NameSpecifier(
2081+
name="Standard_Integer"
2082+
)
2083+
]
2084+
)
2085+
)
2086+
)
2087+
]
2088+
),
2089+
),
2090+
NameSpecifier(name="PName"),
2091+
]
2092+
),
2093+
parameters=[],
2094+
inline=True,
2095+
has_body=True,
2096+
template=[TemplateDecl(), TemplateDecl()],
2097+
const=True,
2098+
)
2099+
]
2100+
)
2101+
)
2102+
2103+
2104+
def test_member_class_template_specialization() -> None:
2105+
content = """
2106+
template <> // specialization of a member class template
2107+
template <class U>
2108+
struct A<char>::C {
2109+
void f();
2110+
};
2111+
"""
2112+
data = parse_string(content, cleandoc=True)
2113+
2114+
assert data == ParsedData(
2115+
namespace=NamespaceScope(
2116+
classes=[
2117+
ClassScope(
2118+
class_decl=ClassDecl(
2119+
typename=PQName(
2120+
segments=[
2121+
NameSpecifier(
2122+
name="A",
2123+
specialization=TemplateSpecialization(
2124+
args=[
2125+
TemplateArgument(
2126+
arg=Type(
2127+
typename=PQName(
2128+
segments=[
2129+
FundamentalSpecifier(
2130+
name="char"
2131+
)
2132+
]
2133+
)
2134+
)
2135+
)
2136+
]
2137+
),
2138+
),
2139+
NameSpecifier(name="C"),
2140+
],
2141+
classkey="struct",
2142+
),
2143+
template=[
2144+
TemplateDecl(),
2145+
TemplateDecl(
2146+
params=[TemplateTypeParam(typekey="class", name="U")]
2147+
),
2148+
],
2149+
),
2150+
methods=[
2151+
Method(
2152+
return_type=Type(
2153+
typename=PQName(
2154+
segments=[FundamentalSpecifier(name="void")]
2155+
)
2156+
),
2157+
name=PQName(segments=[NameSpecifier(name="f")]),
2158+
parameters=[],
2159+
access="public",
2160+
)
2161+
],
2162+
)
2163+
]
2164+
)
2165+
)

0 commit comments

Comments
 (0)