mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-21 20:55:11 +00:00
Add default and converting constructors for all concrete Python types
* Deprecate the `py::object::str()` member function since `py::str(obj)` is now equivalent and preferred * Make `py::repr()` a free function * Make sure obj.cast<T>() works as expected when T is a Python type `obj.cast<T>()` should be the same as `T(obj)`, i.e. it should convert the given object to a different Python type. However, `obj.cast<T>()` usually calls `type_caster::load()` which only checks the type without doing any actual conversion. That causes a very unexpected `cast_error`. This commit makes it so that `obj.cast<T>()` and `T(obj)` are the same when T is a Python type. * Simplify pytypes converting constructor implementation It's not necessary to maintain a full set of converting constructors and assignment operators + const& and &&. A single converting const& constructor will work and there is no impact on binary size. On the other hand, the conversion functions can be significantly simplified.
This commit is contained in:
parent
b4498ef44d
commit
e18bc02fc9
@ -28,7 +28,7 @@ Breaking changes queued for v2.0.0 (Not yet released)
|
||||
(now uses prefix increment operator); it now also accepts iterators with
|
||||
different begin/end types as long as they are equality comparable.
|
||||
* ``arg()`` now accepts a wider range of argument types for default values
|
||||
* Added ``repr()`` method to the ``handle`` class.
|
||||
* Added ``py::repr()`` function which is equivalent to Python's builtin ``repr()``.
|
||||
* Added support for registering structured dtypes via ``PYBIND11_NUMPY_DTYPE()`` macro.
|
||||
* Added ``PYBIND11_STR_TYPE`` macro which maps to the ``builtins.str`` type.
|
||||
* Added a simplified ``buffer_info`` constructor for 1-dimensional buffers.
|
||||
|
@ -265,9 +265,9 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
auto descr = "'" + std::string(a.name) + ": " + a.type + "'";
|
||||
if (r->class_) {
|
||||
if (r->name)
|
||||
descr += " in method '" + (std::string) r->class_.str() + "." + (std::string) r->name + "'";
|
||||
descr += " in method '" + (std::string) str(r->class_) + "." + (std::string) r->name + "'";
|
||||
else
|
||||
descr += " in method of '" + (std::string) r->class_.str() + "'";
|
||||
descr += " in method of '" + (std::string) str(r->class_) + "'";
|
||||
} else if (r->name) {
|
||||
descr += " in function named '" + (std::string) r->name + "'";
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ PYBIND11_NOINLINE inline std::string error_string() {
|
||||
errorString += ": ";
|
||||
}
|
||||
if (scope.value)
|
||||
errorString += (std::string) handle(scope.value).str();
|
||||
errorString += (std::string) str(scope.value);
|
||||
|
||||
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
|
||||
|
||||
@ -1056,7 +1056,7 @@ template <typename T, typename SFINAE> type_caster<T, SFINAE> &load_type(type_ca
|
||||
throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)");
|
||||
#else
|
||||
throw cast_error("Unable to cast Python instance of type " +
|
||||
(std::string) handle.get_type().str() + " to C++ type '" + type_id<T>() + "''");
|
||||
(std::string) str(handle.get_type()) + " to C++ type '" + type_id<T>() + "''");
|
||||
#endif
|
||||
}
|
||||
return conv;
|
||||
@ -1070,16 +1070,20 @@ template <typename T> make_caster<T> load_type(const handle &handle) {
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
template <typename T> T cast(const handle &handle) {
|
||||
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
|
||||
T cast(const handle &handle) {
|
||||
static_assert(!detail::cast_is_temporary_value_reference<T>::value,
|
||||
"Unable to cast type to reference: value is local to type caster");
|
||||
using type_caster = detail::make_caster<T>;
|
||||
return detail::load_type<T>(handle).operator typename type_caster::template cast_op_type<T>();
|
||||
}
|
||||
|
||||
template <typename T> object cast(const T &value,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
handle parent = handle()) {
|
||||
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
|
||||
T cast(const handle &handle) { return {handle, true}; }
|
||||
|
||||
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
|
||||
object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference,
|
||||
handle parent = handle()) {
|
||||
if (policy == return_value_policy::automatic)
|
||||
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
|
||||
else if (policy == return_value_policy::automatic_reference)
|
||||
@ -1097,7 +1101,7 @@ detail::enable_if_t<detail::move_always<T>::value || detail::move_if_unreference
|
||||
throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references"
|
||||
" (compile in debug mode for details)");
|
||||
#else
|
||||
throw cast_error("Unable to move from Python " + (std::string) obj.get_type().str() +
|
||||
throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) +
|
||||
" instance to C++ " + type_id<T>() + " instance: instance has multiple references");
|
||||
#endif
|
||||
|
||||
@ -1274,7 +1278,7 @@ public:
|
||||
int _[] = { 0, (process(args_list, std::forward<Ts>(values)), 0)... };
|
||||
ignore_unused(_);
|
||||
|
||||
m_args = object(PyList_AsTuple(args_list.ptr()), false);
|
||||
m_args = std::move(args_list);
|
||||
}
|
||||
|
||||
const tuple &args() const & { return m_args; }
|
||||
@ -1336,7 +1340,7 @@ private:
|
||||
#if defined(NDEBUG)
|
||||
multiple_values_error();
|
||||
#else
|
||||
multiple_values_error(k.first.str());
|
||||
multiple_values_error(str(k.first));
|
||||
#endif
|
||||
}
|
||||
m_kwargs[k.first] = k.second;
|
||||
|
@ -111,7 +111,6 @@
|
||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
|
||||
#define PYBIND11_BYTES_CHECK PyBytes_Check
|
||||
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) PyLong_AsUnsignedLongLong(o)
|
||||
@ -130,7 +129,6 @@
|
||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING PyString_AsString
|
||||
#define PYBIND11_BYTES_CHECK PyString_Check
|
||||
#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
|
||||
#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) (PyInt_Check(o) ? (unsigned long long) PyLong_AsUnsignedLong(o) : PyLong_AsUnsignedLongLong(o))
|
||||
|
@ -540,10 +540,12 @@ protected:
|
||||
|
||||
template <typename T, int ExtraFlags = array::forcecast> class array_t : public array {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(array_t, array, is_non_null, m_ptr = ensure_(m_ptr));
|
||||
|
||||
array_t() : array() { }
|
||||
|
||||
array_t(handle h, bool borrowed) : array(h, borrowed) { m_ptr = ensure_(m_ptr); }
|
||||
|
||||
array_t(const object &o) : array(o) { m_ptr = ensure_(m_ptr); }
|
||||
|
||||
explicit array_t(const buffer_info& info) : array(info) { }
|
||||
|
||||
array_t(const std::vector<size_t> &shape,
|
||||
@ -588,8 +590,6 @@ public:
|
||||
return *(static_cast<T*>(array::mutable_data()) + byte_offset(size_t(index)...) / itemsize());
|
||||
}
|
||||
|
||||
static bool is_non_null(PyObject *ptr) { return ptr != nullptr; }
|
||||
|
||||
static PyObject *ensure_(PyObject *ptr) {
|
||||
if (ptr == nullptr)
|
||||
return nullptr;
|
||||
|
@ -504,7 +504,7 @@ protected:
|
||||
msg += "\nInvoked with: ";
|
||||
tuple args_(args, true);
|
||||
for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) {
|
||||
msg += static_cast<std::string>(static_cast<object>(args_[ti]).str());
|
||||
msg += static_cast<std::string>(pybind11::str(args_[ti]));
|
||||
if ((ti + 1) != args_.size() )
|
||||
msg += ", ";
|
||||
}
|
||||
@ -665,11 +665,9 @@ protected:
|
||||
#endif
|
||||
|
||||
size_t num_bases = rec->bases.size();
|
||||
tuple bases(num_bases);
|
||||
for (size_t i = 0; i < num_bases; ++i)
|
||||
bases[i] = rec->bases[i];
|
||||
auto bases = tuple(rec->bases);
|
||||
|
||||
std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + rec->name)
|
||||
std::string full_name = (scope_module ? ((std::string) pybind11::str(scope_module) + "." + rec->name)
|
||||
: std::string(rec->name));
|
||||
|
||||
char *tp_doc = nullptr;
|
||||
@ -1470,7 +1468,7 @@ NAMESPACE_BEGIN(detail)
|
||||
PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) {
|
||||
auto strings = tuple(args.size());
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
strings[i] = args[i].str();
|
||||
strings[i] = str(args[i]);
|
||||
}
|
||||
auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" ");
|
||||
auto line = sep.attr("join")(strings);
|
||||
@ -1654,7 +1652,7 @@ inline function get_type_overload(const void *this_ptr, const detail::type_info
|
||||
|
||||
/* Don't call dispatch code if invoked from overridden function */
|
||||
PyFrameObject *frame = PyThreadState_Get()->frame;
|
||||
if (frame && (std::string) pybind11::handle(frame->f_code->co_name).str() == name &&
|
||||
if (frame && (std::string) str(frame->f_code->co_name) == name &&
|
||||
frame->f_code->co_argcount > 0) {
|
||||
PyFrame_FastToLocals(frame);
|
||||
PyObject *self_caller = PyDict_GetItem(
|
||||
|
@ -68,8 +68,8 @@ public:
|
||||
object call(Args&&... args) const;
|
||||
|
||||
bool is_none() const { return derived().ptr() == Py_None; }
|
||||
PYBIND11_DEPRECATED("Instead of obj.str(), use py::str(obj)")
|
||||
pybind11::str str() const;
|
||||
pybind11::str repr() const;
|
||||
|
||||
int ref_count() const { return static_cast<int>(Py_REFCNT(derived().ptr())); }
|
||||
handle get_type() const;
|
||||
@ -222,13 +222,13 @@ public:
|
||||
|
||||
template <typename T = Policy>
|
||||
PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)")
|
||||
operator enable_if_t<std::is_same<T, accessor_policies::str_attr>::value ||
|
||||
explicit operator enable_if_t<std::is_same<T, accessor_policies::str_attr>::value ||
|
||||
std::is_same<T, accessor_policies::obj_attr>::value, bool>() const {
|
||||
return hasattr(obj, key);
|
||||
}
|
||||
template <typename T = Policy>
|
||||
PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)")
|
||||
operator enable_if_t<std::is_same<T, accessor_policies::generic_item>::value, bool>() const {
|
||||
explicit operator enable_if_t<std::is_same<T, accessor_policies::generic_item>::value, bool>() const {
|
||||
return obj.contains(key);
|
||||
}
|
||||
|
||||
@ -390,20 +390,23 @@ class unpacking_collector;
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, CvtStmt) \
|
||||
#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
|
||||
public: \
|
||||
Name(const handle &h, bool borrowed) : Parent(h, borrowed) { CvtStmt; } \
|
||||
/* These are deliberately not 'explicit' to allow implicit conversion from object: */ \
|
||||
Name(const object& o): Parent(o) { CvtStmt; } \
|
||||
Name(object&& o) noexcept : Parent(std::move(o)) { CvtStmt; } \
|
||||
Name& operator=(object&& o) noexcept { (void) object::operator=(std::move(o)); CvtStmt; return *this; } \
|
||||
Name& operator=(const object& o) { return static_cast<Name&>(object::operator=(o)); CvtStmt; } \
|
||||
PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \
|
||||
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \
|
||||
static bool _check(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); }
|
||||
|
||||
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
|
||||
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
|
||||
Name(handle h, bool borrowed) : Name(object(h, borrowed)) { } \
|
||||
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
|
||||
Name(const object &o) : Parent(ConvertFun(o.ptr()), false) { if (!m_ptr) throw error_already_set(); }
|
||||
|
||||
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
|
||||
PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, )
|
||||
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
|
||||
Name(handle h, bool borrowed) : Parent(h, borrowed) { } \
|
||||
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
|
||||
Name(const object &o) : Parent(o) { }
|
||||
|
||||
#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
|
||||
PYBIND11_OBJECT(Name, Parent, CheckFun) \
|
||||
@ -411,26 +414,9 @@ NAMESPACE_END(detail)
|
||||
|
||||
class iterator : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(iterator, object, PyIter_Check, value = object(); ready = false)
|
||||
iterator() : object(), value(object()), ready(false) { }
|
||||
iterator(const iterator& it) : object(it), value(it.value), ready(it.ready) { }
|
||||
iterator(iterator&& it) : object(std::move(it)), value(std::move(it.value)), ready(it.ready) { }
|
||||
|
||||
/** Caveat: this copy constructor does not (and cannot) clone the internal
|
||||
/** Caveat: copying an iterator does not (and cannot) clone the internal
|
||||
state of the Python iterable */
|
||||
iterator &operator=(const iterator &it) {
|
||||
(void) object::operator=(it);
|
||||
value = it.value;
|
||||
ready = it.ready;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator &operator=(iterator &&it) noexcept {
|
||||
(void) object::operator=(std::move(it));
|
||||
value = std::move(it.value);
|
||||
ready = it.ready;
|
||||
return *this;
|
||||
}
|
||||
PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)
|
||||
|
||||
iterator& operator++() {
|
||||
if (m_ptr)
|
||||
@ -465,8 +451,8 @@ private:
|
||||
void advance() { value = object(PyIter_Next(m_ptr), false); }
|
||||
|
||||
private:
|
||||
object value;
|
||||
bool ready;
|
||||
object value = {};
|
||||
bool ready = false;
|
||||
};
|
||||
|
||||
class iterable : public object {
|
||||
@ -478,7 +464,7 @@ class bytes;
|
||||
|
||||
class str : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(str, object, detail::PyUnicode_Check_Permissive)
|
||||
PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str)
|
||||
|
||||
str(const char *c, size_t n)
|
||||
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), false) {
|
||||
@ -486,7 +472,7 @@ public:
|
||||
}
|
||||
|
||||
// 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects
|
||||
str(const char *c)
|
||||
str(const char *c = "")
|
||||
: object(PyUnicode_FromString(c), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
@ -495,6 +481,8 @@ public:
|
||||
|
||||
explicit str(const bytes &b);
|
||||
|
||||
explicit str(handle h) : object(raw_str(h.ptr()), false) { }
|
||||
|
||||
operator std::string() const {
|
||||
object temp = *this;
|
||||
if (PyUnicode_Check(m_ptr)) {
|
||||
@ -513,6 +501,18 @@ public:
|
||||
str format(Args &&...args) const {
|
||||
return attr("format")(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Return string representation -- always returns a new reference, even if already a str
|
||||
static PyObject *raw_str(PyObject *op) {
|
||||
PyObject *str_value = PyObject_Str(op);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (!str_value) throw error_already_set();
|
||||
PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
|
||||
Py_XDECREF(str_value); str_value = unicode;
|
||||
#endif
|
||||
return str_value;
|
||||
}
|
||||
};
|
||||
|
||||
inline namespace literals {
|
||||
@ -522,10 +522,10 @@ inline str operator"" _s(const char *s, size_t size) { return {s, size}; }
|
||||
|
||||
class bytes : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(bytes, object, PYBIND11_BYTES_CHECK)
|
||||
PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK)
|
||||
|
||||
// Allow implicit conversion:
|
||||
bytes(const char *c)
|
||||
bytes(const char *c = "")
|
||||
: object(PYBIND11_BYTES_FROM_STRING(c), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
|
||||
}
|
||||
@ -585,15 +585,25 @@ public:
|
||||
|
||||
class bool_ : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(bool_, object, PyBool_Check)
|
||||
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
|
||||
bool_() : object(Py_False, true) { }
|
||||
// Allow implicit conversion from and to `bool`:
|
||||
bool_(bool value) : object(value ? Py_True : Py_False, true) { }
|
||||
operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
|
||||
|
||||
private:
|
||||
/// Return the truth value of an object -- always returns a new reference
|
||||
static PyObject *raw_bool(PyObject *op) {
|
||||
const auto value = PyObject_IsTrue(op);
|
||||
if (value == -1) return nullptr;
|
||||
return handle(value ? Py_True : Py_False).inc_ref().ptr();
|
||||
}
|
||||
};
|
||||
|
||||
class int_ : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(int_, object, PYBIND11_LONG_CHECK)
|
||||
PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long)
|
||||
int_() : object(PyLong_FromLong(0), false) { }
|
||||
// Allow implicit conversion from C++ integral types:
|
||||
template <typename T,
|
||||
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
@ -631,12 +641,12 @@ public:
|
||||
|
||||
class float_ : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(float_, object, PyFloat_Check)
|
||||
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
|
||||
// Allow implicit conversion from float/double:
|
||||
float_(float value) : object(PyFloat_FromDouble((double) value), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate float object!");
|
||||
}
|
||||
float_(double value) : object(PyFloat_FromDouble((double) value), false) {
|
||||
float_(double value = .0) : object(PyFloat_FromDouble((double) value), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate float object!");
|
||||
}
|
||||
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
|
||||
@ -685,7 +695,7 @@ public:
|
||||
|
||||
class tuple : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT(tuple, object, PyTuple_Check)
|
||||
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple)
|
||||
explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
|
||||
}
|
||||
@ -695,7 +705,7 @@ public:
|
||||
|
||||
class dict : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT(dict, object, PyDict_Check)
|
||||
PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict)
|
||||
dict() : object(PyDict_New(), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate dict object!");
|
||||
}
|
||||
@ -711,6 +721,14 @@ public:
|
||||
void clear() const { PyDict_Clear(ptr()); }
|
||||
bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; }
|
||||
bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; }
|
||||
|
||||
private:
|
||||
/// Call the `dict` Python type -- always returns a new reference
|
||||
static PyObject *raw_dict(PyObject *op) {
|
||||
if (PyDict_Check(op))
|
||||
return handle(op).inc_ref().ptr();
|
||||
return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
class sequence : public object {
|
||||
@ -722,7 +740,7 @@ public:
|
||||
|
||||
class list : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT(list, object, PyList_Check)
|
||||
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List)
|
||||
explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate list object!");
|
||||
}
|
||||
@ -736,7 +754,7 @@ class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check)
|
||||
|
||||
class set : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT(set, object, PySet_Check)
|
||||
PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New)
|
||||
set() : object(PySet_New(nullptr), false) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate set object!");
|
||||
}
|
||||
@ -797,7 +815,7 @@ public:
|
||||
pybind11_fail("Unable to create memoryview from buffer descriptor");
|
||||
}
|
||||
|
||||
PYBIND11_OBJECT_DEFAULT(memoryview, object, PyMemoryView_Check)
|
||||
PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject)
|
||||
};
|
||||
|
||||
inline size_t len(handle h) {
|
||||
@ -807,6 +825,17 @@ inline size_t len(handle h) {
|
||||
return (size_t) result;
|
||||
}
|
||||
|
||||
inline str repr(handle h) {
|
||||
PyObject *str_value = PyObject_Repr(h.ptr());
|
||||
if (!str_value) throw error_already_set();
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
|
||||
Py_XDECREF(str_value); str_value = unicode;
|
||||
if (!str_value) throw error_already_set();
|
||||
#endif
|
||||
return {str_value, false};
|
||||
}
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
template <typename D> iterator object_api<D>::begin() const { return {PyObject_GetIter(derived().ptr()), false}; }
|
||||
template <typename D> iterator object_api<D>::end() const { return {nullptr, false}; }
|
||||
@ -820,24 +849,7 @@ template <typename D> template <typename T> bool object_api<D>::contains(T &&key
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
pybind11::str object_api<D>::str() const {
|
||||
PyObject *str_value = PyObject_Str(derived().ptr());
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
|
||||
Py_XDECREF(str_value); str_value = unicode;
|
||||
#endif
|
||||
return {str_value, false};
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
pybind11::str object_api<D>::repr() const {
|
||||
PyObject *str_value = PyObject_Repr(derived().ptr());
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
|
||||
Py_XDECREF(str_value); str_value = unicode;
|
||||
#endif
|
||||
return {str_value, false};
|
||||
}
|
||||
pybind11::str object_api<D>::str() const { return pybind11::str(derived()); }
|
||||
|
||||
template <typename D>
|
||||
handle object_api<D>::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); }
|
||||
|
@ -248,7 +248,7 @@ template<> struct type_caster<std::experimental::nullopt_t>
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
|
||||
os << (std::string) obj.str();
|
||||
os << (std::string) str(obj);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
@ -196,14 +196,14 @@ py::list print_format_descriptors() {
|
||||
|
||||
py::list print_dtypes() {
|
||||
const auto dtypes = {
|
||||
py::dtype::of<SimpleStruct>().str(),
|
||||
py::dtype::of<PackedStruct>().str(),
|
||||
py::dtype::of<NestedStruct>().str(),
|
||||
py::dtype::of<PartialStruct>().str(),
|
||||
py::dtype::of<PartialNestedStruct>().str(),
|
||||
py::dtype::of<StringStruct>().str(),
|
||||
py::dtype::of<EnumStruct>().str(),
|
||||
py::dtype::of<StructWithUglyNames>().str()
|
||||
py::str(py::dtype::of<SimpleStruct>()),
|
||||
py::str(py::dtype::of<PackedStruct>()),
|
||||
py::str(py::dtype::of<NestedStruct>()),
|
||||
py::str(py::dtype::of<PartialStruct>()),
|
||||
py::str(py::dtype::of<PartialNestedStruct>()),
|
||||
py::str(py::dtype::of<StringStruct>()),
|
||||
py::str(py::dtype::of<EnumStruct>()),
|
||||
py::str(py::dtype::of<StructWithUglyNames>())
|
||||
};
|
||||
auto l = py::list();
|
||||
for (const auto &s : dtypes) {
|
||||
|
@ -153,8 +153,8 @@ public:
|
||||
}
|
||||
|
||||
void test_print(const py::object& obj) {
|
||||
py::print(obj.str());
|
||||
py::print(obj.repr());
|
||||
py::print(py::str(obj));
|
||||
py::print(py::repr(obj));
|
||||
}
|
||||
|
||||
static int value;
|
||||
@ -321,4 +321,46 @@ test_initializer python_types([](py::module &m) {
|
||||
|
||||
m.attr("has_optional") = py::cast(has_optional);
|
||||
m.attr("has_exp_optional") = py::cast(has_exp_optional);
|
||||
|
||||
m.def("test_default_constructors", []() {
|
||||
return py::dict(
|
||||
"str"_a=py::str(),
|
||||
"bool"_a=py::bool_(),
|
||||
"int"_a=py::int_(),
|
||||
"float"_a=py::float_(),
|
||||
"tuple"_a=py::tuple(),
|
||||
"list"_a=py::list(),
|
||||
"dict"_a=py::dict(),
|
||||
"set"_a=py::set()
|
||||
);
|
||||
});
|
||||
|
||||
m.def("test_converting_constructors", [](py::dict d) {
|
||||
return py::dict(
|
||||
"str"_a=py::str(d["str"]),
|
||||
"bool"_a=py::bool_(d["bool"]),
|
||||
"int"_a=py::int_(d["int"]),
|
||||
"float"_a=py::float_(d["float"]),
|
||||
"tuple"_a=py::tuple(d["tuple"]),
|
||||
"list"_a=py::list(d["list"]),
|
||||
"dict"_a=py::dict(d["dict"]),
|
||||
"set"_a=py::set(d["set"]),
|
||||
"memoryview"_a=py::memoryview(d["memoryview"])
|
||||
);
|
||||
});
|
||||
|
||||
m.def("test_cast_functions", [](py::dict d) {
|
||||
// When converting between Python types, obj.cast<T>() should be the same as T(obj)
|
||||
return py::dict(
|
||||
"str"_a=d["str"].cast<py::str>(),
|
||||
"bool"_a=d["bool"].cast<py::bool_>(),
|
||||
"int"_a=d["int"].cast<py::int_>(),
|
||||
"float"_a=d["float"].cast<py::float_>(),
|
||||
"tuple"_a=d["tuple"].cast<py::tuple>(),
|
||||
"list"_a=d["list"].cast<py::list>(),
|
||||
"dict"_a=d["dict"].cast<py::dict>(),
|
||||
"set"_a=d["set"].cast<py::set>(),
|
||||
"memoryview"_a=d["memoryview"].cast<py::memoryview>()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -330,3 +330,29 @@ def test_exp_optional():
|
||||
assert test_nullopt_exp(None) == 42
|
||||
assert test_nullopt_exp(42) == 42
|
||||
assert test_nullopt_exp(43) == 43
|
||||
|
||||
|
||||
def test_constructors():
|
||||
"""C++ default and converting constructors are equivalent to type calls in Python"""
|
||||
from pybind11_tests import (test_default_constructors, test_converting_constructors,
|
||||
test_cast_functions)
|
||||
|
||||
types = [str, bool, int, float, tuple, list, dict, set]
|
||||
expected = {t.__name__: t() for t in types}
|
||||
assert test_default_constructors() == expected
|
||||
|
||||
data = {
|
||||
str: 42,
|
||||
bool: "Not empty",
|
||||
int: "42",
|
||||
float: "+1e3",
|
||||
tuple: range(3),
|
||||
list: range(3),
|
||||
dict: [("two", 2), ("one", 1), ("three", 3)],
|
||||
set: [4, 4, 5, 6, 6, 6],
|
||||
memoryview: b'abc'
|
||||
}
|
||||
inputs = {k.__name__: v for k, v in data.items()}
|
||||
expected = {k.__name__: k(v) for k, v in data.items()}
|
||||
assert test_converting_constructors(inputs) == expected
|
||||
assert test_cast_functions(inputs) == expected
|
||||
|
Loading…
Reference in New Issue
Block a user