Merge branch 'master' into smart_holder

This commit is contained in:
Ralf W. Grosse-Kunstleve 2021-07-09 06:47:46 -07:00
commit e7d146bdbd
17 changed files with 116 additions and 117 deletions

View File

@ -1,11 +1,13 @@
FormatStyle: file FormatStyle: file
Checks: ' Checks: '
clang-analyzer-optin.cplusplus.VirtualCall,
llvm-namespace-comment, llvm-namespace-comment,
misc-misplaced-const, misc-misplaced-const,
misc-static-assert, misc-static-assert,
misc-uniqueptr-reset-release, misc-uniqueptr-reset-release,
modernize-avoid-bind, modernize-avoid-bind,
modernize-redundant-void-arg,
modernize-replace-auto-ptr, modernize-replace-auto-ptr,
modernize-replace-disallow-copy-and-assign-macro, modernize-replace-disallow-copy-and-assign-macro,
modernize-shrink-to-fit, modernize-shrink-to-fit,
@ -20,6 +22,7 @@ modernize-use-override,
modernize-use-using, modernize-use-using,
*performance*, *performance*,
readability-container-size-empty, readability-container-size-empty,
readability-else-after-return,
readability-make-member-function-const, readability-make-member-function-const,
readability-redundant-function-ptr-dereference, readability-redundant-function-ptr-dereference,
readability-redundant-smartptr-get, readability-redundant-smartptr-get,

View File

@ -110,22 +110,25 @@ public:
#define PYBIND11_TYPE_CASTER(type, py_name) \ #define PYBIND11_TYPE_CASTER(type, py_name) \
protected: \ protected: \
type value; \ type value; \
\
public: \ public: \
static constexpr auto name = py_name; \ static constexpr auto name = py_name; \
template <typename T_, enable_if_t<std::is_same<type, remove_cv_t<T_>>::value, int> = 0> \ template <typename T_, enable_if_t<std::is_same<type, remove_cv_t<T_>>::value, int> = 0> \
static handle cast(T_ *src, return_value_policy policy, handle parent) { \ static handle cast(T_ *src, return_value_policy policy, handle parent) { \
if (!src) return none().release(); \ if (!src) \
return none().release(); \
if (policy == return_value_policy::take_ownership) { \ if (policy == return_value_policy::take_ownership) { \
auto h = cast(std::move(*src), policy, parent); delete src; return h; \ auto h = cast(std::move(*src), policy, parent); \
} else { \ delete src; \
return cast(*src, policy, parent); \ return h; \
} \ } \
return cast(*src, policy, parent); \
} \ } \
operator type *() { return &value; } \ operator type *() { return &value; } \
operator type &() { return value; } \ operator type &() { return value; } \
operator type &&() && { return std::move(value); } \ operator type &&() && { return std::move(value); } \
template <typename T_> using cast_op_type = pybind11::detail::movable_cast_op_type<T_> template <typename T_> \
using cast_op_type = pybind11::detail::movable_cast_op_type<T_>
template <typename CharT> using is_std_char_type = any_of< template <typename CharT> using is_std_char_type = any_of<
std::is_same<CharT, char>, /* std::string */ std::is_same<CharT, char>, /* std::string */
@ -269,7 +272,8 @@ public:
bool load(handle h, bool) { bool load(handle h, bool) {
if (!h) { if (!h) {
return false; return false;
} else if (h.is_none()) { }
if (h.is_none()) {
value = nullptr; value = nullptr;
return true; return true;
} }
@ -294,7 +298,6 @@ public:
static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) {
if (ptr) if (ptr)
return capsule(ptr).release(); return capsule(ptr).release();
else
return none().inc_ref(); return none().inc_ref();
} }
@ -311,9 +314,15 @@ template <> class type_caster<bool> {
public: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!src) return false; if (!src) return false;
else if (src.ptr() == Py_True) { value = true; return true; } if (src.ptr() == Py_True) {
else if (src.ptr() == Py_False) { value = false; return true; } value = true;
else if (convert || !std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { return true;
}
if (src.ptr() == Py_False) {
value = false;
return true;
}
if (convert || !std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) {
// (allow non-implicit conversion for numpy booleans) // (allow non-implicit conversion for numpy booleans)
Py_ssize_t res = -1; Py_ssize_t res = -1;
@ -337,9 +346,8 @@ public:
if (res == 0 || res == 1) { if (res == 0 || res == 1) {
value = (bool) res; value = (bool) res;
return true; return true;
} else {
PyErr_Clear();
} }
PyErr_Clear();
} }
return false; return false;
} }
@ -373,7 +381,8 @@ template <typename StringType, bool IsView = false> struct string_caster {
handle load_src = src; handle load_src = src;
if (!src) { if (!src) {
return false; return false;
} else if (!PyUnicode_Check(load_src.ptr())) { }
if (!PyUnicode_Check(load_src.ptr())) {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
return load_bytes(load_src); return load_bytes(load_src);
#else #else
@ -576,10 +585,11 @@ public:
static handle cast(T *src, return_value_policy policy, handle parent) { static handle cast(T *src, return_value_policy policy, handle parent) {
if (!src) return none().release(); if (!src) return none().release();
if (policy == return_value_policy::take_ownership) { if (policy == return_value_policy::take_ownership) {
auto h = cast(std::move(*src), policy, parent); delete src; return h; auto h = cast(std::move(*src), policy, parent);
} else { delete src;
return cast(*src, policy, parent); return h;
} }
return cast(*src, policy, parent);
} }
static constexpr auto name = _("Tuple[") + concat(make_caster<Ts>::name...) + _("]"); static constexpr auto name = _("Tuple[") + concat(make_caster<Ts>::name...) + _("]");
@ -686,15 +696,15 @@ protected:
value = v_h.value_ptr(); value = v_h.value_ptr();
holder = v_h.template holder<holder_type>(); holder = v_h.template holder<holder_type>();
return true; return true;
} else { }
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) " throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
#if defined(NDEBUG) #if defined(NDEBUG)
"(compile in debug mode for type information)"); "(compile in debug mode for type information)");
#else #else
"of type '" + type_id<holder_type>() + "''"); "of type '"
+ type_id<holder_type>() + "''");
#endif #endif
} }
}
template <typename T = holder_type, detail::enable_if_t<!std::is_constructible<T, const T &, type*>::value, int> = 0> template <typename T = holder_type, detail::enable_if_t<!std::is_constructible<T, const T &, type*>::value, int> = 0>
bool try_implicit_casts(handle, bool) { return false; } bool try_implicit_casts(handle, bool) { return false; }
@ -944,7 +954,6 @@ template <typename T> detail::enable_if_t<detail::move_always<T>::value, T> cast
template <typename T> detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) { template <typename T> detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) {
if (object.ref_count() > 1) if (object.ref_count() > 1)
return cast<T>(object); return cast<T>(object);
else
return move<T>(std::move(object)); return move<T>(std::move(object));
} }
template <typename T> detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) { template <typename T> detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) {

View File

@ -53,11 +53,11 @@ public:
return true; return true;
} }
// If invoked with a float we assume it is seconds and convert // If invoked with a float we assume it is seconds and convert
else if (PyFloat_Check(src.ptr())) { if (PyFloat_Check(src.ptr())) {
value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr())))); value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr()))));
return true; return true;
} }
else return false; return false;
} }
// If this is a duration just return it back // If this is a duration just return it back

View File

@ -162,10 +162,8 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
Py_INCREF(descr); Py_INCREF(descr);
return descr; return descr;
} }
else {
return PyType_Type.tp_getattro(obj, name); return PyType_Type.tp_getattro(obj, name);
} }
}
#endif #endif
/// metaclass `__call__` function that is used to create all pybind11 objects. /// metaclass `__call__` function that is used to create all pybind11 objects.

View File

@ -670,7 +670,7 @@ public:
return true; return true;
} }
// Case 2: We have a derived class // Case 2: We have a derived class
else if (PyType_IsSubtype(srctype, typeinfo->type)) { if (PyType_IsSubtype(srctype, typeinfo->type)) {
auto &bases = all_type_info(srctype); auto &bases = all_type_info(srctype);
bool no_cpp_mi = typeinfo->simple_type; bool no_cpp_mi = typeinfo->simple_type;
@ -687,7 +687,7 @@ public:
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
// can safely reinterpret_cast to the relevant pointer. // can safely reinterpret_cast to the relevant pointer.
else if (bases.size() > 1) { if (bases.size() > 1) {
for (auto base : bases) { for (auto base : bases) {
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) {
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base)); this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));

View File

@ -169,22 +169,19 @@ template <typename Type_> struct EigenProps {
return false; // Vector size mismatch return false; // Vector size mismatch
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
} }
else if (fixed) { if (fixed) {
// The type has a fixed size, but is not a vector: abort // The type has a fixed size, but is not a vector: abort
return false; return false;
} }
else if (fixed_cols) { if (fixed_cols) {
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly // Since this isn't a vector, cols must be != 1. We allow this only if it exactly
// equals the number of elements (rows is Dynamic, and so 1 row is allowed). // equals the number of elements (rows is Dynamic, and so 1 row is allowed).
if (cols != n) return false; if (cols != n) return false;
return {1, n, stride}; return {1, n, stride};
} } // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
else {
// Otherwise it's either fully dynamic, or column dynamic; both become a column vector
if (fixed_rows && rows != n) return false; if (fixed_rows && rows != n) return false;
return {n, 1, stride}; return {n, 1, stride};
} }
}
static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value; static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
static constexpr bool show_order = is_eigen_dense_map<Type>::value; static constexpr bool show_order = is_eigen_dense_map<Type>::value;

View File

@ -98,7 +98,6 @@ public:
auto result = f_.template target<function_type>(); auto result = f_.template target<function_type>();
if (result) if (result)
return cpp_function(*result, policy).release(); return cpp_function(*result, policy).release();
else
return cpp_function(std::forward<Func>(f_), policy).release(); return cpp_function(std::forward<Func>(f_), policy).release();
} }

View File

@ -1340,9 +1340,8 @@ public:
if (++m_index[i] != m_shape[i]) { if (++m_index[i] != m_shape[i]) {
increment_common_iterator(i); increment_common_iterator(i);
break; break;
} else {
m_index[i] = 0;
} }
m_index[i] = 0;
} }
return *this; return *this;
} }
@ -1493,7 +1492,6 @@ struct vectorize_returned_array {
static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) { static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) {
if (trivial == broadcast_trivial::f_trivial) if (trivial == broadcast_trivial::f_trivial)
return array_t<Return, array::f_style>(shape); return array_t<Return, array::f_style>(shape);
else
return array_t<Return>(shape); return array_t<Return>(shape);
} }

View File

@ -370,7 +370,7 @@ protected:
std::memset(rec->def, 0, sizeof(PyMethodDef)); std::memset(rec->def, 0, sizeof(PyMethodDef));
rec->def->ml_name = rec->name; rec->def->ml_name = rec->name;
rec->def->ml_meth rec->def->ml_meth
= reinterpret_cast<PyCFunction>(reinterpret_cast<void (*)(void)>(dispatcher)); = reinterpret_cast<PyCFunction>(reinterpret_cast<void (*)()>(dispatcher));
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule rec_capsule(unique_rec.release(), [](void *ptr) { capsule rec_capsule(unique_rec.release(), [](void *ptr) {
@ -898,21 +898,21 @@ protected:
append_note_if_missing_header_is_suspected(msg); append_note_if_missing_header_is_suspected(msg);
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} else if (!result) { }
if (!result) {
std::string msg = "Unable to convert function return value to a " std::string msg = "Unable to convert function return value to a "
"Python type! The signature was\n\t"; "Python type! The signature was\n\t";
msg += it->signature; msg += it->signature;
append_note_if_missing_header_is_suspected(msg); append_note_if_missing_header_is_suspected(msg);
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} else { }
if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) {
auto *pi = reinterpret_cast<instance *>(parent.ptr()); auto *pi = reinterpret_cast<instance *>(parent.ptr());
self_value_and_holder.type->init_instance(pi, nullptr); self_value_and_holder.type->init_instance(pi, nullptr);
} }
return result.ptr(); return result.ptr();
} }
}
}; };
/// Wrapper for Python extension modules /// Wrapper for Python extension modules
@ -1946,9 +1946,9 @@ PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, func
auto get_arg = [&](size_t n) { auto get_arg = [&](size_t n) {
if (n == 0) if (n == 0)
return ret; return ret;
else if (n == 1 && call.init_self) if (n == 1 && call.init_self)
return call.init_self; return call.init_self;
else if (n <= call.args.size()) if (n <= call.args.size())
return call.args[n - 1]; return call.args[n - 1];
return handle(); return handle();
}; };
@ -2275,14 +2275,15 @@ template <class T> function get_override(const T *this_ptr, const char *name) {
#define PYBIND11_OVERRIDE_IMPL(ret_type, cname, name, ...) \ #define PYBIND11_OVERRIDE_IMPL(ret_type, cname, name, ...) \
do { \ do { \
pybind11::gil_scoped_acquire gil; \ pybind11::gil_scoped_acquire gil; \
pybind11::function override = pybind11::get_override(static_cast<const cname *>(this), name); \ pybind11::function override \
= pybind11::get_override(static_cast<const cname *>(this), name); \
if (override) { \ if (override) { \
auto o = override(__VA_ARGS__); \ auto o = override(__VA_ARGS__); \
if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value) { \ if (pybind11::detail::cast_is_temporary_value_reference<ret_type>::value) { \
static pybind11::detail::override_caster_t<ret_type> caster; \ static pybind11::detail::override_caster_t<ret_type> caster; \
return pybind11::detail::cast_ref<ret_type>(std::move(o), caster); \ return pybind11::detail::cast_ref<ret_type>(std::move(o), caster); \
} \ } \
else return pybind11::detail::cast_safe<ret_type>(std::move(o)); \ return pybind11::detail::cast_safe<ret_type>(std::move(o)); \
} \ } \
} while (false) } while (false)

View File

@ -440,20 +440,18 @@ inline object getattr(handle obj, const char *name) {
inline object getattr(handle obj, handle name, handle default_) { inline object getattr(handle obj, handle name, handle default_) {
if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) {
return reinterpret_steal<object>(result); return reinterpret_steal<object>(result);
} else { }
PyErr_Clear(); PyErr_Clear();
return reinterpret_borrow<object>(default_); return reinterpret_borrow<object>(default_);
} }
}
inline object getattr(handle obj, const char *name, handle default_) { inline object getattr(handle obj, const char *name, handle default_) {
if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) {
return reinterpret_steal<object>(result); return reinterpret_steal<object>(result);
} else { }
PyErr_Clear(); PyErr_Clear();
return reinterpret_borrow<object>(default_); return reinterpret_borrow<object>(default_);
} }
}
inline void setattr(handle obj, handle name, handle value) { inline void setattr(handle obj, handle name, handle value) {
if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); }
@ -791,11 +789,10 @@ inline bool PyIterable_Check(PyObject *obj) {
if (iter) { if (iter) {
Py_DECREF(iter); Py_DECREF(iter);
return true; return true;
} else { }
PyErr_Clear(); PyErr_Clear();
return false; return false;
} }
}
inline bool PyNone_Check(PyObject *o) { return o == Py_None; } inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; }
@ -1188,11 +1185,9 @@ Unsigned as_unsigned(PyObject *o) {
unsigned long v = PyLong_AsUnsignedLong(o); unsigned long v = PyLong_AsUnsignedLong(o);
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
} }
else {
unsigned long long v = PyLong_AsUnsignedLongLong(o); unsigned long long v = PyLong_AsUnsignedLongLong(o);
return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
} }
}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
class int_ : public object { class int_ : public object {

View File

@ -278,7 +278,8 @@ template<typename T> struct optional_caster {
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!src) { if (!src) {
return false; return false;
} else if (src.is_none()) { }
if (src.is_none()) {
return true; // default-constructed value is already empty return true; // default-constructed value is already empty
} }
value_conv inner_caster; value_conv inner_caster;

View File

@ -414,13 +414,12 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
if (step == 1) { if (step == 1) {
return Vector(p, end); return Vector(p, end);
} }
else {
Vector vec; Vector vec;
vec.reserve((size_t) info.shape[0]); vec.reserve((size_t) info.shape[0]);
for (; p != end; p += step) for (; p != end; p += step)
vec.push_back(*p); vec.push_back(*p);
return vec; return vec;
}
})); }));
return; return;

View File

@ -82,7 +82,7 @@ TEST_SUBMODULE(callbacks, m) {
// Export the payload constructor statistics for testing purposes: // Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>); m.def("payload_cstats", &ConstructorStats::get<Payload>);
/* Test cleanup of lambda closure */ /* Test cleanup of lambda closure */
m.def("test_cleanup", []() -> std::function<void(void)> { m.def("test_cleanup", []() -> std::function<void()> {
Payload p; Payload p;
return [p]() { return [p]() {
@ -108,12 +108,13 @@ TEST_SUBMODULE(callbacks, m) {
if (!result) { if (!result) {
auto r = f(1); auto r = f(1);
return "can't convert to function pointer: eval(1) = " + std::to_string(r); return "can't convert to function pointer: eval(1) = " + std::to_string(r);
} else if (*result == dummy_function) { }
if (*result == dummy_function) {
auto r = (*result)(1); auto r = (*result)(1);
return "matches dummy_function: eval(1) = " + std::to_string(r); return "matches dummy_function: eval(1) = " + std::to_string(r);
} else {
return "argument does NOT match dummy_function. This should never happen!";
} }
return "argument does NOT match dummy_function. This should never happen!";
}); });
class AbstractBase { class AbstractBase {

View File

@ -173,7 +173,6 @@ TEST_SUBMODULE(class_, m) {
// return py::type::of<int>(); // return py::type::of<int>();
if (category == 1) if (category == 1)
return py::type::of<DerivedClass1>(); return py::type::of<DerivedClass1>();
else
return py::type::of<Invalid>(); return py::type::of<Invalid>();
}); });

View File

@ -203,7 +203,6 @@ TEST_SUBMODULE(copy_move_policies, m) {
void *ptr = std::malloc(bytes); void *ptr = std::malloc(bytes);
if (ptr) if (ptr)
return ptr; return ptr;
else
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
}; };

View File

@ -263,13 +263,13 @@ TEST_SUBMODULE(pytypes, m) {
if (type == "bytes") { if (type == "bytes") {
return move ? py::bytes(std::move(value)) : py::bytes(value); return move ? py::bytes(std::move(value)) : py::bytes(value);
} }
else if (type == "none") { if (type == "none") {
return move ? py::none(std::move(value)) : py::none(value); return move ? py::none(std::move(value)) : py::none(value);
} }
else if (type == "ellipsis") { if (type == "ellipsis") {
return move ? py::ellipsis(std::move(value)) : py::ellipsis(value); return move ? py::ellipsis(std::move(value)) : py::ellipsis(value);
} }
else if (type == "type") { if (type == "type") {
return move ? py::type(std::move(value)) : py::type(value); return move ? py::type(std::move(value)) : py::type(value);
} }
throw std::runtime_error("Invalid type"); throw std::runtime_error("Invalid type");
@ -385,9 +385,7 @@ TEST_SUBMODULE(pytypes, m) {
if (is_unsigned) if (is_unsigned)
return py::memoryview::from_buffer( return py::memoryview::from_buffer(
ui16, { 4 }, { sizeof(uint16_t) }); ui16, { 4 }, { sizeof(uint16_t) });
else return py::memoryview::from_buffer(si16, {5}, {sizeof(int16_t)});
return py::memoryview::from_buffer(
si16, { 5 }, { sizeof(int16_t) });
}); });
m.def("test_memoryview_from_buffer_nativeformat", []() { m.def("test_memoryview_from_buffer_nativeformat", []() {

View File

@ -112,7 +112,9 @@ public:
void operator=(const NonCopyable &) = delete; void operator=(const NonCopyable &) = delete;
void operator=(NonCopyable &&) = delete; void operator=(NonCopyable &&) = delete;
std::string get_value() const { std::string get_value() const {
if (value) return std::to_string(*value); else return "(null)"; if (value)
return std::to_string(*value);
return "(null)";
} }
~NonCopyable() { print_destroyed(this); } ~NonCopyable() { print_destroyed(this); }