While working with an embedded Python interpreter in a C++ program, i stumbled accross an annoying issue. In the docs, under the "Extending and Embedding the Python Interpreter" chapter [link], we are told we can define new PyTypeObjects the following way:



static PyTypeObject CustomType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Custom",
.tp_doc = "Custom objects",
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
};


But if you try that in your own C++ program it won't compile. That is because the syntax you see with a point "." before the designated struct member name is a specifity of C99 which is called Designated Initializer. This is a feature specification that C++ doesn't comply with, although it seems to be planned for C++20 [link]

In order to instanciate new PyTypeObject objects, you then have to deal with the classic fixed order Initializer List for C++ struct, but for that you have to specify all members in the right order. Fortunately it is not hard to check in the Python sources the definition of the PyTypeObject struct and check all the fields.
Here is the full list as of Python 3.6 for future reuse and for reference.

tp_name
tp_basicsize
tp_itemsize
tp_dealloc
tp_print
tp_getattr
tp_setattr
tp_as_async
tp_repr
tp_as_number
tp_as_sequence
tp_as_mapping
tp_hash
tp_call
tp_str
tp_getattro
tp_setattro
tp_as_buffer
tp_flags
tp_doc
tp_traverse
tp_clear
tp_richcompare
tp_weaklistoffset
tp_iter
tp_iternext
tp_methods
tp_members
tp_getset
tp_base
tp_dict
tp_descr_get
tp_descr_set
tp_dictoffset
tp_init
tp_alloc
tp_new
tp_free
tp_is_gc
tp_bases
tp_mro
tp_cache
tp_subclasses
tp_weaklist
tp_del
tp_version_tag
tp_finalize

Hopefully the type of the fields does not really matter for us here. We then have to instanciate new PyTypeObjects like that:

static PyTypeObject CustomType = {
PyVarObject_HEAD_INIT(NULL, 0)
0, /* tp_name */
0, /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
0, /* 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 */
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 */
0, /* 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 */
0, /* tp_version_tag */
0, /* tp_finalize */
};

Setting all values to 0 which is the default behaviour for unspecified Designated Fields anyways, only overriding the fields we are interested in, such as the following in the minimal sample case:
.tp_name
.tp_doc
.tp_basicsize
.tp_itemsize
.tp_flags
.tp_new
Custom Types Tip When Extending or Embedding Python in a C++ Application
Date Created:20/06/2018
Date Posted:20/06/2018
Type: Programming
Technique/Tools: C/C++, CPython, Python, Python C API
Small tip for people working with the CPython API and C++