@@ -116,20 +116,36 @@ namespace cpp2py {
116116
117117 // default version is that the type is wrapped.
118118 // Will be specialized for type which are just converted.
119- template <typename T> struct py_converter {
119+ template <typename TUREF> struct py_converter {
120+
121+ using T = std::decay_t <TUREF>;
122+ static constexpr bool is_ref = std::is_reference_v<TUREF>;
120123
121124 typedef struct {
122125 PyObject_HEAD;
123126 T *_c;
127+ PyObject *parent = nullptr ;
124128 } py_type;
125129
126130 using is_wrapped_type = void ; // to recognize
127131
128- template <typename U> static PyObject *c2py (U &&x) {
132+ template <typename U> static PyObject *c2py (U &&x, PyObject *parent = nullptr ) {
129133 PyTypeObject *p = get_type_ptr (typeid (T));
130134 if (p == nullptr ) return NULL ;
131135 py_type *self = (py_type *)p->tp_alloc (p, 0 );
132- if (self != NULL ) { self->_c = new T{std::forward<U>(x)}; }
136+ if (self != NULL ) {
137+ if constexpr (is_ref && wrapped_members_as_shared_refs) {
138+ // Keep parent alive for lifetime of self
139+ if (parent != nullptr ) {
140+ self->parent = parent;
141+ Py_INCREF (parent);
142+ self->_c = &x;
143+ return (PyObject *)self;
144+ }
145+ }
146+ // Create heap copy of x to guarantee lifetime
147+ self->_c = new T{std::forward<U>(x)};
148+ }
133149 return (PyObject *)self;
134150 }
135151
@@ -147,7 +163,7 @@ namespace cpp2py {
147163 if (p == nullptr ) return false ;
148164 if (PyObject_TypeCheck (ob, p)) {
149165 if (((py_type *)ob)->_c != NULL ) return true ;
150- auto err = std::string{" Severe internal error : Python object of " } + p->tp_name + " has a _c NULL pointer !!" ;
166+ auto err = std::string{" Severe internal error : Python object of " } + p->tp_name + " has a _c NULL pointer !!" ;
151167 if (raise_exception) PyErr_SetString (PyExc_TypeError, err.c_str ());
152168 return false ;
153169 }
@@ -157,6 +173,12 @@ namespace cpp2py {
157173 }
158174 };
159175
176+ // is_wrapped<T> if py_converter has been reimplemented.
177+ template <typename T, class = void > struct is_wrapped : std::false_type {};
178+ template <typename T> struct is_wrapped <T, typename py_converter<T>::is_wrapped_type> : std::true_type {};
179+
180+ template <typename T> constexpr bool is_wrapped_v = is_wrapped<T>::value;
181+
160182 // helpers for better error message
161183 // some class (e.g. range !) only have ONE conversion, i.e. C -> Py, but not both
162184 // we need to distinguish
@@ -168,9 +190,15 @@ namespace cpp2py {
168190 struct does_have_a_converterC2Py <T, std::void_t <decltype (py_converter<std::decay_t <T>>::c2py(std::declval<T>()))>> : std::true_type {};
169191
170192 // We only use these functions in the code, not directly the converter
171- template <typename T> static PyObject *convert_to_python (T &&x) {
193+ template <typename T> static PyObject *convert_to_python (T &&x, PyObject *parent = nullptr ) {
172194 static_assert (does_have_a_converterC2Py<T>::value, " The type does not have a converter from C++ to Python" );
173- return py_converter<std::decay_t <T>>::c2py (std::forward<T>(x));
195+ PyObject *r;
196+ if constexpr (is_wrapped_v<std::decay_t <T>>) {
197+ r = py_converter<T>::c2py (std::forward<T>(x), parent);
198+ } else { // Converted type
199+ r = py_converter<std::decay_t <T>>::c2py (std::forward<T>(x));
200+ }
201+ return r;
174202 }
175203 template <typename T> static bool convertible_from_python (PyObject *ob, bool raise_exception) {
176204 return py_converter<T>::is_convertible (ob, raise_exception);
@@ -188,12 +216,6 @@ namespace cpp2py {
188216 *
189217 */
190218
191- // is_wrapped<T> if py_converter has been reimplemented.
192- template <typename T, class = void > struct is_wrapped : std::false_type {};
193- template <typename T> struct is_wrapped <T, typename py_converter<T>::is_wrapped_type> : std::true_type {};
194-
195- template <typename T> constexpr bool is_wrapped_v = is_wrapped<T>::value;
196-
197219 template <typename T> static auto convert_from_python (PyObject *ob) -> decltype(py_converter<T>::py2c(ob)) {
198220 static_assert (does_have_a_converterPy2C<T>::value, " The type does not have a converter from Python to C++" );
199221 return py_converter<T>::py2c (ob);
0 commit comments