#!/usr/bin/env python ''' ctypesgencore.ctypedescs contains classes to represent a C type. All of them classes are subclasses of CtypesType. Unlike in previous versions of ctypesgen, CtypesType and its subclasses are completely independent of the parser module. The most important method of CtypesType and its subclasses is the py_string method. str(ctype) returns a string which, when evaluated in the wrapper at runtime, results in a ctypes type object. For example, a CtypesType representing an array of four integers could be created using: >>> ctype = CtypesArray(CtypesSimple("int",True,0),4) str(ctype) would evaluate to "c_int * 4". ''' import warnings __docformat__ = 'restructuredtext' ctypes_type_map = { # typename signed longs ('void', True, 0): 'None', ('int', True, 0): 'c_int', ('int', False, 0): 'c_uint', ('int', True, 1): 'c_long', ('int', False, 1): 'c_ulong', ('int', True, 2): 'c_longlong', ('int', False, 2): 'c_ulonglong', ('char', True, 0): 'c_char', ('char', False, 0): 'c_ubyte', ('short', True, 0): 'c_short', ('short', False, 0): 'c_ushort', ('float', True, 0): 'c_float', ('double', True, 0): 'c_double', ('size_t', True, 0): 'c_size_t', ('int8_t', True, 0): 'c_int8', ('int16_t', True, 0): 'c_int16', ('int32_t', True, 0): 'c_int32', ('int64_t', True, 0): 'c_int64', ('apr_int64_t',True,0): 'c_int64', ('off64_t', True, 0): 'c_int64', ('uint8_t', True, 0): 'c_uint8', ('uint16_t',True, 0): 'c_uint16', ('uint32_t',True, 0): 'c_uint32', ('uint64_t',True, 0): 'c_uint64', ('apr_uint64_t',True,0): 'c_uint64', ('wchar_t', True, 0): 'c_wchar', ('ptrdiff_t',True, 0): 'c_ptrdiff_t', # Requires definition in preamble ('ssize_t', True, 0): 'c_ptrdiff_t', # Requires definition in preamble ('va_list', True, 0): 'c_void_p', } # This protocol is used for walking type trees. class CtypesTypeVisitor(object): def visit_struct(self, struct): pass def visit_enum(self, enum): pass def visit_typedef(self, name): pass def visit_error(self, error, cls): pass def visit_identifier(self, identifier): # This one comes from inside ExpressionNodes. There may be # ExpressionNode objects in array count expressions. pass def visit_type_and_collect_info(ctype): class Visitor(CtypesTypeVisitor): def visit_struct(self,struct): structs.append(struct) def visit_enum(self,enum): enums.append(enum) def visit_typedef(self,typedef): typedefs.append(typedef) def visit_error(self,error,cls): errors.append((error,cls)) def visit_identifier(self,identifier): identifiers.append(identifier) structs = [] enums = [] typedefs = [] errors = [] identifiers = [] v = Visitor() ctype.visit(v) return structs,enums,typedefs,errors,identifiers # Remove one level of indirection from funtion pointer; needed for typedefs # and function parameters. def remove_function_pointer(t): if type(t) == CtypesPointer and type(t.destination) == CtypesFunction: return t.destination elif type(t) == CtypesPointer: t.destination = remove_function_pointer(t.destination) return t else: return t class CtypesType(object): def __init__(self): self.errors=[] def __repr__(self): return "" % self.py_string() def error(self,message,cls=None): self.errors.append((message,cls)) def visit(self,visitor): for error,cls in self.errors: visitor.visit_error(error,cls) class CtypesSimple(CtypesType): """Represents a builtin type, like "char" or "int".""" def __init__(self, name, signed, longs): CtypesType.__init__(self) self.name = name self.signed = signed self.longs = longs def py_string(self): return ctypes_type_map[(self.name,self.signed,self.longs)] class CtypesSpecial(CtypesType): def __init__(self,name): CtypesType.__init__(self) self.name = name def py_string(self): return self.name class CtypesTypedef(CtypesType): """Represents a type defined by a typedef.""" def __init__(self, name): CtypesType.__init__(self) self.name = name def visit(self,visitor): if not self.errors: visitor.visit_typedef(self.name) CtypesType.visit(self,visitor) def py_string(self): return self.name class CtypesBitfield(CtypesType): def __init__(self, base, bitfield): CtypesType.__init__(self) self.base = base self.bitfield = bitfield def visit(self,visitor): self.base.visit(visitor) CtypesType.visit(self,visitor) def py_string(self): return self.base.py_string() class CtypesPointer(CtypesType): def __init__(self, destination, qualifiers): CtypesType.__init__(self) self.destination = destination self.qualifiers = qualifiers def visit(self,visitor): if self.destination: self.destination.visit(visitor) CtypesType.visit(self,visitor) def py_string(self): return 'POINTER(%s)' % self.destination.py_string() class CtypesArray(CtypesType): def __init__(self, base, count): CtypesType.__init__(self) self.base = base self.count = count def visit(self,visitor): self.base.visit(visitor) if self.count: self.count.visit(visitor) CtypesType.visit(self,visitor) def py_string(self): if self.count is None: return 'POINTER(%s)' % self.base.py_string() if type(self.base) == CtypesArray: return '(%s) * %s' % (self.base.py_string(), self.count.py_string(False)) else: return '%s * %s' % (self.base.py_string(), self.count.py_string(False)) class CtypesFunction(CtypesType): def __init__(self, restype, parameters, variadic=False): CtypesType.__init__(self) self.restype = restype # Don't allow POINTER(None) (c_void_p) as a restype... causes errors # when ctypes automagically returns it as an int. # Instead, convert to POINTER(c_void). c_void is not a ctypes type, # you can make it any arbitrary type. if type(self.restype) == CtypesPointer and \ type(self.restype.destination) == CtypesSimple and \ self.restype.destination.name == 'None': self.restype = CtypesPointer(CtypesSpecial('c_void'), ()) # Return 'ReturnString' instead of simply 'String' if self.restype.py_string() == 'POINTER(c_char)': self.restype = CtypesSpecial('ReturnString') self.argtypes = [remove_function_pointer(p) for p in parameters] self.variadic = variadic def visit(self,visitor): self.restype.visit(visitor) for a in self.argtypes: a.visit(visitor) CtypesType.visit(self,visitor) def py_string(self): return 'CFUNCTYPE(UNCHECKED(%s), %s)' % (self.restype.py_string(), ', '.join([a.py_string() for a in self.argtypes])) last_tagnum = 0 def anonymous_struct_tag(): global last_tagnum last_tagnum += 1 return 'anon_%d' % last_tagnum class CtypesStruct(CtypesType): def __init__(self, tag, variety, members, src=None): CtypesType.__init__(self) self.tag = tag self.variety = variety # "struct" or "union" self.members = members if not self.tag: self.tag = anonymous_struct_tag() self.anonymous = True else: self.anonymous = False if self.members==None: self.opaque = True else: self.opaque = False self.src = src def get_required_types(self): types = CtypesType.get_required_types(self) types.add((self.variety,self.tag)) return types def visit(self,visitor): visitor.visit_struct(self) if not self.opaque: for name,ctype in self.members: ctype.visit(visitor) CtypesType.visit(self,visitor) def get_subtypes(self): if self.opaque: return set() else: return set([m[1] for m in self.members]) def py_string(self): return "%s_%s" % (self.variety,self.tag) last_tagnum = 0 def anonymous_enum_tag(): global last_tagnum last_tagnum += 1 return 'anon_%d' % last_tagnum class CtypesEnum(CtypesType): def __init__(self, tag, enumerators, src=None): CtypesType.__init__(self) self.tag = tag self.enumerators = enumerators if not self.tag: self.tag = anonymous_enum_tag() self.anonymous = True else: self.anonymous = False if self.enumerators==None: self.opaque = True else: self.opaque = False self.src = src def visit(self,visitor): visitor.visit_enum(self) CtypesType.visit(self,visitor) def py_string(self): return 'enum_%s' % self.tag