patternpythonMinor
Hello World using Python embedded in C++, with RAII library initialization
Viewed 0 times
raiiembeddedwithworldpythonusinglibraryhelloinitialization
Problem
I have written a small Hello World application that uses Python's C-API. The C library requires some functions to be called for global library (de-)initialization and the objects created by the library eventually need to be released by passing the pointer to a specific function. I think this is a fairly common pattern in C libraries.
I attempted to use RAII, to get automatic cleanup, even when exceptions interfere with the planned control flow.
Compiles and runs with:
I'm interested in all kinds of feedback, as to whether my attempt makes sense and if there is room for improvement. Are there best practices, or certain idioms that facilitate this, that I might have missed in my approach?
I attempted to use RAII, to get automatic cleanup, even when exceptions interfere with the planned control flow.
#include
#include
// helper to get function pointer for the macro
void Python_DecreaseRefCount(PyObject* p) {
Py_DECREF(p);
}
int main() {
// declare struct and create instance that handles global initialization and
// deinitialization of Python API
static struct PythonApi {
PythonApi() { Py_Initialize(); }
~PythonApi() { Py_Finalize(); }
} python_api;
// create object
// use smart pointer for automatic refcount decrement at scope end
std::tr1::shared_ptr string(
PyString_FromString("Hello World!\n"),
&Python_DecreaseRefCount);
// do something with object
PyObject_Print(string.get(), stdout, Py_PRINT_RAW);
}Compiles and runs with:
$ g++ test.cc pkg-config --cflags --libs python2 && ./a.out
Hello World!
I'm interested in all kinds of feedback, as to whether my attempt makes sense and if there is room for improvement. Are there best practices, or certain idioms that facilitate this, that I might have missed in my approach?
Solution
-
Are you sure also scoping de-initialization of Python to
As-is, you cannot use Python in a globals initializer or destructor anyway.
Having taken a look at
This function is provided for a number of reasons. An embedding application might want to restart Python without having to restart the application itself. An application that has loaded the Python interpreter from a dynamically loadable library (or DLL) might want to free all memory allocated by Python before unloading the DLL. During a hunt for memory leaks in an application a developer might want to free all memory allocated by Python before exiting from the application
Bugs and caveats: [... lots ...]
-
Next, you shouldn't be using a function-pointer, use an empty function-object instead.
Using a function-object facilitates inlining and as it's empty, reduces memory-overhead.
-
Actually, are you sure you actually need / want a
Either because you are already irretrievably locked into using that type, or because you actually might need weak pointers somewhere?
If the answer is no, look into
Are you sure also scoping de-initialization of Python to
main (by removing static) is not an option?As-is, you cannot use Python in a globals initializer or destructor anyway.
Having taken a look at
Py_Finalize, I actually would not deinitialize it at all, at least not in a release-build, as none of the reasons that option is provided apply to you:This function is provided for a number of reasons. An embedding application might want to restart Python without having to restart the application itself. An application that has loaded the Python interpreter from a dynamically loadable library (or DLL) might want to free all memory allocated by Python before unloading the DLL. During a hunt for memory leaks in an application a developer might want to free all memory allocated by Python before exiting from the application
Bugs and caveats: [... lots ...]
-
Next, you shouldn't be using a function-pointer, use an empty function-object instead.
Using a function-object facilitates inlining and as it's empty, reduces memory-overhead.
-
Actually, are you sure you actually need / want a
std::shared_ptr?Either because you are already irretrievably locked into using that type, or because you actually might need weak pointers somewhere?
If the answer is no, look into
boost::intrusive_ptr, as that will save you an allocation every time you hand a PyObject to a smart-pointer's oversight, and reduces smartpointer-size by half, to raw-pointer-size.// Hooks in same scope as PyObject (global scope), so ADL in intrusive_ptr finds them:
void intrusive_ptr_add_ref(PyObject* p) noexcept { Py_INCREF(p); }
void intrusive_ptr_release(PyObject* p) noexcept { Py_DECREF(p); }
// typedef for ease of use:
typedef boost::intrusive_ptr PyPtr;
// Use as:
PyPtr string(PyString_FromString("Hello World!\n"), false);Code Snippets
// Hooks in same scope as PyObject (global scope), so ADL in intrusive_ptr finds them:
void intrusive_ptr_add_ref(PyObject* p) noexcept { Py_INCREF(p); }
void intrusive_ptr_release(PyObject* p) noexcept { Py_DECREF(p); }
// typedef for ease of use:
typedef boost::intrusive_ptr<PyObject> PyPtr;
// Use as:
PyPtr string(PyString_FromString("Hello World!\n"), false);Context
StackExchange Code Review Q#120525, answer score: 3
Revisions (0)
No revisions yet.