From 4788a1e37ce13fe093205047768ce6a6a87e419c Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Wed, 24 Mar 2021 03:03:33 -0400 Subject: [PATCH 1/3] fixed This instantiation --- gtwrap/template_instantiator.py | 79 +++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/gtwrap/template_instantiator.py b/gtwrap/template_instantiator.py index 8e918e2..b31079b 100644 --- a/gtwrap/template_instantiator.py +++ b/gtwrap/template_instantiator.py @@ -61,6 +61,7 @@ def instantiate_type(ctype: parser.Type, cpp_typename = parser.Typename( namespaces_name, instantiations=instantiated_class.instantiations) + return parser.Type( typename=cpp_typename, is_const=ctype.is_const, @@ -272,28 +273,27 @@ def __init__(self, original, instantiations=(), new_name=''): self.parent_class = original.parent_class self.parent = original.parent - if not original.template: - self.name = original.name - self.ctors = list(original.ctors) - self.static_methods = list(original.static_methods) - class_instantiated_methods = list(original.methods) - self.properties = list(original.properties) - else: + if original.template: # Check conditions. assert len(original.template.typenames) == len( instantiations), "Typenames and instantiations mismatch!" - self.name = instantiate_name( - original.name, instantiations) if not new_name else new_name - self.ctors = self.instantiate_ctors() - self.static_methods = self.instantiate_static_methods() - class_instantiated_methods = \ - self.instantiate_class_templates_in_methods() - self.properties = self.instantiate_properties() + self.name = instantiate_name( + original.name, instantiations) if not new_name else new_name + + # Check for typenames if templated. + # By passing in typenames, we can gracefully handle both templated and non-templated classes + # This will allow the `This` keyword to be used in both templated and non-templated classes. + typenames = self.original.template.typenames if self.original.template else [] + + self.ctors = self.instantiate_ctors(typenames) + self.static_methods = self.instantiate_static_methods(typenames) + instantiated_methods = self.instantiate_class_templates_in_methods(typenames) + self.properties = self.instantiate_properties(typenames) # Second instantiation round to instantiate template methods. self.methods = [] - for method in class_instantiated_methods: + for method in instantiated_methods: if not method.template: self.methods.append(InstantiatedMethod(method, '')) else: @@ -328,13 +328,19 @@ def __repr__(self): for m in self.static_methods]), ) - def instantiate_ctors(self): - """Instantiate the class constructors.""" + def instantiate_ctors(self, typenames): + """ + Instantiate the class constructors. + + Args: + typenames: List of template types to instantiate. + """ instantiated_ctors = [] + for ctor in self.original.ctors: instantiated_args = instantiate_args_list( ctor.args.args_list, - self.original.template.typenames, + typenames, self.instantiations, self.cpp_typename(), ) @@ -345,13 +351,18 @@ def instantiate_ctors(self): )) return instantiated_ctors - def instantiate_static_methods(self): - """Instantiate static methods in the class.""" + def instantiate_static_methods(self, typenames): + """ + Instantiate static methods in the class. + + Args: + typenames: List of template types to instantiate. + """ instantiated_static_methods = [] for static_method in self.original.static_methods: instantiated_args = instantiate_args_list( static_method.args.args_list, - self.original.template.typenames, + typenames, self.instantiations, self.cpp_typename() ) @@ -360,7 +371,7 @@ def instantiate_static_methods(self): name=static_method.name, return_type=instantiate_return_type( static_method.return_type, - self.original.template.typenames, + typenames, self.instantiations, self.cpp_typename(), instantiated_class=self @@ -371,17 +382,20 @@ def instantiate_static_methods(self): ) return instantiated_static_methods - def instantiate_class_templates_in_methods(self): + def instantiate_class_templates_in_methods(self, typenames): """ This function only instantiates class templates in the methods. Template methods are instantiated in InstantiatedMethod in the second round. + + Args: + typenames: List of template types to instantiate. """ class_instantiated_methods = [] for method in self.original.methods: instantiated_args = instantiate_args_list( method.args.args_list, - self.original.template.typenames, + typenames, self.instantiations, self.cpp_typename(), ) @@ -390,7 +404,7 @@ def instantiate_class_templates_in_methods(self): name=method.name, return_type=instantiate_return_type( method.return_type, - self.original.template.typenames, + typenames, self.instantiations, self.cpp_typename(), ), @@ -400,11 +414,16 @@ def instantiate_class_templates_in_methods(self): )) return class_instantiated_methods - def instantiate_properties(self): - """Instantiate the class properties.""" + def instantiate_properties(self, typenames): + """ + Instantiate the class properties. + + Args: + typenames: List of template types to instantiate. + """ instantiated_properties = instantiate_args_list( self.original.properties, - self.original.template.typenames, + typenames, self.instantiations, self.cpp_typename(), ) @@ -448,6 +467,8 @@ def instantiate_namespace_inplace(namespace): instantiated_content.append( InstantiatedClass(original_class, [])) else: + # This case is for when the templates have enumerated instantiations. + # Use itertools to get all possible combinations of instantiations # Works even if one template does not have an instantiation list for instantiations in itertools.product( @@ -471,6 +492,8 @@ def instantiate_namespace_inplace(namespace): list(instantiations))) elif isinstance(element, parser.TypedefTemplateInstantiation): + # This is for the case where `typedef` statements are used + # to specify the template parameters. typedef_inst = element top_level = namespace.top_level() original_element = top_level.find_class_or_function( From 526301499ab3f81a9205877ac22f28cef4348945 Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Wed, 24 Mar 2021 17:28:28 -0400 Subject: [PATCH 2/3] updated the test to test for non-templated This --- tests/expected/matlab/FunRange.m | 8 ++++---- tests/expected/matlab/class_wrapper.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/expected/matlab/FunRange.m b/tests/expected/matlab/FunRange.m index 09cd3c5..1d1a6f7 100644 --- a/tests/expected/matlab/FunRange.m +++ b/tests/expected/matlab/FunRange.m @@ -5,10 +5,10 @@ %FunRange() % %-------Methods------- -%range(double d) : returns This +%range(double d) : returns FunRange % %-------Static Methods------- -%create() : returns This +%create() : returns FunRange % %-------Serialization Interface------- %string_serialize() : returns string @@ -40,7 +40,7 @@ function delete(obj) function disp(obj), obj.display; end %DISP Calls print on the object function varargout = range(this, varargin) - % RANGE usage: range(double d) : returns This + % RANGE usage: range(double d) : returns FunRange % Doxygen can be found at https://gtsam.org/doxygen/ if length(varargin) == 1 && isa(varargin{1},'double') varargout{1} = class_wrapper(3, this, varargin{:}); @@ -53,7 +53,7 @@ function delete(obj) methods(Static = true) function varargout = Create(varargin) - % CREATE usage: create() : returns This + % CREATE usage: create() : returns FunRange % Doxygen can be found at https://gtsam.org/doxygen/ if length(varargin) == 0 varargout{1} = class_wrapper(4, varargin{:}); diff --git a/tests/expected/matlab/class_wrapper.cpp b/tests/expected/matlab/class_wrapper.cpp index cae9cba..a743066 100644 --- a/tests/expected/matlab/class_wrapper.cpp +++ b/tests/expected/matlab/class_wrapper.cpp @@ -169,13 +169,13 @@ void FunRange_range_3(int nargout, mxArray *out[], int nargin, const mxArray *in checkArguments("range",nargout,nargin-1,1); auto obj = unwrap_shared_ptr(in[0], "ptr_FunRange"); double d = unwrap< double >(in[1]); - out[0] = wrap_shared_ptr(boost::make_shared(obj->range(d)),"This", false); + out[0] = wrap_shared_ptr(boost::make_shared(obj->range(d)),"FunRange", false); } void FunRange_create_4(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { checkArguments("FunRange.create",nargout,nargin,0); - out[0] = wrap_shared_ptr(boost::make_shared(FunRange::create()),"This", false); + out[0] = wrap_shared_ptr(boost::make_shared(FunRange::create()),"FunRange", false); } void FunDouble_collectorInsertAndMakeBase_5(int nargout, mxArray *out[], int nargin, const mxArray *in[]) From 3e22d6516d1e1b3c649059ce4c757e544a6eff92 Mon Sep 17 00:00:00 2001 From: Varun Agrawal Date: Wed, 24 Mar 2021 18:07:33 -0400 Subject: [PATCH 3/3] more documenatation and some formatting --- gtwrap/template_instantiator.py | 112 +++++++++++++++++++------------- 1 file changed, 67 insertions(+), 45 deletions(-) diff --git a/gtwrap/template_instantiator.py b/gtwrap/template_instantiator.py index b31079b..65a1e72 100644 --- a/gtwrap/template_instantiator.py +++ b/gtwrap/template_instantiator.py @@ -93,15 +93,18 @@ def instantiate_args_list(args_list, template_typenames, instantiations, """ instantiated_args = [] for arg in args_list: - new_type = instantiate_type( - arg.ctype, template_typenames, instantiations, cpp_typename) - instantiated_args.append( - parser.Argument(name=arg.name, ctype=new_type)) + new_type = instantiate_type(arg.ctype, template_typenames, + instantiations, cpp_typename) + instantiated_args.append(parser.Argument(name=arg.name, + ctype=new_type)) return instantiated_args -def instantiate_return_type(return_type, template_typenames, instantiations, - cpp_typename, instantiated_class=None): +def instantiate_return_type(return_type, + template_typenames, + instantiations, + cpp_typename, + instantiated_class=None): """Instantiate the return type.""" new_type1 = instantiate_type(return_type.type1, template_typenames, @@ -136,6 +139,7 @@ def instantiate_name(original_name, instantiations): return "{}{}".format(original_name, "".join(instantiated_names)) + class InstantiatedGlobalFunction(parser.GlobalFunction): """ Instantiate global functions. @@ -184,22 +188,24 @@ def __init__(self, original, instantiations=(), new_name=''): def to_cpp(self): """Generate the C++ code for wrapping.""" if self.original.template: - instantiated_names = [inst.instantiated_name() for inst in self.instantiations] - ret = "{}<{}>".format(self.original.name, ",".join(instantiated_names)) + instantiated_names = [ + inst.instantiated_name() for inst in self.instantiations + ] + ret = "{}<{}>".format(self.original.name, + ",".join(instantiated_names)) else: ret = self.original.name return ret def __repr__(self): return "Instantiated {}".format( - super(InstantiatedGlobalFunction, self).__repr__() - ) + super(InstantiatedGlobalFunction, self).__repr__()) + class InstantiatedMethod(parser.Method): """ We can only instantiate template methods with a single template parameter. """ - def __init__(self, original, instantiation=''): self.original = original self.instantiation = instantiation @@ -252,8 +258,7 @@ def to_cpp(self): def __repr__(self): return "Instantiated {}".format( - super(InstantiatedMethod, self).__repr__() - ) + super(InstantiatedMethod, self).__repr__()) class InstantiatedClass(parser.Class): @@ -273,11 +278,13 @@ def __init__(self, original, instantiations=(), new_name=''): self.parent_class = original.parent_class self.parent = original.parent + # If the class is templated, check if the number of provided instantiations + # match the number of templates, else it's only a partial instantiation which is bad. if original.template: - # Check conditions. assert len(original.template.typenames) == len( instantiations), "Typenames and instantiations mismatch!" + # Get the instantiated name of the class. E.g. FuncDouble self.name = instantiate_name( original.name, instantiations) if not new_name else new_name @@ -286,12 +293,15 @@ def __init__(self, original, instantiations=(), new_name=''): # This will allow the `This` keyword to be used in both templated and non-templated classes. typenames = self.original.template.typenames if self.original.template else [] + # Instantiate the constructors, static methods, properties and instance methods, respectively. self.ctors = self.instantiate_ctors(typenames) self.static_methods = self.instantiate_static_methods(typenames) - instantiated_methods = self.instantiate_class_templates_in_methods(typenames) self.properties = self.instantiate_properties(typenames) + instantiated_methods = self.instantiate_class_templates_in_methods( + typenames) - # Second instantiation round to instantiate template methods. + # Second instantiation round to instantiate templated methods. + # This is done in case both the class and the method are templated. self.methods = [] for method in instantiated_methods: if not method.template: @@ -331,9 +341,11 @@ def __repr__(self): def instantiate_ctors(self, typenames): """ Instantiate the class constructors. - + Args: typenames: List of template types to instantiate. + + Return: List of constructors instantiated with provided template args. """ instantiated_ctors = [] @@ -344,11 +356,12 @@ def instantiate_ctors(self, typenames): self.instantiations, self.cpp_typename(), ) - instantiated_ctors.append(parser.Constructor( - name=self.name, - args=parser.ArgumentList(instantiated_args), - parent=self, - )) + instantiated_ctors.append( + parser.Constructor( + name=self.name, + args=parser.ArgumentList(instantiated_args), + parent=self, + )) return instantiated_ctors def instantiate_static_methods(self, typenames): @@ -357,15 +370,14 @@ def instantiate_static_methods(self, typenames): Args: typenames: List of template types to instantiate. + + Return: List of static methods instantiated with provided template args. """ instantiated_static_methods = [] for static_method in self.original.static_methods: instantiated_args = instantiate_args_list( - static_method.args.args_list, - typenames, - self.instantiations, - self.cpp_typename() - ) + static_method.args.args_list, typenames, self.instantiations, + self.cpp_typename()) instantiated_static_methods.append( parser.StaticMethod( name=static_method.name, @@ -374,22 +386,29 @@ def instantiate_static_methods(self, typenames): typenames, self.instantiations, self.cpp_typename(), - instantiated_class=self - ), + instantiated_class=self), args=parser.ArgumentList(instantiated_args), parent=self, - ) - ) + )) return instantiated_static_methods def instantiate_class_templates_in_methods(self, typenames): """ - This function only instantiates class templates in the methods. + This function only instantiates the class-level templates in the methods. Template methods are instantiated in InstantiatedMethod in the second round. + E.g. + ``` + template + class Greeter{ + void sayHello(T& name); + }; + Args: typenames: List of template types to instantiate. + + Return: List of methods instantiated with provided template args on the class. """ class_instantiated_methods = [] for method in self.original.methods: @@ -399,19 +418,20 @@ def instantiate_class_templates_in_methods(self, typenames): self.instantiations, self.cpp_typename(), ) - class_instantiated_methods.append(parser.Method( - template=method.template, - name=method.name, - return_type=instantiate_return_type( - method.return_type, - typenames, - self.instantiations, - self.cpp_typename(), - ), - args=parser.ArgumentList(instantiated_args), - is_const=method.is_const, - parent=self, - )) + class_instantiated_methods.append( + parser.Method( + template=method.template, + name=method.name, + return_type=instantiate_return_type( + method.return_type, + typenames, + self.instantiations, + self.cpp_typename(), + ), + args=parser.ArgumentList(instantiated_args), + is_const=method.is_const, + parent=self, + )) return class_instantiated_methods def instantiate_properties(self, typenames): @@ -420,6 +440,8 @@ def instantiate_properties(self, typenames): Args: typenames: List of template types to instantiate. + + Return: List of properties instantiated with provided template args. """ instantiated_properties = instantiate_args_list( self.original.properties,