Author: Armin Rigo <[email protected]>
Branch:
Changeset: r1152:a5e3e36914a5
Date: 2013-02-12 18:27 +0100
http://bitbucket.org/cffi/cffi/changeset/a5e3e36914a5/
Log: hg merge auto-types
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -3346,7 +3346,7 @@
return NULL;
flag = CT_STRUCT;
- if (strcmp(name, "_IO_FILE") == 0)
+ if (strcmp(name, "_IO_FILE") == 0 || strcmp(name, "$FILE") == 0)
flag |= CT_IS_FILE;
return _b_struct_or_union_type("struct", name, flag);
}
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2582,7 +2582,7 @@
if sys.platform == "win32":
py.test.skip("testing FILE not implemented")
#
- BFILE = new_struct_type("_IO_FILE")
+ BFILE = new_struct_type("$FILE")
BFILEP = new_pointer_type(BFILE)
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -71,7 +71,6 @@
if name.startswith('RTLD_'):
setattr(self, name, getattr(backend, name))
#
- self._parser._declarations['typedef FILE'] = model.file_type
BVoidP = self._get_cached_btype(model.voidp_type)
if isinstance(backend, types.ModuleType):
# _cffi_backend: attach these constants to the class
diff --git a/cffi/commontypes.py b/cffi/commontypes.py
new file mode 100644
--- /dev/null
+++ b/cffi/commontypes.py
@@ -0,0 +1,246 @@
+import sys
+from . import api, model
+
+
+COMMON_TYPES = {
+ 'FILE': model.unknown_type('FILE', '_IO_FILE'),
+ 'bool': '_Bool',
+ }
+
+for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+ if _type.endswith('_t'):
+ COMMON_TYPES[_type] = _type
+del _type
+
+_CACHE = {}
+
+def resolve_common_type(commontype):
+ try:
+ return _CACHE[commontype]
+ except KeyError:
+ result = COMMON_TYPES.get(commontype, commontype)
+ if not isinstance(result, str):
+ pass # result is already a BaseType
+ elif result.endswith(' *'):
+ if result.startswith('const '):
+ result = model.ConstPointerType(
+ resolve_common_type(result[6:-2]))
+ else:
+ result = model.PointerType(resolve_common_type(result[:-2]))
+ elif result in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+ result = model.PrimitiveType(result)
+ else:
+ assert commontype != result
+ result = resolve_common_type(result) # recursively
+ assert isinstance(result, model.BaseTypeByIdentity)
+ _CACHE[commontype] = result
+ return result
+
+
+# ____________________________________________________________
+# Windows common types
+
+
+def win_common_types(maxsize):
+ result = {}
+ if maxsize < (1<<32):
+ result.update({ # Windows 32-bits
+ 'HALF_PTR': 'short',
+ 'INT_PTR': 'int',
+ 'LONG_PTR': 'long',
+ 'UHALF_PTR': 'unsigned short',
+ 'UINT_PTR': 'unsigned int',
+ 'ULONG_PTR': 'unsigned long',
+ })
+ else:
+ result.update({ # Windows 64-bits
+ 'HALF_PTR': 'int',
+ 'INT_PTR': 'long long',
+ 'LONG_PTR': 'long long',
+ 'UHALF_PTR': 'unsigned int',
+ 'UINT_PTR': 'unsigned long long',
+ 'ULONG_PTR': 'unsigned long long',
+ })
+ result.update({
+ "BYTE": "unsigned char",
+ "BOOL": "int",
+ "CCHAR": "char",
+ "CHAR": "char",
+ "DWORD": "unsigned long",
+ "DWORD32": "unsigned int",
+ "DWORD64": "unsigned long long",
+ "FLOAT": "float",
+ "INT": "int",
+ "INT8": "signed char",
+ "INT16": "short",
+ "INT32": "int",
+ "INT64": "long long",
+ "LONG": "long",
+ "LONGLONG": "long long",
+ "LONG32": "int",
+ "LONG64": "long long",
+ "WORD": "unsigned short",
+ "PVOID": model.voidp_type,
+ "ULONGLONG": "unsigned long long",
+ "WCHAR": "wchar_t",
+ "SHORT": "short",
+ "TBYTE": "WCHAR",
+ "TCHAR": "WCHAR",
+ "UCHAR": "unsigned char",
+ "UINT": "unsigned int",
+ "UINT8": "unsigned char",
+ "UINT16": "unsigned short",
+ "UINT32": "unsigned int",
+ "UINT64": "unsigned long long",
+ "ULONG": "unsigned long",
+ "ULONG32": "unsigned int",
+ "ULONG64": "unsigned long long",
+ "USHORT": "unsigned short",
+
+ "SIZE_T": "ULONG_PTR",
+ "SSIZE_T": "LONG_PTR",
+ "ATOM": "WORD",
+ "BOOLEAN": "BYTE",
+ "COLORREF": "DWORD",
+
+ "HANDLE": "PVOID",
+ "DWORDLONG": "ULONGLONG",
+ "DWORD_PTR": "ULONG_PTR",
+ "HACCEL": "HANDLE",
+
+ "HBITMAP": "HANDLE",
+ "HBRUSH": "HANDLE",
+ "HCOLORSPACE": "HANDLE",
+ "HCONV": "HANDLE",
+ "HCONVLIST": "HANDLE",
+ "HDC": "HANDLE",
+ "HDDEDATA": "HANDLE",
+ "HDESK": "HANDLE",
+ "HDROP": "HANDLE",
+ "HDWP": "HANDLE",
+ "HENHMETAFILE": "HANDLE",
+ "HFILE": "int",
+ "HFONT": "HANDLE",
+ "HGDIOBJ": "HANDLE",
+ "HGLOBAL": "HANDLE",
+ "HHOOK": "HANDLE",
+ "HICON": "HANDLE",
+ "HCURSOR": "HICON",
+ "HINSTANCE": "HANDLE",
+ "HKEY": "HANDLE",
+ "HKL": "HANDLE",
+ "HLOCAL": "HANDLE",
+ "HMENU": "HANDLE",
+ "HMETAFILE": "HANDLE",
+ "HMODULE": "HINSTANCE",
+ "HMONITOR": "HANDLE",
+ "HPALETTE": "HANDLE",
+ "HPEN": "HANDLE",
+ "HRESULT": "LONG",
+ "HRGN": "HANDLE",
+ "HRSRC": "HANDLE",
+ "HSZ": "HANDLE",
+ "WINSTA": "HANDLE",
+ "HWND": "HANDLE",
+
+ "LANGID": "WORD",
+ "LCID": "DWORD",
+ "LCTYPE": "DWORD",
+ "LGRPID": "DWORD",
+ "LPARAM": "LONG_PTR",
+ "LPBOOL": "BOOL *",
+ "LPBYTE": "BYTE *",
+ "LPCOLORREF": "DWORD *",
+ "LPCSTR": "const char *",
+
+ "LPCVOID": model.const_voidp_type,
+ "LPCWSTR": "const WCHAR *",
+ "LPCTSTR": "LPCWSTR",
+ "LPDWORD": "DWORD *",
+ "LPHANDLE": "HANDLE *",
+ "LPINT": "int *",
+ "LPLONG": "long *",
+ "LPSTR": "CHAR *",
+ "LPWSTR": "WCHAR *",
+ "LPTSTR": "LPWSTR",
+ "LPVOID": model.voidp_type,
+ "LPWORD": "WORD *",
+ "LRESULT": "LONG_PTR",
+ "PBOOL": "BOOL *",
+ "PBOOLEAN": "BOOLEAN *",
+ "PBYTE": "BYTE *",
+ "PCHAR": "CHAR *",
+ "PCSTR": "const CHAR *",
+ "PCTSTR": "LPCWSTR",
+ "PCWSTR": "const WCHAR *",
+ "PDWORD": "DWORD *",
+ "PDWORDLONG": "DWORDLONG *",
+ "PDWORD_PTR": "DWORD_PTR *",
+ "PDWORD32": "DWORD32 *",
+ "PDWORD64": "DWORD64 *",
+ "PFLOAT": "FLOAT *",
+ "PHALF_PTR": "HALF_PTR *",
+ "PHANDLE": "HANDLE *",
+ "PHKEY": "HKEY *",
+ "PINT": "int *",
+ "PINT_PTR": "INT_PTR *",
+ "PINT8": "INT8 *",
+ "PINT16": "INT16 *",
+ "PINT32": "INT32 *",
+ "PINT64": "INT64 *",
+ "PLCID": "PDWORD",
+ "PLONG": "LONG *",
+ "PLONGLONG": "LONGLONG *",
+ "PLONG_PTR": "LONG_PTR *",
+ "PLONG32": "LONG32 *",
+ "PLONG64": "LONG64 *",
+ "PSHORT": "SHORT *",
+ "PSIZE_T": "SIZE_T *",
+ "PSSIZE_T": "SSIZE_T *",
+ "PSTR": "CHAR *",
+ "PTBYTE": "TBYTE *",
+ "PTCHAR": "TCHAR *",
+ "PTSTR": "LPWSTR",
+ "PUCHAR": "UCHAR *",
+ "PUHALF_PTR": "UHALF_PTR *",
+ "PUINT": "UINT *",
+ "PUINT_PTR": "UINT_PTR *",
+ "PUINT8": "UINT8 *",
+ "PUINT16": "UINT16 *",
+ "PUINT32": "UINT32 *",
+ "PUINT64": "UINT64 *",
+ "PULONG": "ULONG *",
+ "PULONGLONG": "ULONGLONG *",
+ "PULONG_PTR": "ULONG_PTR *",
+ "PULONG32": "ULONG32 *",
+ "PULONG64": "ULONG64 *",
+ "PUSHORT": "USHORT *",
+ "PWCHAR": "WCHAR *",
+ "PWORD": "WORD *",
+ "PWSTR": "WCHAR *",
+ "QWORD": "unsigned long long",
+ "SC_HANDLE": "HANDLE",
+ "SC_LOCK": "LPVOID",
+ "SERVICE_STATUS_HANDLE": "HANDLE",
+
+ "UNICODE_STRING": model.StructType(
+ "_UNICODE_STRING",
+ ["Length",
+ "MaximumLength",
+ "Buffer"],
+ [model.PrimitiveType("unsigned short"),
+ model.PrimitiveType("unsigned short"),
+ model.PointerType(model.PrimitiveType("wchar_t"))],
+ [-1, -1, -1]),
+ "PUNICODE_STRING": "UNICODE_STRING *",
+ "PCUNICODE_STRING": "const UNICODE_STRING *",
+
+ "USN": "LONGLONG",
+ "VOID": model.void_type,
+ "WPARAM": "UINT_PTR",
+ })
+ return result
+
+
+if sys.platform == 'win32':
+ COMMON_TYPES.update(win_common_types(sys.maxsize))
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -1,5 +1,6 @@
from . import api, model
+from .commontypes import COMMON_TYPES, resolve_common_type
import pycparser.c_parser, weakref, re, sys
try:
@@ -17,6 +18,7 @@
_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}")
_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
+_r_words = re.compile(r"\w+|\S")
_parser_cache = None
def _get_parser():
@@ -58,10 +60,34 @@
# which is declared with a typedef for the purpose of C parsing.
return csource.replace('...', ' __dotdotdot__ '), macros
+def _common_type_names(csource):
+ # Look in the source for what looks like usages of types from the
+ # list of common types. A "usage" is approximated here as the
+ # appearance of the word, minus a "definition" of the type, which
+ # is the last word in a "typedef" statement. Approximative only
+ # but should be fine for all the common types.
+ look_for_words = set(COMMON_TYPES)
+ look_for_words.add(';')
+ look_for_words.add('typedef')
+ words_used = set()
+ is_typedef = False
+ previous_word = ''
+ for word in _r_words.findall(csource):
+ if word in look_for_words:
+ if word == ';':
+ if is_typedef:
+ words_used.discard(previous_word)
+ look_for_words.discard(previous_word)
+ is_typedef = False
+ elif word == 'typedef':
+ is_typedef = True
+ else: # word in COMMON_TYPES
+ words_used.add(word)
+ previous_word = word
+ return words_used
+
+
class Parser(object):
- TYPEDEFS = sorted(
- [_name for _name in model.PrimitiveType.ALL_PRIMITIVE_TYPES
- if _name.endswith('_t')])
def __init__(self):
self._declarations = {}
@@ -70,17 +96,23 @@
self._override = False
def _parse(self, csource):
+ csource, macros = _preprocess(csource)
# XXX: for more efficiency we would need to poke into the
# internals of CParser... the following registers the
# typedefs, because their presence or absence influences the
# parsing itself (but what they are typedef'ed to plays no role)
- typenames = self.TYPEDEFS[:]
+ ctn = _common_type_names(csource)
+ print `csource`, ctn
+ typenames = []
for name in sorted(self._declarations):
if name.startswith('typedef '):
- typenames.append(name[8:])
+ name = name[8:]
+ typenames.append(name)
+ ctn.discard(name)
+ typenames += sorted(ctn)
+ #
csourcelines = ['typedef int %s;' % typename for typename in typenames]
csourcelines.append('typedef int __dotdotdot__;')
- csource, macros = _preprocess(csource)
csourcelines.append(csource)
csource = '\n'.join(csourcelines)
if lock is not None:
@@ -267,7 +299,7 @@
return model.void_type
if ident == '__dotdotdot__':
raise api.FFIError('bad usage of "..."')
- return model.PrimitiveType(ident)
+ return resolve_common_type(ident)
#
if isinstance(type, pycparser.c_ast.Struct):
# 'struct foobar'
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -73,9 +73,9 @@
'float': 'f',
'double': 'f',
'long double': 'f',
- 'wchar_t': 'c',
'_Bool': 'u',
# the following types are not primitive in the C sense
+ 'wchar_t': 'c',
'int8_t': 'i',
'uint8_t': 'u',
'int16_t': 'i',
@@ -183,6 +183,8 @@
BPtr = PointerType(self.totype).get_cached_btype(ffi, finishlist)
return BPtr
+const_voidp_type = ConstPointerType(void_type)
+
class NamedPointerType(PointerType):
_attrs_ = ('totype', 'name')
@@ -383,8 +385,6 @@
tp = StructType(structname, None, None, None)
return NamedPointerType(tp, name)
-file_type = unknown_type('FILE', '_IO_FILE')
-
def global_cache(srctype, ffi, funcname, *args, **kwds):
key = kwds.pop('key', (funcname, args))
assert not kwds
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -390,25 +390,39 @@
* wchar_t (if supported by the backend)
* *New in version 0.4:* _Bool. If not directly supported by the C compiler,
- this is declared with the size of ``unsigned char``. Note that the
- effects of ``<stdbool.h>`` are not automatically included: you have
- to say ``typedef _Bool bool;`` in your ``cdef()`` if you want to
- use this ``_Bool`` with the more standard name ``bool``. This is because
- some headers declare a different type (e.g. an enum) and also call it
- ``bool``.
+ this is declared with the size of ``unsigned char``.
+
+* *New in version 0.6:* bool. In CFFI 0.4 or 0.5, you had to manually say
+ ``typedef _Bool bool;``. Now such a line is optional.
* *New in version 0.4:* FILE. You can declare C functions taking a
``FILE *`` argument and call them with a Python file object. If needed,
you can also do ``c_f = ffi.cast("FILE *", fileobj)`` and then pass around
``c_f``.
-.. "versionadded:: 0.4": bool
+* *New in version 0.6:* all `common Windows types`_ are defined if you run
+ on Windows (``DWORD``, ``LPARAM``, etc.).
+
+.. _`common Windows types`:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
+
+.. "versionadded:: 0.4": _Bool
+.. "versionadded:: 0.6": bool
.. "versionadded:: 0.4": FILE
+.. "versionadded:: 0.6": Wintypes
As we will see on `the verification step`_ below, the declarations can
also contain "``...``" at various places; these are placeholders that will
be completed by a call to ``verify()``.
+.. versionadded:: 0.6
+ The standard type names listed above are now handled as *defaults*
+ only (apart from the ones that are keywords in the C language).
+ If your ``cdef`` contains an explicit typedef that redefines one of
+ the types above, then the default described above is ignored. (This
+ is a bit hard to implement cleanly, so in some corner cases it might
+ fail, notably with the error ``Multiple type specifiers with a type
+ tag``. Please report it as a bug if it does.)
+
Loading libraries
-----------------
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -208,3 +208,49 @@
assert str(e.value).startswith('cannot parse "foobarbazunknown*"')
e = py.test.raises(CDefError, ffi.cast, "int(*)(foobarbazunknown)", 0)
assert str(e.value).startswith('cannot parse "int(*)(foobarbazunknown)"')
+
+def test_redefine_common_type():
+ ffi = FFI()
+ ffi.cdef("typedef char FILE;")
+ assert repr(ffi.cast("FILE", 123)) == "<cdata 'char' '{'>"
+ ffi.cdef("typedef char int32_t;")
+ assert repr(ffi.cast("int32_t", 123)) == "<cdata 'char' '{'>"
+
+def test_bool():
+ ffi = FFI()
+ ffi.cdef("void f(bool);")
+ #
+ ffi = FFI()
+ ffi.cdef("typedef _Bool bool; void f(bool);")
+
+def test_win_common_types():
+ from cffi.commontypes import COMMON_TYPES, _CACHE
+ from cffi.commontypes import win_common_types, resolve_common_type
+ #
+ def clear_all(extra={}, old_dict=COMMON_TYPES.copy()):
+ COMMON_TYPES.clear()
+ COMMON_TYPES.update(old_dict)
+ COMMON_TYPES.update(extra)
+ _CACHE.clear()
+ #
+ for maxsize in [2**32-1, 2**64-1]:
+ ct = win_common_types(maxsize)
+ clear_all(ct)
+ for key in sorted(ct):
+ resolve_common_type(key)
+ # assert did not crash
+ # now try to use e.g. WPARAM (-> UINT_PTR -> unsigned 32/64-bit)
+ for maxsize in [2**32-1, 2**64-1]:
+ ct = win_common_types(maxsize)
+ clear_all(ct)
+ ffi = FFI()
+ value = int(ffi.cast("WPARAM", -1))
+ assert value == maxsize
+ #
+ clear_all()
+
+def test_WPARAM_on_windows():
+ if sys.platform != 'win32':
+ py.test.skip("Only for Windows")
+ ffi = FFI()
+ ffi.cdef("void f(WPARAM);")
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -1258,6 +1258,7 @@
for sign in ['signed', 'unsigned']:
type = '%s %s' % (sign, basetype)
assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1
+ assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1
assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0
def test_addressof():
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit