How to passing user data for `PyMethodDef`

I’ve read many documents on how to create a method for a type using additional data. I want a feature similar to PyGetSetDef.closure that allows passing user data when there is no good way to do it otherwise.

struct PyMethodDef {
    const char  *ml_name;   /* The name of the built-in function/method */
    PyCFunction ml_meth;    /* The C function that implements it */
    int         ml_flags;   /* Combination of METH_xxx flags, describing expected args */
    const char  *ml_doc;    /* The __doc__ attribute, or NULL */
};

Although I can access some data from self:

typedef PyObject *(*PyCFunction)(PyObject */*self*/, PyObject */*arguments*/);

I still need to use many hacks to access method-related data. My situation is: I have an array that stores native function pointers and userdata:

struct NativeMethodDef {
    void* NativeImpl;
    void* UserData;
}

And I’m creating a PyMethodDef array to map these. It’s difficult to create the function without using lambda capture.

Desired Feature

  struct PyMethodDef {
      const char  *ml_name;   /* The name of the built-in function/method */
      PyCFunction ml_meth;    /* The C function that implements it */
      int         ml_flags;   /* Combination of METH_xxx flags, describing expected args */
++    void *closure;  /* Optional user data pointer for PyCFunction. */
      const char  *ml_doc;    /* The __doc__ attribute, or NULL */
  };
typedef PyObject *(*PyCFunction)(PyObject */*self*/, PyObject */*arguments*/, void */*closure*/);

You’d normally use the PyCMethod’s self argument to do this. You can set it on creation with PyCMethod_New.

Thank you, It can be a solution for this.
Can I define it at tp_members?

struct _typeobject {
    /* Attribute descriptor and subclassing stuff */
    PyMethodDef *tp_methods; // <-- I want 
    PyMemberDef *tp_members;
    PyGetSetDef *tp_getset;
}

No - it won’t work so well for anything defined at tp_methods because that’ll automatically get an instance of the class as self when it’s called as a bound method.

I’d (wrongly) assumed you were using module-level functions. Sorry.

1 Like

The way you’d actually do it is with a descriptor type I think.

Written here in Python just to make it easier to read, but it’s relatively straight-forward to translate to C (just long…)

class Descriptor:
    def __init__(self, func, closure):
        self.func = func
        self.closure = closure
        self.instance = None
    def __get__(self, instance, owner=None):
        result = Descriptor(self.func, self.closure)
        result.instance = instance
        return result
    def __call__(self, *args, **kwds):
        return self.func(self.instance, self.closure, *args, **kwds)

class C:
    func = Descriptor(some_function, some_closure)

In C you can’t initialize the descriptor in tp_methods. Instead you have to manually create an instance of it and add it to C’s class dictionary.

Hopefully that makes sense.

1 Like