Пример #1
0
 def WrapFunc(self, f, ln, unused_ns, class_ns=''):
     """Process AST.FuncDecl f."""
     cname = Ident(f.name.cpp_name)
     assert cname, 'cpp_name is empty for ' + f.name.native
     pyname = f.name.native.rstrip('#')
     if pyname.endswith('@'):
         ctxmgr = pyname
         pyname = pyname[:-1]
     else:
         ctxmgr = None
     if self.nested and cname.startswith('operator'):
         wrapper_name = 'wrap' + pyname
     elif pyname == '__init__':
         wrapper_name = 'wrap' + types.Mangle(self.name) + '_as___init__'
     else:
         wrapper_name = 'wrap' + types.Mangle(cname)
         if pyname != cname:
             wrapper_name += '_as_' + pyname
     if f.ignore_return_value:
         assert len(
             f.returns) < 2, ('Func with ignore_return_value has too many'
                              ' returns (%d)' % len(f.returns))
         del f.returns[:]
     for s in self._WrapAllCallables(f, cname, ln, class_ns, False):
         yield s
     if f.cpp_opfunction or (f.is_extend_method and not f.classmethod
                             and not f.constructor):
         self_param = f.params.pop(0)
     else:
         self_param = None
     if ctxmgr:
         assert not f.classmethod, "Context manager methods can't be static"
         # Force context manager args API.
         meth = VARARGS if ctxmgr == '__exit__@' else NOARGS
     else:
         meth = VARARGS if len(f.params) else NOARGS
     for s in gen.FunctionCall(
             # Keep '@' in method name to distinguish a ctxmgr.
             f.name.native.rstrip('#'),
             wrapper_name,
             func_ast=f,
             lineno=ln,
             call=self._FunctionCallExpr(f, cname, pyname),
             doc=next(astutils.Docstring(f)),
             prepend_self=self_param,
             catch=self.catch_cpp_exceptions and not f.cpp_noexcept,
             postcall_init=None,
             typepostconversion=self.typemap):
         yield s
     if f.classmethod:
         meth += ' | METH_CLASS'
     # Keep '#' in method name to distinguish map/seq slots.
     self.methods.append(
         (f.name.native.rstrip('@'), wrapper_name, meth,
          '\\n'.join(astutils.Docstring(f)).replace('"', '\\"')))
Пример #2
0
 def testMangle(self):
     self.assertEqual(types.Mangle('::A<B*, const C&>'),
                      'A_B_ptr_constC_ref')
     self.assertEqual(types.Mangle('::A<B::C>'), 'A_B_C')
     self.assertEqual(types.Mangle('Abc'), 'Abc')
     self.assertEqual(types.Mangle('Abc::D'), 'Abc_D')
     self.assertEqual(types.Mangle('Abc<D>'), 'Abc_D')
     self.assertEqual(types.Mangle('Abc<D>&&'), 'Abc_D__rref')
     self.assertEqual(types.Mangle('Abc<D::E>'), 'Abc_D_E')
     self.assertEqual(types.Mangle('Abc<D *> *'), 'Abc_D_ptr__ptr')
     self.assertEqual(types.Mangle('Abc<const D *> *'),
                      'Abc_constD_ptr__ptr')
     self.assertEqual(types.Mangle('Abc const&'), 'Abcconst_ref')
Пример #3
0
  def testMangleEscapedCharLiteral(self):
    # Octal escapes.
    self.assertEqual(
        types.Mangle(r"A<'\0', '\000', '\5', '\777'>"), 'A_c0__c0__c5__c511_')

    # Hexadecimal escapes.
    self.assertEqual(
        types.Mangle(r"A<'\x00', '\xff', '\xFF', '\xA0'>"),
        'A_c0__c255__c255__c160_')

    # Unicode escapes.
    self.assertEqual(
        types.Mangle(r"A<'\u0000', '\u0001', '\u1000', '\u9999'>"),
        'A_c0__c1__c4096__c39321_')
    self.assertEqual(
        types.Mangle(r"A<'\U00000000', '\U00109999'>"), 'A_c0__c1087897_')

    # ASCII control code escapes.
    self.assertEqual(
        types.Mangle(r"A<'\a', '\b', '\f', '\n', '\r', '\t', '\v'>"),
        'A_c7__c8__c12__c10__c13__c9__c11_')

    # Other low-ASCII escapes.
    self.assertEqual(
        types.Mangle("A<'\\'', '\\\"', '\\\\'>"), 'A_c39__c34__c92_')
Пример #4
0
 def WrapClass(self, c, unused_ln, cpp_namespace, unused_class_ns=''):
     """Process AST.ClassDecl c."""
     cname = Ident(c.name.cpp_name)
     pyname = c.name.native
     self.Add(Context(cname, pyname, c.name.cpp_name))
     ns = self.class_namespace
     yield ''
     yield 'namespace %s {' % ns
     virtual, has_iterator = _IsSpecialClass(c, pyname)
     if virtual:
         for s in gen.VirtualOverriderClass(
                 VIRTUAL_OVERRIDER_CLASS,
                 pyname,
                 c.name.cpp_name,
                 c.name.cpp_name + '::' + cname,
                 c.cpp_abstract,
                 Ident,
                 vfuncs=virtual,
                 pcfunc=lambda t, tm=self.typemap: postconv.Initializer(
                     t, tm)):
             yield s
         c.bases.add().cpp_name = c.name.cpp_name
     # Flag that we're now in the nested __iter__ class.
     iter_class = (pyname == _ITER_KW)
     for s in gen.WrapperClassDef(
             WRAPPER_CLASS_NAME,
             is_iter=iter_class,
             has_iter=has_iterator,
             iter_ns=_ClassNamespace(_ITER_KW),
             cname=c.name.cpp_name,
             ctype=VIRTUAL_OVERRIDER_CLASS if virtual else c.name.cpp_name,
             enable_instance_dict=c.enable_instance_dict):
         yield s
     tracked_slot_groups = {}
     cpp_has_ext_def_ctor = False
     if iter_class:
         # Special-case nested __iter__ class.
         for s in _WrapIterSubclass(c.members, self.typemap):
             yield s
         tp_slots = {
             'tp_flags': ['Py_TPFLAGS_DEFAULT'],
             'tp_iter': 'PyObject_SelfIter',
             'tp_iternext': gen.IterNext.name
         }
         ctor = None
     else:
         # Normal class generator.
         if c.final:
             self.final = True
         else:
             yield ''
             yield 'static %s* ThisPtr(PyObject*);' % c.name.cpp_name
         ctor = ('DEF' if c.cpp_has_def_ctor and
                 (not c.cpp_abstract or virtual) else None)
         for d in c.members:
             if d.decltype == d.FUNC:
                 f = d.func
                 if f.name.native == '__init__':
                     if virtual:
                         # Constructors are not virtual, but we mark the constructor as
                         # virtual to indicate that there exist virtual functions in the
                         # Clif class declaration.
                         f.virtual = True
                     if not f.params:
                         if f.is_extend_method:
                             cpp_has_ext_def_ctor = True
                         else:
                             # Skip generating wrapper function for unextended default
                             # ctor. But wrapper function for extended default ctor is still
                             # necessary.
                             continue
                     ctor = 'wrap%s_as___init__' % types.Mangle(cname)
                 elif c.cpp_abstract and f.virtual:
                     continue  # Skip calling virtual func from the abstract base class.
             for s in self.WrapDecl(d, parent_ns=cpp_namespace,
                                    class_ns=ns):
                 yield s
         if virtual and not ctor:
             raise ValueError(
                 'A constructor should be declared in the Clif wrapper for %s as it '
                 'has virtual method declarations.' % c.name.cpp_name)
         # For Py2 slots.py relies on Py_TPFLAGS_DEFAULT being set.
         tp_slots = {'tp_flags': ['Py_TPFLAGS_DEFAULT']}
         if c.cpp_abstract:
             tp_slots['tp_flags'].append('Py_TPFLAGS_IS_ABSTRACT')
         if not self.py3output:
             tp_slots['tp_flags'].append('Py_TPFLAGS_CHECKTYPES')
         if not c.final:
             tp_slots['tp_flags'].append('Py_TPFLAGS_BASETYPE')
         if has_iterator:
             n = _ClassNamespace(_ITER_KW) + '::'
             w = n + WRAPPER_CLASS_NAME
             # Python convention to have a type struct named FOO_Type.
             for s in gen.NewIter(_GetCppObj(), n, w, w + '_Type'):
                 yield s
             tp_slots['tp_iter'] = gen.NewIter.name
         if self.properties or c.enable_instance_dict:
             yield ''
             for s in gen.GetSetDef(self.properties,
                                    c.enable_instance_dict):
                 yield s
             tp_slots['tp_getset'] = gen.GetSetDef.name
         for b in c.bases:
             if b.cpp_name and not b.native:
                 p = b.cpp_name
                 w = 'as_' + types.Mangle(p)  # pyname == cname == w
                 for s in gen.CastAsCapsule(_GetCppObj(), p, w):
                     yield s
                 self.methods.append((w, w, NOARGS, 'Upcast to %s*' % p))
         _AppendReduceExIfNeeded(self.methods)
         if self.methods:
             for s in slots.GenSlots(self.methods,
                                     tp_slots,
                                     py3=self.py3output,
                                     tracked_groups=tracked_slot_groups):
                 yield s
             if self.methods:  # If all methods are slots, it's empty.
                 for s in gen.MethodDef(self.methods):
                     yield s
                 tp_slots['tp_methods'] = gen.MethodDef.name
     qualname = '.'.join(f.pyname for f in self.nested)
     tp_slots['tp_name'] = '"%s.%s"' % (self.path, qualname)
     if c.docstring:
         docstring = c.docstring.strip()
         # Escape characters for inclusion in the raw C string.
         if str is bytes:  # PY2
             docstring = docstring.encode('unicode-escape')
         docstring = docstring.replace('\n', r'\n')
         docstring = docstring.replace('"', r'\"')
         tp_slots['tp_doc'] = '"%s"' % docstring
     if c.async_dtor and c.cpp_has_trivial_dtor:
         raise ValueError(
             'Class %s has a trivial dtor yet @async__del__ decorator' %
             pyname)
     # Python convention to have a type struct named FOO_Type.
     # Generate wrapper Type object in wname+'_Type' static var.
     for s in gen.TypeObject(
             qualname,
             tracked_slot_groups,
             tp_slots,
             pyname,
             ctor,
             wname=WRAPPER_CLASS_NAME,
             fqclassname=c.name.cpp_name,
             abstract=c.cpp_abstract,
             iterator=_GetCppObj('iter') if iter_class else None,
             trivial_dtor=c.cpp_has_trivial_dtor,
             subst_cpp_ptr=VIRTUAL_OVERRIDER_CLASS if virtual else '',
             enable_instance_dict=c.enable_instance_dict,
             cpp_has_ext_def_ctor=cpp_has_ext_def_ctor):
         yield s
     if not iter_class:
         for s in types.GenThisPointerFunc(c.name.cpp_name,
                                           WRAPPER_CLASS_NAME, c.final):
             yield s
     yield ''
     yield '}  // namespace ' + ns
     type_dict = self.dict
     wrapns = '::'.join(f.class_namespace for f in self.nested) + '::'
     wclass = wrapns + WRAPPER_CLASS_NAME
     vclass = wrapns + VIRTUAL_OVERRIDER_CLASS
     # Python convention to have a type struct named FOO_Type.
     wtype = wclass + '_Type'
     self.DropContext()
     base, wrapped_base = _ProcessInheritance(
         c.bases, '::%s_Type' % WRAPPER_CLASS_NAME, self.namemap)
     self.types_init.append((wtype, base, wrapped_base, type_dict))
     if iter_class:
         if base:
             raise TypeError(
                 "__iter__ class can't be derived, base '%s' found" % base)
     else:
         self.dict.append((pyname, types.AsPyObj(wtype)))
         self.types.append(
             types.ClassType(c.name.cpp_name,
                             qualname,
                             wclass,
                             wtype,
                             wrapns,
                             can_copy=c.cpp_copyable and not c.cpp_abstract,
                             can_move=c.cpp_movable and not c.cpp_abstract,
                             can_destruct=c.cpp_has_public_dtor,
                             virtual=vclass if virtual else '',
                             ns=cpp_namespace))
Пример #5
0
    def WrapVar(self, v, unused_ln, unused_ns, unused_class_ns=''):
        """Process AST.VarDecl v."""
        assert self.nested, 'C++ global vars not allowed, use const'
        assert '::' not in v.name.cpp_name
        vname = v.name.cpp_name
        ctype = v.type.cpp_type
        if v.cpp_set and not v.cpp_get:
            raise NameError('Property %s has setter, but no getter' %
                            v.name.native)
        getter = 'get_' + vname
        setter = 'set_' + vname
        if self.final:
            base = None
            cobj = _GetCppObj() + '->'
        else:
            base = 'auto cpp = ThisPtr(self); if (!cpp) '
            cobj = 'cpp->'
        is_property = False
        if v.cpp_get.name.cpp_name:
            # It's a property var (with cpp only `getter`, `setter`).
            assert not v.cpp_get.name.native
            is_property = True
            if v.cpp_get.classmethod or v.cpp_set.classmethod:
                raise ValueError('Static properties not supported')
            if v.cpp_set.name.cpp_name:
                assert not v.cpp_set.name.native
            else:
                setter = 'nullptr'
            vname = Ident(v.cpp_get.name.cpp_name) + '()'
        nested_container = not is_property
        cvar = cobj + vname
        getval = cvar
        setval = (cobj + Ident(v.cpp_set.name.cpp_name)
                  if v.cpp_set.name.cpp_name else '')
        if v.is_extend_variable:
            orig_getter_name = v.cpp_get.name.cpp_name.split(
                ast_manipulations.EXTEND_INFIX)[-1]
            cname = Ident(v.cpp_get.name.cpp_name)
            wrapper_name = 'wrap' + types.Mangle(cname)
            getval = wrapper_name + '_as_' + orig_getter_name + '(self)'

            if v.cpp_set:
                setval = v.cpp_set.name.cpp_name
        unproperty = False
        if v.cpp_get.name.native:
            # It's an unproperty var (@getter pyname / @setter pyname).
            assert not v.cpp_get.name.cpp_name
            unproperty = True
            nested_container = False
            self.methods.append((v.cpp_get.name.native, getter, NOARGS,
                                 '%s()->%s  C++ %s.%s getter' %
                                 (v.cpp_get.name.native, v.type.lang_type,
                                  self.name, v.name.cpp_name)))
            if v.cpp_set.name.native:
                assert not v.cpp_set.name.cpp_name
                self.methods.append((v.cpp_set.name.native, setter, 'METH_O',
                                     '%s(%s)  C++ %s.%s setter' %
                                     (v.cpp_set.name.native, v.type.lang_type,
                                      self.name, v.name.cpp_name)))
            else:
                setter = 'nullptr'
        else:
            self.properties.append(
                (v.name.native, getter, setter,
                 'C++ %s %s.%s' % (ctype, self.CppName(), vname)))
            if not v.type.cpp_abstract and not ctype.startswith(
                    '::std::shared_ptr'):
                if v.type.cpp_raw_pointer or ctype.startswith(
                        '::std::unique_ptr'):
                    getval = '*' + cvar
                    nested_container = False
                elif nested_container and v.type.cpp_toptr_conversion:
                    # For a nested container we'll try to return it (we use
                    # cpp_toptr_conversion as an indicator for a custom container).
                    getval = '::clif::MakeStdShared(%s, &%s)' % (_GetCppObj(),
                                                                 cvar)
        for s in gen.VarGetter(getter,
                               unproperty,
                               base,
                               getval,
                               postconv.Initializer(v.type, self.typemap),
                               is_extend=v.is_extend_variable):
            yield s
        if setter != 'nullptr':
            for s in gen.VarSetter(setter,
                                   unproperty,
                                   base,
                                   cvar,
                                   v,
                                   setval,
                                   as_str=('PyUnicode_AsUTF8' if self.py3output
                                           else 'PyString_AS_STRING'),
                                   is_extend=v.is_extend_variable):
                yield s
Пример #6
0
 def WrapClass(self, c, unused_ln, cpp_namespace):
     """Process AST.ClassDecl c."""
     cname = Ident(c.name.cpp_name)
     pyname = c.name.native
     self.Add(Context(cname, pyname, c.name.cpp_name))
     ns = self.class_namespace
     yield ''
     yield 'namespace %s {' % ns
     virtual = False
     # If c has @virtual members, generate a derived redirector class.
     for d in c.members:
         if d.decltype == d.FUNC and d.func.virtual:
             if not virtual:
                 yield ''
                 yield 'struct %s : PyObj, %s {' % (VIRTUAL_OVERRIDER_CLASS,
                                                    c.name.cpp_name)
                 yield I + 'using %s::%s;' % (c.name.cpp_name, cname)
                 virtual = True
             for s in gen.VirtualFunctionCall(
                     Ident(d.func.name.cpp_name), d.func, pyname,
                     c.cpp_abstract, lambda atype: postconv.Initializer(
                         atype, self.typemap)):
                 yield s
     if virtual:
         if c.final:
             raise ValueError("Final class %s can't have virtual methods." %
                              pyname)
         yield '};'
         b = c.bases.add()
         b.cpp_name = c.name.cpp_name
     yield ''
     yield 'struct %s {' % self.wrapper_class_name
     yield I + 'PyObject_HEAD'
     yield I + '::clif::SharedPtr<%s> cpp;' % (VIRTUAL_OVERRIDER_CLASS if
                                               virtual else c.name.cpp_name)
     yield '};'
     if c.final:
         self.final = True
     else:
         yield 'static %s* ThisPtr(PyObject*);' % c.name.cpp_name
     ctor = ('DEF' if c.cpp_has_def_ctor and
             (not c.cpp_abstract or virtual) else None)
     for d in c.members:
         if d.decltype == d.FUNC:
             f = d.func
             if f.name.native == '__init__':
                 if virtual:
                     # Constructors are not virtual, but we mark the constructor as
                     # virtual to indicate that there exist virtual functions in the
                     # Clif class declaration.
                     f.virtual = True
                 if not f.params:
                     continue  # Skip generating wrapper function for default ctor.
                 ctor = 'wrap%s_as___init__' % types.Mangle(cname)
             elif c.cpp_abstract and f.virtual:
                 continue  # Skip calling virtual func from the abstract base class.
         for s in (self.WrapDecl(d, cpp_namespace)):
             yield s
     if virtual and not ctor:
         raise ValueError(
             'A constructor should be declared in the Clif wrapper for %s as it '
             'has virtual method declarations.' % c.name.cpp_name)
     # For Py2 slots.py relies on Py_TPFLAGS_DEFAULT being set.
     tp_slots = {
         'tp_flags': ['Py_TPFLAGS_DEFAULT', 'Py_TPFLAGS_TYPE_SUBCLASS']
     }
     if c.cpp_abstract:
         tp_slots['tp_flags'].append('Py_TPFLAGS_IS_ABSTRACT')
     if not self.py3output:
         tp_slots['tp_flags'].append('Py_TPFLAGS_CHECKTYPES')
     if not c.final: tp_slots['tp_flags'].append('Py_TPFLAGS_BASETYPE')
     if self.properties:
         for s in (gen.GetSetDef(self.properties)):
             yield s
         tp_slots['tp_getset'] = gen.GetSetDef.name
     for b in c.bases:
         if b.cpp_name and not b.native:
             p = b.cpp_name
             w = 'as_' + types.Mangle(p)  # pyname == cname == w
             yield ''
             yield '// Implicit cast this as %s*' % p
             yield 'static PyObject* %s(PyObject* self) {' % w
             yield I + ('%s* p = ::clif::python::Get(reinterpret_cast<%s*>('
                        'self)->cpp);' % (p, self.wrapper_class_name))
             yield I + 'if (p == nullptr) return nullptr;'
             yield I + ('return PyCapsule_New(p, C("%s"), nullptr);') % p
             yield '}'
             self.methods.append((w, w, NOARGS, 'Upcast to %s*' % p))
     if self.methods:
         for s in (slots.GenSlots(self.methods,
                                  tp_slots,
                                  py3=self.py3output)):
             yield s
         if self.methods:  # If all methods are slots, it's empty.
             for s in (gen.MethodDef(self.methods)):
                 yield s
             tp_slots['tp_methods'] = gen.MethodDef.name
     pypath = '.'.join(f.pyname for f in self.nested)
     tp_slots['tp_name'] = '"%s.%s"' % (self.path, pypath)
     for s in (
             # Generate wrapper Type object in wname+'_Type' static var.
             gen.TypeObject(
                 tp_slots,
                 slots.GenTypeSlots,
                 pyname,
                 ctor=ctor,
                 wname=self.wrapper_class_name,
                 fqclassname=c.name.cpp_name,
                 abstract=c.cpp_abstract,
                 subst_cpp_ptr=VIRTUAL_OVERRIDER_CLASS if virtual else '',
                 async_dtor=c.async_dtor,
             )):
         yield s
     for s in types.GenThisPointerFunc(c.name.cpp_name,
                                       self.wrapper_class_name, c.final):
         yield s
     yield '}  // namespace ' + ns
     type_dict = self.dict
     wrapns = '::'.join(f.class_namespace for f in self.nested) + '::'
     wclass = wrapns + self.wrapper_class_name
     vclass = wrapns + VIRTUAL_OVERRIDER_CLASS
     wrap = '::' + self.wrapper_class_name
     wtype = wclass + '_Type'
     wrapt = wrap + '_Type'
     self.DropContext()
     base = cpp_replacement = None
     if c.bases:
         # Pytd fills bases with native name (cpp_name='') for Python inheritance.
         # Matcher extends it with cpp_name (native=='') for C++ parent classes.
         base = [b.native for b in c.bases if b.native and not b.cpp_name]
         if base:
             if len(base) > 1:
                 raise ValueError('Multiple inheritance not supported')
             base = base[0]
             if '.' not in base:
                 # base defined in the same .clif wrapper
                 base = _ClassNamespace(base) + wrapt
         elif c.bases[0].native == 'replacement':
             assert c.bases[0].cpp_name
             cpp_replacement = c.bases[0].cpp_name
     self.types_init.append((wtype, base, type_dict))
     self.dict.append(
         (pyname, types.AsPyObj(self.wrap_namespace + '::' + wtype)))
     if cpp_replacement:
         # Find replacement class namespace.
         for b in c.cpp_bases:
             if b.name == cpp_replacement:
                 cpp_namespace = b.namespace
                 break
         else:
             cpp_namespace = ''
     self.types.add(
         types.ClassType(c.name.cpp_name,
                         pypath,
                         wclass,
                         wtype,
                         wrapns,
                         can_copy=c.cpp_copyable and not c.cpp_abstract,
                         can_destruct=c.cpp_has_public_dtor,
                         down_cast=cpp_replacement,
                         virtual=vclass if virtual else '',
                         ns=cpp_namespace))
Пример #7
0
 def WrapFunc(self, f, ln, unused_ns):
     """Process AST.FuncDecl f."""
     cname = Ident(f.name.cpp_name)
     assert cname
     pyname = f.name.native.rstrip('#')
     if pyname.endswith('@'):
         ctxmgr = pyname
         pyname = pyname[:-1]
     else:
         ctxmgr = None
     if self.nested and cname.startswith('operator'):
         wrapper_name = 'wrap' + pyname
     elif pyname == '__init__':
         wrapper_name = 'wrap' + types.Mangle(self.name) + '_as___init__'
     else:
         wrapper_name = 'wrap' + types.Mangle(cname)
         if pyname != cname:
             wrapper_name += '_as_' + pyname
     if f.ignore_return_value:
         assert len(
             f.returns) < 2, ('Func with ignore_return_value has too many'
                              ' returns (%d)' % len(f.returns))
         del f.returns[:]
     for i, r in enumerate(f.returns):
         if r.type.HasField('callable'):
             for s in (self.WrapCallable(r.type, cname, i, ln)):
                 yield s
     self_param = f.params.pop(0) if f.cpp_opfunction else None
     if ctxmgr:
         assert not f.classmethod, "Context manager methods can't be static"
         # Force context manager args API.
         meth = VARARGS if ctxmgr == '__exit__@' else NOARGS
     else:
         meth = VARARGS if len(f.params) else NOARGS
     call = f.name.cpp_name
     postcall = None
     if self.nested and not f.classmethod:
         cpp = 'reinterpret_cast<%s*>(self)->cpp' % self.wrapper_class_name
         if f.constructor:
             assert not f.returns, cname + ' ctor must return void'
             if f.virtual:
                 ctor = VIRTUAL_OVERRIDER_CLASS
                 postcall = '->::clif::PyObj::Init(self);'
             else:
                 ctor = self.fqname
             if pyname == '__init__':
                 call = '%s = ::clif::MakeShared<%s>' % (cpp, ctor)
                 if postcall:
                     postcall = cpp + postcall
                 # C++ constructors do not return anything.
                 f.cpp_void_return = True
             else:  # additional ctors
                 f.classmethod = True
                 call = '::gtl::MakeUnique<%s>' % ctor
                 # Pretend we're returning a new instance.
                 r = f.returns.add()
                 r.type.lang_type = self.pyname
                 r.type.cpp_type = 'std::unique_ptr<%s>' % ctor
                 f.cpp_void_return = False
         elif not f.cpp_opfunction:
             if self.final:
                 call = cpp + '->' + cname
             else:
                 call = [
                     '%s* c = ThisPtr(self);' % self.fqname,
                     'if (!c) return nullptr;',
                 ]
                 if f.virtual:
                     call.append('c->' + self.name + '::' + cname)
                 else:
                     call.append('c->' + cname)
     for s in (
             # Keep '@' in method name to distinguish ctxmgr.
             gen.FunctionCall(f.name.native.rstrip('#'),
                              wrapper_name,
                              doc=next(astutils.Docstring(f)),
                              catch=self.catch_cpp_exceptions
                              and not f.cpp_noexcept,
                              call=call,
                              postcall_init=postcall,
                              lineno=ln,
                              prepend_self=self_param,
                              typepostconversion=self.typemap,
                              func_ast=f)):
         yield s
     if f.classmethod:
         meth += ' | METH_CLASS'
     # Keep '#' in method name to distinguish map/seq slots.
     self.methods.append(
         (f.name.native.rstrip('@'), wrapper_name, meth,
          '\\n'.join(astutils.Docstring(f)).replace('"', '\\"')))