2 Code to use the parsed results and convert it to a format
3 that Matlab's MEX compiler can use.
12 from functools
import partial, reduce
13 from typing
import Dict, Iterable, List, Union
24 """ Wrap the given C++ code into Matlab.
27 module: the C++ module being wrapped
28 module_name: name of the C++ module being wrapped
29 top_module_namespace: C++ namespace for the top module (default '')
30 ignore_classes: A list of classes to ignore (default [])
35 top_module_namespace='',
37 use_boost_serialization=
False):
51 'unsigned char':
'unsigned char',
63 'unsigned char':
'unsigned char',
76 self.wrapper_map: Dict = {}
78 self.includes: List[parser.Include] = []
80 self.classes: List[Union[parser.Class,
81 instantiator.InstantiatedClass]] = []
82 self.classes_elems: Dict[Union[parser.Class,
83 instantiator.InstantiatedClass],
88 self.content: List[str] = []
91 dir_path = osp.dirname(osp.realpath(__file__))
92 with open(osp.join(dir_path,
"matlab_wrapper.tpl"))
as f:
96 """Add `instantiated_class` to the list of classes."""
97 if self.classes_elems.
get(instantiated_class)
is None:
98 self.classes_elems[instantiated_class] = 0
99 self.classes.append(instantiated_class)
102 collector_function=None,
104 function_name: str =
None):
106 Get and define wrapper ids.
107 Generates the map of id -> collector function.
110 collector_function: tuple storing info about the wrapper function
111 (namespace, class instance, function name, function object)
112 id_diff: constant to add to the id in the map
113 function_name: Optional custom function_name.
116 the current wrapper id
118 if collector_function
is not None:
119 is_instantiated_class =
isinstance(collector_function[1],
120 instantiator.InstantiatedClass)
122 if function_name
is None:
123 if is_instantiated_class:
124 function_name = collector_function[0] + \
125 collector_function[1].name +
'_' + collector_function[2]
127 function_name = collector_function[1].name
130 collector_function[0], collector_function[1],
131 collector_function[2], function_name +
'_' +
139 return 'handle' if names ==
'' else names
142 """Insert spaces at the beginning of each line
145 x: the statement currently generated
146 y: the addition to add to the statement
148 return x +
'\n' + (
'' if y ==
'' else ' ') + y
152 """Recursively expand all possibilities for optional default arguments.
153 We create "overload" functions with fewer arguments, but since we have to "remember" what
154 the default arguments are for later, we make a backup.
158 return ArgumentList([copy.copy(arg)
for arg
in args.list()])
160 def method_copy(method):
161 method2 = copy.copy(method)
162 method2.args = args_copy(method.args)
163 method2.args.backup = method.args.backup
167 method.args.backup = args_copy(method.args)
168 method = method_copy(method)
169 for arg
in reversed(method.args.list()):
170 if arg.default
is not None:
172 methodWithArg = method_copy(method)
173 method.args.list().remove(arg)
176 *MatlabWrapper._expand_default_arguments(method,
180 assert all(arg.default
is None for arg
in method.args.list()), \
181 'In parsing method {:}: Arguments with default values cannot appear before ones ' \
182 'without default values.'.
format(method.name)
186 """Group overloaded methods together"""
190 for method
in methods:
191 method_index = method_map.get(method.name)
193 if method_index
is None:
194 method_map[method.name] =
len(method_out)
196 MatlabWrapper._expand_default_arguments(method))
199 method_index] += MatlabWrapper._expand_default_arguments(
205 """Wrap an interface_parser.ArgumentList into a list of arguments.
208 A string representation of the arguments. For example:
213 for i, arg
in enumerate(args.list(), 1):
215 include_namespace=
False)
217 arg_wrap +=
'{c_type} {arg_name}{comma}'.
format(
220 comma=
'' if i ==
len(args.list())
else ', ')
225 """ Wrap an interface_parser.ArgumentList into a statement of argument
229 A string representation of a variable arguments for an if
230 statement. For example:
231 ' && isa(varargin{1},'double') && isa(varargin{2},'numeric')'
235 for i, arg
in enumerate(args.list(), 1):
236 name = arg.ctype.typename.name
237 if name
in self.not_check_type:
245 if check_type
is None:
249 is_constructor=
not wrap_datatypes)
251 var_arg_wrap +=
" && isa(varargin{{{num}}},'{data_type}')".
format(
252 num=i, data_type=check_type)
254 var_arg_wrap +=
' && size(varargin{{{num}}},2)==1'.
format(
257 var_arg_wrap +=
' && size(varargin{{{num}}},1)==2'.
format(
259 var_arg_wrap +=
' && size(varargin{{{num}}},2)==1'.
format(
262 var_arg_wrap +=
' && size(varargin{{{num}}},1)==3'.
format(
264 var_arg_wrap +=
' && size(varargin{{{num}}},2)==1'.
format(
270 """ Wrap an interface_parser.ArgumentList into a list of argument
274 A string representation of a list of variable arguments.
276 'varargin{1}, varargin{2}, varargin{3}'
281 for i
in range(1,
len(args.list()) + 1):
283 var_list_wrap +=
'varargin{{{}}}'.
format(i)
286 var_list_wrap +=
', varargin{{{}}}'.
format(i)
292 Wrap the given arguments into either just a varargout call or a
293 call in an if statement that checks if the parameters are accurate.
295 TODO Update this method so that default arguments are supported.
299 param_count =
len(args)
300 check_statement =
'if length(varargin) == {param_count}'.
format(
301 param_count=param_count)
303 for _, arg
in enumerate(args.list()):
304 name = arg.ctype.typename.name
306 if name
in self.not_check_type:
315 if check_type
is None:
319 check_statement +=
" && isa(varargin{{{id}}},'{ctype}')".
format(
320 id=arg_id, ctype=check_type)
323 check_statement +=
' && size(varargin{{{num}}},2)==1'.
format(
326 check_statement +=
' && size(varargin{{{num}}},1)==2'.
format(
328 check_statement +=
' && size(varargin{{{num}}},2)==1'.
format(
331 check_statement +=
' && size(varargin{{{num}}},1)==3'.
format(
333 check_statement +=
' && size(varargin{{{num}}},2)==1'.
format(
338 check_statement = check_statement \
339 if check_statement ==
'' \
340 else check_statement +
'\n'
342 return check_statement
348 if instantiated_class
and \
349 self.
is_enum(arg.ctype, instantiated_class):
350 enum_type = f
"{arg.ctype.typename}"
351 arg_type = f
"{enum_type}"
352 unwrap = f
'unwrap_enum<{enum_type}>(in[{arg_id}]);'
354 elif self.
is_ref(arg.ctype):
355 arg_type =
"{ctype}&".
format(ctype=ctype_sep)
356 unwrap =
'*unwrap_shared_ptr< {ctype} >(in[{id}], "ptr_{ctype_camel}");'.
format(
357 ctype=ctype_sep, ctype_camel=ctype_camel, id=arg_id)
359 elif self.
is_ptr(arg.ctype)
and \
360 arg.ctype.typename.name
not in self.ignore_namespace:
362 arg_type =
"{ctype_sep}*".
format(ctype_sep=ctype_sep)
363 unwrap =
'unwrap_ptr< {ctype_sep} >(in[{id}], "ptr_{ctype}");'.
format(
364 ctype_sep=ctype_sep, ctype=ctype_camel, id=arg_id)
367 arg.ctype.typename.name
not in self.ignore_namespace:
369 arg_type =
"std::shared_ptr<{ctype_sep}>".
format(
371 unwrap =
'unwrap_shared_ptr< {ctype_sep} >(in[{id}], "ptr_{ctype}");'.
format(
372 ctype_sep=ctype_sep, ctype=ctype_camel, id=arg_id)
375 arg_type =
"{ctype}".
format(ctype=arg.ctype.typename.name)
376 unwrap =
'unwrap< {ctype} >(in[{id}]);'.
format(
377 ctype=arg.ctype.typename.name, id=arg_id)
379 return arg_type, unwrap
384 instantiated_class=None):
385 """Format the interface_parser.Arguments.
388 ((a), unsigned char a = unwrap< unsigned char >(in[1]);),
389 ((a), Test& t = *unwrap_shared_ptr< Test >(in[1], "ptr_Test");),
390 ((a), std::shared_ptr<Test> p1 = unwrap_shared_ptr< Test >(in[1], "ptr_Test");)
394 for arg
in args.list():
396 arg, arg_id, instantiated_class=instantiated_class)
398 body_args += textwrap.indent(textwrap.dedent(
'''\
399 {arg_type} {name} = {unwrap}
400 '''.
format(arg_type=arg_type, name=arg.name,
406 explicit_arg_names = [arg.name
for arg
in args.list()]
408 for arg
in args.backup.list():
412 if (arg.default
is not None)
and (arg.name
413 not in explicit_arg_names):
414 params += arg.default
419 not self.
is_enum(arg.ctype, instantiated_class)
and \
420 arg.ctype.typename.name
not in self.ignore_namespace:
421 if arg.ctype.is_shared_ptr:
422 call_type = arg.ctype.is_shared_ptr
424 call_type = arg.ctype.is_ptr
429 return params, body_args
433 """The amount of objects returned by the given
434 interface_parser.ReturnType.
436 return 1
if return_type.type2 ==
'' else 2
439 """Determine the name of wrapper function."""
443 """Generate comments for serialize methods."""
445 static_methods = sorted(static_methods, key=
lambda name: name.name)
447 for static_method
in static_methods:
448 if comment_wrap ==
'':
449 comment_wrap =
'%-------Static Methods-------\n'
451 comment_wrap +=
'%{name}({args}) : returns {return_type}\n'.
format(
452 name=static_method.name,
455 include_namespace=
True))
457 comment_wrap += textwrap.dedent(
'''\
459 %-------Serialization Interface-------
460 %string_serialize() : returns string
461 %string_deserialize(string serialized) : returns {class_name}
463 ''').
format(class_name=class_name)
468 """Generate comments for the given class in Matlab.
471 instantiated_class: the class being wrapped
472 ctors: a list of the constructors in the class
473 methods: a list of the methods in the class
475 class_name = instantiated_class.name
476 ctors = instantiated_class.ctors
477 properties = instantiated_class.properties
478 methods = instantiated_class.methods
479 static_methods = instantiated_class.static_methods
481 comment = textwrap.dedent(
'''\
482 %class {class_name}, see Doxygen page for details
483 %at https://gtsam.org/doxygen/
484 ''').
format(class_name=class_name)
487 comment +=
'%\n%-------Constructors-------\n'
491 comment +=
'%{ctor_name}({args})\n'.
format(ctor_name=ctor.name,
495 if len(properties) != 0:
497 '%-------Properties-------\n'
498 for propty
in properties:
499 comment +=
'%{}\n'.
format(propty.name)
501 if len(methods) != 0:
503 '%-------Methods-------\n'
505 methods = sorted(methods, key=
lambda name: name.name)
508 for method
in methods:
509 if method.name
in self.whitelist:
511 if method.name
in self.ignore_methods:
514 comment +=
'%{name}({args})'.
format(name=method.name,
518 if method.return_type.type2 ==
'':
520 method.return_type.type1.typename)
522 return_type =
'pair< {type1}, {type2} >'.
format(
524 method.return_type.type1.typename),
526 method.return_type.type2.typename))
528 comment +=
' : returns {return_type}\n'.
format(
529 return_type=return_type)
533 if len(static_methods) != 0:
540 Wrap methods in the body of a class.
549 Wrap a sequence of methods/functions. Groups methods with the same names
551 If global_funcs is True then output every method into its own file.
556 for method
in methods:
557 if method
in self.ignore_methods:
562 self.content.append((
"".join([
563 '+' + x +
'/' for x
in global_ns.full_namespaces()[1:]
564 ])[:-1], [(method[0].name +
'.m', method_text)]))
572 """Wrap the given global function."""
574 function = [function]
576 function_name = function[0].name
581 for i, overload
in enumerate(function):
582 param_wrap +=
' if' if i == 0
else ' elseif'
583 param_wrap +=
' length(varargin) == '
585 if len(overload.args.list()) == 0:
588 param_wrap +=
str(
len(overload.args.list())) \
593 overload.return_type, include_namespace=
True, separator=
".")
595 return_type_formatted)
597 param_wrap += textwrap.indent(textwrap.dedent(
'''\
598 {varargout}{module_name}_wrapper({num}, varargin{{:}});
599 ''').
format(varargout=varargout,
602 collector_function=(function[0].parent.name,
603 function[i],
'global_function',
607 param_wrap += textwrap.indent(textwrap.dedent(
'''\
609 error('Arguments do not match any overload of function {func_name}');
610 end''').
format(func_name=function_name),
613 global_function = textwrap.indent(textwrap.dedent(
'''\
614 function varargout = {m_method}(varargin)
617 ''').
format(m_method=function_name, statements=param_wrap),
620 return global_function
624 """Wrap class constructor.
627 namespace_name: the name of the namespace ('' if it does not exist)
628 inst_class: instance of the class
629 parent_name: the name of the parent class if it exists
630 ctors: the interface_parser.Constructor in the class
631 is_virtual: whether the class is part of a virtual inheritance
634 has_parent = parent_name !=
''
635 class_name = inst_class.name
641 ctors = sum((MatlabWrapper._expand_default_arguments(ctor)
642 for ctor
in ctors), [])
644 methods_wrap = textwrap.indent(textwrap.dedent(
"""\
646 function obj = {class_name}(varargin)
647 """).
format(class_name=class_name),
651 methods_wrap +=
" if (nargin == 2 || (nargin == 3 && strcmp(varargin{3}, 'void')))"
653 methods_wrap +=
' if nargin == 2'
655 methods_wrap +=
" && isa(varargin{1}, 'uint64')"
656 methods_wrap +=
" && varargin{1} == uint64(5139824614673773682)\n"
659 methods_wrap += textwrap.indent(textwrap.dedent(
'''\
661 my_ptr = varargin{{2}};
663 my_ptr = {wrapper_name}({id}, varargin{{2}});
669 methods_wrap +=
' my_ptr = varargin{2};\n'
672 (namespace_name, inst_class,
'collectorInsertAndMakeBase',
None),
673 id_diff=-1
if is_virtual
else 0)
675 methods_wrap +=
' {ptr}{wrapper_name}({id}, my_ptr);\n' \
677 ptr=
'base_ptr = ' if has_parent
else '',
679 id=collector_base_id - (1
if is_virtual
else 0))
682 wrapper_return =
'[ my_ptr, base_ptr ] = ' \
686 methods_wrap += textwrap.indent(textwrap.dedent(
'''\
687 elseif nargin == {len}{varargin}
688 {ptr}{wrapper}({num}{comma}{var_arg});
695 (namespace_name, inst_class,
'constructor', ctor)),
696 comma=
'' if len(ctor.args.list()) == 0
else ', ',
703 base_obj =
' obj = obj@{parent_name}(uint64(5139824614673773682), base_ptr);'.
format(
704 parent_name=parent_name)
707 base_obj =
'\n' + base_obj
709 methods_wrap += textwrap.indent(textwrap.dedent(
'''\
711 error('Arguments do not match any overload of {class_name_doc} constructor');
713 obj.ptr_{class_name} = my_ptr;
715 ''').
format(namespace=namespace_name,
716 d=
'' if namespace_name ==
'' else '.',
727 """Generate Matlab properties block of the class.
732 ptr_gtsamISAM2Params = 0
738 class_name: Class name with namespace to assign unique pointer.
739 inst_class: The instantiated class whose properties we want to wrap.
742 str: The `properties` block in a Matlab `classdef`.
745 class_pointer =
" ptr_{class_name} = 0".
format(class_name=class_name)
747 if len(inst_class.properties) > 0:
748 properties =
'\n' +
"\n".join(
749 [
" {}".
format(p.name)
for p
in inst_class.properties])
753 properties = class_pointer + properties
754 properties_block = textwrap.dedent(
'''\
758 ''').
format(properties=properties)
759 return properties_block
762 inst_class: InstantiatedClass):
763 """Generate wrappers for the setters & getters of class properties.
766 inst_class: The instantiated class whose properties we wish to wrap.
769 for propty
in inst_class.properties:
771 function_name = namespace_name + inst_class.name +
'_get_' + propty.name
773 function varargout = get.{name}(this)
774 {varargout} = {wrapper}({num}, this);
775 this.{name} = {varargout};
777 """.
format(name=propty.name,
778 varargout=
'varargout{1}',
781 (namespace_name, inst_class, propty.name, propty),
782 function_name=function_name))
783 properties.append(getter)
786 function_name = namespace_name + inst_class.name +
'_set_' + propty.name
788 function set.{name}(this, value)
790 {wrapper}({num}, this, value);
792 """.
format(name=propty.name,
795 (namespace_name, inst_class, propty.name, propty),
796 function_name=function_name))
797 properties.append(setter)
802 """Generate the delete function for the Matlab class."""
803 class_name = inst_class.name
805 methods_text = textwrap.indent(textwrap.dedent(
"""\
807 {wrapper}({num}, obj.ptr_{class_name});
810 (namespace_name, inst_class,
'deconstructor',
None)),
812 class_name=
"".join(inst_class.parent.full_namespaces()) +
819 """Generate the display function for the Matlab class."""
820 return textwrap.indent(textwrap.dedent(
"""\
821 function display(obj), obj.print(''); end
822 %DISPLAY Calls print on the object
823 function disp(obj), obj.display; end
824 %DISP Calls print on the object
829 """Group overloaded methods together"""
834 """Determine format of return and varargout statements"""
837 if return_type_formatted ==
'void' \
838 else 'varargout{1} = '
840 varargout =
'[ varargout{1} varargout{2} ] = '
848 serialize=(
False, )):
849 """Wrap the methods in the class.
852 namespace_name: the name of the class's namespace
853 inst_class: the instantiated class whose methods to wrap
854 methods: the methods to wrap in the order to wrap them
855 serialize: mutable param storing if one of the methods is serialize
863 serialize =
list(serialize)
865 for method
in methods:
866 method_name = method[0].name
867 if method_name
in self.whitelist
and method_name !=
'serialize':
869 if method_name
in self.ignore_methods:
872 if method_name ==
'serialize':
876 namespace_name, inst_class)
880 method_text += textwrap.indent(textwrap.dedent(
"""\
881 function varargout = {method_name}(this, varargin)
882 """).
format(caps_name=method_name.upper(),
883 method_name=method_name),
886 for overload
in method:
887 method_text += textwrap.indent(textwrap.dedent(
"""\
888 % {caps_name} usage: {method_name}(""").
format(
889 caps_name=method_name.upper(),
890 method_name=method_name),
895 overload.return_type,
896 include_namespace=
True,
899 return_type_formatted)
903 class_name = namespace_name + (
'' if namespace_name ==
''
904 else '.') + inst_class.name
907 if check_statement ==
'' \
908 else textwrap.indent(textwrap.dedent(
"""\
912 class_name=class_name,
913 method_name=overload.original.name), prefix=
' ')
915 method_text += textwrap.dedent(
"""\
916 {method_args}) : returns {return_type}
917 % Doxygen can be found at https://gtsam.org/doxygen/
918 {check_statement}{spacing}{varargout}{wrapper}({num}, this, varargin{{:}});
919 {end_statement}""").
format(
921 return_type=return_type_formatted,
923 (namespace_name, inst_class,
924 overload.original.name, overload)),
925 check_statement=check_statement,
926 spacing=
'' if check_statement ==
'' else ' ',
929 end_statement=end_statement)
931 final_statement = textwrap.indent(textwrap.dedent(
"""\
932 error('Arguments do not match any overload of function {class_name}.{method_name}');
933 """.
format(class_name=class_name, method_name=method_name)),
935 method_text += final_statement +
'end\n\n'
942 Wrap the static methods in the class.
944 class_name = instantiated_class.name
946 method_text =
'methods(Static = true)\n'
947 static_methods = sorted(instantiated_class.static_methods,
948 key=
lambda name: name.name)
952 for static_method
in static_methods:
953 format_name =
list(static_method[0].name)
954 format_name[0] = format_name[0]
956 if static_method[0].name
in self.ignore_methods:
959 method_text += textwrap.indent(textwrap.dedent(
'''\
960 function varargout = {name}(varargin)
961 '''.
format(name=
''.join(format_name))),
964 for static_overload
in static_method:
966 static_overload.args)
969 if check_statement ==
'' \
970 else textwrap.indent(textwrap.dedent(
"""
974 method_text += textwrap.indent(textwrap.dedent(
'''\
975 % {name_caps} usage: {name_upper_case}({args}) : returns {return_type}
976 % Doxygen can be found at https://gtsam.org/doxygen/
977 {check_statement}{spacing}varargout{{1}} = {wrapper}({id}, varargin{{:}});{end_statement}
979 name=
''.join(format_name),
980 name_caps=static_overload.name.upper(),
981 name_upper_case=static_overload.name,
984 static_overload.return_type,
985 include_namespace=
True,
987 length=
len(static_overload.args.list()),
989 static_overload.args),
990 check_statement=check_statement,
991 spacing=
'' if check_statement ==
'' else ' ',
994 (namespace_name, instantiated_class,
995 static_overload.name, static_overload)),
996 class_name=instantiated_class.name,
997 end_statement=end_statement),
1002 method_text += textwrap.indent(textwrap.dedent(
"""\
1003 error('Arguments do not match any overload of function {class_name}.{method_name}');
1004 """.
format(class_name=class_name,
1005 method_name=static_overload.name)),
1008 method_text += textwrap.indent(textwrap.dedent(
"""\
1014 method_text += WrapperTemplate.matlab_deserialize.format(
1015 class_name=namespace_name +
'.' + instantiated_class.name,
1018 (namespace_name, instantiated_class,
'string_deserialize',
1025 namespace_name: str =
''):
1026 """Generate comments and code for given class.
1029 instantiated_class: template_instantiator.InstantiatedClass
1030 instance storing the class to wrap
1031 namespace_name: the name of the namespace if there is one
1034 namespace_file_name = namespace_name + file_name
1036 uninstantiated_name =
"::".join(instantiated_class.namespaces()
1037 [1:]) +
"::" + instantiated_class.name
1043 content_text += self.
wrap_methods(instantiated_class.methods)
1051 content_text +=
'classdef {class_name} < {parent}\n'.
format(
1052 class_name=file_name,
1054 instantiated_class.parent_class)).replace(
"::",
"."))
1057 content_text +=
' ' + reduce(
1060 instantiated_class).splitlines()) +
'\n'
1063 content_text +=
' ' + reduce(
1068 instantiated_class.parent_class,
1069 instantiated_class.ctors,
1070 instantiated_class.is_virtual,
1071 ).splitlines()) +
'\n'
1074 content_text +=
' ' + reduce(
1077 namespace_name, instantiated_class).splitlines()) +
'\n'
1080 content_text +=
' ' + reduce(
1087 if len(instantiated_class.methods) != 0:
1088 methods = sorted(instantiated_class.methods,
1089 key=
lambda name: name.name)
1094 serialize=serialize).splitlines()
1095 if len(class_methods_wrapped) > 0:
1096 content_text +=
' ' + reduce(
1097 lambda x, y: x +
'\n' + (
'' if y ==
'' else ' ') + y,
1098 class_methods_wrapped) +
'\n'
1101 if len(instantiated_class.properties) != 0:
1103 namespace_name, instantiated_class)
1104 content_text += textwrap.indent(textwrap.dedent(
1105 "".join(property_accessors)),
1108 content_text +=
' end'
1111 content_text +=
'\n\n ' + reduce(
1114 serialize[0]).splitlines()) +
'\n' + \
1118 content_text += textwrap.dedent(
'''\
1125 for enum
in instantiated_class.enums:
1127 if namespace_name !=
'':
1128 submodule = f
"+{namespace_name}/"
1131 submodule += f
"+{instantiated_class.name}"
1132 self.content.append((submodule, [enum_text]))
1134 return file_name +
'.m', content_text
1138 Wrap an enum definition as a Matlab class.
1141 enum: The interface_parser.Enum instance
1143 file_name = enum.name +
'.m'
1144 enum_template = textwrap.dedent(
"""\
1145 classdef {0} < uint32
1151 enumerators =
"\n ".join([
1152 f
"{enumerator.name}({idx})"
1153 for idx, enumerator
in enumerate(enum.enumerators)
1156 content = enum_template.format(enum.name, enumerators)
1157 return file_name, content
1160 """Wrap a namespace by wrapping all of its components.
1163 namespace: the interface_parser.namespace instance of the namespace
1164 add_cpp_file: Flag indicating whether the mex file should be added
1166 namespaces = namespace.full_namespaces()
1167 inner_namespace = namespace.name !=
''
1170 top_level_scope = []
1171 inner_namespace_scope = []
1173 for element
in namespace.content:
1175 self.includes.append(element)
1184 '+' + x +
'/' for x
in namespace.full_namespaces()[1:]
1186 inner_namespace_scope.append((module, [(file, content)]))
1188 top_level_scope.append((file, content))
1190 elif isinstance(element, instantiator.InstantiatedClass):
1195 element,
"".join(namespace.full_namespaces()))
1197 if not class_text
is None:
1198 inner_namespace_scope.append((
"".join([
1200 for x
in namespace.full_namespaces()[1:]
1201 ])[:-1], [(class_text[0], class_text[1])]))
1204 top_level_scope.append((class_text[0], class_text[1]))
1206 self.content.extend(top_level_scope)
1209 self.content.append(inner_namespace_scope)
1217 func
for func
in namespace.content
1221 self.
wrap_methods(all_funcs,
True, global_ns=namespace)
1230 """Wrap the collector function which returns a shared pointer."""
1231 new_line =
'\n' if new_line
else ''
1233 return WrapperTemplate.collector_function_shared_return.format(
1235 include_namespace=
False),
1236 shared_obj=shared_obj,
1242 Wrap the return type of the collector function when a std::pair is returned.
1244 return_type_text =
' out[' +
str(func_id) +
'] = '
1245 pair_value =
'first' if func_id == 0
else 'second'
1246 new_line =
'\n' if func_id == 0
else ''
1250 shared_obj =
'pairResult.' + pair_value
1252 if not (return_type.is_shared_ptr
or return_type.is_ptr):
1253 shared_obj =
'std::make_shared<{name}>({shared_obj})' \
1255 shared_obj=
'pairResult.' + pair_value)
1257 if return_type.typename.name
in self.ignore_namespace:
1259 return_type.typename, shared_obj, func_id, func_id == 0)
1261 return_type_text +=
'wrap_shared_ptr({0},"{1}", false);{new_line}' \
1267 return_type_text +=
'wrap< {0} >(pairResult.{1});{2}'.
format(
1269 pair_value, new_line)
1271 return return_type_text
1276 instantiated_class: InstantiatedClass =
None):
1277 """Helper method to get the final statement before the return in the collector function."""
1280 if instantiated_class
and \
1281 self.
is_enum(ctype, instantiated_class):
1283 class_name =
".".join(instantiated_class.namespaces()[1:] +
1284 [instantiated_class.name])
1287 class_name =
".".join(
1288 instantiated_class.parent.full_namespaces()[1:])
1290 if class_name !=
"":
1293 enum_type = f
"{class_name}{ctype.typename.name}"
1294 expanded = textwrap.indent(
1295 f
'out[0] = wrap_enum({obj},\"{enum_type}\");', prefix=
' ')
1301 include_namespace=
True)
1303 if ctype.typename.name
in self.ignore_namespace:
1305 ctype.typename, obj, 0, new_line=
False)
1307 if ctype.is_shared_ptr
or ctype.is_ptr:
1308 shared_obj =
'{obj},"{method_name_sep}"'.
format(
1309 obj=obj, method_name_sep=sep_method_name(
'.'))
1311 method_name_sep_dot = sep_method_name(
'.')
1315 if isinstance(ctype, parser.TemplatedType)
and \
1316 "std::optional" ==
str(ctype.typename)[:13]:
1318 type_name = ctype.template_params[0].typename
1319 method_name_sep_dot =
".".join(
1320 type_name.namespaces) + f
".{type_name.name}"
1323 shared_obj_template =
'std::make_shared<{method_name_sep_col}>({obj}),' \
1324 '"{method_name_sep_dot}"'
1325 shared_obj = shared_obj_template \
1326 .
format(method_name_sep_col=sep_method_name(),
1327 method_name_sep_dot=method_name_sep_dot,
1330 if ctype.typename.name
not in self.ignore_namespace:
1331 expanded += textwrap.indent(
1332 'out[0] = wrap_shared_ptr({0}, false);'.
format(shared_obj),
1335 expanded +=
' out[0] = wrap< {0} >({1});'.
format(
1336 ctype.typename.name, obj)
1342 Wrap the complete return type of the function.
1347 method.args, arg_id=1, instantiated_class=instantiated_class)[0]
1349 return_1 = method.return_type.type1
1351 return_1_name = method.return_type.type1.typename.name
1354 if isinstance(method, instantiator.InstantiatedMethod):
1355 method_name = method.to_cpp()
1358 if method.instantiations:
1361 method = method.to_cpp()
1363 elif isinstance(method, instantiator.InstantiatedStaticMethod):
1365 method_name += method.original.name
1367 elif isinstance(method, parser.GlobalFunction):
1369 method_name += method.name
1372 if isinstance(method.parent, instantiator.InstantiatedClass):
1373 method_name = method.parent.to_cpp() +
"::"
1376 method_name += method.name
1378 obj =
' ' if return_1_name ==
'void' else ''
1379 obj +=
'{}{}({})'.
format(obj_start, method_name, params)
1381 if return_1_name !=
'void':
1382 if return_count == 1:
1384 obj, return_1, instantiated_class=instantiated_class)
1386 elif return_count == 2:
1387 return_2 = method.return_type.type2
1389 expanded +=
' auto pairResult = {};\n'.
format(obj)
1395 expanded += obj +
';'
1401 class_property: parser.Variable,
1402 instantiated_class: InstantiatedClass =
None):
1403 """Get the last collector function statement before return for a property."""
1404 property_name = class_property.name
1405 obj =
'obj->{}'.
format(property_name)
1408 class_property.ctype,
1409 instantiated_class=instantiated_class)
1414 Add function to upcast type from void type.
1416 return WrapperTemplate.collector_function_upcast_from_void.format(
1417 class_name=class_name, cpp_name=cpp_name, id=func_id)
1421 Generate the complete collector function that goes into the wrapper.cpp file.
1423 A collector function is the Mex function used to interact between
1424 the C++ object and the Matlab .m files.
1426 collector_func = self.wrapper_map.
get(func_id)
1428 if collector_func
is None:
1431 method_name = collector_func[3]
1433 collector_function =
"void {}" \
1434 "(int nargout, mxArray *out[], int nargin, const mxArray *in[])\n".
format(method_name)
1436 if isinstance(collector_func[1], instantiator.InstantiatedClass):
1439 extra = collector_func[4]
1441 class_name = collector_func[0] + collector_func[1].name
1442 class_name_separated = collector_func[1].
to_cpp()
1444 is_static_method =
isinstance(extra, parser.StaticMethod)
1445 is_property =
isinstance(extra, parser.Variable)
1447 if collector_func[2] ==
'collectorInsertAndMakeBase':
1448 body += textwrap.indent(textwrap.dedent(
'''\
1449 mexAtExit(&_deleteAllObjects);
1450 typedef std::shared_ptr<{class_name_sep}> Shared;\n
1451 Shared *self = *reinterpret_cast<Shared**> (mxGetData(in[0]));
1452 collector_{class_name}.insert(self);
1453 ''').
format(class_name_sep=class_name_separated,
1454 class_name=class_name),
1457 if collector_func[1].parent_class:
1458 body += textwrap.indent(textwrap.dedent(
'''
1459 typedef std::shared_ptr<{}> SharedBase;
1460 out[0] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);
1461 *reinterpret_cast<SharedBase**>(mxGetData(out[0])) = new SharedBase(*self);
1462 ''').
format(collector_func[1].parent_class),
1465 elif collector_func[2] ==
'constructor':
1468 extra.args, instantiated_class=collector_func[1])
1470 if collector_func[1].parent_class:
1471 base += textwrap.indent(textwrap.dedent(
'''
1472 typedef std::shared_ptr<{}> SharedBase;
1473 out[1] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);
1474 *reinterpret_cast<SharedBase**>(mxGetData(out[1])) = new SharedBase(*self);
1475 ''').
format(collector_func[1].parent_class),
1478 body += textwrap.dedent(
'''\
1479 mexAtExit(&_deleteAllObjects);
1480 typedef std::shared_ptr<{class_name_sep}> Shared;\n
1481 {body_args} Shared *self = new Shared(new {class_name_sep}({params}));
1482 collector_{class_name}.insert(self);
1483 out[0] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);
1484 *reinterpret_cast<Shared**> (mxGetData(out[0])) = self;
1485 {base}''').
format(class_name_sep=class_name_separated,
1486 body_args=body_args,
1488 class_name=class_name,
1491 elif collector_func[2] ==
'deconstructor':
1492 body += textwrap.indent(textwrap.dedent(
'''\
1493 typedef std::shared_ptr<{class_name_sep}> Shared;
1494 checkArguments("delete_{class_name}",nargout,nargin,1);
1495 Shared *self = *reinterpret_cast<Shared**>(mxGetData(in[0]));
1496 Collector_{class_name}::iterator item;
1497 item = collector_{class_name}.find(self);
1498 if(item != collector_{class_name}.end()) {{
1499 collector_{class_name}.erase(item);
1502 ''').
format(class_name_sep=class_name_separated,
1503 class_name=class_name),
1506 elif extra ==
'serialize':
1509 collector_func[1].name,
1510 full_name=collector_func[1].
to_cpp(),
1511 namespace=collector_func[0])
1513 elif extra ==
'deserialize':
1516 collector_func[1].name,
1517 full_name=collector_func[1].
to_cpp(),
1518 namespace=collector_func[0])
1520 elif is_method
or is_static_method:
1523 if is_static_method:
1526 method_name += extra.name
1530 arg_id=1
if is_method
else 0,
1531 instantiated_class=collector_func[1])
1534 extra, collector_func[1])
1539 shared_obj =
' auto obj = unwrap_shared_ptr<{class_name_sep}>' \
1540 '(in[0], "ptr_{class_name}");\n'.
format(
1541 class_name_sep=class_name_separated,
1542 class_name=class_name)
1544 body +=
' checkArguments("{method_name}",nargout,nargin{min1},' \
1548 '{return_body}\n'.
format(
1549 min1=
'-1' if is_method
else '',
1550 shared_obj=shared_obj,
1551 method_name=method_name,
1552 num_args=
len(extra.args.list()),
1553 body_args=body_args,
1554 return_body=return_body)
1557 shared_obj =
' auto obj = unwrap_shared_ptr<{class_name_sep}>' \
1558 '(in[0], "ptr_{class_name}");\n'.
format(
1559 class_name_sep=class_name_separated,
1560 class_name=class_name)
1564 extra, arg_id=1, instantiated_class=collector_func[1])
1565 unpack_property = textwrap.indent(textwrap.dedent(
'''\
1566 {arg_type} {name} = {unwrap}
1567 '''.
format(arg_type=property_type,
1573 if "_get_" in method_name:
1575 extra, instantiated_class=collector_func[1])
1577 getter =
' checkArguments("{property_name}",nargout,nargin{min1},' \
1580 '{return_body}\n'.
format(
1581 property_name=extra.name,
1584 shared_obj=shared_obj,
1585 return_body=return_body)
1590 if "_set_" in method_name:
1592 not self.
is_enum(extra.ctype, collector_func[1])
1593 return_body =
' obj->{0} = {1}{0};'.
format(
1594 extra.name,
'*' if is_ptr_type
else '')
1596 setter =
' checkArguments("{property_name}",nargout,nargin{min1},' \
1599 '{unpack_property}' \
1600 '{return_body}\n'.
format(
1601 property_name=extra.name,
1604 shared_obj=shared_obj,
1605 unpack_property=unpack_property,
1606 return_body=return_body)
1612 if extra
not in [
'serialize',
'deserialize']:
1615 collector_function += body
1618 body = textwrap.dedent(
'''\
1620 checkArguments("{function_name}",nargout,nargin,{len});
1621 ''').
format(function_name=collector_func[1].name,
1623 len=
len(collector_func[1].args.list()))
1627 collector_func[1]) +
'\n}\n'
1629 collector_function += body
1633 return collector_function
1637 Generate the wrapped MEX function.
1643 id_val = self.wrapper_map.
get(wrapper_id)
1644 set_next_case =
False
1647 id_val = self.wrapper_map.
get(wrapper_id + 1)
1652 set_next_case =
True
1654 cases += textwrap.indent(textwrap.dedent(
'''\
1656 {}(nargout, out, nargin-1, in+1);
1658 ''').
format(wrapper_id, next_case
if next_case
else id_val[3]),
1662 next_case =
'{}_upcastFromVoid_{}'.
format(
1663 id_val[1].name, wrapper_id + 1)
1667 mex_function = WrapperTemplate.mex_function.format(
1673 """Get the name of the class `cls` taking template instantiations into account."""
1674 if cls.instantiations:
1675 class_name_sep = cls.name
1677 class_name_sep = cls.to_cpp()
1681 return class_name, class_name_sep
1685 Generate the preamble of the wrapper file, which includes
1686 the Boost exports, typedefs for collectors, and
1687 the _deleteAllObjects and _RTTIRegister functions.
1690 typedef_instances = []
1691 boost_class_export_guid =
''
1692 typedef_collectors =
''
1695 for cls
in self.classes:
1698 uninstantiated_name =
"::".join(cls.namespaces()[1:] + [cls.name])
1705 if cls.instantiations:
1707 for i, inst
in enumerate(cls.instantiations):
1713 typedef_instances.append(
'typedef {original_class_name} {class_name_sep};' \
1714 .
format(original_class_name=cls.to_cpp(),
1715 class_name_sep=cls.name))
1720 boost_class_export_guid +=
'BOOST_CLASS_EXPORT_GUID({}, "{}");\n'.
format(
1721 class_name_sep, class_name)
1724 typedef_collectors += WrapperTemplate.typdef_collectors.format(
1725 class_name_sep=class_name_sep, class_name=class_name)
1728 delete_objs += WrapperTemplate.delete_obj.format(
1729 class_name=class_name)
1733 rtti_classes +=
' types.insert(std::make_pair(typeid({}).name(), "{}"));\n' \
1734 .
format(class_name_sep, class_name)
1737 typedef_instances =
"\n".join(typedef_instances)
1740 delete_all_objs = WrapperTemplate.delete_all_objects.format(
1741 delete_objs=delete_objs)
1744 rtti_register = WrapperTemplate.rtti_register.format(
1745 module_name=self.
module_name, rtti_classes=rtti_classes)
1747 return typedef_instances, boost_class_export_guid, \
1748 typedef_collectors, delete_all_objs, rtti_register
1751 """Generate the c++ wrapper."""
1752 assert namespace,
"Namespace if empty"
1755 includes_list = sorted(self.includes,
1756 key=
lambda include: include.header)
1760 boost_headers = WrapperTemplate.boost_headers
1764 includes = textwrap.dedent(
"""\
1765 {wrapper_file_headers}
1769 boost_headers=boost_headers,
1770 includes_list=
'\n'.join(map(str, includes_list)))
1773 typedef_instances, boost_class_export_guid, \
1774 typedef_collectors, delete_all_objs, \
1775 rtti_register = preamble
1778 set_next_case =
False
1781 id_val = self.wrapper_map.
get(idx)
1782 queue_set_next_case = set_next_case
1784 set_next_case =
False
1787 id_val = self.wrapper_map.
get(idx + 1)
1792 set_next_case =
True
1796 if queue_set_next_case:
1798 id_val[1].name, idx, id_val[1].
to_cpp())
1800 wrapper_file = textwrap.dedent(
'''\
1803 {boost_class_export_guid}
1804 {typedefs_collectors}
1807 {pointer_constructor_fragment}{mex_function}''') \
1808 .
format(includes=includes,
1809 typedef_instances=typedef_instances,
1810 boost_class_export_guid=boost_class_export_guid,
1811 typedefs_collectors=typedef_collectors,
1812 delete_all_objs=delete_all_objs,
1813 rtti_register=rtti_register,
1814 pointer_constructor_fragment=ptr_ctor_frag,
1817 self.content.append((self.
_wrapper_name() +
'.cpp', wrapper_file))
1821 Wrap the serizalize method of the class.
1823 class_name = inst_class.name
1825 (namespace_name, inst_class,
'string_serialize',
'serialize'))
1827 return WrapperTemplate.class_serialize_method.format(
1829 wrapper_id=wrapper_id,
1830 class_name=namespace_name +
'.' + class_name)
1837 Wrap the serizalize collector function.
1839 return WrapperTemplate.collector_function_serialize.format(
1840 class_name=class_name, full_name=full_name, namespace=namespace)
1847 Wrap the deserizalize collector function.
1849 return WrapperTemplate.collector_function_deserialize.format(
1850 class_name=class_name, full_name=full_name, namespace=namespace)
1854 Generate files and folders from matlab wrapper content.
1857 cc_content: The content to generate formatted as
1858 (file_name, file_content) or
1859 (folder_name, [(file_name, file_content)])
1860 path: The path to the files parent folder within the main folder
1862 for c
in cc_content:
1868 path_to_folder = osp.join(path, c[0][0])
1870 if not osp.isdir(path_to_folder):
1872 os.makedirs(path_to_folder, exist_ok=
True)
1876 for sub_content
in c:
1881 path_to_folder = osp.join(path, c[0])
1883 if not osp.isdir(path_to_folder):
1885 os.makedirs(path_to_folder, exist_ok=
True)
1889 for sub_content
in c[1]:
1890 path_to_file = osp.join(path_to_folder, sub_content[0])
1891 with open(path_to_file,
'w')
as f:
1892 f.write(sub_content[1])
1895 path_to_file = osp.join(path, c[0])
1897 if not osp.isdir(path_to_file):
1903 with open(path_to_file,
'w')
as f:
1907 """High level function to wrap the project."""
1911 with open(file,
'r')
as f:
1915 parsed_result = parser.Module.parseString(content)
1918 module = instantiator.instantiate_namespace(parsed_result)
1920 if module.name
in modules:
1922 module.name].content[0].content += module.content[0].content
1924 modules[module.name] = module
1926 for module
in modules.values():