*.c: Make all of the type-definition tables read-only.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 20 Oct 2019 21:46:05 +0000 (22:46 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 11 Apr 2020 11:44:14 +0000 (12:44 +0100)
The hard part is a new collection of macros which strip the `const'
qualifier from the actual table definitions, after checking that they
actually have the correct type.

And then there's the slog of actually changing all of the definitions
and using the new macros.

mapping.c
pyke.c
pyke.h

index 229dfa9..615c5ed 100644 (file)
--- a/mapping.c
+++ b/mapping.c
@@ -160,7 +160,7 @@ static PyTypeObject valiter_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
-PySequenceMethods gmap_pysequence = {
+const PySequenceMethods gmap_pysequence = {
   0,                                   /* @sq_length@ */
   0,                                   /* @sq_concat@ */
   0,                                   /* @sq_repeat@ */
@@ -403,7 +403,7 @@ end:
   return (rc);
 }
 
-PyMethodDef gmap_pymethods[] = {
+const PyMethodDef gmap_pymethods[] = {
   GMAP_METHODS
   { 0 }
 };
diff --git a/pyke.c b/pyke.c
index 883223a..b462a1f 100644 (file)
--- a/pyke.c
+++ b/pyke.c
@@ -260,7 +260,7 @@ PyTypeObject *inittype(PyTypeObject *tyskel, PyTypeObject *meta)
 /*----- Populating modules ------------------------------------------------*/
 
 PyObject *mkexc(PyObject *mod, PyObject *base,
-               const char *name, PyMethodDef *mm)
+               const char *name, const PyMethodDef *mm)
 {
   PyObject *nameobj = 0;
   PyObject *dict = 0;
@@ -272,7 +272,8 @@ PyObject *mkexc(PyObject *mod, PyObject *base,
 
   if (mm) {
     while (mm->ml_name) {
-      if ((func = PyCFunction_NewEx(mm, 0, mod)) == 0 ||
+      if ((func = PyCFunction_NewEx((/*unconst*/ PyMethodDef *)mm,
+                                   0, mod)) == 0 ||
          (meth = PyMethod_New(func, 0, exc)) == 0 ||
          PyDict_SetItemString(dict, mm->ml_name, meth))
        goto fail;
diff --git a/pyke.h b/pyke.h
index b91b48d..e200c09 100644 (file)
--- a/pyke.h
+++ b/pyke.h
@@ -269,6 +269,20 @@ extern PyTypeObject *inittype(PyTypeObject */*skel*/,
 #define MEMBER(name, ty, f, doc)                                       \
   { #name, ty, offsetof(MEMBERSTRUCT, name), f, doc },
 
+/* Wrappers for filling in pointers in a `PyTypeObject' structure, (a)
+ * following Pyke naming convention, and (b) stripping `const' from the types
+ * without losing type safety.
+ */
+#define UNCONST_TYPE_SLOT(type, suffix, op, ty)                                \
+  CONVERT_CAREFULLY(type *, const type *, op ty##_py##suffix)
+#define PYGETSET(ty) UNCONST_TYPE_SLOT(PyGetSetDef, getset, NOTHING, ty)
+#define PYMETHODS(ty) UNCONST_TYPE_SLOT(PyMethodDef, methods, NOTHING, ty)
+#define PYMEMBERS(ty) UNCONST_TYPE_SLOT(PyMemberDef, members, NOTHING, ty)
+#define PYNUMBER(ty) UNCONST_TYPE_SLOT(PyNumberMethods, number, &, ty)
+#define PYSEQUENCE(ty) UNCONST_TYPE_SLOT(PySequenceMethods, sequence, &, ty)
+#define PYMAPPING(ty) UNCONST_TYPE_SLOT(PyMappingMethods, mapping, &, ty)
+#define PYBUFFER(ty) UNCONST_TYPE_SLOT(PyBufferProcs, buffer, &, ty)
+
 /*----- Populating modules ------------------------------------------------*/
 
 extern PyObject *modname;
@@ -278,7 +292,7 @@ extern PyObject *home_module;
   /* The overall module object. */
 
 extern PyObject *mkexc(PyObject */*mod*/, PyObject */*base*/,
-                      const char */*name*/, PyMethodDef */*methods*/);
+                      const char */*name*/, const PyMethodDef */*methods*/);
   /* Make and return an exception class called NAME, which will end up in
    * module MOD (though it is not added at this time).  The new class is a
    * subclass of BASE.  Attach the METHODS to it.
@@ -376,8 +390,8 @@ GMAP_DOMETHODS(GMAP_METHDECL, GMAP_KWMETHDECL)
 
 /* Mapping protocol implementation. */
 extern Py_ssize_t gmap_pysize(PyObject *); /* for `mp_length' */
-extern PySequenceMethods gmap_pysequence; /* for `tp_as_sequence' */
-extern PyMethodDef gmap_pymethods[]; /* all the standard methods */
+extern const PySequenceMethods gmap_pysequence; /* for `tp_as_sequence' */
+extern const PyMethodDef gmap_pymethods[]; /* all the standard methods */
 
 /*----- That's all, folks -------------------------------------------------*/