Well, as the title says, this time I would like to talk about "I want to put my own structure in the structure created by Python's C extension module". It's crazy, and it's very difficult to understand in terms of words, so I'll write an explanation code.
testcord.py
import myModule
test = myModule.Custom1()
test.a = 200
logging.debug(test.a)
test.t2.a = 400
logging.debug(test.t2.a)
As a requirement, I want to make a C extension module of python, and in it, I want to make an integer type "a" and an integer type "a" in some structure "t2". .. Looking at the variable definition inside in C ++, it looks like the following.
test.cpp
namespace test2 {
class CustomObject{
int a;
}
}
namespace test1 {
class CustomObject{
int a;
test2::CustomObject t2;
}
}
In the test1:: CustomObject structure, a of type int and t2 of the test1:: CustomObject structure exist. (* As a bonus, there is an int type a in testobject2)
The explanation on the above site describes how to make a C extension module in an easy-to-understand manner.
This time, the contents up to "2.2. Add data and methods to the basic sample" will solve the problem.
main.cpp corresponds to the function to be registered as an extension module in python and the contents of the test1:: CustomObject structure written in "I want something that can be used like this!".
main.cpp
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#include <iostream>
#include <string>
#include "test1.hpp"
namespace test1 {
typedef struct {
PyObject_HEAD
int a = 10;
PyObject *t2;
} CustomObject;
static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);
if (self != NULL) {
self->a = 0;
auto obj = (PyObject *) PyObject_Malloc(sizeof(test2::CustomObject));
if (obj == NULL) {
return PyErr_NoMemory();
}
PyObject_Init(obj, &test2::CustomType);
self->t2 = obj;
}
return (PyObject *) self;
}
static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"a", "t2", NULL};
//PyObject *t2 = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
&self->a,&self->t2))
return -1;
return 0;
}
static void
Custom_dealloc(CustomObject *self)
{
Py_XDECREF(self->t2);
Py_TYPE(self)->tp_free((PyObject *) self);
}
static PyObject* c_helloworld(CustomObject *self)
{
self->a = 300;
return Py_None;
}
// myModule definition(python's name)
static PyMethodDef myMethods[] = {
{ "helloworld", (PyCFunction) c_helloworld, METH_NOARGS, "Prints Hello World" },
{ NULL }
};
static PyMemberDef Custom_members[] = {
{"a", T_INT, offsetof(CustomObject, a), 0,
"custom a"},
{"t2", T_OBJECT_EX, offsetof(CustomObject, t2), 0,
"first t2"},
{NULL} /* Sentinel */
};
static PyTypeObject CustomType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Custom1",
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor) Custom_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = "Custom1 objects",
.tp_methods = myMethods,
.tp_members = Custom_members,
.tp_init = (initproc) Custom_init,
.tp_new = Custom_new,
};
}
// myModule definition struct
static struct PyModuleDef myModule = {
PyModuleDef_HEAD_INIT,
"myModule",
"Python3 C API Module(Sample 1)",
-1,
};
// Initializes myModule
PyMODINIT_FUNC PyInit_myModule(void)
{
PyObject *m;
if (PyType_Ready(&test1::CustomType) < 0 || PyType_Ready(&test2::CustomType) < 0)
return NULL;
m = PyModule_Create(&myModule);
if (m == NULL)
return NULL;
Py_INCREF(&test1::CustomType);
Py_INCREF(&test2::CustomType);
PyModule_AddObject(m, "Custom1", (PyObject *) &test1::CustomType);
PyModule_AddObject(m, "Custom2", (PyObject *) &test2::CustomType);
return m;
}
test1.hpp corresponds to the contents of the test2 :: CustomObject structure written in "I want something that can be used like this!".
test1.hpp
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#include <iostream>
#include <string>
namespace test2{
struct CustomObject {
PyObject_HEAD
int a = 10;
CustomObject(){
this->a = 100;
}
~CustomObject(){}
} ;
static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);
if (self != NULL) {
self->a = 0;
//self->t2 =
}
return (PyObject *) self;
}
static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"a", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,
&self->a))
return -1;
return 0;
}
static void
Custom_dealloc(CustomObject *self)
{
Py_TYPE(self)->tp_free((PyObject *) self);
}
static PyObject* c_helloworld(CustomObject *self)
{
self->a = 300;
return Py_None;
}
// myModule definition(python's name)
static PyMethodDef myMethods[] = {
{ "helloworld", (PyCFunction) c_helloworld, METH_NOARGS, "Prints Hello World" },
{ NULL }
};
static PyMemberDef Custom_members[] = {
{"a", T_INT, offsetof(CustomObject, a), 0,
"custom a"},
{NULL}
};
static PyTypeObject CustomType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Custom2",
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor) Custom_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = "Custom2 objects",
.tp_methods = myMethods,
.tp_members = Custom_members,
.tp_init = (initproc) Custom_init,
.tp_new = Custom_new,
};
}
main.cpp
typedef struct {
PyObject_HEAD
int a = 10;
PyObject *t2;
} CustomObject;
Add the PyObject_HEAD macro to the normally created structure. And, t2 is originally planned to be a test2 :: CustomObject structure, but in order to handle it as a C extension module of python, it needs to be PyObject, so it is described as PyObject.
static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);
if (self != NULL) {
self->a = 0;
auto obj = (PyObject *) PyObject_Malloc(sizeof(test2::CustomObject));
if (obj == NULL) {
return PyErr_NoMemory();
}
PyObject_Init(obj, &test2::CustomType);
self->t2 = obj;
}
return (PyObject *) self;
}
test1: It is necessary to allocate memory for CustomObject. The int type can be just initialized, but the original class (test2 :: CustomObjec t2) does not.
Allocate only for the original class with PyObject_Malloc. In PyObject_Init, let us know to use your own class as part of the C extension module. Finally,
self->t2 = obj;
Then, memory allocation is completed as PyObject *.
Others can be written according to the contents of "2. Definition of extension type: Tutorial --Python 3.6.5 documentation". test2 :: CustomObjec can be written in the same way as a normal C extension module.
It was a difficult time because I couldn't understand all this. I couldn't understand it even if I looked at various sites, and after all, when I read the source code sentence of "cpython / Objects / unicodeobject.c" of "python / cpython github", it was solved immediately. The sentence "PyUnicode_FromString (" ");" in the commentary says that it only allocates memory ... It took a long time to know this much ...
Oware.
I'm using python with mackerel, but it took more than 3 seconds to process, which was a dangerous bottleneck, so I decided to make a C extension module to solve it. The people who make numpy are amazing. I can't sleep with my legs turned. I say python god, but I think it's better to say python god after knowing the person who wrote C behind the scenes with the intention of dying.
Recommended Posts