I have a singleton class in C++ (no public constructor, C++ programmers call class.instance() to create the singleton or return the existing one).
I'd prefer to hide this at the Python level. If I was writing a Python singleton, I'd handle that in __new__
. If a class has no public constructor I don't think I can create an __init__
wrapper (my attempts at that have failed). I saw no mention of __new__
in the pybind11 docs (though might have missed it, and Google seems happy to elide underscores an return pages containing "new", with no mention of __new__
).
Is there a singleton recipe for pybind11 (or even Boost.Python)?
You don't need to expose __init__
if you don't instantiate your class from Python. As for your question, you can try something like this:
py::class_<CppSingle>(mod, "Single")
.def_static("__new__", [](py:object) { return CppSingle::instance(); )},
py::return_value_policy::reference_internal);
I'm not sure about pybind11, but I believe it should be possible to wrap your class using Boost.Python. (Your question reads "or even Boost.Python"...)
Use noncopyable
and/or no_init
:
class_<Klass, boost::noncopyable>("Klass", no_init)
.staticmethod("instance")
;
https://mail.python.org/pipermail/cplusplus-sig/2004-March/006647.html
assuming your singleton class looks like this:
class MySingleton{
// private constructor here
public:
MySingleton& instance();
};
You could wrap it up like so:
py::class_<MySingleton, std::unique_ptr<MySingleton, py::nodelete>>(m, "MySingleton")
.def(py::init([](){
return std::unique_ptr<MySingleton, py::nodelete>>(&MySingleton::instance());
});
Key here is to use py::nodelete
so the destructor is not referenced (and your c++ singleton is not destructed when the unique_ptrs used by multiple python instances are garbage-collected).
That code also relies on the custom constructor support introduced with pybind11 v2.2.0 (August 31st, 2017) which allows us to wrap a lambda instead of a constructor inside the init.
References
- Pybind11 v2.2.0 changelog
- Pybind11 doc about custom constructors
- Pybind11 doc on non-public destructors