Skip to content

Commit 1740473

Browse files
authored
Merge pull request #180 from borglab/fix-175
2 parents 60b680f + 2b5eaea commit 1740473

File tree

16 files changed

+359
-178
lines changed

16 files changed

+359
-178
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ configure_package_config_file(
3737
INSTALL_INCLUDE_DIR)
3838

3939
# Set all the install paths
40-
set(GTWRAP_CMAKE_INSTALL_DIR $${INSTALL_CMAKE_DIR})
40+
set(GTWRAP_CMAKE_INSTALL_DIR ${INSTALL_CMAKE_DIR})
4141
set(GTWRAP_LIB_INSTALL_DIR ${INSTALL_LIB_DIR})
4242
set(GTWRAP_BIN_INSTALL_DIR ${INSTALL_BIN_DIR})
4343
set(GTWRAP_INCLUDE_INSTALL_DIR ${INSTALL_INCLUDE_DIR})

gtwrap/interface_parser/enum.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ def cpp_typename(self):
6262
Return a Typename with the namespaces and cpp name of this
6363
class.
6464
"""
65-
namespaces_name = self.namespaces()
66-
namespaces_name.append(self.name)
67-
return Typename(namespaces_name)
65+
return Typename(self.name, self.namespaces())
6866

6967
def __repr__(self):
7068
return "Enum: {0}".format(self.name)

gtwrap/interface_parser/function.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212

1313
from typing import Any, Iterable, List, Union
1414

15-
from pyparsing import (Literal, Optional, ParseResults, # type: ignore
16-
delimitedList)
15+
from pyparsing import Literal, Optional, ParseResults, delimitedList
1716

1817
from .template import Template
1918
from .tokens import (COMMA, DEFAULT_ARG, EQUAL, IDENT, LOPBRACK, LPAREN, PAIR,

gtwrap/interface_parser/type.py

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@ class Typename:
4242
namespaces_name_rule = delimitedList(IDENT, "::")
4343
rule = (
4444
namespaces_name_rule("namespaces_and_name") #
45-
).setParseAction(lambda t: Typename(t))
45+
).setParseAction(lambda t: Typename.from_parse_result(t))
4646

4747
def __init__(self,
48-
t: ParseResults,
48+
name: str,
49+
namespaces: list[str],
4950
instantiations: Sequence[ParseResults] = ()):
50-
self.name = t[-1] # the name is the last element in this list
51-
self.namespaces = t[:-1]
51+
self.name = name
52+
self.namespaces = namespaces
5253

5354
# If the first namespace is empty string, just get rid of it.
5455
if self.namespaces and self.namespaces[0] == '':
@@ -63,12 +64,38 @@ def __init__(self,
6364
self.instantiations = []
6465

6566
@staticmethod
66-
def from_parse_result(parse_result: Union[str, list]):
67+
def from_parse_result(parse_result: list):
6768
"""Unpack the parsed result to get the Typename instance."""
68-
return parse_result[0]
69+
name = parse_result[-1] # the name is the last element in this list
70+
namespaces = parse_result[:-1]
71+
return Typename(name, namespaces)
6972

7073
def __repr__(self) -> str:
71-
return self.to_cpp()
74+
if self.get_template_args():
75+
templates = f"<{self.get_template_args()}>"
76+
else:
77+
templates = ""
78+
79+
if len(self.namespaces) > 0:
80+
namespaces = "::".join(self.namespaces) + "::"
81+
else:
82+
namespaces = ""
83+
84+
return f"{namespaces}{self.name}{templates}"
85+
86+
def get_template_args(self) -> str:
87+
"""Return the template args as a string, e.g. <double, gtsam::Pose3>."""
88+
return ", ".join([inst.to_cpp() for inst in self.instantiations])
89+
90+
def templated_name(self) -> str:
91+
"""Return the name without namespace and with the template instantiations if any."""
92+
if self.instantiations:
93+
templates = self.get_template_args()
94+
name = f"{self.name}<{templates}>"
95+
else:
96+
name = self.name
97+
98+
return name
7299

73100
def instantiated_name(self) -> str:
74101
"""Get the instantiated name of the type."""
@@ -84,8 +111,7 @@ def qualified_name(self):
84111
def to_cpp(self) -> str:
85112
"""Generate the C++ code for wrapping."""
86113
if self.instantiations:
87-
cpp_name = self.name + "<{}>".format(", ".join(
88-
[inst.to_cpp() for inst in self.instantiations]))
114+
cpp_name = self.name + f"<{self.get_template_args()}>"
89115
else:
90116
cpp_name = self.name
91117
return '{}{}{}'.format(
@@ -129,7 +155,7 @@ class BasicType:
129155
rule = (Or(BASIC_TYPES)("typename")).setParseAction(lambda t: BasicType(t))
130156

131157
def __init__(self, t: ParseResults):
132-
self.typename = Typename(t)
158+
self.typename = Typename.from_parse_result(t)
133159

134160

135161
class CustomType:
@@ -148,7 +174,7 @@ class CustomType:
148174
rule = (Typename.rule("typename")).setParseAction(lambda t: CustomType(t))
149175

150176
def __init__(self, t: ParseResults):
151-
self.typename = Typename(t)
177+
self.typename = Typename.from_parse_result(t)
152178

153179

154180
class Type:
@@ -226,18 +252,16 @@ def to_cpp(self) -> str:
226252
"""
227253

228254
if self.is_shared_ptr:
229-
typename = "std::shared_ptr<{typename}>".format(
230-
typename=self.get_typename())
255+
typename = f"std::shared_ptr<{self.get_typename()}>"
231256
elif self.is_ptr:
232-
typename = "{typename}*".format(typename=self.typename.to_cpp())
257+
typename = f"{self.typename.to_cpp()}*"
233258
elif self.is_ref:
234-
typename = typename = "{typename}&".format(
235-
typename=self.get_typename())
259+
typename = f"{self.get_typename()}&"
236260
else:
237261
typename = self.get_typename()
238262

239-
return ("{const}{typename}".format(
240-
const="const " if self.is_const else "", typename=typename))
263+
const = "const " if self.is_const else ""
264+
return f"{const}{typename}"
241265

242266

243267
class TemplatedType:
@@ -265,7 +289,7 @@ def __init__(self, typename: Typename, template_params: List[Type],
265289
is_const: str, is_shared_ptr: str, is_ptr: str, is_ref: str):
266290
instantiations = [param.typename for param in template_params]
267291
# Recreate the typename but with the template params as instantiations.
268-
self.typename = Typename(typename.namespaces + [typename.name],
292+
self.typename = Typename(typename.name, typename.namespaces,
269293
instantiations)
270294

271295
self.template_params = template_params
@@ -278,22 +302,33 @@ def __init__(self, typename: Typename, template_params: List[Type],
278302
@staticmethod
279303
def from_parse_result(t: ParseResults):
280304
"""Get the TemplatedType from the parser results."""
281-
return TemplatedType(t.typename, t.template_params, t.is_const,
282-
t.is_shared_ptr, t.is_ptr, t.is_ref)
305+
return TemplatedType(t.typename, t.template_params.as_list(),
306+
t.is_const, t.is_shared_ptr, t.is_ptr, t.is_ref)
283307

284308
def __repr__(self):
285-
return "TemplatedType({typename.namespaces}::{typename.name})".format(
286-
typename=self.typename)
309+
return "TemplatedType({typename.namespaces}::{typename.name}<{template_params}>)".format(
310+
typename=self.typename, template_params=self.template_params)
311+
312+
def get_template_params(self):
313+
"""
314+
Get the template args for the type as a string.
315+
E.g. for
316+
```
317+
template <T = {double}, U = {string}>
318+
class Random(){};
319+
```
320+
it returns `<double, string>`.
321+
322+
"""
323+
# Use Type.to_cpp to do the heavy lifting for the template parameters.
324+
return ", ".join([t.to_cpp() for t in self.template_params])
287325

288326
def get_typename(self):
289327
"""
290328
Get the typename of this type without any qualifiers.
291329
E.g. for `const std::vector<double>& indices` this will return `std::vector<double>`.
292330
"""
293-
# Use Type.to_cpp to do the heavy lifting for the template parameters.
294-
template_args = ", ".join([t.to_cpp() for t in self.template_params])
295-
296-
return f"{self.typename.qualified_name()}<{template_args}>"
331+
return f"{self.typename.qualified_name()}<{self.get_template_params()}>"
297332

298333
def to_cpp(self):
299334
"""

gtwrap/pybind_wrapper.py

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818

1919
import gtwrap.interface_parser as parser
2020
import gtwrap.template_instantiator as instantiator
21-
21+
from gtwrap.interface_parser.function import ArgumentList
2222
from gtwrap.xml_parser.xml_parser import XMLDocParser
2323

24+
2425
class PybindWrapper:
2526
"""
2627
Class to generate binding code for Pybind11 specifically.
@@ -30,7 +31,7 @@ def __init__(self,
3031
module_name,
3132
top_module_namespaces='',
3233
use_boost_serialization=False,
33-
ignore_classes=(),
34+
ignore_classes=(),
3435
module_template="",
3536
xml_source=""):
3637
self.module_name = module_name
@@ -76,7 +77,7 @@ def _py_args_names(self, args):
7677
else:
7778
return ''
7879

79-
def _method_args_signature(self, args):
80+
def _method_args_signature(self, args: ArgumentList):
8081
"""Generate the argument types and names as per the method signature."""
8182
cpp_types = args.to_cpp()
8283
names = args.names()
@@ -260,37 +261,42 @@ def _wrap_method(self,
260261
args_names=', '.join(args_names),
261262
))
262263

263-
ret = ('{prefix}.{cdef}("{py_method}",'
264-
'[]({opt_self}{opt_comma}{args_signature_with_names}){{'
265-
'{function_call}'
266-
'}}'
267-
'{py_args_names}{docstring}){suffix}'.format(
268-
prefix=prefix,
269-
cdef="def_static" if is_static else "def",
270-
py_method=py_method,
271-
opt_self="{cpp_class}* self".format(
272-
cpp_class=cpp_class) if is_method else "",
273-
opt_comma=', ' if is_method and args_names else '',
274-
args_signature_with_names=args_signature_with_names,
275-
function_call=function_call,
276-
py_args_names=py_args_names,
277-
suffix=suffix,
278-
# Try to get the function's docstring from the Doxygen XML.
279-
# If extract_docstring errors or fails to find a docstring, it just prints a warning.
280-
# The incantation repr(...)[1:-1].replace('"', r'\"') replaces newlines with \n
281-
# and " with \" so that the docstring can be put into a C++ string on a single line.
282-
docstring=', "' + repr(self.xml_parser.extract_docstring(self.xml_source, cpp_class, cpp_method, method.args.names()))[1:-1].replace('"', r'\"') + '"'
283-
if self.xml_source != "" else "",
284-
))
264+
result = (
265+
'{prefix}.{cdef}("{py_method}",'
266+
'[]({opt_self}{opt_comma}{args_signature_with_names}){{'
267+
'{function_call}'
268+
'}}'
269+
'{py_args_names}{docstring}){suffix}'.format(
270+
prefix=prefix,
271+
cdef="def_static" if is_static else "def",
272+
py_method=py_method,
273+
opt_self="{cpp_class}* self".format(
274+
cpp_class=cpp_class) if is_method else "",
275+
opt_comma=', '
276+
if is_method and args_signature_with_names else '',
277+
args_signature_with_names=args_signature_with_names,
278+
function_call=function_call,
279+
py_args_names=py_args_names,
280+
suffix=suffix,
281+
# Try to get the function's docstring from the Doxygen XML.
282+
# If extract_docstring errors or fails to find a docstring, it just prints a warning.
283+
# The incantation repr(...)[1:-1].replace('"', r'\"') replaces newlines with \n
284+
# and " with \" so that the docstring can be put into a C++ string on a single line.
285+
docstring=', "' + repr(
286+
self.xml_parser.extract_docstring(
287+
self.xml_source, cpp_class, cpp_method,
288+
method.args.names()))[1:-1].replace('"', r'\"') +
289+
'"' if self.xml_source != "" else "",
290+
))
285291

286292
# Create __repr__ override
287293
# We allow all arguments to .print() and let the compiler handle type mismatches.
288294
if method.name == 'print':
289-
ret = self._wrap_print(ret, method, cpp_class, args_names,
290-
args_signature_with_names, py_args_names,
291-
prefix, suffix)
295+
result = self._wrap_print(result, method, cpp_class, args_names,
296+
args_signature_with_names, py_args_names,
297+
prefix, suffix)
292298

293-
return ret
299+
return result
294300

295301
def wrap_dunder_methods(self,
296302
methods,

gtwrap/template_instantiator/classes.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import gtwrap.interface_parser as parser
44
from gtwrap.template_instantiator.constructor import InstantiatedConstructor
5-
from gtwrap.template_instantiator.helpers import (InstantiationHelper,
5+
from gtwrap.template_instantiator.helpers import (InstantiatedMember,
6+
InstantiationHelper,
67
instantiate_args_list,
78
instantiate_name,
89
instantiate_return_type,
@@ -57,7 +58,7 @@ def __init__(self, original: parser.Class, instantiations=(), new_name=''):
5758

5859
# Instantiate all instance methods
5960
self.methods = self.instantiate_methods(typenames)
60-
61+
6162
self.dunder_methods = original.dunder_methods
6263

6364
super().__init__(
@@ -99,9 +100,11 @@ def instantiate_parent_class(self, typenames):
99100
"""
100101

101102
if isinstance(self.original.parent_class, parser.type.TemplatedType):
102-
return instantiate_type(
103-
self.original.parent_class, typenames, self.instantiations,
104-
parser.Typename(self.namespaces())).typename
103+
namespaces = self.namespaces()
104+
typename = parser.Typename(name=namespaces[-1],
105+
namespaces=namespaces[:-1])
106+
return instantiate_type(self.original.parent_class, typenames,
107+
self.instantiations, typename).typename
105108
else:
106109
return self.original.parent_class
107110

@@ -140,7 +143,7 @@ def instantiate_static_methods(self, typenames):
140143

141144
return instantiated_static_methods
142145

143-
def instantiate_methods(self, typenames):
146+
def instantiate_methods(self, typenames) -> list[InstantiatedMember]:
144147
"""
145148
Instantiate regular methods in the class.
146149
@@ -225,9 +228,8 @@ def cpp_typename(self):
225228
", ".join([inst.to_cpp() for inst in self.instantiations]))
226229
else:
227230
name = self.original.name
228-
namespaces_name = self.namespaces()
229-
namespaces_name.append(name)
230-
return parser.Typename(namespaces_name)
231+
232+
return parser.Typename(name=name, namespaces=self.namespaces())
231233

232234
def to_cpp(self):
233235
"""Generate the C++ code for wrapping."""

gtwrap/template_instantiator/declaration.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,8 @@ def to_cpp(self):
3535
]
3636
name = "{}<{}>".format(self.original.name,
3737
",".join(instantiated_names))
38-
namespaces_name = self.namespaces()
39-
namespaces_name.append(name)
4038
# Leverage Typename to generate the fully qualified C++ name
41-
return parser.Typename(namespaces_name).to_cpp()
39+
return parser.Typename(name=name, namespaces=self.namespaces()).to_cpp()
4240

4341
def __repr__(self):
4442
return "Instantiated {}".format(

gtwrap/template_instantiator/function.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,4 @@ def to_cpp(self):
6565
return ret
6666

6767
def __repr__(self):
68-
return f"Instantiated {super().__repr__}"
68+
return f"Instantiated {super().__repr__()}"

0 commit comments

Comments
 (0)