📄 bgencxxsupport.py
字号:
# C++ support for bgen.# Preliminary, 14-jun-05, Jack.Jansen@cwi.nlfrom bgen import *from scantools import Scannerimport reclass CxxFunctionGenerator(FunctionGenerator): """C++ Function generator. One refinement when compared to the C function generator: we assign the function return value in the declaration of the return variable. This allows support of functions returning const values""" def declarations(self): # Don't declare return value just yet if self.rv: del self.argumentList[0] FunctionGenerator.declarations(self) if self.rv: self.argumentList.insert(0, self.rv) def getrvforcallit(self): if self.rv: decl = self.rv.getArgDeclarations() if len(decl) != 1: raise RuntimeError, "Type %s cannot be used as return value" % self.rv.type return "%s = " % decl[0] return "" class CxxMethodGenerator(CxxFunctionGenerator): """C++ Method generator. Almost identical to a FunctionGenerator (not a MethodGenerator: that class expects that the underlying C object needs to be passed as an argument!) but knows that the method can be called through the object pointer. """ def __init__(self, returntype, name, *argumentlist, **conditionlist): CxxFunctionGenerator.__init__(self, returntype, name, *argumentlist, **conditionlist) def setselftype(self, selftype, itselftype): CxxFunctionGenerator.setselftype(self, selftype, itselftype) if itselftype[-1] == '*': self.callname = "_self->ob_itself->" + self.name else: # Assume non-pointer type self.callname = "_self->ob_itself." + self.name class CxxConstructorGenerator(CxxFunctionGenerator): """Specialized Method: constructor. For these objects generate() and such will not be called, in stead outputConstructorBody() will output the code fragment needed in the Python tp_init function.""" def setselftype(self, selftype, itselftype): CxxFunctionGenerator.setselftype(self, selftype, itselftype) if itselftype[-1] == '*': self.callname = "new %s" % itselftype[:-1] else: self.callname = itselftype def generate(self): raise RuntimeError, "Cannot call generate() on constructor %s::%s"%(self.itselftype, self.name) def outputConstructorBody(self): OutLbrace() self.declarations() sep = ",\n" + ' '*len("if (PyArg_ParseTuple(") fmt, lst = self.getargsFormatArgs(sep) Output("if (PyArg_ParseTuple(_args, \"%s\"%s))", fmt, lst) OutLbrace() for arg in self.argumentList: if arg.flags == SelfMode: continue if arg.mode in (InMode, InOutMode): arg.getargsCheck() self.callit() Output("return 0;") OutRbrace() OutRbrace() Output() def getrvforcallit(self): return "((%s *)_self)->ob_itself = " % self.objecttype class CxxGeneratorGroupMixin: """Mixin class for ObjectDefinition and Module that handles duplicate names.""" def resolveduplicates(self): names = {} dupnames = {} # First collect duplicate names in dupnames, and # also initialize the counter to 1 for g in self.generators: name = g.name if name in names: dupnames[name] = 1 print 'DBG: duplicate %s.%s' % (self.name, name) names[name] = True for g in self.generators: name = g.name if name in dupnames: count = dupnames[name] dupnames[name] = count + 1 name = '%s_%d' % (name, count) g.name = name for g in self.generators: if isinstance(g, CxxGeneratorGroupMixin): g.resolveduplicates() class CxxMixin(CxxGeneratorGroupMixin): """Mixin for ObjectDefinition. The C++ compiler is more picky on use preceding declaration so we need a workaround for that. """ def outputCheck(self): Output("extern PyTypeObject %s;", self.typename) Output() Output("inline bool %s_Check(PyObject *x)", self.prefix) OutLbrace()## DedentLevel()## Output("#ifdef BGEN_BACK_SUPPORT_%s", self.name)## IndentLevel() # # This version will only accept the exact object type, or # C++ subclasses of it. # XXXX The "C++ subclasses" bit is not true yet. # Objects with Python subtypes as their class will be wrapped # in the C++ to Python wrapper, so that added functionality # is exported to C++. subtypes = ""## for stn in self.subtypenames:## subtypes += "|| (x)->ob_type == &%s" % stn Output("return ((x)->ob_type == &%s%s);", self.typename, subtypes)## DedentLevel()## Output("#else")## IndentLevel()## # This version allows subclasses of the given type to## # be passed. But note that the object passed to C++ will be the## # one with the baseclass type, i.e. without any modified methods.## Output("return ((x)->ob_type == &%s || PyObject_TypeCheck((x), &%s));",## self.typename, self.typename)## DedentLevel()## Output("#endif")## IndentLevel() OutRbrace() Output() def outputCheckNewArg(self): """This implementation assumes we are generating a two-way bridge, and the Convert method has been declared a friend of the C++ encapsulation class. And it assumes RTTI.""" # XXX Not sure this is actually correct. What will happen to classes # that have been subtyped in C++?? Maybe we should use typeid()? DedentLevel() Output("#ifdef BGEN_BACK_SUPPORT_%s", self.name) IndentLevel() Output("%s *encaps_itself = dynamic_cast<%s *>(itself);", self.name, self.name) Output("if (encaps_itself && encaps_itself->py_%s)", self.name) OutLbrace() Output("Py_INCREF(encaps_itself->py_%s);", self.name) Output("return encaps_itself->py_%s;", self.name) OutRbrace() DedentLevel() Output("#endif") IndentLevel() def outputCheckConvertArg(self): DedentLevel() Output("#ifdef BGEN_BACK_SUPPORT_%s", self.name) IndentLevel() Output("if (!%s_Check(v))", self.prefix) OutLbrace() Output("*p_itself = Py_WrapAs_%s(v);", self.name) Output("if (*p_itself) return 1;") OutRbrace() DedentLevel() Output("#endif") IndentLevel() def outputTypeObjectInitializerCompat(self): pass class CxxModule(CxxGeneratorGroupMixin, Module): pass class CxxScanner(Scanner): """Pretty dumb C++ scanner. It is basically the standard Scanner, but it caters for namespace and class declarations. It also has rudimental understanding of default arguments and inline code (it tries to skip it). Methods must be declared virtual in C++ to be seen, functions extern. Change self.head_pat and type_pat to fix this. Unqualified class name is used as the Python class name, and methods are for class xyzzy are stored in methods_xyzzy by the generated script. """ def __init__(self, input=None, output=None, defsoutput=None): Scanner.__init__(self, input, output, defsoutput) self.initnamespaces() self.silent = 0 self.debug = 0 def initnamespaces(self): self.namespaces = [] self.in_class_defn = 0 self.last_scope_name = None self.last_scope_was_class = False self.visible = "public" def pythonizename(self, name): if '<' in name or '>' in name: self.error("Use of templates in typename or functionname not supported: %s", name) name = re.sub("\*", " ptr", name) name = re.sub("&", " ref", name) name = re.sub("::", " ", name) name = name.strip() name = re.sub("[ \t]+", "_", name) return name def modifyarg(self, type, name, mode): if type[:6] == "const_": type = type[6:] mode = mode + "+ConstMode" # Note that const ref and const ptr parameters stay InMode if type[-4:] == '_ref': type = type[:-4] mode = mode + "+RefMode" elif type[-4:] == '_ref': type = type[:-4] mode = "OutMode+RefMode" elif type in self.inherentpointertypes: mode = "OutMode" if type[-4:] == "_far": type = type[:-4] return type, name, mode def initpatterns(self): # Patterns to find function/method declaration outside of classes self.head1_pat = r"^\s*(AMBULANTAPI|static|virtual|extern|pyapi)\s+" self.tail_pat = r"[;={}]" self.type1_pat = r"(?P<storage>AMBULANTAPI|static|virtual|extern|pyapi)" + \ r"\s+" + \ r"(?P<type>[a-zA-Z0-9_:\s]*[a-zA-Z0-9_*&\s]+[\s*&]\s*)" self.name_pat = r"(?P<name>[a-zA-Z0-9_]+)\s*" self.args_pat = r"\((?P<args>[^)]*)\)" self.const_pat = r"\s*(?P<const>const)?" self.whole1_pat = self.type1_pat + self.name_pat + self.args_pat + self.const_pat # Patterns to detect same within class declarations self.head2_pat = r"^\s*[a-zA-Z0-9_:\s]*[a-zA-Z0-9_]+[\s*\&]" self.type2_pat = r"\s*(?P<storage>AMBULANTAPI|static|virtual|extern|pyapi)?" + \ r"\s+" + \ r"(?P<type>[a-zA-Z0-9_:\s]*[a-zA-Z0-9_*&\s]+[\s*&]\s*)" self.whole2_pat = self.type2_pat + self.name_pat + self.args_pat + self.const_pat # Default to outside-class: self.head_pat = self.head1_pat self.type_pat = self.type1_pat self.whole_pat = self.whole1_pat # More special patterns to find constructors self.headconstructor_pat = r"\s*(?P<name>[a-zA-Z0-9_]+)\s*\(" self.typeconstructor_pat = r"^(?P<const>)(?P<storage>)(?P<type>)\s*" self.wholeconstructor_pat = self.typeconstructor_pat + self.name_pat + self.args_pat + r"\s*(:[^;{]*)?" self.sym_pat = r"^[ \t]*(?P<name>[a-zA-Z0-9_]+)[ \t]*=" + \ r"[ \t]*(?P<defn>[-0-9_a-zA-Z'\"\(][^\t\n,;}]*),?" self.asplit_pat = r"^(?P<type>[^=]*[^=a-zA-Z0-9_])(?P<name>[a-zA-Z0-9_]+)(?P<array>\[\])?(?P<initializer>\s*=[a-zA-Z0-9_ ]+)?$" self.comment1_pat = r"(?P<rest>.*)//.*" # note that the next pattern only removes comments that are wholly within one line self.comment2_pat = r"(?P<rest1>.*)/\*.*\*/(?P<rest2>.*)" self.namespace_pat = r"^\s*namespace\s+(?P<name>[a-zA-Z0-9_:]+)\s+{" self.klass_pat = r"^\s*class\s+(AMBULANTAPI\s+)?(?P<name>[a-zA-Z0-9_:]+)\s+[{:]" self.visibility_pat = r"^\s*(?P<visibility>protected|private|public)\s*:\s*$" def donamespace(self, match): if self.in_class_defn: self.report("Cannot do namespace inside class") name = match.group("name") self.last_scope_name = name self.last_scope_was_class = False if self.debug: self.report(" %d: namespace %s" % (len(self.namespaces), name)) def doclass(self, match): name = match.group("name") self.last_scope_name = name self.last_scope_was_class = True self.visible = "private" if self.debug: self.report(" %d: namespace %s" % (len(self.namespaces), name)) def dobeginscope(self, count): for i in range(count): if self.last_scope_name: self.namespaces.append(self.last_scope_name) self.last_scope_name = None if self.last_scope_was_class: self.in_class_defn += 1 else: self.namespaces.append("<scope>") if self.in_class_defn: self.in_class_defn += 1 if self.in_class_defn: self.head = self.head2 self.type = self.type2 self.whole = self.whole2 def doendscope(self, count): for i in range(count): if self.in_class_defn: self.in_class_defn -= 1 assert self.in_class_defn >= 0 if self.debug:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -