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))
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))