pybind_wrapper.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 """
3 GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
4 Atlanta, Georgia 30332-0415
5 All Rights Reserved
6 
7 See LICENSE for the license information
8 
9 Code generator for wrapping a C++ module with Pybind11
10 Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellaert
11 """
12 
13 # pylint: disable=too-many-arguments, too-many-instance-attributes, no-self-use, no-else-return, too-many-arguments, unused-format-string-argument, line-too-long, consider-using-f-string
14 
15 import re
16 from pathlib import Path
17 from typing import List
18 
19 import gtwrap.interface_parser as parser
20 import gtwrap.template_instantiator as instantiator
21 
22 
24  """
25  Class to generate binding code for Pybind11 specifically.
26  """
27 
28  def __init__(self,
29  module_name,
30  top_module_namespaces='',
31  use_boost_serialization=False,
32  ignore_classes=(),
33  module_template=""):
34  self.module_name = module_name
35  self.top_module_namespaces = top_module_namespaces
36  self.use_boost_serialization = use_boost_serialization
37  self.ignore_classes = ignore_classes
39  self.module_template = module_template
40  self.python_keywords = [
41  'lambda', 'False', 'def', 'if', 'raise', 'None', 'del', 'import',
42  'return', 'True', 'elif', 'in', 'try', 'and', 'else', 'is',
43  'while', 'as', 'except', 'lambda', 'with', 'assert', 'finally',
44  'nonlocal', 'yield', 'break', 'for', 'not', 'class', 'from', 'or',
45  'continue', 'global', 'pass'
46  ]
47 
48  # amount of indentation to add before each function/method declaration.
49  self.method_indent = '\n' + (' ' * 8)
50 
51  # Special methods which are leveraged by ipython/jupyter notebooks
53  "svg", "png", "jpeg", "html", "javascript", "markdown", "latex"
54  ]
55 
56  def _py_args_names(self, args):
57  """Set the argument names in Pybind11 format."""
58  names = args.names()
59  if names:
60  py_args = []
61  for arg in args.list():
62  if arg.default is not None:
63  default = ' = {arg.default}'.format(arg=arg)
64  else:
65  default = ''
66  argument = 'py::arg("{name}"){default}'.format(
67  name=arg.name, default='{0}'.format(default))
68  py_args.append(argument)
69  return ", " + ", ".join(py_args)
70  else:
71  return ''
72 
73  def _method_args_signature(self, args):
74  """Generate the argument types and names as per the method signature."""
75  cpp_types = args.to_cpp()
76  names = args.names()
77  types_names = [
78  "{} {}".format(ctype, name)
79  for ctype, name in zip(cpp_types, names)
80  ]
81 
82  return ', '.join(types_names)
83 
84  def wrap_ctors(self, my_class):
85  """Wrap the constructors."""
86  res = ""
87  for ctor in my_class.ctors:
88  res += (self.method_indent + '.def(py::init<{args_cpp_types}>()'
89  '{py_args_names})'.format(
90  args_cpp_types=", ".join(ctor.args.to_cpp()),
91  py_args_names=self._py_args_names(ctor.args),
92  ))
93  return res
94 
95  def _wrap_serialization(self, cpp_class):
96  """Helper method to add serialize, deserialize and pickle methods to the wrapped class."""
97  if not cpp_class in self._serializing_classes:
98  self._serializing_classes.append(cpp_class)
99 
100  serialize_method = self.method_indent + \
101  ".def(\"serialize\", []({class_inst} self){{ return gtsam::serialize(*self); }})".format(class_inst=cpp_class + '*')
102 
103  deserialize_method = self.method_indent + \
104  '.def("deserialize", []({class_inst} self, string serialized)' \
105  '{{ gtsam::deserialize(serialized, *self); }}, py::arg("serialized"))' \
106  .format(class_inst=cpp_class + '*')
107 
108  # Since this class supports serialization, we also add the pickle method.
109  pickle_method = self.method_indent + \
110  ".def(py::pickle({indent} [](const {cpp_class} &a){{ /* __getstate__: Returns a string that encodes the state of the object */ return py::make_tuple(gtsam::serialize(a)); }},{indent} [](py::tuple t){{ /* __setstate__ */ {cpp_class} obj; gtsam::deserialize(t[0].cast<std::string>(), obj); return obj; }}))"
111 
112  return serialize_method + deserialize_method + \
113  pickle_method.format(cpp_class=cpp_class, indent=self.method_indent)
114 
115  def _wrap_print(self, ret: str, method: parser.Method, cpp_class: str,
116  args_names: List[str], args_signature_with_names: str,
117  py_args_names: str, prefix: str, suffix: str):
118  """
119  Update the print method to print to the output stream and append a __repr__ method.
120 
121  Args:
122  ret (str): The result of the parser.
123  method (parser.Method): The method to be wrapped.
124  cpp_class (str): The C++ name of the class to which the method belongs.
125  args_names (List[str]): List of argument variable names passed to the method.
126  args_signature_with_names (str): C++ arguments containing their names and type signatures.
127  py_args_names (str): The pybind11 formatted version of the argument list.
128  prefix (str): Prefix to add to the wrapped method when writing to the cpp file.
129  suffix (str): Suffix to add to the wrapped method when writing to the cpp file.
130 
131  Returns:
132  str: The wrapped print method.
133  """
134  # Redirect stdout - see pybind docs for why this is a good idea:
135  # https://pybind11.readthedocs.io/en/stable/advanced/pycpp/utilities.html#capturing-standard-output-from-ostream
136  ret = ret.replace('self->print',
137  'py::scoped_ostream_redirect output; self->print')
138 
139  # Make __repr__() call .print() internally
140  ret += '''{prefix}.def("__repr__",
141  [](const {cpp_class}& self{opt_comma}{args_signature_with_names}){{
142  gtsam::RedirectCout redirect;
143  self.{method_name}({method_args});
144  return redirect.str();
145  }}{py_args_names}){suffix}'''.format(
146  prefix=prefix,
147  cpp_class=cpp_class,
148  opt_comma=', ' if args_names else '',
149  args_signature_with_names=args_signature_with_names,
150  method_name=method.name,
151  method_args=", ".join(args_names) if args_names else '',
152  py_args_names=py_args_names,
153  suffix=suffix)
154  return ret
155 
156  def _wrap_method(self,
157  method,
158  cpp_class,
159  prefix,
160  suffix,
161  method_suffix=""):
162  """
163  Wrap the `method` for the class specified by `cpp_class`.
164 
165  Args:
166  method: The method to wrap.
167  cpp_class: The C++ name of the class to which the method belongs.
168  prefix: Prefix to add to the wrapped method when writing to the cpp file.
169  suffix: Suffix to add to the wrapped method when writing to the cpp file.
170  method_suffix: A string to append to the wrapped method name.
171  """
172  py_method = method.name + method_suffix
173  cpp_method = method.to_cpp()
174 
175  args_names = method.args.names()
176  py_args_names = self._py_args_names(method.args)
177  args_signature_with_names = self._method_args_signature(method.args)
178 
179  # Special handling for the serialize/serializable method
180  if cpp_method in ["serialize", "serializable"]:
181  if self.use_boost_serialization:
182  return self._wrap_serialization(cpp_class)
183  else:
184  return ""
185 
186  # Special handling of ipython specific methods
187  # https://ipython.readthedocs.io/en/stable/config/integrating.html
188  if cpp_method in self._ipython_special_methods:
189  idx = self._ipython_special_methods.index(cpp_method)
190  py_method = f"_repr_{self._ipython_special_methods[idx]}_"
191 
192  # Add underscore to disambiguate if the method name matches a python keyword
193  if py_method in self.python_keywords:
194  py_method = py_method + "_"
195 
196  is_method = isinstance(
197  method, (parser.Method, instantiator.InstantiatedMethod))
198  is_static = isinstance(
199  method,
200  (parser.StaticMethod, instantiator.InstantiatedStaticMethod))
201  return_void = method.return_type.is_void()
202 
203  caller = cpp_class + "::" if not is_method else "self->"
204  function_call = ('{opt_return} {caller}{method_name}'
205  '({args_names});'.format(
206  opt_return='return' if not return_void else '',
207  caller=caller,
208  method_name=cpp_method,
209  args_names=', '.join(args_names),
210  ))
211 
212  ret = ('{prefix}.{cdef}("{py_method}",'
213  '[]({opt_self}{opt_comma}{args_signature_with_names}){{'
214  '{function_call}'
215  '}}'
216  '{py_args_names}){suffix}'.format(
217  prefix=prefix,
218  cdef="def_static" if is_static else "def",
219  py_method=py_method,
220  opt_self="{cpp_class}* self".format(
221  cpp_class=cpp_class) if is_method else "",
222  opt_comma=', ' if is_method and args_names else '',
223  args_signature_with_names=args_signature_with_names,
224  function_call=function_call,
225  py_args_names=py_args_names,
226  suffix=suffix,
227  ))
228 
229  # Create __repr__ override
230  # We allow all arguments to .print() and let the compiler handle type mismatches.
231  if method.name == 'print':
232  ret = self._wrap_print(ret, method, cpp_class, args_names,
233  args_signature_with_names, py_args_names,
234  prefix, suffix)
235 
236  return ret
237 
238  def wrap_methods(self,
239  methods,
240  cpp_class,
241  prefix='\n' + ' ' * 8,
242  suffix=''):
243  """
244  Wrap all the methods in the `cpp_class`.
245  """
246  res = ""
247  for method in methods:
248 
249  # To avoid type confusion for insert
250  if method.name == 'insert' and cpp_class == 'gtsam::Values':
251  name_list = method.args.names()
252  type_list = method.args.to_cpp()
253  # inserting non-wrapped value types
254  if type_list[0].strip() == 'size_t':
255  method_suffix = '_' + name_list[1].strip()
256  res += self._wrap_method(method=method,
257  cpp_class=cpp_class,
258  prefix=prefix,
259  suffix=suffix,
260  method_suffix=method_suffix)
261 
262  res += self._wrap_method(
263  method=method,
264  cpp_class=cpp_class,
265  prefix=prefix,
266  suffix=suffix,
267  )
268 
269  return res
270 
271  def wrap_variable(self,
272  namespace,
273  module_var,
274  variable,
275  prefix='\n' + ' ' * 8):
276  """
277  Wrap a variable that's not part of a class (i.e. global)
278  """
279  variable_value = ""
280  if variable.default is None:
281  variable_value = variable.name
282  else:
283  variable_value = variable.default
284 
285  return '{prefix}{module_var}.attr("{variable_name}") = {namespace}{variable_value};'.format(
286  prefix=prefix,
287  module_var=module_var,
288  variable_name=variable.name,
289  namespace=namespace,
290  variable_value=variable_value)
291 
292  def wrap_properties(self, properties, cpp_class, prefix='\n' + ' ' * 8):
293  """Wrap all the properties in the `cpp_class`."""
294  res = ""
295  for prop in properties:
296  res += ('{prefix}.def_{property}("{property_name}", '
297  '&{cpp_class}::{property_name})'.format(
298  prefix=prefix,
299  property="readonly"
300  if prop.ctype.is_const else "readwrite",
301  cpp_class=cpp_class,
302  property_name=prop.name,
303  ))
304  return res
305 
306  def wrap_operators(self, operators, cpp_class, prefix='\n' + ' ' * 8):
307  """Wrap all the overloaded operators in the `cpp_class`."""
308  res = ""
309  template = "{prefix}.def({{0}})".format(prefix=prefix)
310  for op in operators:
311  if op.operator == "[]": # __getitem__
312  res += "{prefix}.def(\"__getitem__\", &{cpp_class}::operator[])".format(
313  prefix=prefix, cpp_class=cpp_class)
314  elif op.operator == "()": # __call__
315  res += "{prefix}.def(\"__call__\", &{cpp_class}::operator())".format(
316  prefix=prefix, cpp_class=cpp_class)
317  elif op.is_unary:
318  res += template.format("{0}py::self".format(op.operator))
319  else:
320  res += template.format("py::self {0} py::self".format(
321  op.operator))
322  return res
323 
324  def wrap_enum(self, enum, class_name='', module=None, prefix=' ' * 4):
325  """
326  Wrap an enum.
327 
328  Args:
329  enum: The parsed enum to wrap.
330  class_name: The class under which the enum is defined.
331  prefix: The amount of indentation.
332  """
333  if module is None:
334  module = self._gen_module_var(enum.namespaces())
335 
336  cpp_class = enum.cpp_typename().to_cpp()
337  if class_name:
338  # If class_name is provided, add that as the namespace
339  cpp_class = class_name + "::" + cpp_class
340 
341  res = '{prefix}py::enum_<{cpp_class}>({module}, "{enum.name}", py::arithmetic())'.format(
342  prefix=prefix, module=module, enum=enum, cpp_class=cpp_class)
343  for enumerator in enum.enumerators:
344  res += '\n{prefix} .value("{enumerator.name}", {cpp_class}::{enumerator.name})'.format(
345  prefix=prefix, enumerator=enumerator, cpp_class=cpp_class)
346  res += ";\n\n"
347  return res
348 
349  def wrap_enums(self, enums, instantiated_class, prefix=' ' * 4):
350  """Wrap multiple enums defined in a class."""
351  cpp_class = instantiated_class.to_cpp()
352  module_var = instantiated_class.name.lower()
353  res = ''
354 
355  for enum in enums:
356  res += "\n" + self.wrap_enum(
357  enum, class_name=cpp_class, module=module_var, prefix=prefix)
358  return res
359 
361  self, instantiated_class: instantiator.InstantiatedClass):
362  """Wrap the class."""
363  module_var = self._gen_module_var(instantiated_class.namespaces())
364  cpp_class = instantiated_class.to_cpp()
365  if cpp_class in self.ignore_classes:
366  return ""
367  if instantiated_class.parent_class:
368  class_parent = "{instantiated_class.parent_class}, ".format(
369  instantiated_class=instantiated_class)
370  else:
371  class_parent = ''
372 
373  if instantiated_class.enums:
374  # If class has enums, define an instance and set module_var to the instance
375  instance_name = instantiated_class.name.lower()
376  class_declaration = (
377  '\n py::class_<{cpp_class}, {class_parent}'
378  'std::shared_ptr<{cpp_class}>> '
379  '{instance_name}({module_var}, "{class_name}");'
380  '\n {instance_name}').format(
381  cpp_class=cpp_class,
382  class_name=instantiated_class.name,
383  class_parent=class_parent,
384  instance_name=instance_name,
385  module_var=module_var)
386  module_var = instance_name
387 
388  else:
389  class_declaration = (
390  '\n py::class_<{cpp_class}, {class_parent}'
391  'std::shared_ptr<{cpp_class}>>({module_var}, "{class_name}")'
392  ).format(cpp_class=cpp_class,
393  class_name=instantiated_class.name,
394  class_parent=class_parent,
395  module_var=module_var)
396 
397  return ('{class_declaration}'
398  '{wrapped_ctors}'
399  '{wrapped_methods}'
400  '{wrapped_static_methods}'
401  '{wrapped_properties}'
402  '{wrapped_operators};\n'.format(
403  class_declaration=class_declaration,
404  wrapped_ctors=self.wrap_ctors(instantiated_class),
405  wrapped_methods=self.wrap_methods(
406  instantiated_class.methods, cpp_class),
407  wrapped_static_methods=self.wrap_methods(
408  instantiated_class.static_methods, cpp_class),
409  wrapped_properties=self.wrap_properties(
410  instantiated_class.properties, cpp_class),
411  wrapped_operators=self.wrap_operators(
412  instantiated_class.operators, cpp_class)))
413 
415  self, instantiated_decl: instantiator.InstantiatedDeclaration):
416  """Wrap the forward declaration."""
417  module_var = self._gen_module_var(instantiated_decl.namespaces())
418  cpp_class = instantiated_decl.to_cpp()
419  if cpp_class in self.ignore_classes:
420  return ""
421 
422  res = ('\n py::class_<{cpp_class}, '
423  'std::shared_ptr<{cpp_class}>>({module_var}, "{class_name}");'
424  ).format(cpp_class=cpp_class,
425  class_name=instantiated_decl.name,
426  module_var=module_var)
427  return res
428 
429  def wrap_stl_class(self, stl_class):
430  """Wrap STL containers."""
431  module_var = self._gen_module_var(stl_class.namespaces())
432  cpp_class = stl_class.to_cpp()
433  if cpp_class in self.ignore_classes:
434  return ""
435 
436  return ('\n py::class_<{cpp_class}, {class_parent}'
437  'std::shared_ptr<{cpp_class}>>({module_var}, "{class_name}")'
438  '{wrapped_ctors}'
439  '{wrapped_methods}'
440  '{wrapped_static_methods}'
441  '{wrapped_properties};\n'.format(
442  cpp_class=cpp_class,
443  class_name=stl_class.name,
444  class_parent=str(stl_class.parent_class) +
445  (', ' if stl_class.parent_class else ''),
446  module_var=module_var,
447  wrapped_ctors=self.wrap_ctors(stl_class),
448  wrapped_methods=self.wrap_methods(stl_class.methods,
449  cpp_class),
450  wrapped_static_methods=self.wrap_methods(
451  stl_class.static_methods, cpp_class),
452  wrapped_properties=self.wrap_properties(
453  stl_class.properties, cpp_class),
454  ))
455 
456  def wrap_functions(self,
457  functions,
458  namespace,
459  prefix='\n' + ' ' * 8,
460  suffix=''):
461  """
462  Wrap all the global functions.
463  """
464  res = ""
465  for function in functions:
466 
467  function_name = function.name
468 
469  # Add underscore to disambiguate if the function name matches a python keyword
470  python_keywords = self.python_keywords + ['print']
471  if function_name in python_keywords:
472  function_name = function_name + "_"
473 
474  cpp_method = function.to_cpp()
475 
476  is_static = isinstance(function, parser.StaticMethod)
477  return_void = function.return_type.is_void()
478  args_names = function.args.names()
479  py_args_names = self._py_args_names(function.args)
480  args_signature = self._method_args_signature(function.args)
481 
482  caller = namespace + "::"
483  function_call = ('{opt_return} {caller}{function_name}'
484  '({args_names});'.format(
485  opt_return='return'
486  if not return_void else '',
487  caller=caller,
488  function_name=cpp_method,
489  args_names=', '.join(args_names),
490  ))
491 
492  ret = ('{prefix}.{cdef}("{function_name}",'
493  '[]({args_signature}){{'
494  '{function_call}'
495  '}}'
496  '{py_args_names}){suffix}'.format(
497  prefix=prefix,
498  cdef="def_static" if is_static else "def",
499  function_name=function_name,
500  args_signature=args_signature,
501  function_call=function_call,
502  py_args_names=py_args_names,
503  suffix=suffix))
504 
505  res += ret
506 
507  return res
508 
509  def _partial_match(self, namespaces1, namespaces2):
510  for i in range(min(len(namespaces1), len(namespaces2))):
511  if namespaces1[i] != namespaces2[i]:
512  return False
513  return True
514 
515  def _gen_module_var(self, namespaces):
516  """Get the Pybind11 module name from the namespaces."""
517  # We skip the first value in namespaces since it is empty
518  sub_module_namespaces = namespaces[len(self.top_module_namespaces):]
519  return "m_{}".format('_'.join(sub_module_namespaces))
520 
521  def _add_namespaces(self, name, namespaces):
522  if namespaces:
523  # Ignore the first empty global namespace.
524  idx = 1 if not namespaces[0] else 0
525  return '::'.join(namespaces[idx:] + [name])
526  else:
527  return name
528 
529  def wrap_namespace(self, namespace):
530  """Wrap the complete `namespace`."""
531  wrapped = ""
532  includes = ""
533 
534  namespaces = namespace.full_namespaces()
535  if not self._partial_match(namespaces, self.top_module_namespaces):
536  return "", ""
537 
538  if len(namespaces) < len(self.top_module_namespaces):
539  for element in namespace.content:
540  if isinstance(element, parser.Include):
541  include = "{}\n".format(element)
542  # replace the angle brackets with quotes
543  include = include.replace('<', '"').replace('>', '"')
544  includes += include
545  if isinstance(element, parser.Namespace):
546  (
547  wrapped_namespace,
548  includes_namespace,
549  ) = self.wrap_namespace( # noqa
550  element)
551  wrapped += wrapped_namespace
552  includes += includes_namespace
553  else:
554  module_var = self._gen_module_var(namespaces)
555 
556  if len(namespaces) > len(self.top_module_namespaces):
557  wrapped += (
558  ' ' * 4 + 'pybind11::module {module_var} = '
559  '{parent_module_var}.def_submodule("{namespace}", "'
560  '{namespace} submodule");\n'.format(
561  module_var=module_var,
562  namespace=namespace.name,
563  parent_module_var=self._gen_module_var(
564  namespaces[:-1]),
565  ))
566 
567  # Wrap an include statement, namespace, class or enum
568  for element in namespace.content:
569  if isinstance(element, parser.Include):
570  include = "{}\n".format(element)
571  # replace the angle brackets with quotes
572  include = include.replace('<', '"').replace('>', '"')
573  includes += include
574  elif isinstance(element, parser.Namespace):
575  wrapped_namespace, includes_namespace = self.wrap_namespace(
576  element)
577  wrapped += wrapped_namespace
578  includes += includes_namespace
579 
580  elif isinstance(element, instantiator.InstantiatedClass):
581  wrapped += self.wrap_instantiated_class(element)
582  wrapped += self.wrap_enums(element.enums, element)
583 
584  elif isinstance(element, instantiator.InstantiatedDeclaration):
585  wrapped += self.wrap_instantiated_declaration(element)
586 
587  elif isinstance(element, parser.Variable):
588  variable_namespace = self._add_namespaces('', namespaces)
589  wrapped += self.wrap_variable(namespace=variable_namespace,
590  module_var=module_var,
591  variable=element,
592  prefix='\n' + ' ' * 4)
593 
594  elif isinstance(element, parser.Enum):
595  wrapped += self.wrap_enum(element)
596 
597  # Global functions.
598  all_funcs = [
599  func for func in namespace.content
600  if isinstance(func, (parser.GlobalFunction,
601  instantiator.InstantiatedGlobalFunction))
602  ]
603  wrapped += self.wrap_functions(
604  all_funcs,
605  self._add_namespaces('', namespaces)[:-2],
606  prefix='\n' + ' ' * 4 + module_var,
607  suffix=';',
608  )
609 
610  return wrapped, includes
611 
612  def wrap_file(self, content, module_name=None, submodules=None):
613  """
614  Wrap the code in the interface file.
615 
616  Args:
617  content: The contents of the interface file.
618  module_name: The name of the module.
619  submodules: List of other interface file names that should be linked to.
620  """
621  # Parse the contents of the interface file
622  module = parser.Module.parseString(content)
623  # Instantiate all templates
624  module = instantiator.instantiate_namespace(module)
625 
626  wrapped_namespace, includes = self.wrap_namespace(module)
627 
628  if self.use_boost_serialization:
629  includes += "#include <boost/serialization/export.hpp>"
630 
631  # Export classes for serialization.
632  boost_class_export = ""
633  for cpp_class in self._serializing_classes:
634  new_name = cpp_class
635  # The boost's macro doesn't like commas, so we have to typedef.
636  if ',' in cpp_class:
637  new_name = re.sub("[,:<> ]", "", cpp_class)
638  boost_class_export += "typedef {cpp_class} {new_name};\n".format( # noqa
639  cpp_class=cpp_class, new_name=new_name)
640 
641  boost_class_export += "BOOST_CLASS_EXPORT({new_name})\n".format(
642  new_name=new_name, )
643  else:
644  boost_class_export = ""
645 
646  # Reset the serializing classes list
647  self._serializing_classes = []
648 
649  submodules_init = []
650 
651  if submodules is not None:
652  module_def = "PYBIND11_MODULE({0}, m_)".format(module_name)
653 
654  for idx, submodule in enumerate(submodules):
655  submodules[idx] = "void {0}(py::module_ &);".format(submodule)
656  submodules_init.append("{0}(m_);".format(submodule))
657 
658  else:
659  module_def = "void {0}(py::module_ &m_)".format(module_name)
660  submodules = []
661 
662  return self.module_template.format(
663  module_def=module_def,
664  module_name=module_name,
665  includes=includes,
666  wrapped_namespace=wrapped_namespace,
667  boost_class_export=boost_class_export,
668  submodules="\n".join(submodules),
669  submodules_init="\n".join(submodules_init),
670  )
671 
672  def wrap_submodule(self, source):
673  """
674  Wrap a list of submodule files, i.e. a set of interface files which are
675  in support of a larger wrapping project.
676 
677  E.g. This is used in GTSAM where we have a main gtsam.i, but various smaller .i files
678  which are the submodules.
679  The benefit of this scheme is that it reduces compute and memory usage during compilation.
680 
681  Args:
682  source: Interface file which forms the submodule.
683  """
684  filename = Path(source).name
685  module_name = Path(source).stem
686 
687  # Read in the complete interface (.i) file
688  with open(source, "r", encoding="UTF-8") as f:
689  content = f.read()
690  # Wrap the read-in content
691  cc_content = self.wrap_file(content, module_name=module_name)
692 
693  # Generate the C++ code which Pybind11 will use.
694  with open(filename.replace(".i", ".cpp"), "w", encoding="UTF-8") as f:
695  f.write(cc_content)
696 
697  def wrap(self, sources, main_module_name):
698  """
699  Wrap all the main interface file.
700 
701  Args:
702  sources: List of all interface files.
703  The first file should be the main module.
704  main_module_name: The name for the main module.
705  """
706  main_module = sources[0]
707 
708  # Get all the submodule names.
709  submodules = []
710  for source in sources[1:]:
711  module_name = Path(source).stem
712  submodules.append(module_name)
713 
714  with open(main_module, "r", encoding="UTF-8") as f:
715  content = f.read()
716  cc_content = self.wrap_file(content,
717  module_name=self.module_name,
718  submodules=submodules)
719 
720  # Generate the C++ code which Pybind11 will use.
721  with open(main_module_name, "w", encoding="UTF-8") as f:
722  f.write(cc_content)
def wrap_properties(self, properties, cpp_class, prefix='\n'+' ' *8)
#define min(a, b)
Definition: datatypes.h:19
def _gen_module_var(self, namespaces)
def wrap_functions(self, functions, namespace, prefix='\n'+' ' *8, suffix='')
def __init__(self, module_name, top_module_namespaces='', use_boost_serialization=False, ignore_classes=(), module_template="")
def wrap_methods(self, methods, cpp_class, prefix='\n'+' ' *8, suffix='')
def wrap_enum(self, enum, class_name='', module=None, prefix=' ' *4)
bool isinstance(handle obj)
Definition: pytypes.h:700
def _partial_match(self, namespaces1, namespaces2)
def wrap_enums(self, enums, instantiated_class, prefix=' ' *4)
def wrap_file(self, content, module_name=None, submodules=None)
def _wrap_serialization(self, cpp_class)
Definition: pytypes.h:1403
def wrap_variable(self, namespace, module_var, variable, prefix='\n'+' ' *8)
def _add_namespaces(self, name, namespaces)
def wrap(self, sources, main_module_name)
Double_ range(const Point2_ &p, const Point2_ &q)
std::string format(const std::string &str, const std::vector< std::string > &find, const std::vector< std::string > &replace)
def wrap_operators(self, operators, cpp_class, prefix='\n'+' ' *8)
size_t len(handle h)
Get the length of a Python object.
Definition: pytypes.h:2244
def _wrap_method(self, method, cpp_class, prefix, suffix, method_suffix="")


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:35:26