コード例 #1
0
ファイル: pyext.py プロジェクト: yijunyu/clif
 def WrapVar(self, v, unused_ln, unused_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
   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) + '()'
   cvar = cobj + vname
   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
     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' % (
         v.type.cpp_type, self.CppName(), vname)))
   # For a nested container we'll try to return it (we use cpp_toptr_conversion
   # as an indicator for a custom container).
   for s in gen.VarGetter(getter, unproperty, base, cvar, _GetCppObj(),
                          pc=postconv.Initializer(v.type, self.typemap),
                          get_nested=(not is_property and not unproperty and
                                      v.type.cpp_toptr_conversion)): yield s
   if setter != 'nullptr':
     for s in gen.VarSetter(setter, unproperty, base, cvar, v,
                            cobj + Ident(v.cpp_set.name.cpp_name)
                            if v.cpp_set.name.cpp_name else '',
                            as_str=('PyUnicode_AsUTF8' if self.py3output else
                                    'PyString_AS_STRING')): yield s
コード例 #2
0
 def testPatternStr(self):
   """str -> _1"""
   index = {'str': '_1', 'ztype': '_2'}
   ast_type = ast_pb2.Type()
   text_format.Parse("""
       lang_type: "str"
     """, ast_type)
   self.assertEqual(postconv.Initializer(ast_type, index), '_1')
コード例 #3
0
 def testPatternInt(self):
   """int -> {}"""
   index = {'str': 1, 'ztype': 2}
   ast_type = ast_pb2.Type()
   text_format.Parse("""
       lang_type: "int"
     """, ast_type)
   self.assertEqual(postconv.Initializer(ast_type, index), '{}')
コード例 #4
0
 def testPatternListStr(self):
   """list<str> -> {_1}"""
   index = {'str': '_1', 'ztype': '_2'}
   ast_type = ast_pb2.Type()
   text_format.Parse("""
       lang_type: "list<str>"
       params {
         lang_type: "str"
       }
     """, ast_type)
   self.assertEqual(postconv.Initializer(ast_type, index), '{_1}')
コード例 #5
0
def _WrapIterSubclass(members, typemap):
  """Special-case nested __iter__ class."""
  assert len(members) == 1, ('__iter__ class must have only one "def",'
                             ' %d members found' % len(members))
  d = members[0]
  assert d.decltype == d.FUNC, ('__iter__ class must have only one "def",'
                                ' %s member found' % d.decltype)
  assert d.func.name.native == '__next__', (
      '__iter__ class must have only one "def __next__", "def %s" found'
      % d.func.name.native)
  for s in gen.IterNext(_GetCppObj('iter'), not d.func.py_keep_gil,
                        postconv.Initializer(d.func.returns[0].type, typemap)):
    yield s
コード例 #6
0
 def testPatternNested(self):
   """dict<int, tuple<str, ztype>> -> {_0,{_1,_2}}"""
   index = {'str': '_1', 'ztype': '_2'}
   ast_type = ast_pb2.Type()
   text_format.Parse("""
       lang_type: "dict<int, tuple<str, ztype>>"
       params {
         lang_type: "int"
       }
       params {
         lang_type: "tuple<str, ztype>"
         params {
           lang_type: "str"
         }
         params {
           lang_type: "ztype"
         }
       }
     """, ast_type)
   self.assertEqual(postconv.Initializer(ast_type, index), '{_0,{_1,_2}}')
コード例 #7
0
ファイル: pyext.py プロジェクト: sr-gi/clif
 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))
コード例 #8
0
ファイル: pyext.py プロジェクト: sr-gi/clif
    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
コード例 #9
0
ファイル: pyext.py プロジェクト: sr-gi/clif
 def WrapConst(self, c, unused_ln, unused_ns, unused_class_ns=''):
     """Process AST.ConstDecl c."""
     self.dict.append((c.name.native, 'Clif_PyObjFrom(%s, %s)' %
                       (types.AsType(c.type.cpp_type, c.name.cpp_name),
                        postconv.Initializer(c.type, self.typemap))))
     return []
コード例 #10
0
def FunctionCall(pyname,
                 wrapper,
                 doc,
                 catch,
                 call,
                 postcall_init,
                 typepostconversion,
                 func_ast,
                 lineno,
                 prepend_self=None):
    """Generate PyCFunction wrapper from AST.FuncDecl func_ast.

  Args:
    pyname: str - Python function name (may be special: ends with @)
    wrapper: str - generated function name
    doc: str - C++ sinature
    catch: bool - catch C++ exceptions
    call: str | [str] - C++ command(s) to call the wrapped function
      (without "(params);" part).
    postcall_init: str - C++ command; to (re)set ret0.
    typepostconversion: dict(pytype, index) to convert to pytype
    func_ast: AST.FuncDecl protobuf
    lineno: int - .clif line number where func_ast defined
    prepend_self: AST.Param - Use self as 1st parameter.

  Yields:
     Source code for wrapped function.

  Raises:
    ValueError: for non-supported default arguments
  """
    ctxmgr = pyname.endswith('@')
    if ctxmgr:
        ctxmgr = pyname
        assert ctxmgr in ('__enter__@',
                          '__exit__@'), ('Invalid context manager name ' +
                                         pyname)
        pyname = pyname.rstrip('@')
    nret = len(func_ast.returns)
    return_type = astutils.FuncReturnType(
        func_ast)  # Can't use cpp_exact_type.
    # return_type mangled to FQN and drop &, sadly it also drop const.
    void_return_type = 'void' == return_type
    # Has extra func parameters for output values.
    xouts = nret > (0 if void_return_type else 1)
    params = []  # C++ parameter names.
    nargs = len(func_ast.params)
    yield ''
    if func_ast.classmethod:
        yield '// @classmethod ' + doc
        arg0 = 'cls'  # Extra protection that generated code does not use 'self'.
    else:
        yield '// ' + doc
        arg0 = 'self'
    yield 'static PyObject* %s(PyObject* %s%s) {' % (
        wrapper, arg0, ', PyObject* args, PyObject* kw' if nargs else '')
    if prepend_self:
        yield I + _CreateInputParameter(pyname + ' line %d' % lineno,
                                        prepend_self, 'arg0', params)
        yield I + 'if (!Clif_PyObjAs(self, &arg0)) return nullptr;'
    minargs = sum(1 for p in func_ast.params if not p.default_value)
    if nargs:
        yield I + 'PyObject* a[%d]%s;' % (nargs,
                                          '' if minargs == nargs else '{}')
        yield I + 'char* names[] = {'
        for p in func_ast.params:
            yield I + I + I + 'C("%s"),' % p.name.native
        yield I + I + I + 'nullptr'
        yield I + '};'
        yield I + (
            'if (!PyArg_ParseTupleAndKeywords(args, kw, "%s:%s", names, %s)) '
            'return nullptr;' %
            ('O' * nargs if minargs == nargs else 'O' * minargs + '|' + 'O' *
             (nargs - minargs), pyname, ', '.join('&a[%d]' % i
                                                  for i in range(nargs))))
        if minargs < nargs and not xouts:
            yield I + 'int nargs;  // Find how many args actually passed in.'
            yield I + 'for (nargs = %d; nargs > %d; --nargs) {' % (nargs,
                                                                   minargs)
            yield I + I + 'if (a[nargs-1] != nullptr) break;'
            yield I + '}'
        # Convert input parameters from Python.
        for i, p in enumerate(func_ast.params):
            n = i + 1
            arg = 'arg%d' % n
            yield I + _CreateInputParameter(pyname + ' line %d' % lineno, p,
                                            arg, params)
            cvt = (
                'if (!Clif_PyObjAs(a[{i}], &{cvar}{postconv})) return ArgError'
                '("{func_name}", names[{i}], "{ctype}", a[{i}]);').format(
                    i=i,
                    cvar=arg,
                    func_name=pyname,
                    ctype=astutils.Type(p),
                    # Add post conversion parameter for std::function.
                    postconv='' if p.type.cpp_type else ', {%s}' % ', '.join(
                        postconv.Initializer(t.type, typepostconversion)
                        for t in p.type.callable.params))
            if i < minargs:
                # Non-default parameter.
                yield I + cvt
            else:
                if xouts:
                    _I = ''  # pylint: disable=invalid-name
                else:
                    _I = I  # pylint: disable=invalid-name
                    yield I + 'if (nargs > %d) {' % i
                # Check if we're passed kw args, skipping some default C++ args.
                # In this case we must substitute missed default args with default_value
                if (p.default_value ==
                        'default'  # Matcher could not find the default.
                        or 'inf' in p.default_value):  # W/A for b/29437257
                    if xouts:
                        raise ValueError(
                            "Can't supply the default for C++ function"
                            ' argument. Drop =default in def %s(%s).' %
                            (pyname, p.name.native))
                    if n < nargs:
                        yield I + I + (
                            'if (!a[{i}]) return DefaultArgMissedError('
                            '"{}", names[{i}]);'.format(pyname, i=i))
                    yield I + I + cvt
                elif (p.default_value and params[-1].startswith('&')
                      and p.type.cpp_raw_pointer):
                    # Special case for a pointer to an integral type param (like int*).
                    raise ValueError(
                        'A default for integral type pointer argument is '
                        ' not supported. Drop =default in def %s(%s).' %
                        (pyname, p.name.native))
                else:
                    # C-cast takes care of the case where |arg| is an enum value, while
                    # the matcher would return an integral literal. Using static_cast
                    # would be ideal, but its argument should be an expression, which a
                    # struct value like {1, 2, 3} is not.
                    yield _I + I + 'if (!a[%d]) %s = (%s)%s;' % (
                        i, arg, astutils.Type(p), p.default_value)
                    yield _I + I + 'else ' + cvt
                if not xouts:
                    yield I + '}'
    # Create input parameters for extra return values.
    for n, p in enumerate(func_ast.returns):
        if n or void_return_type:
            yield I + '%s ret%d{};' % (astutils.Type(p), n)
            params.append('&ret%d' % n)
    yield I + '// Call actual C++ method.'
    if isinstance(call, list):
        for s in call[:-1]:
            yield I + s
        call = call[-1]
    if not func_ast.py_keep_gil:
        if nargs:
            yield I + 'Py_INCREF(args);'
            yield I + 'Py_XINCREF(kw);'
        yield I + 'PyThreadState* _save;'
        yield I + 'Py_UNBLOCK_THREADS'
    optional_ret0 = False
    if (minargs < nargs or catch) and not void_return_type:
        if func_ast.returns[0].type.cpp_has_def_ctor:
            yield I + return_type + ' ret0;'
        else:
            # Using optional<> requires T be have T(x) and T::op=(x) available.
            # While we need only t=x, implementing it will be a pain we skip for now.
            yield I + '::gtl::optional<%s> ret0;' % return_type
            optional_ret0 = True
    if catch:
        for s in _GenExceptionTry():
            yield s
    if minargs < nargs and not xouts:
        if not void_return_type:
            call = 'ret0 = ' + call
        yield I + 'switch (nargs) {'
        for n in range(minargs, nargs + 1):
            yield I + 'case %d:' % n
            yield I + I + '%s; break;' % (call + astutils.TupleStr(params[:n]))
        yield I + '}'
    else:
        call += astutils.TupleStr(params)
        _I = I if catch else ''  # pylint: disable=invalid-name
        if void_return_type:
            yield _I + I + call + ';'
        elif catch:
            yield _I + I + 'ret0 = ' + call + ';'
        else:
            yield _I + I + return_type + ' ret0 = ' + call + ';'
    if catch:
        for s in _GenExceptionCatch():
            yield s
    if postcall_init:
        if void_return_type:
            yield I + postcall_init
        else:
            yield I + 'ret0' + postcall_init
    if not func_ast.py_keep_gil:
        yield I + 'Py_BLOCK_THREADS'
        if nargs:
            yield I + 'Py_DECREF(args);'
            yield I + 'Py_XDECREF(kw);'
    if catch:
        for s in _GenExceptionRaise():
            yield s
    if func_ast.postproc == '->self':
        func_ast.postproc = ''
        return_self = True
        assert nret == 0, '-> self must have no other output parameters'
    else:
        return_self = False
    # If ctxmgr, force return self on enter, None on exit.
    if nret > 1 or (func_ast.postproc or ctxmgr) and nret:
        yield I + '// Convert return values to Python.'
        yield I + 'PyObject* p, * result_tuple = PyTuple_New(%d);' % nret
        yield I + 'if (result_tuple == nullptr) return nullptr;'
        for i in range(nret):
            yield I + 'if ((p=Clif_PyObjFrom(std::move(ret%d), %s)) == nullptr) {' % (
                i,
                postconv.Initializer(func_ast.returns[i].type,
                                     typepostconversion))
            yield I + I + 'Py_DECREF(result_tuple);'
            yield I + I + 'return nullptr;'
            yield I + '}'
            yield I + 'PyTuple_SET_ITEM(result_tuple, %d, p);' % i
        if func_ast.postproc:
            yield I + 'PyObject* pyproc = ImportFQName("%s");' % func_ast.postproc
            yield I + 'if (pyproc == nullptr) {'
            yield I + I + 'Py_DECREF(result_tuple);'
            yield I + I + 'return nullptr;'
            yield I + '}'
            yield I + 'p = PyObject_CallObject(pyproc, result_tuple);'
            yield I + 'Py_DECREF(pyproc);'
            yield I + 'Py_CLEAR(result_tuple);'
            if ctxmgr:
                yield I + 'if (p == nullptr) return nullptr;'
                yield I + 'Py_DECREF(p);  // Not needed by the context manager.'
            else:
                yield I + 'result_tuple = p;'
        if ctxmgr == '__enter__@':
            yield I + 'Py_XDECREF(result_tuple);'
            yield I + 'Py_INCREF(self);'
            yield I + 'return self;'
        elif ctxmgr == '__exit__@':
            yield I + 'Py_XDECREF(result_tuple);'
            yield I + 'Py_RETURN_NONE;'
        else:
            yield I + 'return result_tuple;'
    elif nret:
        yield I + 'return Clif_PyObjFrom(std::move(ret0%s), %s);' % (
            ('.value()' if optional_ret0 else ''),
            postconv.Initializer(func_ast.returns[0].type, typepostconversion))
    elif return_self or ctxmgr == '__enter__@':
        yield I + 'Py_INCREF(self);'
        yield I + 'return self;'
    else:
        yield I + 'Py_RETURN_NONE;'
    yield '}'
コード例 #11
0
ファイル: pyext.py プロジェクト: LqNoob/C-Python-interaction
 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))
コード例 #12
0
ファイル: pyext.py プロジェクト: LqNoob/C-Python-interaction
 def WrapVar(self, v, unused_ln, unused_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
     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
     cpp = 'reinterpret_cast<%s*>(self)->cpp' % self.wrapper_class_name
     if self.final:
         base = None
         cobj = cpp + '->'
     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 = v.cpp_get.name.cpp_name + '()'
     cvar = cobj + vname
     cfunc_getset = False
     if v.cpp_get.name.native:
         # It's an unproperty var (@getter pyname / @setter pyname).
         assert not v.cpp_get.name.cpp_name
         cfunc_getset = True
         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' % (v.type.cpp_type, self.CppName(), vname)))
     yield ''
     yield 'static PyObject* %s(PyObject* self%s) {' % (getter, (
         '' if cfunc_getset else ', void* xdata'))
     if base:
         yield I + base + 'return nullptr;'
     # Try to return a nested container (we use cpp_toptr_conversion as
     # an indicator for a custom container).
     if not is_property and not cfunc_getset and v.type.cpp_toptr_conversion:
         yield I + 'auto var_sp = ::clif::MakeStdShared(%s, &%s);' % (cpp,
                                                                      cvar)
         c = 'var_sp'
     else:
         c = cvar
     yield I + 'return Clif_PyObjFrom(%s, %s);' % (
         c, postconv.Initializer(v.type, self.typemap))
     yield '}'
     if setter == 'nullptr': return
     yield ''
     if cfunc_getset:
         yield 'static PyObject* %s(PyObject* self, PyObject* value) {' % setter
     else:
         yield (
             'static int %s(PyObject* self, PyObject* value, void* xdata) {'
             % setter)
         yield I + 'if (value == nullptr) {'
         yield I + I + (
             'PyErr_SetString(PyExc_TypeError, "Cannot delete the'
             ' %s attribute");' % v.name.native)
         yield I + I + 'return -1;'
         yield I + '}'
         if v.cpp_set.name.cpp_name:
             # Workaround BUG "v.type.cpp_type not updated by Matcher", so get p[0].
             yield I + '%s cval;' % v.cpp_set.params[0].type.cpp_type
             yield I + 'if (Clif_PyObjAs(value, &cval)) {'
             if base:
                 yield I + I + base + 'return -1;'
             yield I + I + cobj + v.cpp_set.name.cpp_name + '(cval);'
             yield I + I + 'return 0;'
             yield I + '}'
     ret_error = 'return ' + ('nullptr' if cfunc_getset else '-1') + ';'
     if not v.cpp_set.name.cpp_name:
         ret_ok = 'Py_RETURN_NONE' if cfunc_getset else 'return 0'
         if base:
             yield I + base + ret_error
         yield I + 'if (Clif_PyObjAs(value, &%s)) %s;' % (cvar, ret_ok)
     yield I + 'PyObject* s = PyObject_Repr(value);'
     yield I + (
         'PyErr_Format(PyExc_ValueError, "%s is not valid for {}:{}", '
         's? {}(s): "input");').format(
             v.name.native, v.type.lang_type,
             'PyUnicode_AsUTF8' if self.py3output else 'PyString_AS_STRING')
     yield I + 'Py_XDECREF(s);'
     yield I + ret_error
     yield '}'