mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 13:15:12 +00:00
Add support custom sized operator deletes (#952)
If a class doesn't provide a `T::operator delete(void *)` but does have a `T::operator delete(void *, size_t)` the latter is invoked by a `delete someT`. Pybind currently only look for and call the former; this commit adds detection and calling of the latter when the former doesn't exist.
This commit is contained in:
parent
7c0e2c247b
commit
a03408c839
@ -906,11 +906,19 @@ void set_operator_new(type_record *r) { r->operator_new = &T::operator new; }
|
||||
|
||||
template <typename> void set_operator_new(...) { }
|
||||
|
||||
template <typename T, typename SFINAE = void> struct has_operator_delete : std::false_type { };
|
||||
template <typename T> struct has_operator_delete<T, void_t<decltype(static_cast<void (*)(void *)>(T::operator delete))>>
|
||||
: std::true_type { };
|
||||
template <typename T, typename SFINAE = void> struct has_operator_delete_size : std::false_type { };
|
||||
template <typename T> struct has_operator_delete_size<T, void_t<decltype(static_cast<void (*)(void *, size_t)>(T::operator delete))>>
|
||||
: std::true_type { };
|
||||
/// Call class-specific delete if it exists or global otherwise. Can also be an overload set.
|
||||
template <typename T, typename = void_t<decltype(static_cast<void (*)(void *)>(T::operator delete))>>
|
||||
void call_operator_delete(T *p) { T::operator delete(p); }
|
||||
template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0>
|
||||
void call_operator_delete(T *p, size_t) { T::operator delete(p); }
|
||||
template <typename T, enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int> = 0>
|
||||
void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); }
|
||||
|
||||
inline void call_operator_delete(void *p) { ::operator delete(p); }
|
||||
inline void call_operator_delete(void *p, size_t) { ::operator delete(p); }
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
@ -1212,7 +1220,7 @@ private:
|
||||
if (v_h.holder_constructed())
|
||||
v_h.holder<holder_type>().~holder_type();
|
||||
else
|
||||
detail::call_operator_delete(v_h.value_ptr<type>());
|
||||
detail::call_operator_delete(v_h.value_ptr<type>(), v_h.type->type_size);
|
||||
}
|
||||
|
||||
static detail::function_record *get_function_record(handle h) {
|
||||
|
@ -184,6 +184,46 @@ TEST_SUBMODULE(class_, m) {
|
||||
auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
|
||||
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, nullptr, m.ptr()));
|
||||
}());
|
||||
|
||||
// test_operator_new_delete
|
||||
struct HasOpNewDel {
|
||||
std::uint64_t i;
|
||||
static void *operator new(size_t s) { py::print("A new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("A placement-new", s); return ptr; }
|
||||
static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); }
|
||||
};
|
||||
struct HasOpNewDelSize {
|
||||
std::uint32_t i;
|
||||
static void *operator new(size_t s) { py::print("B new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("B placement-new", s); return ptr; }
|
||||
static void operator delete(void *p, size_t s) { py::print("B delete", s); return ::operator delete(p); }
|
||||
};
|
||||
struct AliasedHasOpNewDelSize {
|
||||
std::uint64_t i;
|
||||
static void *operator new(size_t s) { py::print("C new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; }
|
||||
static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); }
|
||||
virtual ~AliasedHasOpNewDelSize() = default;
|
||||
};
|
||||
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
|
||||
PyAliasedHasOpNewDelSize() = default;
|
||||
PyAliasedHasOpNewDelSize(int) { }
|
||||
std::uint64_t j;
|
||||
};
|
||||
struct HasOpNewDelBoth {
|
||||
std::uint32_t i[8];
|
||||
static void *operator new(size_t s) { py::print("D new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("D placement-new", s); return ptr; }
|
||||
static void operator delete(void *p) { py::print("D delete"); return ::operator delete(p); }
|
||||
static void operator delete(void *p, size_t s) { py::print("D wrong delete", s); return ::operator delete(p); }
|
||||
};
|
||||
py::class_<HasOpNewDel>(m, "HasOpNewDel").def(py::init<>());
|
||||
py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>());
|
||||
py::class_<HasOpNewDelBoth>(m, "HasOpNewDelBoth").def(py::init<>());
|
||||
py::class_<AliasedHasOpNewDelSize, PyAliasedHasOpNewDelSize> aliased(m, "AliasedHasOpNewDelSize");
|
||||
aliased.def(py::init<>());
|
||||
aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize));
|
||||
aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize));
|
||||
}
|
||||
|
||||
template <int N> class BreaksBase {};
|
||||
|
@ -127,3 +127,55 @@ def test_implicit_conversion_life_support():
|
||||
assert m.implicitly_convert_variable(UserType(5)) == 5
|
||||
|
||||
assert "outside a bound function" in m.implicitly_convert_variable_fail(UserType(5))
|
||||
|
||||
|
||||
def test_operator_new_delete(capture):
|
||||
"""Tests that class-specific operator new/delete functions are invoked"""
|
||||
|
||||
class SubAliased(m.AliasedHasOpNewDelSize):
|
||||
pass
|
||||
|
||||
with capture:
|
||||
a = m.HasOpNewDel()
|
||||
b = m.HasOpNewDelSize()
|
||||
d = m.HasOpNewDelBoth()
|
||||
assert capture == """
|
||||
A new 8
|
||||
A placement-new 8
|
||||
B new 4
|
||||
B placement-new 4
|
||||
D new 32
|
||||
D placement-new 32
|
||||
"""
|
||||
sz_alias = str(m.AliasedHasOpNewDelSize.size_alias)
|
||||
sz_noalias = str(m.AliasedHasOpNewDelSize.size_noalias)
|
||||
with capture:
|
||||
c = m.AliasedHasOpNewDelSize()
|
||||
c2 = SubAliased()
|
||||
assert capture == (
|
||||
"C new " + sz_alias + "\nC placement-new " + sz_noalias + "\n" +
|
||||
"C new " + sz_alias + "\nC placement-new " + sz_alias + "\n"
|
||||
)
|
||||
|
||||
with capture:
|
||||
del a
|
||||
ConstructorStats.detail_reg_inst()
|
||||
del b
|
||||
ConstructorStats.detail_reg_inst()
|
||||
del d
|
||||
ConstructorStats.detail_reg_inst()
|
||||
assert capture == """
|
||||
A delete
|
||||
B delete 4
|
||||
D delete
|
||||
"""
|
||||
|
||||
with capture:
|
||||
del c
|
||||
ConstructorStats.detail_reg_inst()
|
||||
del c2
|
||||
ConstructorStats.detail_reg_inst()
|
||||
assert capture == (
|
||||
"C delete " + sz_noalias + "\n" +
|
||||
"C delete " + sz_alias + "\n"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user