Commits

Ronald Oussoren committed d9d540d

- All opaque pointers, like NSZone* and NSModalSession, are now wrapped using the
same code: as with structs a function in the core creates a new wrapper type
for every opaque pointer type.
- Struct wrappers now have a default __init__ that sets fields to an appropriate
default value, instead of None. Use this to do away with NSRect.__init__ that
was defined in _Foundation.
- Both opaque pointer types and struct types have a __typestr__ attribute that
contains the encoded type of the underlying Objective-C type.
- Both types can now be created from Python, using functions in the _objc module.

  • Participants
  • Parent commits 0b86b9c
  • Branches pyobjc-ancient

Comments (0)

Files changed (18)

File Doc/TODO.html

 the wrong value (type, number of values)</li>
 <li>Add tests for accepting any sequence when depythonifying structs and arrays.</li>
 <li>Add more tests for objc_support.m to unittest.c</li>
+<li>Tests for <code><span>objc.createOpaquePointerType</span></code>.</li>
 </ul>
-<p>The first two bullets require at least some GUI tests.</p>
 <h2><a href="#id6" name="less-important-items">Less important items</a></h2>
 <h3><a href="#id7" name="refactor-some-parts-of-the-bridge">Refactor some parts of the bridge</a></h3>
 <p>From the top of my head:</p>
 </ul>
 <ul>
 <li>Also restructure class-builder.m, this file is way to large.</li>
-<li>We've several types that wrap pointer values, such as <code><span>SessionWrapper</span></code> in
-<code><span>Modules/AppKit</span></code>. Should add a function for creating these types, 
-possibly exposed to C code.</li>
 </ul>
 <h3><a href="#id8" name="support-for-gnustep">Support for GNUstep</a></h3>
 <p>The current SVN version contains some support for GNUstep, this needs to
 <li>Check all error/exception messages</li>
 <li>Check/cleanup error handling</li>
 <li>Finish in-code documentation for the C code</li>
-<li>SessionWrapper (in <code><span>Modules/AppKit</span></code>) is one of several types that wrap 
-a single pointer, create a function to build such types (similar to
-<code><span>Modules/objc/struct-wrapper.m</span></code>)</li>
 </ul>
 <h3><a href="#id15" name="cleanup-examples">Cleanup Examples</a></h3>
 <p>The CurrencyConverter example should be removed, this should be the same as the

File Doc/TODO.txt

 
 * Add more tests for objc_support.m to unittest.c
 
-
-The first two bullets require at least some GUI tests.
-
+* Tests for ``objc.createOpaquePointerType``.
 
 
 Less important items
 
 * Also restructure class-builder.m, this file is way to large.
 
-* We've several types that wrap pointer values, such as ``SessionWrapper`` in
-  ``Modules/AppKit``. Should add a function for creating these types, 
-  possibly exposed to C code.
-
 Support for GNUstep
 ...................
 
 
 * Finish in-code documentation for the C code
 
-* SessionWrapper (in ``Modules/AppKit``) is one of several types that wrap 
-  a single pointer, create a function to build such types (similar to
-  ``Modules/objc/struct-wrapper.m``)
-
 Cleanup Examples
 ................
 

File Lib/AppKit/test/test_structs.py

     def testConstructor(self):
         p = NSAffineTransformStruct()
         self.assert_(isinstance(p, NSAffineTransformStruct))
-        self.assertEquals(p.m11, None)
-        self.assertEquals(p.m12, None)
-        self.assertEquals(p.m21, None)
-        self.assertEquals(p.m22, None)
-        self.assertEquals(p.tX, None)
-        self.assertEquals(p.tY, None)
+        self.assertEquals(p.m11, 0)
+        self.assertEquals(p.m12, 0)
+        self.assertEquals(p.m21, 0)
+        self.assertEquals(p.m22, 0)
+        self.assertEquals(p.tX, 0)
+        self.assertEquals(p.tY, 0)
 
         p = NSAffineTransformStruct(1,2, 3, 4, 5, 6)
         self.assert_(isinstance(p, NSAffineTransformStruct))
 
     def testRepr(self):
         p = NSAffineTransformStruct()
-        self.assertEquals(repr(p), "<AppKit.NSAffineTransformStruct m11=None m12=None m21=None m22=None tX=None tY=None>")
+        self.assertEquals(repr(p), "<AppKit.NSAffineTransformStruct m11=0.0 m12=0.0 m21=0.0 m22=0.0 tX=0.0 tY=0.0>")
 
         p = NSAffineTransformStruct(1, 2, 3, 4, 5, 6)
         self.assertEquals(repr(p), "<AppKit.NSAffineTransformStruct m11=1 m12=2 m21=3 m22=4 tX=5 tY=6>")
 
     def testStr(self):
         p = NSAffineTransformStruct()
-        self.assertEquals(str(p), "<AppKit.NSAffineTransformStruct m11=None m12=None m21=None m22=None tX=None tY=None>")
+        self.assertEquals(str(p), "<AppKit.NSAffineTransformStruct m11=0.0 m12=0.0 m21=0.0 m22=0.0 tX=0.0 tY=0.0>")
 
         p = NSAffineTransformStruct(1, 2, 3, 4, 5, 6)
         self.assertEquals(str(p), "<AppKit.NSAffineTransformStruct m11=1 m12=2 m21=3 m22=4 tX=5 tY=6>")

File Lib/Foundation/test/test_structs.py

     def testConstructor(self):
         p = NSPoint()
         self.assert_(isinstance(p, NSPoint))
-        self.assertEquals(p.x, None)
-        self.assertEquals(p.y, None)
+        self.assertEquals(p.x, 0)
+        self.assertEquals(p.y, 0)
 
         p = NSPoint(1,2)
         self.assert_(isinstance(p, NSPoint))
 
     def testRepr(self):
         p = NSPoint()
-        self.assertEquals(repr(p), "<Foundation.NSPoint x=None y=None>")
+        self.assertEquals(repr(p), "<Foundation.NSPoint x=0.0 y=0.0>")
 
         p = NSPoint(42, 98)
         self.assertEquals(repr(p), "<Foundation.NSPoint x=42 y=98>")
 
     def testStr(self):
         p = NSPoint()
-        self.assertEquals(str(p), "<Foundation.NSPoint x=None y=None>")
+        self.assertEquals(str(p), "<Foundation.NSPoint x=0.0 y=0.0>")
 
         p = NSPoint(42, 98)
         self.assertEquals(str(p), "<Foundation.NSPoint x=42 y=98>")
     def testConstructor(self):
         p = NSSize()
         self.assert_(isinstance(p, NSSize))
-        self.assertEquals(p.width, None)
-        self.assertEquals(p.height, None)
+        self.assertEquals(p.width, 0)
+        self.assertEquals(p.height, 0)
 
         p = NSSize(1,2)
         self.assert_(isinstance(p, NSSize))
     def testConstructor(self):
         p = NSRange()
         self.assert_(isinstance(p, NSRange))
-        self.assertEquals(p.location, None)
-        self.assertEquals(p.length, None)
+        self.assertEquals(p.location, 0)
+        self.assertEquals(p.length, 0)
 
         p = NSRange(1,2)
         self.assert_(isinstance(p, NSRange))

File Lib/objc/test/test_objc.py

 
 import objc
 from objc.test.testbndl import PyObjC_TestClass4
+import Foundation
 
 NSObject = objc.lookUpClass('NSObject')
 NSArray = objc.lookUpClass('NSArray')
         self.assertEquals(zone.pointer, zone2.pointer)
 
 
-        self.assertRaises(ValueError, NSObject.allocWithZone_, 10)
+        self.assertRaises(TypeError, NSObject.allocWithZone_, 10)
 
 
     def testClassInvocation(self):

File Modules/AppKit/_AppKit.m

 	Py_DECREF(v);
 
 	/* register other method mappings */
-	if (_pyobjc_install_NSApplication() < 0) return;
+	if (_pyobjc_install_NSApplication(d) < 0) return;
 	if (_pyobjc_install_NSATSTypesetter() < 0) return;
 	if (_pyobjc_install_NSBezierPath() < 0) return;
 	if (_pyobjc_install_NSBitmap() < 0) return;

File Modules/AppKit/_AppKitMapping_NSApplication.m

  * able to check if two NSModalSessions are actually the same.
  */
 
-typedef struct SessionWrapper {
-	PyObject_HEAD
-	NSModalSession* ptr;
-} SessionWrapper;
-
-static PyObject* 
-Session_pointer_get(
-	SessionWrapper* self, 
-	void* closure __attribute__((__unused__)))
-{
-	return PyInt_FromLong((long)self->ptr);
-}
-
-static PyObject* 
-Session_new(PyTypeObject* type __attribute__((__unused__)),
-	PyObject* args __attribute__((__unused__)), 
-	PyObject* kwds __attribute__((__unused__)))
-{
-	PyErr_SetString(PyExc_TypeError, "Cannot create NSModalSession objects");
-	return NULL;
-}
-
-static void
-Session_dealloc(PyObject* self)
-{
-	PyObject_Del(self);
-}
-
-
-
-static PyGetSetDef Session_getset[] = {
-	{
-		"pointer",
-		(getter)Session_pointer_get,
-		NULL,
-		NULL,
-		NULL
-	},
-	{
-		NULL,
-		NULL,
-		NULL,
-		NULL,
-		NULL
-	}
-};
-
-PyTypeObject SessionWrapper_Type = {
-	PyObject_HEAD_INIT(&PyType_Type)
-	0,					/* ob_size */
-	"NSModalSession",			/* tp_name */
-	sizeof(SessionWrapper),			/* tp_basicsize */
-	0,					/* tp_itemsize */
-	/* methods */
-	Session_dealloc, 			/* tp_dealloc */
-	0,					/* tp_print */
-	0,					/* tp_getattr */
-	0,					/* tp_setattr */
-	0,					/* tp_compare */
-	0,					/* tp_repr */
-	0,					/* tp_as_number */
-	0,					/* tp_as_sequence */
-	0,		       			/* tp_as_mapping */
-	0,					/* tp_hash */
-	0,					/* tp_call */
-	0,					/* tp_str */
-	PyObject_GenericGetAttr,		/* tp_getattro */
-	0,					/* tp_setattro */
-	0,					/* tp_as_buffer */
-	Py_TPFLAGS_DEFAULT,			/* tp_flags */
- 	0,					/* tp_doc */
- 	0,					/* tp_traverse */
- 	0,					/* tp_clear */
-	0,					/* tp_richcompare */
-	0,					/* tp_weaklistoffset */
-	0,					/* tp_iter */
-	0,					/* tp_iternext */
-	0,					/* tp_methods */
-	0,					/* tp_members */
-	Session_getset,				/* tp_getset */
-	0,					/* tp_base */
-	0,					/* tp_dict */
-	0,					/* tp_descr_get */
-	0,					/* tp_descr_set */
-	0,					/* tp_dictoffset */
-	0,					/* tp_init */
-	0,					/* tp_alloc */
-	Session_new,				/* tp_new */
-	0,		        		/* tp_free */
-	0,					/* tp_is_gc */
-        0,                                      /* tp_bases */
-        0,                                      /* tp_mro */
-        0,                                      /* tp_cache */
-        0,                                      /* tp_subclasses */
-        0                                       /* tp_weaklist */
-#if PY_VERSION_HEX >= 0x020300A2
-        , 0                                     /* tp_del */
-#endif
-};
-
-#define SessionWrapper_Check(obj) PyObject_TypeCheck((obj), &SessionWrapper_Type)
-
-
-/* This should do for now, although we should generate a new type for this */
-static PyObject* 
-NSModalSession_New(void* ptr __attribute__((__unused__)))
-{
-	SessionWrapper* res;
-
-	res  = PyObject_New(SessionWrapper, &SessionWrapper_Type);
-	if (res == NULL) {
-		return NULL;
-	}
-	res->ptr = ptr;
-	return (PyObject*)res;
-}
-
 static int 
-NSModalSession_Convert(PyObject* value, void* pSessionPtr)
-{
-	if (SessionWrapper_Check(value)) {
-		*(void**)pSessionPtr = ((SessionWrapper*)value)->ptr;
-		return 0;
-	}
-	PyErr_SetString(PyExc_ValueError, "Require NSModalSession object");
-	return -1;
-}
-
-static int 
-_pyobjc_install_NSApplication(void)
+_pyobjc_install_NSApplication(PyObject* module_dict)
 {
 	int r = 0;
+	PyObject* v;
 
-	r = PyObjCPointerWrapper_Register(@encode(NSModalSession), 
-		NSModalSession_New, NSModalSession_Convert);
-	if (r == -1) return -1;
+	v = PyObjCCreateOpaquePointerType("NSModalSession", @encode(NSModalSession), NULL);
+	if (v == NULL) return -1;
+
+	r = PyDict_SetItemString(module_dict, "NSModalSession", v);
+	Py_DECREF(v);
+	if (r != 0) {
+		return -1;
+	}
 
 	return 0;
 }

File Modules/Foundation/_Foundation.m

 	NULL
 };
 
-/* A special init method for rects, this sets a different default value
- * for the fields, makes the type more convenient to use.
- */
-static int 
-NSRect_init(PyObject* self, PyObject* args, PyObject* kwds)
-{
-	PyObject* origin = NULL;
-	PyObject* size  = NULL;
-	int r;
-
-	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", 
-				(char**)NSRect_fields, &origin, &size)) {
-		return -1;
-	}
-
-	if (origin == NULL) {
-		NSPoint aPoint = { 0, 0 };
-		origin = PyObjC_ObjCToPython(@encode(NSPoint), &aPoint);
-		if (origin == NULL) return -1;
-	} else {
-		Py_INCREF(origin);
-	}
-
-	if (size == NULL) {
-		NSSize aSize = { 0, 0 };
-		size = PyObjC_ObjCToPython(@encode(NSSize), &aSize);
-		if (size == NULL) {
-			Py_DECREF(origin);
-			return -1;
-		}
-	} else {
-		Py_INCREF(size);
-	}
-
-	r = PyObject_SetAttrString(self, "origin", origin);
-	if (r == -1)  {
-		Py_DECREF(origin);
-		Py_DECREF(size);
-		return -1;
-	}
-	r = PyObject_SetAttrString(self, "size", size);
-	if (r == -1)  {
-		Py_DECREF(origin);
-		Py_DECREF(size);
-		return -1;
-	}
-	Py_DECREF(origin);
-	Py_DECREF(size);
-
-	return 0;
-}
-
 void init_Foundation(void);
 
 void init_Foundation(void)
 	PyObject *m, *d, *v;
 	CFBundleRef bundle;
 	const char** name;
+	int r;
 
 	m = Py_InitModule4("_Foundation", foundation_methods, foundation_doc, 
 			NULL, PYTHON_API_VERSION);
 	bundle = NULL;
 #endif
 
+	v = PyObjCCreateOpaquePointerType("NSZonePointer", @encode(NSZone*), NULL);
+	if (v == NULL) return;
+
+	r = PyDict_SetItemString(d, "NSZonePointer", v);
+	Py_DECREF(v);
+	if (r != 0) {
+		return;
+	}
+
+
 	/* Register information in generated tables */
 	if (register_ints(d, enum_table) < 0) return;
 	if (register_variableList(d, bundle, string_table, (sizeof(string_table)/sizeof(string_table[0]))-1) < 0) return;
 	Py_DECREF(v);
 
 	v = PyObjC_RegisterStructType(@encode(NSRect),
-			NSRect_name, NSRect_doc, NSRect_init, 2, NSRect_fields);
+			NSRect_name, NSRect_doc, NULL, 2, NSRect_fields);
 	if (v == NULL) return;
 	PyDict_SetItemString(d, "NSRect", v);
 	Py_DECREF(v);

File Modules/objc/module.m

 	return protocols;
 }
 
+PyDoc_STRVAR(createOpaquePointerType_doc,
+	"createOpaquePointerType(name, typestr, doc) -> type\n"
+	"\n"
+	"Return a wrapper type for opaque pointers of the given type. The type \n"
+	"will be registered with PyObjC and will be used to wrap pointers of the \n"
+	"given type."
+);
+static PyObject*
+createOpaquePointerType(PyObject* self __attribute__((__unused__)),
+		PyObject* args, PyObject* kwds)
+{
+static char* keywords[] = { "name", "typestr", "doc", NULL };
+	char* name;
+	char* typestr;
+	char* docstr = NULL;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|s", keywords, 
+				&name, &typestr, &docstr)) {
+		return NULL;
+	}
+
+	return PyObjCCreateOpaquePointerType(name, typestr, docstr);
+}
+
+PyDoc_STRVAR(createStructType_doc,
+	"createStructType(name, typestr, fieldnames, doc) -> type\n"
+	"\n"
+	"Return a wrapper type for structs of the given type. The wrapper will \n"
+	"registered with PyObjC and will be used to wrap structs of the given type."
+);
+static PyObject*
+createStructType(PyObject* self __attribute__((__unused__)),
+		PyObject* args, PyObject* kwds)
+{
+static char* keywords[] = { "name", "typestr", "fieldnames", "doc", NULL };
+	char* name;
+	char* typestr;
+	PyObject* pyfieldnames;
+	char* docstr = NULL;
+	PyObject* retval;
+	char** fieldnames = NULL;
+	int i;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssO|s", keywords, 
+				&name, &typestr, &pyfieldnames, &docstr)) {
+		return NULL;
+	}
+
+	name = strdup(name);
+	typestr = strdup(typestr);
+	if (docstr) {
+		docstr = strdup(docstr);
+	}
+	pyfieldnames = PySequence_Fast(pyfieldnames, 
+			"fieldnames must be a sequence of strings");
+
+	if (pyfieldnames == NULL) goto error_cleanup;
+	if (name == NULL || typestr == NULL) {
+		PyErr_NoMemory();
+		goto error_cleanup;
+	}
+
+	fieldnames = malloc(sizeof(char*) * PySequence_Fast_GET_SIZE(pyfieldnames));
+	if (fieldnames == NULL) {
+		PyErr_NoMemory();
+		goto error_cleanup;
+	}
+	memset(fieldnames, 0, 
+			sizeof(char*) * PySequence_Fast_GET_SIZE(pyfieldnames));
+	for (i = 0; i < PySequence_Fast_GET_SIZE(pyfieldnames); i++) {
+		PyObject* v = PySequence_Fast_GET_ITEM(pyfieldnames, i);
+		if (!PyString_Check(v)) {
+			PyErr_SetString(PyExc_TypeError,
+				"fieldnames must be a sequence of strings");
+			goto error_cleanup;
+		}
+		fieldnames[i] = strdup(PyString_AS_STRING(v));
+		if (fieldnames[i] == NULL) {
+			PyErr_NoMemory();
+			goto error_cleanup;
+		}
+	}
+
+	retval = PyObjC_RegisterStructType(typestr, name, docstr, NULL,
+			PySequence_Fast_GET_SIZE(pyfieldnames), 
+			(const char**)fieldnames);
+	if (retval == NULL) goto error_cleanup;
+	Py_DECREF(pyfieldnames);
+
+	return retval;
+
+error_cleanup:
+	if (name) free(name);
+	if (typestr) free(typestr);
+	if (docstr) free(docstr);
+	if (fieldnames) {
+		for (i = 0; i < PySequence_Fast_GET_SIZE(pyfieldnames); i++) {
+			if (fieldnames[i]) free(fieldnames[i]);
+		}
+		free(fieldnames);
+	}
+	Py_XDECREF(pyfieldnames);
+
+	return NULL;
+}
+
 PyDoc_STRVAR(PyObjCIvar_Info_doc, 
 	"listInstanceVariables(classOrInstance) -> [ (name, typestr), ... ]\n"
 	"\n"
 	{ "getInstanceVariable", (PyCFunction)PyObjCIvar_Get,
 		METH_VARARGS|METH_KEYWORDS, PyObjCIvar_Get_doc },
 	{ "setInstanceVariable", (PyCFunction)PyObjCIvar_Set,
-		METH_VARARGS|METH_KEYWORDS, PyObjCIvar_Get_doc },
+		METH_VARARGS|METH_KEYWORDS, PyObjCIvar_Set_doc },
+	{ "createOpaquePointerType", (PyCFunction)createOpaquePointerType,
+		METH_VARARGS|METH_KEYWORDS, createOpaquePointerType_doc },
+	{ "createStructType", (PyCFunction)createStructType,
+		METH_VARARGS|METH_KEYWORDS, createStructType_doc },
 
 	{ 0, 0, 0, 0 } /* sentinel */
 };
 	PyType_Ready(&PyObjCUnicode_Type);
 	PyType_Ready(&PyObjCIMP_Type);
 	PyType_Ready(&PyObjCMethodAccessor_Type);
-	PyType_Ready(&PyObjCZoneWrapper_Type);
 
 	m = Py_InitModule4("_objc", mod_methods, NULL,
 			NULL, PYTHON_API_VERSION);
 
-    d = PyModule_GetDict(m);
-    /* use PyDict_SetItemString for the retain, non-heap types can't be dealloc'ed */
+	d = PyModule_GetDict(m);
+	/* use PyDict_SetItemString for the retain, non-heap types can't be dealloc'ed */
 	PyDict_SetItemString(d, "objc_class", (PyObject*)&PyObjCClass_Type);
 	PyDict_SetItemString(d, "objc_object", (PyObject*)&PyObjCObject_Type);
 	PyDict_SetItemString(d, "pyobjc_unicode", (PyObject*)&PyObjCUnicode_Type);

File Modules/objc/opaque-pointer.m

+/*
+ * Generic support for opaque pointer types, such as NSZone*
+ */
+#include "pyobjc.h"
+
+#ifndef FFI_CLOSURES
+#    error "Need FFI_CLOSURES!"
+#endif
+
+typedef struct {
+	PyObject_HEAD
+	void* pointer_value;
+} OpaquePointerObject;
+
+static PyMemberDef opaque_members[] = {
+	{
+		"pointer",
+		T_LONG,
+		offsetof(OpaquePointerObject, pointer_value),
+		READONLY,
+		"raw value of the pointer"
+	},
+	{ 0, 0, 0, 0, 0 }
+};
+
+static PyObject* 
+opaque_new(
+	PyTypeObject* type,
+	PyObject* args __attribute__((__unused__)),
+	PyObject* kwds __attribute__((__unused__)))
+{
+	PyErr_Format(PyExc_TypeError, "Cannot create %s objects",
+			type->tp_name);
+	return NULL;
+}
+
+static void
+opaque_dealloc(PyObject* self)
+{
+	PyObject_Del(self);
+}
+
+static PyTypeObject opaque_template = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,					/* ob_size */
+	"OpaquePointerWrapper",			/* tp_name */
+	sizeof(OpaquePointerObject),		/* tp_basicsize */
+	0,					/* tp_itemsize */
+	/* methods */
+	opaque_dealloc,	 			/* tp_dealloc */
+	0,					/* tp_print */
+	0,					/* tp_getattr */
+	0,					/* tp_setattr */
+	0,					/* tp_compare */
+	0,					/* tp_repr */
+	0,					/* tp_as_number */
+	0,					/* tp_as_sequence */
+	0,		       			/* tp_as_mapping */
+	0,					/* tp_hash */
+	0,					/* tp_call */
+	0,					/* tp_str */
+	PyObject_GenericGetAttr,		/* tp_getattro */
+	0,					/* tp_setattro */
+	0,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,			/* tp_flags */
+	0,					/* tp_doc */
+	0,					/* tp_traverse */
+	0,					/* tp_clear */
+	0,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	0,					/* tp_iter */
+	0,					/* tp_iternext */
+	0,					/* tp_methods */
+	opaque_members,				/* tp_members */
+	0,					/* tp_getset */
+	0,					/* tp_base */
+	0,					/* tp_dict */
+	0,					/* tp_descr_get */
+	0,					/* tp_descr_set */
+	0,					/* tp_dictoffset */
+	0,					/* tp_init */
+	0,					/* tp_alloc */
+	opaque_new,				/* tp_new */
+	0,		        		/* tp_free */
+	0,					/* tp_is_gc */
+	0,                                      /* tp_bases */
+	0,                                      /* tp_mro */
+	0,                                      /* tp_cache */
+	0,                                      /* tp_subclasses */
+	0,                                      /* tp_weaklist */
+	0                                       /* tp_del */
+};
+
+static void
+opaque_from_c(
+	ffi_cif* cif __attribute__((__unused__)),
+	void* retval,
+	void** args,
+	void* userdata)
+{
+	void* pointer_value = *(void**)args[0];
+	PyTypeObject* opaque_type = (PyTypeObject*)userdata;
+	OpaquePointerObject* result;
+
+	result = PyObject_New(OpaquePointerObject, opaque_type);
+	if (result == NULL) {
+		*(PyObject**)retval = NULL;
+		return;
+	}
+	result->pointer_value = pointer_value;
+	*(PyObject**)retval = (PyObject*)result;
+}
+
+static void
+opaque_to_c(
+	ffi_cif* cif __attribute__((__unused__)),
+	void* retval,
+	void** args,
+	void* userdata)
+{
+	PyObject* obj = *(PyObject**)args[0];
+	void* pObj = *(void**)args[1];
+	PyTypeObject* opaque_type = (PyTypeObject*)userdata;
+
+	if (!PyObject_TypeCheck((obj), opaque_type)) {
+		*(void**)pObj = (void*)0xDEADBEEF; /* force errors */
+		PyErr_Format(PyExc_TypeError, 
+			"Need instance of %s, got instance of %s",
+			opaque_type->tp_name, obj->ob_type->tp_name);
+		*(int*)retval = -1;
+		return;
+	}
+
+	*(void**)pObj = ((OpaquePointerObject*)obj)->pointer_value;
+	*(int*)retval = 0;
+}
+
+
+/*
+ * Usage:
+ * 	PyDict_SetItemString(moduleDict, "NSZonePointer",
+ * 		PyObjCCreateOpaquePointerType(
+ * 			"Foundation.NSZonePointer",
+ * 			@encode(NSZone*),
+ *	 		NSZonePointer_doc));
+ */
+PyObject*
+PyObjCCreateOpaquePointerType(
+		const char* name, 
+		const char* typestr,
+		const char* docstr)
+{
+static  ffi_cif* convert_cif = NULL;
+static  ffi_cif* new_cif = NULL;
+
+	PyTypeObject* newType = NULL;
+	PyObjCPointerWrapper_ToPythonFunc from_c = NULL;
+	PyObjCPointerWrapper_FromPythonFunc to_c = NULL;
+	ffi_closure* cl = NULL;
+	ffi_status rv;
+	int r;
+	PyObject* v = NULL;
+	PyObject* w = NULL;
+
+	if (new_cif == NULL) {
+		PyObjCMethodSignature* signature;
+		signature = PyObjCMethodSignature_FromSignature("^v^v");
+		new_cif = PyObjCFFI_CIFForSignature(signature, NULL);
+		PyObjCMethodSignature_Free(signature);
+		if (new_cif == NULL) {
+			return NULL;
+		}
+	}
+
+	if (convert_cif == NULL) {
+		PyObjCMethodSignature* signature;
+		signature = PyObjCMethodSignature_FromSignature("i^v^v");
+		convert_cif = PyObjCFFI_CIFForSignature(signature, NULL);
+		PyObjCMethodSignature_Free(signature);
+		if (convert_cif == NULL) {
+			return NULL;
+		}
+	}
+
+
+	newType = malloc(sizeof(*newType));
+	if (newType == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	memcpy(newType, &opaque_template, sizeof(*newType));
+
+	newType->tp_name = strdup(name);
+	if (newType->tp_name == NULL) {
+		free(newType);
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	v = PyDict_New();
+	if (v == NULL) {
+		goto error_cleanup;
+	}
+
+	w = PyString_FromString(typestr);
+	if (w ==  NULL) {
+		goto error_cleanup;
+	}
+
+	if (PyDict_SetItemString(v, "__typestr__", w) != 0) {
+		goto error_cleanup;
+	}
+	Py_DECREF(w); w = NULL;
+
+	newType->tp_dict = v; v = NULL;
+
+	if (docstr != NULL) {
+		newType->tp_doc = strdup(docstr);
+		if (newType->tp_doc == NULL) {
+			PyErr_NoMemory();
+			goto error_cleanup;
+		}
+	}
+
+	cl = malloc(sizeof(*cl));
+	if (cl == NULL) {
+		PyErr_NoMemory();
+		goto error_cleanup;
+	}
+
+	PyType_Ready(newType);
+	Py_INCREF(newType);
+	Py_INCREF(newType);
+	Py_INCREF(newType);
+
+	rv = ffi_prep_closure(cl, convert_cif, opaque_to_c, newType);
+	if (rv != FFI_OK) {
+		PyErr_Format(PyExc_RuntimeError,
+			"Cannot create FFI closure: %d", rv);
+		goto error_cleanup;
+	}
+	to_c = (PyObjCPointerWrapper_FromPythonFunc)cl;
+	cl = NULL;
+
+	cl = malloc(sizeof(*cl));
+	if (cl == NULL) {
+		PyErr_NoMemory();
+		goto error_cleanup;
+	}
+
+	rv = ffi_prep_closure(cl, new_cif, opaque_from_c, newType);
+	if (rv != FFI_OK) {
+		PyErr_Format(PyExc_RuntimeError,
+			"Cannot create FFI closure: %d", rv);
+		goto error_cleanup;
+	}
+	from_c = (PyObjCPointerWrapper_ToPythonFunc)cl;
+	cl = NULL;
+
+
+	r = PyObjCPointerWrapper_Register(typestr, from_c, to_c);
+	if (r == -1) {
+		goto error_cleanup;
+	}
+
+	return (PyObject*)newType;
+
+error_cleanup:
+	if (newType) {
+		if (newType->tp_name) free(newType->tp_name);
+		if (newType->tp_doc) free(newType->tp_doc);
+		Py_XDECREF(newType->tp_dict);
+		free(newType);
+	}
+	if (cl) {
+		free(cl);
+	}
+	if (to_c) {
+		free(to_c);
+	}
+	if (from_c) {
+		free(from_c);
+	}
+	Py_XDECREF(v);
+	Py_XDECREF(w);
+	return NULL;
+}

File Modules/objc/pointer-support.m

 	}
 }
 
-/*
- * NSZone support
- *
- * Zones are opaque values, the 'pointer' attribute is provided to be able 
- * to check if two NSZones are actually the same (one Objective-C zone may
- * have two different wrapper objects)
- */
-
-typedef struct ZoneWrapper {
-	PyObject_HEAD
-	NSZone* ptr;
-} ZoneWrapper;
-
-static PyObject* 
-Zone_pointer_get(ZoneWrapper* self, void* closure __attribute__((__unused__)))
-{
-	return PyInt_FromLong((long)self->ptr);
-}
-
-static PyObject* 
-Zone_new(PyTypeObject* type __attribute__((__unused__)),
-	PyObject* args __attribute__((__unused__)), 
-	PyObject* kwds __attribute__((__unused__)))
-{
-	PyErr_SetString(PyExc_TypeError, "Cannot create NSZone objects");
-	return NULL;
-}
-
-static void
-Zone_dealloc(PyObject* self)
-{
-	PyObject_Del(self);
-}
-
-
-
-static PyGetSetDef Zone_getset[] = {
-	{
-		"pointer",
-		(getter)Zone_pointer_get,
-		NULL,
-		NULL,
-		NULL
-	},
-	{
-		NULL,
-		NULL,
-		NULL,
-		NULL,
-		NULL
-	}
-};
-
-PyTypeObject PyObjCZoneWrapper_Type = {
-	PyObject_HEAD_INIT(&PyType_Type)
-	0,					/* ob_size */
-	"NSZone",				/* tp_name */
-	sizeof(ZoneWrapper),			/* tp_basicsize */
-	0,					/* tp_itemsize */
-	/* methods */
-	Zone_dealloc,	 			/* tp_dealloc */
-	0,					/* tp_print */
-	0,					/* tp_getattr */
-	0,					/* tp_setattr */
-	0,					/* tp_compare */
-	0,					/* tp_repr */
-	0,					/* tp_as_number */
-	0,					/* tp_as_sequence */
-	0,		       			/* tp_as_mapping */
-	0,					/* tp_hash */
-	0,					/* tp_call */
-	0,					/* tp_str */
-	PyObject_GenericGetAttr,		/* tp_getattro */
-	0,					/* tp_setattro */
-	0,					/* tp_as_buffer */
-	Py_TPFLAGS_DEFAULT,			/* tp_flags */
- 	0,					/* tp_doc */
- 	0,					/* tp_traverse */
- 	0,					/* tp_clear */
-	0,					/* tp_richcompare */
-	0,					/* tp_weaklistoffset */
-	0,					/* tp_iter */
-	0,					/* tp_iternext */
-	0,					/* tp_methods */
-	0,					/* tp_members */
-	Zone_getset,				/* tp_getset */
-	0,					/* tp_base */
-	0,					/* tp_dict */
-	0,					/* tp_descr_get */
-	0,					/* tp_descr_set */
-	0,					/* tp_dictoffset */
-	0,					/* tp_init */
-	0,					/* tp_alloc */
-	Zone_new,				/* tp_new */
-	0,		        		/* tp_free */
-	0,					/* tp_is_gc */
-        0,                                      /* tp_bases */
-        0,                                      /* tp_mro */
-        0,                                      /* tp_cache */
-        0,                                      /* tp_subclasses */
-        0,                                      /* tp_weaklist */
-        0                                       /* tp_del */
-};
-
-#define ZoneWrapper_Check(obj) PyObject_TypeCheck((obj), &PyObjCZoneWrapper_Type)
-
-
 static PyObject*
 PyObjectPtr_New(void *obj)
 {
 	return 0;
 }
 
-/* This should do for now, although we should generate a new type for this */
-static PyObject* 
-NSZone_New(void* zoneptr __attribute__((__unused__)))
-{
-	ZoneWrapper* res;
-
-	res  = PyObject_New(ZoneWrapper, &PyObjCZoneWrapper_Type);
-	if (res == NULL) {
-		return NULL;
-	}
-	res->ptr = zoneptr;
-
-	return (PyObject*)res;
-}
-
-static int 
-NSZone_Convert(PyObject* zone, void* pZonePtr)
-{
-	if (ZoneWrapper_Check(zone)) {
-		*(void**)pZonePtr = ((ZoneWrapper*)zone)->ptr;
-		return 0;
-	}
-
-	*(void**)pZonePtr = (void*)0xDEADBEEF; /* Force errors */
-	PyErr_SetString(PyExc_ValueError, "Require NSZone object");
-	return -1;
-}
-
-/*
- * End of zone support
- */
-
 #ifdef MACOSX
 /*
  * Generic CF type support 
 PyObjCPointerWrapper_Init(void)
 {
 	int r = 0;
+	PyObject* v;
 
 #ifdef MACOSX
 	r = PyObjCPointerWrapper_Register(@encode(CFURLRef), 
 
 #endif
 
-	r = PyObjCPointerWrapper_Register(@encode(NSZone*), 
-		NSZone_New, NSZone_Convert);
-	if (r == -1) return -1;
-
 	r = PyObjCPointerWrapper_Register(@encode(PyObject*),
 		PyObjectPtr_New, PyObjectPtr_Convert);
 	if (r == -1) return -1;

File Modules/objc/pyobjc-api.h

  * - Version 11 adds PyObjCObject_Convert, PyObjCSelector_Convert,
      PyObjCClass_Convert, PyObjC_ConvertBOOL, and PyObjC_ConvertChar
  * - Version 12 adds PyObjCObject_New
+ * - Version 13 adds PyObjCCreateOpaquePointerType
  */
-#define PYOBJC_API_VERSION 12
+#define PYOBJC_API_VERSION 13
 
 #define PYOBJC_API_NAME "__C_API__"
 
 
 	/* PyObjCObject_New */
 	PyObject* (*pyobjc_object_new)(id);
+
+	/* PyObjCCreateOpaquePointerType */
+	PyObject* (*pointer_type_new)(const char*, const char*, const char*);
+	
 };
 
 #ifndef PYOBJC_BUILD
 #define PyObjC_ConvertBOOL (PyObjC_API->pyobjc_convertbool)
 #define PyObjC_ConvertChar (PyObjC_API->pyobjc_convertchar)
 #define PyObjCObject_New (PyObjC_API->pyobjc_object_new)
+#define PyObjCCreateOpaquePointerType (PyObjC_API->pointer_type_new)
+
 
 #ifndef PYOBJC_METHOD_STUB_IMPL
 

File Modules/objc/pyobjc-api.m

 	PyObjCErr_AsExc,		/* err_python_to_nsexception */
 	PyGILState_Ensure,		/* gilstate_ensure */
 	obj_is_uninitialized,   /* obj_is_uninitialized */
-    PyObjCObject_Convert,   /* pyobjcobject_convert */
-    PyObjCSelector_Convert, /* pyobjcselector_convert */
-    PyObjCClass_Convert,    /* pyobjcclass_convert */
-    PyObjC_ConvertBOOL,     /* pyobjc_convertbool */
-    PyObjC_ConvertChar,     /* pyobjc_convertchar */
-	PyObjCObject_New		/* pyobjc_object_new */
+	PyObjCObject_Convert,   /* pyobjcobject_convert */
+	PyObjCSelector_Convert, /* pyobjcselector_convert */
+	PyObjCClass_Convert,    /* pyobjcclass_convert */
+	PyObjC_ConvertBOOL,     /* pyobjc_convertbool */
+	PyObjC_ConvertChar,     /* pyobjc_convertchar */
+	PyObjCObject_New,		/* pyobjc_object_new */
+	PyObjCCreateOpaquePointerType, /* pointer_type_new */
 };
 
 int PyObjCAPI_Register(PyObject* module)

File Modules/objc/pyobjc.h

 id PyObjC_CFTypeToID(PyObject* argument);
 PyObject* PyObjC_IDToCFType(id argument);
 
+/* opaque-pointer.m */
+PyObject* PyObjCCreateOpaquePointerType(const char* name, 
+		const char* typestr, const char* docstr);
+
 #endif
 
 #define PyObjCErr_InternalError() \

File Modules/objc/struct-wrapper.h

 	const char* doc,
 	initproc tpinit,
 	int numFields,
-	const char** fieldnames);
+	const char** fieldnames,
+	const char* typestr);
 
 
 /*!

File Modules/objc/struct-wrapper.m

 	return -1;
 }
 
+static int set_defaults(PyObject* self, const char* typestr)
+{
+	int i = 0;
+	int r;
+	PyObject* v;
 
-static int
-struct_init(PyObject* self, PyObject* args, PyObject* kwds)
+	while(*typestr != _C_STRUCT_E && *typestr++ != '=');
+	while(typestr && *typestr != _C_STRUCT_E) {
+		const char* next;
+
+		if (*typestr == '"') {
+			/* embedded field names */
+			typestr = strchr(typestr+1, '"');
+			if (typestr) {
+				typestr++;
+			} else {
+				break;
+			}
+		}
+		next = PyObjCRT_SkipTypeSpec(typestr);
+		switch (*typestr) {
+		case _C_BOOL: 
+			v = PyBool_FromLong(0);
+			break;
+
+		case _C_CHR: case _C_UCHR:
+		case _C_SHT: case _C_USHT:
+		case _C_INT: case _C_UINT:
+		case _C_LNG: case _C_ULNG:
+			v = PyInt_FromLong(0);
+			break;
+
+		case _C_FLT: case _C_DBL:
+			v = PyFloat_FromDouble(0.0);
+			break;
+
+		case _C_STRUCT_B:
+			v = PyObjC_CreateRegisteredStruct(typestr, next-typestr);
+			if (v != NULL) {
+				/* call init */
+				r = v->ob_type->tp_init(v, NULL, NULL);
+				if (r == -1) {
+					Py_DECREF(v);
+					return -1;
+				}
+			}
+				
+
+			break;
+
+		default:
+			v = Py_None;
+			Py_INCREF(Py_None);
+		}
+
+		if (v == NULL) {
+			return -1;
+		}
+
+		r = PySequence_SetItem(self, i++, v);
+		Py_DECREF(v);
+		if (r != 0) {
+			return -1;
+		}
+
+		typestr = next;
+	}
+
+	return 0;
+}
+
+
+static void
+struct_init(
+	ffi_cif* cif __attribute__((__unused__)),
+	void* retval,
+	void** cargs,
+	void* userdata
+	   )
 {
+	PyObject* self = *(PyObject**)cargs[0];
+	PyObject* args = *(PyObject**)cargs[1];
+	PyObject* kwds = *(PyObject**)cargs[2];
+	const char* typestr = (char*)userdata;
 	int setUntil = -1;
+	int r;
 
 	if (args != NULL && !PyTuple_Check(args)) {
 		PyErr_Format(PyExc_TypeError, 
 				"%s() argument tuple is not a tuple",
 				self->ob_type->tp_name);
-		return -1;
+		*(int*)retval = -1;
+		return;
 	}
 	if (kwds != NULL && !PyDict_Check(kwds)) {
 		PyErr_Format(PyExc_TypeError, 
 				"%s() keyword dict is not a dict",
 				self->ob_type->tp_name);
-		return -1;
+		*(int*)retval = -1;
+		return;
+	}
+
+	r = set_defaults(self, typestr);
+	if (r != 0) {
+		*(int*)retval = r;
+		return;
 	}
 
 	if (args != NULL) {
 				self->ob_type->tp_name,
 				struct_sq_length(self),
 				kwds?"non-keyword ":"", len);
-			return -1;
+			*(int*)retval = -1;
+			return;
 		}
 		for (i = 0; i < len; i++) {
 			PyObject* v = PyTuple_GET_ITEM(args, i);
 			Py_DECREF(keys);
 			PyErr_SetString(PyExc_TypeError, 
 					"dict.keys didn't return a list");
-			return -1;
+			*(int*)retval = -1;
+			return;
 		}
 
 		len = PyList_GET_SIZE(keys);
 				PyErr_Format(PyExc_TypeError,
 					"%s() keywords must be strings",
 					self->ob_type->tp_name);
-				return -1;
+				*(int*)retval = -1;
+				return;
 			}
 
 			off = LOCATE_MEMBER(self->ob_type, 
 					"no keyword argument: %s",
 					PyString_AS_STRING(k));
 				Py_DECREF(keys);
-				return -1;
+				*(int*)retval = -1;
+				return;
 			}
 
 			if (off <= setUntil) {
 					self->ob_type->tp_name,
 					PyString_AS_STRING(k));
 				Py_DECREF(keys);
-				return -1;
+				*(int*)retval = -1;
+				return;
 			}
 
 			member = self->ob_type->tp_members + off;
 		Py_DECREF(keys);
 	}
 
-	return 0;
+	*(int*)retval = 0;
+	return;
 }
 
+static initproc
+make_init(const char* typestr)
+{
+static 	ffi_cif* init_cif = NULL;
+	ffi_closure* cl = NULL;
+	ffi_status rv;
+
+	if (init_cif == NULL) {
+		PyObjCMethodSignature* signature;
+		signature = PyObjCMethodSignature_FromSignature("i^v^v^v");
+		init_cif = PyObjCFFI_CIFForSignature(signature, NULL);
+		PyObjCMethodSignature_Free(signature);
+		if (init_cif == NULL) {
+			return NULL;
+		}
+	}
+
+	cl = malloc(sizeof(*cl));
+	if (cl == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	rv = ffi_prep_closure(cl, init_cif, struct_init, (char*)typestr);
+	if (rv != FFI_OK) {
+		free(cl);
+		PyErr_Format(PyExc_RuntimeError,
+			"Cannot create FFI closure: %d", rv);
+		return NULL;
+	}
+
+	return (initproc)cl;
+}
+
+
+
 static long
 struct_hash(PyObject* self)
 {
 	0,					/* tp_descr_get */
 	0,					/* tp_descr_set */
 	0,					/* tp_dictoffset */
-	struct_init,				/* tp_init */
+	0,					/* tp_init */
 	0,					/* tp_alloc */
 	struct_new,				/* tp_new */
 	0,					/* tp_free */
 		const char* doc,
 		initproc tpinit,
 		int numFields,
-		const char** fieldnames)
+		const char** fieldnames,
+		const char* typestr)
 {
 	PyTypeObject* result;
 	PyMemberDef* members;
 	*result = StructTemplate_Type;
 	result->tp_name = (char*)name;
 	result->tp_doc = (char*)doc;
+	result->tp_dict = PyDict_New();
+	if (result->tp_dict == NULL) {
+		PyMem_Free(members);
+		PyMem_Free(result);
+		return NULL;
+	}
 	result->ob_refcnt = 1;
 	result->tp_members = members;
 	result->tp_basicsize = sizeof(PyObject) + numFields*sizeof(PyObject*);
 	if (tpinit) {
 		result->tp_init = tpinit;
+	} else {
+		result->tp_init = make_init(typestr);
+		if (result->tp_init == NULL) {
+			PyMem_Free(members);
+			PyMem_Free(result);
+			return NULL;
+		}
 	}
 
 	if (PyType_Ready(result) == -1) {
 		const char** fieldnames)
 {
 	PyObject* structType;
+	PyObject* v;
 	int r;
 
 	structType = PyObjC_MakeStructType(name, doc, tpinit, 
-						numFields, fieldnames);
+				numFields, fieldnames, signature);
 	if (structType == NULL) {
 		return NULL;
 	}
 
+	v = PyString_FromString(signature);
+	if (v == NULL) {
+		Py_DECREF(structType);
+		return NULL;
+	}
+
+	r = PyDict_SetItemString(((PyTypeObject*)structType)->tp_dict, "__typestr__", v);
+	Py_DECREF(v);
+	if (r == -1) {
+		Py_DECREF(structType);
+		return NULL;
+	}
+
 	if (structRegistry == NULL) {
 		structRegistry = PyDict_New();
 		if (structRegistry == NULL) {
 variable is an object. If it is true the bridge will update reference counts,
 otherwise it won't.</p>
 </li>
+<li>All wrappers for opaque pointers (such as <code><span>NSZone*</span></code>) now have the same
+interface and share a single implementation. This decreases code-size and
+makes it easier to add new wrappers.  A new feature is a <code><span>__typestr__</span></code>
+attribute on the type object, this contains the encoded Objective-C type
+of the pointer.<p>A function for creating new wrappers is exposed to python, as 
+<code><span>objc.createOpaquePointerType(name,</span> <span>typestr,</span> <span>doc)</span></code>.  The same function is 
+also exposed in the C-API.</p>
+</li>
 </ul>
 <h2><a name="version-1-2-2004-12-29">Version 1.2 (2004-12-29)</a></h2>
 <ul>
 
 An overview of the relevant changes in new, and older, releases.
 
-
 Version 1.3 (2005-03-??)
 ------------------------
 
   variable is an object. If it is true the bridge will update reference counts,
   otherwise it won't. 
 
+- All wrappers for opaque pointers (such as ``NSZone*``) now have the same
+  interface and share a single implementation. This decreases code-size and
+  makes it easier to add new wrappers.  A new feature is a ``__typestr__``
+  attribute on the type object, this contains the encoded Objective-C type
+  of the pointer.
+  
+  A function for creating new wrappers is exposed to python, as 
+  ``objc.createOpaquePointerType(name, typestr, doc)``.  The same function is 
+  also exposed in the C-API.
+
+- Wrappers for C-structs how have a ``__typestr__`` attribute on their type.
+  This attribute contains the encoded Objective-C type of the struct.
+
+  The default ``__init__`` for struct-wrappers now initializes fields with an 
+  appropriate default value, instead of ``None``. 
+
+  New wrappers can now be created from Python using the function
+  ``objc.createStructType(name, typestr, fieldnames, doc)``. The same
+  function is also exposed in the C API (and has been for a while).
 
 Version 1.2 (2004-12-29)
 ------------------------