def _GenerateHeaderHelper(t_pp, t_ns, u_ns=None, private='', is_extended_from_python=False): m = pyext.Module(f'fq.py.{private}path') ns = 'clif::name::' w = ns + 'wrapper' m.types = [ types.ClassType('c::name::cpp_name', t_pp, w, w + '_Type', ns, can_copy=False, can_move=False, can_destruct=True, virtual='', ns=t_ns) ] if u_ns is not None: m.types.append( types.ClassType('other::cpp_name', 'py.path.u', w, w + '_Type', ns, can_copy=False, can_move=False, can_destruct=True, virtual='', ns=u_ns)) return '\n'.join( m.GenerateHeader('fq/py/my.clif', 'fq/my.h', {}, is_extended_from_python)) + '\n'
def testUncopyableButMovableClassType(self): ns = 'clif::name::' w = ns + 'wrapper' t = types.ClassType('c::name::cpp_name', 'fq.py.path', w, w + '_Type', ns, can_copy=False, can_move=True, can_destruct=True, virtual='') header = '\n'.join(t.GenHeader()) + '\n' self.assertMultiLineEqual( header, textwrap.dedent("""\ // CLIF use `c::name::cpp_name` as fq.py.path bool Clif_PyObjAs(PyObject* input, c::name::cpp_name** output); bool Clif_PyObjAs(PyObject* input, std::shared_ptr<c::name::cpp_name>* output); bool Clif_PyObjAs(PyObject* input, std::unique_ptr<c::name::cpp_name>* output); PyObject* Clif_PyObjFrom(c::name::cpp_name*, py::PostConv); PyObject* Clif_PyObjFrom(std::shared_ptr<c::name::cpp_name>, py::PostConv); PyObject* Clif_PyObjFrom(std::unique_ptr<c::name::cpp_name>, py::PostConv); PyObject* Clif_PyObjFrom(c::name::cpp_name&&, py::PostConv); template<typename T> typename std::enable_if<std::is_same<T, c::name::cpp_name>::value>::type Clif_PyObjFrom(const c::name::cpp_name*, py::PostConv) = delete; template<typename T> typename std::enable_if<std::is_same<T, c::name::cpp_name>::value>::type Clif_PyObjFrom(const c::name::cpp_name&, py::PostConv) = delete; """))
def testClass1Header(self): m = pyext.Module('fq.py.path', for_py3=str is not bytes) ns = 'clif::name::' w = ns + 'wrapper' t = types.ClassType('c::name::cpp_name', 'py.path', w, w + '_Type', ns, can_copy=False, can_move=False, can_destruct=True, virtual='', ns='c::name') m.types = [t] header = '\n'.join(m.GenerateHeader('fq/py/my.clif', 'fq/my.h', {})) + '\n' self.assertMultiLineEqual( header, textwrap.dedent("""\ ////////////////////////////////////////////////////////////////////// // This file was automatically generated by CLIF to run under Python %d // Version 0.3 ////////////////////////////////////////////////////////////////////// // source: fq/py/my.clif #include <memory> #include "absl/types/optional.h" #include "fq/my.h" #include "clif/python/postconv.h" namespace c { namespace name { using namespace ::clif; // CLIF use `c::name::cpp_name` as py.path bool Clif_PyObjAs(PyObject* input, c::name::cpp_name** output); bool Clif_PyObjAs(PyObject* input, std::shared_ptr<c::name::cpp_name>* output); bool Clif_PyObjAs(PyObject* input, std::unique_ptr<c::name::cpp_name>* output); PyObject* Clif_PyObjFrom(c::name::cpp_name*, py::PostConv); PyObject* Clif_PyObjFrom(std::shared_ptr<c::name::cpp_name>, py::PostConv); PyObject* Clif_PyObjFrom(std::unique_ptr<c::name::cpp_name>, py::PostConv); template<typename T> typename std::enable_if<std::is_same<T, c::name::cpp_name>::value>::type Clif_PyObjFrom(const c::name::cpp_name*, py::PostConv) = delete; template<typename T> typename std::enable_if<std::is_same<T, c::name::cpp_name>::value>::type Clif_PyObjFrom(const c::name::cpp_name&, py::PostConv) = delete; } } // namespace c::name // CLIF init_module if (PyObject* m = PyImport_ImportModule("fq.py.path")) Py_DECREF(m); // CLIF init_module else goto err; """ % (3 if m.py3output else 2)))
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 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 if not c.suppress_upcasts: 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, 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))