#ifndef HPY_UNIVERSAL_HPYTYPE_H
#define HPY_UNIVERSAL_HPYTYPE_H
#include <stdbool.h>
#ifdef __GNUC__
#define HPyAPI_UNUSED __attribute__((unused)) static inline
#else
#define HPyAPI_UNUSED static inline
#endif /* __GNUC__ */
// NOTE: HPyType_BuiltinShape_Object == 0, which means it's the default
// if it's not specified in HPyType_spec
[docs]typedef enum {
[docs] /**
* A type whose struct starts with ``PyObject_HEAD`` or equivalent is a
* legacy type. A legacy type must set
* ``.builtin_shape = HPyType_BuiltinShape_Legacy`` in its
* :c:struct:`HPyType_Spec`.
*
* A type is a non-legacy type, also called HPy pure type, if its struct does
* not include ``PyObject_HEAD``. Using pure types should be preferred.
* Legacy types are available to allow gradual porting of existing CPython
* extensions.
*
* A type with ``.legacy_slots != NULL``
* (see :c:member:`HPyType_Spec.legacy_slots`) is required to have
* ``HPyType_BuiltinShape_Legacy`` and to include ``PyObject_HEAD`` at the
* start of its struct. It would be easy to relax this requirement on
* CPython (where the ``PyObject_HEAD`` fields are always present) but a
* large burden on other implementations (e.g. PyPy, GraalPy) where a
* struct starting with ``PyObject_HEAD`` might not exist.
*
* Types created via the old Python C API are automatically legacy types.
*/
HPyType_BuiltinShape_Legacy = -1,
[docs] /** The type inherits from built-in type ``object`` (default). */
HPyType_BuiltinShape_Object = 0,
[docs] /**
* The type inherits from built-in type ``type``. This can be used to
* create metaclasses. If using this shape, you need to specify base class
* ``ctx->h_TypeType``.
*/
HPyType_BuiltinShape_Type = 1,
[docs] /**
* The type inherits from built-in type ``int`` (aka. long object). If using
* this shape, you need to specify base class ``ctx->h_LongType``.
*/
HPyType_BuiltinShape_Long = 2,
[docs] /**
* The type inherits from built-in type ``float``. If using this shape, you
* need to specify base class ``ctx->h_FloatType``.
*/
HPyType_BuiltinShape_Float = 3,
[docs] /**
* The type inherits from built-in type ``str`` (aka. unicode object). If
* using this shape, you need to specify base class ``ctx->h_UnicodeType``.
*/
HPyType_BuiltinShape_Unicode = 4,
[docs] /**
* The type inherits from built-in type ``tuple``. If using this shape, you
* need to specify base class ``ctx->h_TupleType``.
*/
HPyType_BuiltinShape_Tuple = 5,
[docs] /**
* The type inherits from built-in type ``list``. If using this shape, you
* need to specify base class ``ctx->h_ListType``.
*/
HPyType_BuiltinShape_List = 6,
} HPyType_BuiltinShape;
typedef struct {
cpy_vectorcallfunc cpy_trampoline;
HPyCallFunction impl;
} HPyType_Vectorcall;
[docs]typedef struct {
[docs] /** The Python name of type (UTF-8 encoded) */
const char* name;
[docs] /**
* The size in bytes of the types associated native structure. Usually, you
* define some C structure, e.g., ``typedef struct { int a; } MyObject;``,
* and then this field is set to ``sizeof(MyObject)``.
*/
int basicsize;
[docs] /** The size of embedded elements (currently not supported). */
int itemsize;
[docs] /**
* Type flags (see :c:macro:`HPy_TPFLAGS_DEFAULT`,
* :c:macro:`HPy_TPFLAGS_BASETYPE`, :c:macro:`HPy_TPFLAGS_HAVE_GC`, and
* others if available).
*/
unsigned long flags;
[docs] /**
* The internal *shape* of the type.
*
* The shape gives the necessary hint to compute the offset to the data
* pointer of the object's underlying struct that should be returned when
* calling ``MyObject_AsStruct``.
*
* **ATTENTION**:
* It is also necessary to specify the right base class in
* the type's specification parameters (see :c:struct:`HPyType_SpecParam`).
*
* Assuming that the type's C structure is called ``MyObject``, this field
* should be initialized with ``.builtin_shape = SHAPE(MyObject)``. Note:
* This requires that you use :c:macro:`HPyType_HELPERS` or
* :c:macro:`HPyType_LEGACY_HELPERS`.
*
* Some more explanation: It would be possible to reduce this information
* to a Boolean that specifies if the type is a *legacy* type or not.
* Everything else could be determined by looking at the base classes.
* However, with this information it is possible to do the data pointer
* computation statically and thus is performance critical.
*
* Types that do not define a struct of their own, should set the value of
* ``.builtin_shape`` to the same value as the type they inherit from. If
* they inherit from a built-in type, they must set the corresponding
* ``.builtin_shape``.
*/
HPyType_BuiltinShape builtin_shape;
[docs] /**
* Pointer to a ``NULL``-terminated array of legacy (i.e. ``PyType_Slot``)
* slots.
*
* A type with ``.legacy_slots != NULL`` is required to have
* ``HPyType_BuiltinShape_Legacy`` and to include ``PyObject_HEAD`` at the
* start of its struct. It would be easy to relax this requirement on
* CPython (where the ``PyObject_HEAD`` fields are always present) but a
* large burden on other implementations (e.g. PyPy, GraalPy) where a
* struct starting with ``PyObject_HEAD`` might not exist.
*/
void *legacy_slots;
[docs] /**
* Pointer to a ``NULL``-terminated array of pointers to HPy defines (i.e.
* ``HPyDef *``).
*/
HPyDef **defines;
[docs] /** Docstring of the type (UTF-8 encoded; may be ``NULL``) */
const char* doc;
} HPyType_Spec;
[docs]typedef enum {
[docs] /**
* Specify a base class. This parameter may be repeated but cannot be used
* together with
* :c:enumerator:`HPyType_SpecParam_Kind.HPyType_SpecParam_BasesTuple`.
*/
HPyType_SpecParam_Base = 1,
[docs] /**
* Specify a tuple of base classes. Cannot be used together with
* :c:enumerator:`HPyType_SpecParam_Kind.HPyType_SpecParam_Base`
*/
HPyType_SpecParam_BasesTuple = 2,
//HPyType_SpecParam_Module = 4,
} HPyType_SpecParam_Kind;
[docs]typedef struct {
[docs] /** The kind of the type spec param. */
HPyType_SpecParam_Kind kind;
[docs] /** The value of the type spec param (an HPy handle). */
HPy object;
} HPyType_SpecParam;
/* All types are dynamically allocated */
#define _Py_TPFLAGS_HEAPTYPE (1UL << 9)
#define _Py_TPFLAGS_HAVE_VERSION_TAG (1UL << 18)
[docs]/** Default type flags for HPy types. */
#define HPy_TPFLAGS_DEFAULT (_Py_TPFLAGS_HEAPTYPE | _Py_TPFLAGS_HAVE_VERSION_TAG)
/** Set if the type implements the vectorcall protocol (PEP 590) */
#define HPy_TPFLAGS_HAVE_VECTORCALL (1UL << 11)
/** Set if the type allows subclassing */
#define HPy_TPFLAGS_BASETYPE (1UL << 10)
[docs]/** Set if the type allows subclassing */
#define HPy_TPFLAGS_BASETYPE (1UL << 10)
[docs]/** If set, the object will be tracked by CPython's GC. Probably irrelevant for
GC-based alternative implementations. */
#define HPy_TPFLAGS_HAVE_GC (1UL << 14)
[docs]/** Convenience macro which is equivalent to:
``HPyType_HELPERS(TYPE, HPyType_BuiltinShape_Legacy)`` */
#define HPyType_LEGACY_HELPERS(TYPE) \
HPyType_HELPERS(TYPE, HPyType_BuiltinShape_Legacy)
#define _HPyType_HELPER_TYPE(TYPE, ...) TYPE *
#define _HPyType_HELPER_FNAME(TYPE, ...) TYPE##_AsStruct
#define _HPyType_HELPER_DEFINE_SHAPE(TYPE, SHAPE, ...) \
enum { TYPE##_SHAPE = (int)SHAPE }
#define _HPyType_HELPER_AS_STRUCT(TYPE, SHAPE, ...) SHAPE##_AsStruct
// helper macro make MSVC's preprocessor work with our variadic macros
#define _HPyType_HELPER_X(X) X
[docs]/**
A macro for creating (static inline) helper functions for custom types.
Two versions of the helper exist. One for legacy types and one for pure
HPy types.
Example for a pure HPy custom type:
``HPyType_HELPERS(PointObject)``
It is also possible to inherit from some built-in types. The list of
available built-in base types is given in enum `HPyTupe_BuiltinShape`.
In case you want to inherit from one of those, it is necessary to specify
the base built-in type in the `HPyType_HELPERS` macro. Here is an example
for a pure HPy custom type inheriting from a built-in type 'tuple':
``HPyType_HELPERS(PointObject, HPyType_BuiltinShape_Tuple)``
This would generate the following:
* ``PointObject * PointObject_AsStruct(HPyContext *ctx, HPy h)``: a static
inline function that uses HPy_AsStruct to return the PointObject struct
associated with a given handle. The behaviour is undefined if `h`
is associated with an object that is not an instance of PointObject.
However, debug mode will catch an incorrect usage.
* ``SHAPE(PointObject)``: a macro that is meant to be used as static
initializer in the corresponding HPyType_Spec. It is recommended to write
``.builtin_shape = SHAPE(PointObject)`` such that you don't have to
remember to update the spec when the helpers used changes.
Example for a legacy custom type:
``HPyType_LEGACY_HELPERS(PointObject)``
This would generate the same functions and constants as above, except:
* ``_HPy_AsStruct_Legacy`` is used instead of ``_HPy_AsStruct_Object``.
* ``SHAPE(PointObject)`` would be ``HPyType_BuiltinShape_Legacy``.
:param STRUCT: The C structure of the HPy type.
:param SHAPE: Optional. The built-in shape of the type. This defaults to
:c:enumerator:`HPyType_BuiltinShape_Object`. Possible values
are all enumerators of :c:enum:`HPyType_BuiltinShape`.
*/
#define HPyType_HELPERS(...) \
\
_HPyType_HELPER_X( \
_HPyType_HELPER_DEFINE_SHAPE(__VA_ARGS__, HPyType_BuiltinShape_Object)); \
\
HPyAPI_UNUSED _HPyType_HELPER_X(_HPyType_HELPER_TYPE(__VA_ARGS__)) \
_HPyType_HELPER_X(_HPyType_HELPER_FNAME(__VA_ARGS__))(HPyContext *ctx, HPy h) \
{ \
return (_HPyType_HELPER_X(_HPyType_HELPER_TYPE(__VA_ARGS__))) \
_HPyType_HELPER_X(_HPyType_HELPER_AS_STRUCT(__VA_ARGS__, \
HPyType_BuiltinShape_Object))(ctx, h); \
}
#define SHAPE(TYPE) ((HPyType_BuiltinShape)TYPE##_SHAPE)
#define HPyType_BuiltinShape_Legacy_AsStruct _HPy_AsStruct_Legacy
#define HPyType_BuiltinShape_Object_AsStruct _HPy_AsStruct_Object
#define HPyType_BuiltinShape_Type_AsStruct _HPy_AsStruct_Type
#define HPyType_BuiltinShape_Long_AsStruct _HPy_AsStruct_Long
#define HPyType_BuiltinShape_Float_AsStruct _HPy_AsStruct_Float
#define HPyType_BuiltinShape_Unicode_AsStruct _HPy_AsStruct_Unicode
#define HPyType_BuiltinShape_Tuple_AsStruct _HPy_AsStruct_Tuple
#define HPyType_BuiltinShape_List_AsStruct _HPy_AsStruct_List
#endif /* HPY_UNIVERSAL_HPYTYPE_H */