I would like to call Python's pickling routines (dumps
and loads
) from within c++ code. Are they exposed in the official API? I am currently calling those via boost::python from c++, looking for a simpler way perhaps.

- 18,545
- 10
- 61
- 110
-
I do not think they can be "exposed" any more than that, they need a running interpreter, you can get the PyObject*s for `dumps` and `loads` and then call them yourselves using the C-API? – Antti Haapala -- Слава Україні Feb 24 '15 at 10:40
-
There is also [How can I read a python pickle database/file from C?](http://stackoverflow.com/q/1296162) – Martijn Pieters Feb 24 '15 at 10:57
3 Answers
You can call any Python code through the C API:
static PyObject *module = NULL;
PyObject *pickle;
if (module == NULL &&
(module = PyImport_ImportModuleNoBlock("pickle")) == NULL)
return NULL;
now, you can either call it like:
pickle = PyObject_CallMethodObjArgs(module,
PyString_AS_STRING("dumps"),
py_object_to_dump,
NULL);
pickle = PyObject_CallMethodObjArgs(module,
PyUnicode_FromString("dumps"),
py_object_to_dump,
NULL);
or like:
picle = PyObject_CallMethod(module, "dumps", "O", py_object_to_dump);
and then do the error checking and clean up:
if (pickle != NULL) { ... }
Py_XDECREF(pickle);
but in the case of pickle
you can just use the cPickle
functions directly. The only problem there is that the cPickle
module (or _pickle
in Python 3) is statically compiled into the Python binary, or needs to be loaded separately. Using the Python import mechanisms is simply easier here.

- 11,726
- 7
- 55
- 77

- 1,048,767
- 296
- 4,058
- 3,343
-
Thanks, I do call Python through the C API (wrapped in boost::python), but I was hoping to call the pickling functions "directly". – eudoxos Feb 24 '15 at 10:46
-
@eudoxos: are you using Python 2 or 3? The C module was renamed to [`_pickle`](https://hg.python.org/cpython/file/3.4/Modules) in Python 3. It depends a little on wether or not the `cPickle` module has been statically compiled or needs to be dynamically loaded here too. – Martijn Pieters Feb 24 '15 at 10:55
-
I want to support both actually. I import _pickle for py3k and cPickle for py2k. – eudoxos Feb 24 '15 at 10:59
-
@eudoxos: it's easiest to just use import here rather than have to figure out if you can use the functions directly or not. – Martijn Pieters Feb 24 '15 at 11:00
Many years later, not quite answering the question, and based on Martijn Pieters' answer, here is a drop in replacement for Marshal:
static PyObject * PyPickle=NULL;
static PyObject * pDumps=NULL;
static PyObject * pLoads=NULL;
static void init_pickle()
{
if(!pDumps)
pDumps = PyUnicode_FromString("dumps");
if(!pLoads)
pLoads = PyUnicode_FromString("loads");
if(!PyPickle)
PyPickle = PyImport_ImportModule("pickle");
}
static PyObject *PyPickle_ReadObjectFromString(const char *data, Py_ssize_t len)
{
PyObject *pstr = PyBytes_FromStringAndSize(data, len);
PyObject *ret=NULL;
ret = PyObject_CallMethodOneArg(PyPickle, pLoads, pstr);
Py_XDECREF(pstr);
return ret;
}
static PyObject *PyPickle_WriteObjectToString(PyObject *value, int version)
{
//version currently ignored
return PyObject_CallMethodOneArg(PyPickle, pDumps, value);
}
Perhaps someone will find it useful.

- 56
- 1
- 4
Complementing the other C-API-based answer by @aflin, this would be in c++ using pybind11 (and the C-API underneath):
Header file:
#include<string>
#include<pybind11/pybind11.h>
namespace py=pybind11;
class Pickler{
static bool initialized;
static py::handle cPickle_dumps;
static py::handle cPickle_loads;
static void ensureInitialized();
public:
static std::string dumps(py::object o);
static py::object loads(const std::string&);
};
Implementation:
py::handle Pickler::cPickle_dumps;
py::handle Pickler::cPickle_loads;
bool Pickler::initialized=false;
void Pickler::ensureInitialized(){
if(initialized) return;
py::gil_scoped_acquire pyLock;
py::object cPickle=py::module::import("pickle");
cPickle_dumps=cPickle.attr("dumps");
cPickle_loads=cPickle.attr("loads");
initialized=true;
}
std::string Pickler::dumps(py::object o){
ensureInitialized();
py::gil_scoped_acquire pyLock;
py::object minus1=py::cast(-1);
// watch out! the object might have NULL pointer (uninitialized??) so make sure to pass None in that case
PyObject *b=PyObject_CallFunctionObjArgs(cPickle_dumps.ptr(),o.ptr()?o.ptr():Py_None,minus1.ptr(),NULL);
if(!b){
if(PyErr_Occurred()) throw py::error_already_set();
else throw std::runtime_error("Pickling failed but no python error was set??");
}
assert(PyBytes_Check(b));
return std::string(py::reinterpret_steal<py::bytes>(b));
}
py::object Pickler::loads(const std::string& s){
ensureInitialized();
py::gil_scoped_acquire pyLock;
return cPickle_loads(py::handle(PyBytes_FromStringAndSize(s.data(),(Py_ssize_t)s.size())));
}
And then, obviously, calling Pickler::dumps(...)
or Pickler::loads(...)
in your code.

- 18,545
- 10
- 61
- 110