You would ideally write a simple python module like the following,
#include <Python.h>
struct State {
unsigned int bottom[7];
unsigned int top[7];
int turn;
};
struct PyState {
PyObject_HEAD
struct State *internal;
};
static void PyState_free(PyObject *self);
static PyMethodDef py_mygamestate_module_methods[] = {{NULL, NULL, 0, NULL}};
static struct PyModuleDef py_mygamestate_module = {
PyModuleDef_HEAD_INIT,
/* name of module */
"mygamestate",
/* module documentation, may be NULL */
NULL,
/* size of per-interpreter state of the module, or -1
* if the module keeps state in global variables.
*/
-1,
py_mygamestate_module_methods,
NULL,
NULL,
NULL,
NULL
};
static PyObject *py_state_show(PyObject *self, PyObject *args);
static PyMethodDef py_state_methods[] = {
{"show", py_state_show, METH_NOARGS, NULL},
{NULL, NULL, 0, NULL}
};
static PyObject *py_state_new(PyTypeObject *type, PyObject *parent, PyObject *args);
#define Py_BASE_TYPE_FLAGS (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE)
static PyTypeObject py_state_type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"State", /* tp_name */
sizeof(struct PyState), /* tp_basicsize */
0, /* tp_itemsize */
PyState_free, /* 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 */
Py_BASE_TYPE_FLAGS, /* tp_flags */
"Docstring", /* tp_doc */
0, /* tp_travers */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_next */
py_state_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
&PyBaseObject_Type, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
py_state_new, /* 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 */
};
static void
PyState_free(PyObject *self)
{
free(((struct PyState *) self)->internal);
}
static PyObject *
py_state_new(PyTypeObject *type, PyObject *parent, PyObject *args)
{
struct PyState *state;
PyObject *self;
self = PyType_GenericNew(type, parent, args);
if (self == NULL)
return NULL;
// Cast the object to the appropriate type
state = (struct PyState *) self;
// Initialize your internal structure
state->internal = malloc(sizeof(*state->internal));
if (state->internal == NULL)
return NULL;
memset(state->internal, 0, sizeof(*state->internal));
// This means no error occurred
return self;
}
static PyObject *
py_state_show(PyObject *self, PyObject *args)
{
struct State *state;
// Cast the object to the appropriate type
state = ((struct PyState *) self)->internal;
if (state == NULL)
return NULL;
fprintf(stdout, "bottom: ");
for (size_t i = 0; i < 7; ++i)
fprintf(stdout, "%d, ", state->bottom[i]);
fprintf(stdout, "top: ");
for (size_t i = 0; i < 7; ++i)
fprintf(stdout, "%d, ", state->bottom[i]);
fprintf(stdout, "turn: %d\n", state->turn);
return self;
}
PyObject *
PyInit_mygamestate(void)
{
PyObject *module;
// Prepare the base classes to add them
if (PyType_Ready(&py_state_type) < 0)
return NULL;
// Create the apache module
module = PyModule_Create(&py_mygamestate_module);
if (module == NULL)
return NULL;
// Add the base classes
PyModule_AddObject(module, "State", (PyObject *) &py_state_type);
return module;
}
note that the name of the module dll or so file should match the part after the underscore in PyInit_mygamestate.
Now if you install the so file to the site-packages directory, then from python you can do this
import mygamestate
state = mygamestate.State()
state.show()
That way you can have any type, as a python type and c type at the same time.
You can of course grow the py_state_methods array to the size you want, and have any methods that would be useful from within the python code. You can also pass parameters to the constructor and to each method, and so on.
There is another array, namely py_mygamestate_module_methods that will be methods accessible directly to the module in the python code.
Note: The orignal code was modified, so now it allows inheritance and you can do something like this
from mygamestate import State
class CustomState(State):
def __init__(self):
pass
# add all your custom methods here
def sample_method(self):
print('Cool, it works')
state = CustomState()
state.show()
state.sample_method()
The changes were,
Add the Py_TPFLAGS_BASETYPE to the tp_flags member. This allows the type to be subclassable, but then the tp_init function is no longer used, and the initialization needs to be performed in tp_new instead — I have no idea why it is like that, IMHO it's stupid —.
Remove the py_state_init function and instead create a py_state_new function, replacing the PyType_GenericNew function for the tp_new member of the PyTypeObject instance that is our custom type.
We call the original PyType_GenericNew() to create our type object and then, perform what the old py_state_init() did to initialize our internal structure data.
By inheriting from this class, you can have both things that you wish.
15int(after all there are no unsigned integers in python AFAIK) items and interpret it in c the way you need, or simple pass a pointer back and fort. Are you familiar with python extensions in c?PyObjectyou can use like if it was a OO class, and "extend it".PyObjectpointer for python objects, but it's not clear to me how I would use it to read my byte data as a C struct. Creating a newStateobject in C isn't what I want to do. If for example I had a python bytestring in the format'clf?'how could I read that in C as astruct x{ char, long, float, bool}?