pysidecapsulemethod_p.h 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. // Copyright (C) 2025 Ford Motor Company
  2. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
  3. #ifndef PYSIDE_CAPSULEMETHOD_P_H
  4. #define PYSIDE_CAPSULEMETHOD_P_H
  5. #include <sbkpython.h>
  6. extern "C"
  7. {
  8. /**
  9. * This code is needed to solve, in C++ and adhering to the stable API,
  10. * creating what are in effect lambda functions as instance methods on custom
  11. * types. The goal is to be able to add methods to a dynamic type. If the .rep
  12. * file defines a slot `mySlot`, it need to be added to the dynamic type. For
  13. * Source types, this should be an abstract method that raises a
  14. * NotImplementedError unless defined in the Python subclass. For Replica
  15. * types, this should include an implementation that forwards the request
  16. * through the underlying QRemoteObjectReplica instance.
  17. *
  18. * The stable API doesn't currently provide a way define a method that can
  19. * receive both the `self`, `args`, and runtime (but constant per method, i.e.,
  20. * lambda like) data using Py_tp_methods. Possibly post 3.13 when METH_METHOD is
  21. * part of the stable API. But for now, it is not.
  22. *
  23. * The solution is to create a custom descriptor
  24. * (https://docs.python.org/3/howto/descriptor.html) that can hold the runtime
  25. * data and then when called, will return a PyCFunction_New generated PyObject
  26. * that is passed both class instance `self` and the runtime data (a PyCapsule)
  27. * together as a tuple as a new `self` for the method. The static method
  28. * definition needs to expect and handle this, but when combined in C++, we can
  29. * define a single handler that receives both the original `self` of the instance
  30. * and the runtime capsule with data for handling.
  31. */
  32. /**
  33. * The CapsuleDescriptorData struct is what will be passed as the pseudo `self`
  34. * from a CapsuleMethod or CapsuleProperty to the associated handler method. The
  35. * handler method (which should look like a standard PyMethodDef method) should
  36. * parse it into the payload (the "lambda variables") and the actual instance
  37. * (the "self").
  38. */
  39. struct CapsuleDescriptorData
  40. {
  41. PyObject *self;
  42. PyObject *payload;
  43. };
  44. /**
  45. * The new type defining a descriptor that stores a PyCapsule. This is used to
  46. * store the runtime data, with the __get__ method returning a new Callable.
  47. */
  48. PyTypeObject *CapsuleMethod_TypeF(void);
  49. /**
  50. * The new type defining a descriptor that stores a PyCapsule. This is used to
  51. * store the runtime data, with the __get__ (and __set__ if isWritable) providing
  52. * property behavior.
  53. */
  54. PyTypeObject *CapsuleProperty_TypeF(bool isWritable);
  55. /**
  56. * Add a capsule method (a descriptor) to a type. This will create a new capsule
  57. * method descriptor and add it as an attribute to the type, using the given name.
  58. *
  59. * A single handle can then respond to what appear to be distinct methods on the
  60. * type, but using the runtime data (from the capsule) when handling each call.
  61. *
  62. * @param type The type to attach the created descriptor to.
  63. * @param method The method definition to associate with the descriptor.
  64. * The name of the method will be used as the attribute name.
  65. * @param capsule The capsule to store in the descriptor.
  66. * @return True if the descriptor was added successfully, false otherwise.
  67. */
  68. bool add_capsule_method_to_type(PyTypeObject *type, PyMethodDef *method,
  69. PyObject *capsule);
  70. /**
  71. * Make a new CapsuleProperty type.
  72. */
  73. PyObject *make_capsule_property(PyMethodDef *method, PyObject *capsule,
  74. bool isWritable = false);
  75. } // extern "C"
  76. #endif // PYSIDE_CAPSULEMETHOD_P_H