gtsam/gtwrap/template_instantiator.py

347 lines
13 KiB
Python
Raw Normal View History

Squashed 'wrap/' changes from dfa624e77..09f8bbf71 09f8bbf71 Merge pull request #25 from borglab/fix/function-name 0dbfb6c13 fix function name to be the correct one f69f8b01f Merge pull request #24 from borglab/fix/pip 6519a6627 use pip install to overcome superuser issues b11ecf4e8 Merge pull request #23 from borglab/fix/remove-pip-args 813030108 remove pip-args since we are using setup.py 498d233e0 Merge pull request #22 from borglab/fix/package-install 846212ac3 set correct flags for installing gtwrap package 62161cd20 Merge pull request #21 from borglab/feature/script-vars 93be1d9f8 set script variables and move pybind11 loading so gtwrap can be used under gtsam 8770e3c7e Merge pull request #20 from borglab/fix/pybind-include 8c3c83618 proper placement of pybind11 include a9ad4f504 Merge pull request #19 from borglab/feature/package 99d8a12c7 added more documentation 4cbec1579 change to macro so we don't have to deal with function scopes b83e405b8 updates to completely install the package 38a64b3de new scripts which will be installed to bin directory bf9646235 Merge pull request #18 from borglab/fix/cmake-min c7c280099 Consistent cmake minimum required 42df58f62 Merge pull request #17 from borglab/fix/cleanup e580b282d version bump 4ccd66fa5 More finegrained handling of Python version 6476fd710 Merge pull request #16 from borglab/feature/better-find-python 8ac1296a0 use setup.py to install dependencies e9ac473be install dependencies and support versions of CMake<3.12 cf272dbd2 Merge pull request #15 from borglab/feature/utils ffc9cc4f7 new utils to reduce boilerplate 20e8e8b7a Merge pull request #11 from borglab/feature/package 04b844bd6 use new version of FindPython and be consistent 3f9d7a32a Merge pull request #13 from borglab/add_license c791075a6 Add LICENSE 517b67c46 correct working directory for setup.py 1b22b47ae move matlab.h to root directory 37b407214 Proper source directory path for use in other projects 61696dd5d configure PybindWrap within the cmake directory 1b91fc9af add config file so we can use find_package a1e6f4f53 small typo da9f351be updated README and housekeeping 64b8f78d5 files needed to allow for packaging bddda7f54 package structure git-subtree-dir: wrap git-subtree-split: 09f8bbf7172ba8b1bd3d2484795743f16e1a5893
2021-01-05 02:11:36 +08:00
import gtwrap.interface_parser as parser
def instantiate_type(ctype, template_typenames, instantiations, cpp_typename, instantiated_class=None):
"""
Instantiate template typename for @p ctype.
@return If ctype's name is in the @p template_typenames, return the
corresponding type to replace in @p instantiations.
If ctype name is `This`, return the new typename @p `cpp_typename`.
Otherwise, return the original ctype.
"""
str_arg_typename = str(ctype.typename)
if str_arg_typename in template_typenames:
idx = template_typenames.index(str_arg_typename)
return parser.Type(
typename=instantiations[idx],
is_const=ctype.is_const,
is_ptr=ctype.is_ptr,
is_ref=ctype.is_ref,
is_basis=ctype.is_basis,
)
elif str_arg_typename == 'This':
# import sys
if instantiated_class:
name = instantiated_class.original.name
namespaces_name = instantiated_class.namespaces()
namespaces_name.append(name)
# print("INST: {}, {}, CPP: {}, CLS: {}".format(
# ctype, instantiations, cpp_typename, instantiated_class.instantiations
# ), file=sys.stderr)
cpp_typename = parser.Typename(
namespaces_name, instantiations=[inst for inst in instantiated_class.instantiations]
)
return parser.Type(
typename=cpp_typename,
is_const=ctype.is_const,
is_ptr=ctype.is_ptr,
is_ref=ctype.is_ref,
is_basis=ctype.is_basis,
)
else:
return ctype
def instantiate_args_list(args_list, template_typenames, instantiations,
cpp_typename):
"""
Instantiate template typenames in an argument list.
Type with name `This` will be replaced by @p `cpp_typename`.
@param[in] args_list A list of `parser.Argument` to instantiate.
@param[in] template_typenames List of template typenames to instantiate,
e.g. ['T', 'U', 'V'].
@param[in] instantiations List of specific types to instantiate, each
associated with each template typename. Each type is a parser.Typename,
including its name and full namespaces.
@param[in] cpp_typename Full-namespace cpp class name of this instantiation
to replace for arguments of type named `This`.
@return A new list of parser.Argument which types are replaced with their
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))
return instantiated_args
def instantiate_return_type(return_type, template_typenames, instantiations,
cpp_typename, instantiated_class=None):
new_type1 = instantiate_type(
return_type.type1, template_typenames, instantiations, cpp_typename, instantiated_class=instantiated_class)
if return_type.type2:
new_type2 = instantiate_type(
return_type.type2, template_typenames, instantiations,
cpp_typename, instantiated_class=instantiated_class)
else:
new_type2 = ''
return parser.ReturnType(new_type1, new_type2)
def instantiate_name(original_name, instantiations):
"""
Concatenate instantiated types with an @p original name to form a new
instantiated name.
TODO(duy): To avoid conflicts, we should include the instantiation's
namespaces, but I find that too verbose.
"""
inst_name = ''
return "{}{}".format(original_name, "".join(
[inst.instantiated_name() for inst in instantiations]))
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
self.template = ''
self.is_const = original.is_const
self.parent = original.parent
if not original.template:
self.name = original.name
self.return_type = original.return_type
self.args = original.args
else:
if len(self.original.template.typenames) > 1:
raise ValueError("Can only instantiate template method with "
"single template parameter.")
self.name = instantiate_name(original.name, [self.instantiation])
self.return_type = instantiate_return_type(
original.return_type,
[self.original.template.typenames[0]],
[self.instantiation],
# Keyword type name `This` should already be replaced in the
# previous class template instantiation round.
cpp_typename='',
)
instantiated_args = instantiate_args_list(
original.args.args_list,
[self.original.template.typenames[0]],
[self.instantiation],
# Keyword type name `This` should already be replaced in the
# previous class template instantiation round.
cpp_typename='',
)
self.args = parser.ArgumentList(instantiated_args)
def to_cpp(self):
if self.original.template:
return "{}<{}>".format(self.original.name, self.instantiation)
else:
return self.original.name
def __repr__(self):
return "Instantiated {}".format(
super(InstantiatedMethod, self).__repr__()
)
class InstantiatedClass(parser.Class):
def __init__(self, original, instantiations=[], new_name=''):
"""
Template <T, U>
Instantiations: [T1, U1]
"""
self.original = original
self.instantiations = instantiations
self.template = ''
self.is_virtual = original.is_virtual
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.
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()
# Second instantiation round to instantiate template methods.
self.methods = []
for method in class_instantiated_methods:
if not method.template:
self.methods.append(InstantiatedMethod(method, ''))
else:
assert len(
method.template.typenames) == 1, ""\
"Can only instantiate single template methods"
for inst in method.template.instantiations[0]:
self.methods.append(InstantiatedMethod(method, inst))
def __repr__(self):
return "{virtual} class {name} [{cpp_class}]: {parent_class}\n"\
"{ctors}\n{static_methods}\n{methods}".format(
virtual="virtual" if self.is_virtual else '',
name=self.name,
cpp_class=self.cpp_class(),
parent_class=self.parent,
ctors="\n".join([ctor.__repr__() for ctor in self.ctors]),
methods="\n".join([m.__repr__() for m in self.methods]),
static_methods="\n".join([m.__repr__()
for m in self.static_methods]),
)
def instantiate_ctors(self):
instantiated_ctors = []
for ctor in self.original.ctors:
instantiated_args = instantiate_args_list(
ctor.args.args_list,
self.original.template.typenames,
self.instantiations,
self.cpp_typename(),
)
instantiated_ctors.append(parser.Constructor(
name=self.name,
args=parser.ArgumentList(instantiated_args),
parent=self,
))
return instantiated_ctors
def instantiate_static_methods(self):
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()
)
instantiated_static_methods.append(
parser.StaticMethod(
name=static_method.name,
return_type=instantiate_return_type(
static_method.return_type,
self.original.template.typenames,
self.instantiations,
self.cpp_typename(),
instantiated_class=self
),
args=parser.ArgumentList(instantiated_args),
parent=self,
)
)
return instantiated_static_methods
def instantiate_class_templates_in_methods(self):
"""
This function only instantiates class templates in the methods.
Template methods are instantiated in InstantiatedMethod in the second
round.
"""
class_instantiated_methods = []
for method in self.original.methods:
instantiated_args = instantiate_args_list(
method.args.args_list,
self.original.template.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,
))
return class_instantiated_methods
def instantiate_properties(self):
instantiated_properties = instantiate_args_list(
self.original.properties,
self.original.template.typenames,
self.instantiations,
self.cpp_typename(),
)
return instantiated_properties
def cpp_class(self):
return self.cpp_typename().to_cpp()
def cpp_typename(self):
"""
Return a parser.Typename including namespaces and cpp name of this
class.
"""
if self.original.template:
name = "{}<{}>".format(
self.original.name,
", ".join([inst.to_cpp() for inst in self.instantiations]))
else:
name = self.original.name
namespaces_name = self.namespaces()
namespaces_name.append(name)
return parser.Typename(namespaces_name)
def instantiate_namespace_inplace(namespace):
"""
@param[in/out] namespace The namespace which content will be replaced with
the instantiated content.
"""
instantiated_content = []
typedef_content = []
for element in namespace.content:
if isinstance(element, parser.Class):
original_class = element
if not original_class.template:
instantiated_content.append(
InstantiatedClass(original_class, []))
else:
if (len(original_class.template.typenames) > 1
and original_class.template.instantiations[0]):
raise ValueError(
"Can't instantiate multi-parameter templates here. "
"Please use typedef template instantiation."
)
for inst in original_class.template.instantiations[0]:
instantiated_content.append(
InstantiatedClass(original_class, [inst]))
elif isinstance(element, parser.TypedefTemplateInstantiation):
typedef_inst = element
original_class = namespace.top_level().find_class(
typedef_inst.typename)
typedef_content.append(
InstantiatedClass(
original_class,
typedef_inst.typename.instantiations,
typedef_inst.new_name
)
)
elif isinstance(element, parser.Namespace):
instantiate_namespace_inplace(element)
instantiated_content.append(element)
else:
instantiated_content.append(element)
instantiated_content.extend(typedef_content)
namespace.content = instantiated_content