1 """Code to help instantiate templated classes, methods and functions.""" 6 from copy
import deepcopy
7 from typing
import List
13 template_typenames: List[str],
14 instantiations: List[parser.Typename],
15 cpp_typename: parser.Typename,
16 instantiated_class=
None):
18 Instantiate template typename for @p ctype. 21 instiated_class (InstantiatedClass): 23 @return If ctype's name is in the @p template_typenames, return the 24 corresponding type to replace in @p instantiations. 25 If ctype name is `This`, return the new typename @p `cpp_typename`. 26 Otherwise, return the original ctype. 29 ctype = deepcopy(ctype)
32 if len(ctype.typename.instantiations) > 0:
33 for idx, instantiation
in enumerate(ctype.typename.instantiations):
34 if instantiation.name
in template_typenames:
35 template_idx = template_typenames.index(instantiation.name)
36 ctype.typename.instantiations[idx] = instantiations[
41 str_arg_typename =
str(ctype.typename)
43 if str_arg_typename
in template_typenames:
44 idx = template_typenames.index(str_arg_typename)
46 typename=instantiations[idx],
47 is_const=ctype.is_const,
48 is_shared_ptr=ctype.is_shared_ptr,
51 is_basic=ctype.is_basic,
53 elif str_arg_typename ==
'This':
54 if instantiated_class:
55 name = instantiated_class.original.name
56 namespaces_name = instantiated_class.namespaces()
57 namespaces_name.append(name)
61 cpp_typename = parser.Typename(
63 instantiations=instantiated_class.instantiations)
66 typename=cpp_typename,
67 is_const=ctype.is_const,
68 is_shared_ptr=ctype.is_shared_ptr,
71 is_basic=ctype.is_basic,
80 Instantiate template typenames in an argument list. 81 Type with name `This` will be replaced by @p `cpp_typename`. 83 @param[in] args_list A list of `parser.Argument` to instantiate. 84 @param[in] template_typenames List of template typenames to instantiate, 86 @param[in] instantiations List of specific types to instantiate, each
87 associated with each template typename. Each type is a parser.Typename,
88 including its name and full namespaces.
89 @param[in] cpp_typename Full-namespace cpp class name of this instantiation
90 to replace for arguments of type named `This`.
91 @return A new list of parser.Argument which types are replaced with their
94 instantiated_args = []
96 new_type = instantiate_type(arg.ctype, template_typenames,
97 instantiations, cpp_typename)
98 default = [arg.default] if isinstance(arg, parser.Argument) else ''
99 instantiated_args.append(parser.Argument(name=arg.name,
102 return instantiated_args
105 def instantiate_return_type(return_type,
109 instantiated_class=None):
110 """Instantiate the return type."""
111 new_type1 = instantiate_type(return_type.type1,
115 instantiated_class=instantiated_class)
116 if return_type.type2:
117 new_type2 = instantiate_type(return_type.type2,
121 instantiated_class=instantiated_class)
124 return parser.ReturnType(new_type1, new_type2)
127 def instantiate_name(original_name, instantiations):
129 Concatenate instantiated types with an @p original name to form a new
131 TODO(duy): To avoid conflicts, we should include the instantiation's
132 namespaces, but I find that too verbose.
135 instantiated_names = []
136 for inst in instantiations:
137 # Ensure the first character of the type is capitalized
138 name = inst.instantiated_name()
139 # Using `capitalize` on the complete name causes other caps to be lower case
140 instantiated_names.append(name.replace(name[0], name[0].capitalize()))
142 return "{}{}".format(original_name, "".join(instantiated_names))
145 class InstantiatedGlobalFunction(parser.GlobalFunction):
147 Instantiate global functions.
150 template<T = {double}>
151 T add(const T& x, const T& y);
153 def __init__(self, original, instantiations=(), new_name=''):
154 self.original = original
155 self.instantiations = instantiations
157 self.parent = original.parent
159 if not original.template:
160 self.name = original.name
161 self.return_type = original.return_type
162 self.args = original.args
164 self.name = instantiate_name(
165 original.name, instantiations) if not new_name else new_name
166 self.return_type = instantiate_return_type(
167 original.return_type,
168 self.original.template.typenames,
170 # Keyword type name `This` should already be replaced in the
171 # previous class template instantiation round.
174 instantiated_args = instantiate_args_list(
175 original.args.args_list,
176 self.original.template.typenames,
178 # Keyword type name `This` should already be replaced in the
179 # previous class template instantiation round.
182 self.args = parser.ArgumentList(instantiated_args)
184 super().__init__(self.name,
191 """Generate the C++ code for wrapping."""
192 if self.original.template:
193 instantiated_names = [
194 inst.instantiated_name() for inst in self.instantiations
196 ret = "{}<{}>".format(self.original.name,
197 ",".join(instantiated_names))
199 ret = self.original.name
203 return "Instantiated {}".format(
204 super(InstantiatedGlobalFunction, self).__repr__())
207 class InstantiatedMethod(parser.Method):
209 We can only instantiate template methods with a single template parameter.
211 def __init__(self, original, instantiations: List[parser.Typename] = ''):
212 self.original = original
213 self.instantiations = instantiations
215 self.is_const = original.is_const
216 self.parent = original.parent
218 # Check for typenames if templated.
219 # This way, we can gracefully handle bot templated and non-templated methois.
220 typenames = self.original.template.typenames if self.original.template else []
221 self.name = instantiate_name(original.name, self.instantiations)
222 self.return_type = instantiate_return_type(
223 original.return_type,
226 # Keyword type name `This` should already be replaced in the
227 # previous class template instantiation round.
231 instantiated_args = instantiate_args_list(
232 original.args.args_list,
235 # Keyword type name `This` should already be replaced in the
236 # previous class template instantiation round.
239 self.args = parser.ArgumentList(instantiated_args)
241 super().__init__(self.template,
249 """Generate the C++ code for wrapping."""
250 if self.original.template:
251 # to_cpp will handle all the namespacing and templating
252 instantiation_list = [x.to_cpp() for x in self.instantiations]
253 # now can simply combine the instantiations, separated by commas
254 ret = "{}<{}>".format(self.original.name,
255 ",".join(instantiation_list))
257 ret = self.original.name
261 return "Instantiated {}".format(
262 super(InstantiatedMethod, self).__repr__())
265 class InstantiatedClass(parser.Class):
267 Instantiate the class defined in the interface file.
269 def __init__(self, original: parser.Class, instantiations=(), new_name=''):
272 Instantiations: [T1, U1]
274 self.original = original
275 self.instantiations = instantiations
278 self.is_virtual = original.is_virtual
279 self.parent_class = original.parent_class
280 self.parent = original.parent
282 # If the class is templated, check if the number of provided instantiations
283 # match the number of templates, else it's only a partial instantiation which is bad.
284 if original.template:
285 assert len(original.template.typenames) == len(
286 instantiations), "Typenames and instantiations mismatch!"
288 # Get the instantiated name of the class. E.g. FuncDouble
289 self.name = instantiate_name(
290 original.name, instantiations) if not new_name else new_name
292 # Check for typenames if templated.
293 # By passing in typenames, we can gracefully handle both templated and non-templated classes
294 # This will allow the `This` keyword to be used in both templated and non-templated classes.
295 typenames = self.original.template.typenames if self.original.template else []
297 # Instantiate the constructors, static methods, properties, respectively.
298 self.ctors = self.instantiate_ctors(typenames)
299 self.static_methods = self.instantiate_static_methods(typenames)
300 self.properties = self.instantiate_properties(typenames)
302 # Instantiate all operator overloads
303 self.operators = self.instantiate_operators(typenames)
306 self.enums = original.enums
308 # Instantiate all instance methods
309 instantiated_methods = \
310 self.instantiate_class_templates_in_methods(typenames)
312 # Second instantiation round to instantiate templated methods.
313 # This is done in case both the class and the method are templated.
315 for method in instantiated_methods:
316 if not method.template:
317 self.methods.append(InstantiatedMethod(method, ''))
320 # Get all combinations of template parameters
321 for instantiations in itertools.product(
322 *method.template.instantiations):
324 InstantiatedMethod(method, instantiations))
341 return "{virtual} class {name} [{cpp_class}]: {parent_class}\n"\
342 "{ctors}\n{static_methods}\n{methods}".format(
343 virtual="virtual" if self.is_virtual else '',
345 cpp_class=self.cpp_class(),
346 parent_class=self.parent,
347 ctors="\n".join([repr(ctor) for ctor in self.ctors]),
348 methods="\n".join([repr(m) for m in self.methods]),
349 static_methods="\n".join([repr(m)
350 for m in self.static_methods]),
351 operators="\n".join([repr(op) for op in self.operators])
354 def instantiate_ctors(self, typenames):
356 Instantiate the class constructors.
359 typenames: List of template types to instantiate.
361 Return: List of constructors instantiated with provided template args.
363 instantiated_ctors = []
365 for ctor in self.original.ctors:
366 instantiated_args = instantiate_args_list(
372 instantiated_ctors.append(
375 args=parser.ArgumentList(instantiated_args),
378 return instantiated_ctors
380 def instantiate_static_methods(self, typenames):
382 Instantiate static methods in the class.
385 typenames: List of template types to instantiate.
387 Return: List of static methods instantiated with provided template args.
389 instantiated_static_methods = []
390 for static_method in self.original.static_methods:
391 instantiated_args = instantiate_args_list(
392 static_method.args.args_list, typenames, self.instantiations,
394 instantiated_static_methods.append(
396 name=static_method.name,
397 return_type=instantiate_return_type(
398 static_method.return_type,
402 instantiated_class=self),
403 args=parser.ArgumentList(instantiated_args),
406 return instantiated_static_methods
408 def instantiate_class_templates_in_methods(self, typenames):
410 This function only instantiates the class-level templates in the methods.
411 Template methods are instantiated in InstantiatedMethod in the second
418 void sayHello(T& name);
422 typenames: List of template types to instantiate.
424 Return: List of methods instantiated with provided template args on the class.
426 class_instantiated_methods = []
427 for method in self.original.methods:
428 instantiated_args = instantiate_args_list(
429 method.args.args_list,
434 class_instantiated_methods.append(
436 template=method.template,
438 return_type=instantiate_return_type(
444 args=parser.ArgumentList(instantiated_args),
445 is_const=method.is_const,
448 return class_instantiated_methods
450 def instantiate_operators(self, typenames):
452 Instantiate the class-level template in the operator overload.
455 typenames: List of template types to instantiate.
457 Return: List of methods instantiated with provided template args on the class.
459 instantiated_operators = []
460 for operator in self.original.operators:
461 instantiated_args = instantiate_args_list(
462 operator.args.args_list,
467 instantiated_operators.append(
470 operator=operator.operator,
471 return_type=instantiate_return_type(
472 operator.return_type,
477 args=parser.ArgumentList(instantiated_args),
478 is_const=operator.is_const,
481 return instantiated_operators
483 def instantiate_properties(self, typenames):
485 Instantiate the class properties.
488 typenames: List of template types to instantiate.
490 Return: List of properties instantiated with provided template args.
492 instantiated_properties = instantiate_args_list(
493 self.original.properties,
498 return instantiated_properties
501 """Generate the C++ code for wrapping."""
502 return self.cpp_typename().to_cpp()
504 def cpp_typename(self):
506 Return a parser.Typename including namespaces and cpp name of this
509 if self.original.template:
510 name = "{}<{}>".format(
512 ", ".join([inst.to_cpp() for inst in self.instantiations]))
514 name = self.original.name
515 namespaces_name = self.namespaces()
516 namespaces_name.append(name)
517 return parser.Typename(namespaces_name)
520 def instantiate_namespace_inplace(namespace):
522 Instantiate the classes and other elements in the `namespace` content and
523 assign it back to the namespace content attribute.
525 @param[in/out] namespace The namespace whose content will be replaced with
526 the instantiated content.
528 instantiated_content = []
531 for element in namespace.content:
532 if isinstance(element, parser.Class):
533 original_class = element
534 if not original_class.template:
535 instantiated_content.append(
536 InstantiatedClass(original_class, []))
538 # This case is for when the templates have enumerated instantiations.
540 # Use itertools to get all possible combinations of instantiations
541 # Works even if one template does not have an instantiation list
542 for instantiations in itertools.product(
543 *original_class.template.instantiations):
544 instantiated_content.append(
545 InstantiatedClass(original_class,
546 list(instantiations)))
548 elif isinstance(element, parser.GlobalFunction):
549 original_func = element
550 if not original_func.template:
551 instantiated_content.append(
552 InstantiatedGlobalFunction(original_func, []))
554 # Use itertools to get all possible combinations of instantiations
555 # Works even if one template does not have an instantiation list
556 for instantiations in itertools.product(
557 *original_func.template.instantiations):
558 instantiated_content.append(
559 InstantiatedGlobalFunction(original_func,
560 list(instantiations)))
562 elif isinstance(element, parser.TypedefTemplateInstantiation):
563 # This is for the case where `typedef` statements are used
564 # to specify the template parameters.
565 typedef_inst = element
566 top_level = namespace.top_level()
567 original_element = top_level.find_class_or_function(
568 typedef_inst.typename)
570 # Check if element is a typedef'd class or function.
571 if isinstance(original_element, parser.Class):
572 typedef_content.append(
573 InstantiatedClass(original_element,
574 typedef_inst.typename.instantiations,
575 typedef_inst.new_name))
576 elif isinstance(original_element, parser.GlobalFunction):
577 typedef_content.append(
578 InstantiatedGlobalFunction(
579 original_element, typedef_inst.typename.instantiations,
580 typedef_inst.new_name))
582 elif isinstance(element, parser.Namespace):
583 instantiate_namespace_inplace(element)
584 instantiated_content.append(element)
586 instantiated_content.append(element)
588 instantiated_content.extend(typedef_content)
589 namespace.content = instantiated_content
def instantiate_args_list(args_list, template_typenames, instantiations, cpp_typename)