Skip to content

Commit

Permalink
Merge pull request #56 from borglab/fix/this-instantiation
Browse files Browse the repository at this point in the history
Fix This instantiation
  • Loading branch information
varunagrawal authored Mar 24, 2021
2 parents d0f8a39 + 3e22d65 commit a95429e
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 75 deletions.
183 changes: 114 additions & 69 deletions gtwrap/template_instantiator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -92,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,
Expand Down Expand Up @@ -135,6 +139,7 @@ def instantiate_name(original_name, instantiations):

return "{}{}".format(original_name, "".join(instantiated_names))


class InstantiatedGlobalFunction(parser.GlobalFunction):
"""
Instantiate global functions.
Expand Down Expand Up @@ -183,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
Expand Down Expand Up @@ -251,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):
Expand All @@ -272,28 +278,32 @@ 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:
# Check conditions.
# 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:
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()
# Get the instantiated name of the class. E.g. FuncDouble
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 []

# Second instantiation round to instantiate template methods.
# Instantiate the constructors, static methods, properties and instance methods, respectively.
self.ctors = self.instantiate_ctors(typenames)
self.static_methods = self.instantiate_static_methods(typenames)
self.properties = self.instantiate_properties(typenames)
instantiated_methods = self.instantiate_class_templates_in_methods(
typenames)

# 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 class_instantiated_methods:
for method in instantiated_methods:
if not method.template:
self.methods.append(InstantiatedMethod(method, ''))
else:
Expand Down Expand Up @@ -328,83 +338,114 @@ 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.
Return: List of constructors instantiated with provided template args.
"""
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(),
)
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):
"""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.
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,
self.original.template.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,
return_type=instantiate_return_type(
static_method.return_type,
self.original.template.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):
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<T={string}>
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:
instantiated_args = instantiate_args_list(
method.args.args_list,
self.original.template.typenames,
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,
self.original.template.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):
"""Instantiate the class properties."""
def instantiate_properties(self, typenames):
"""
Instantiate the class properties.
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,
self.original.template.typenames,
typenames,
self.instantiations,
self.cpp_typename(),
)
Expand Down Expand Up @@ -448,6 +489,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(
Expand All @@ -471,6 +514,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(
Expand Down
8 changes: 4 additions & 4 deletions tests/expected/matlab/FunRange.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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{:});
Expand All @@ -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{:});
Expand Down
4 changes: 2 additions & 2 deletions tests/expected/matlab/class_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<FunRange>(in[0], "ptr_FunRange");
double d = unwrap< double >(in[1]);
out[0] = wrap_shared_ptr(boost::make_shared<This>(obj->range(d)),"This", false);
out[0] = wrap_shared_ptr(boost::make_shared<FunRange>(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<This>(FunRange::create()),"This", false);
out[0] = wrap_shared_ptr(boost::make_shared<FunRange>(FunRange::create()),"FunRange", false);
}

void FunDouble_collectorInsertAndMakeBase_5(int nargout, mxArray *out[], int nargin, const mxArray *in[])
Expand Down

0 comments on commit a95429e

Please sign in to comment.