From 2640c950ca1e7cd45ebce4a17448462db326bb5f Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 4 Aug 2017 10:42:39 -0400 Subject: [PATCH] Stash std::strings used for tp_name in internals Types need `tp_name` set to a C-style string, but the current `strdup` ends up with a leak (issue #977). This avoids the strdup by storing the `std::string` in internals so that during interpreter shutdown it will be properly destroyed. --- include/pybind11/class_support.h | 9 ++++----- include/pybind11/common.h | 11 +++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/pybind11/class_support.h b/include/pybind11/class_support.h index 8e18c4c6..287412d9 100644 --- a/include/pybind11/class_support.h +++ b/include/pybind11/class_support.h @@ -518,12 +518,11 @@ inline PyObject* make_new_python_type(const type_record &rec) { module = rec.scope.attr("__name__"); } + auto full_name = c_str( #if !defined(PYPY_VERSION) - const auto full_name = module ? str(module).cast() + "." + rec.name - : std::string(rec.name); -#else - const auto full_name = std::string(rec.name); + module ? str(module).cast() + "." + rec.name : #endif + rec.name); char *tp_doc = nullptr; if (rec.doc && options::show_user_defined_docstrings()) { @@ -556,7 +555,7 @@ inline PyObject* make_new_python_type(const type_record &rec) { #endif auto type = &heap_type->ht_type; - type->tp_name = strdup(full_name.c_str()); + type->tp_name = full_name; type->tp_doc = tp_doc; type->tp_base = type_incref((PyTypeObject *)base); type->tp_basicsize = static_cast(sizeof(instance)); diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 518e2456..50c09592 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -494,6 +494,7 @@ struct internals { std::forward_list registered_exception_translators; std::unordered_map shared_data; // Custom data to be shared across extensions std::vector loader_patient_stack; // Used by `loader_life_support` + std::forward_list static_strings; // Stores the std::strings backing detail::c_str() PyTypeObject *static_property_type; PyTypeObject *default_metaclass; PyObject *instance_base; @@ -688,6 +689,16 @@ using expand_side_effects = bool[]; #define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } #endif +/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its +/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only +/// cleared when the program exits or after interpreter shutdown (when embedding), and so are +/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). +template const char *c_str(Args &&...args) { + auto &strings = get_internals().static_strings; + strings.emplace_front(std::forward(args)...); + return strings.front().c_str(); +} + NAMESPACE_END(detail) /// Returns a named pointer that is shared among all extension modules (using the same