Skip to content

Commit e18bc02

Browse files
dean0x7dwjakob
authored andcommitted
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.
1 parent b4498ef commit e18bc02

File tree

11 files changed

+179
-99
lines changed

11 files changed

+179
-99
lines changed

docs/changelog.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Breaking changes queued for v2.0.0 (Not yet released)
2828
(now uses prefix increment operator); it now also accepts iterators with
2929
different begin/end types as long as they are equality comparable.
3030
* ``arg()`` now accepts a wider range of argument types for default values
31-
* Added ``repr()`` method to the ``handle`` class.
31+
* Added ``py::repr()`` function which is equivalent to Python's builtin ``repr()``.
3232
* Added support for registering structured dtypes via ``PYBIND11_NUMPY_DTYPE()`` macro.
3333
* Added ``PYBIND11_STR_TYPE`` macro which maps to the ``builtins.str`` type.
3434
* Added a simplified ``buffer_info`` constructor for 1-dimensional buffers.

include/pybind11/attr.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,9 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
265265
auto descr = "'" + std::string(a.name) + ": " + a.type + "'";
266266
if (r->class_) {
267267
if (r->name)
268-
descr += " in method '" + (std::string) r->class_.str() + "." + (std::string) r->name + "'";
268+
descr += " in method '" + (std::string) str(r->class_) + "." + (std::string) r->name + "'";
269269
else
270-
descr += " in method of '" + (std::string) r->class_.str() + "'";
270+
descr += " in method of '" + (std::string) str(r->class_) + "'";
271271
} else if (r->name) {
272272
descr += " in function named '" + (std::string) r->name + "'";
273273
}

include/pybind11/cast.h

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ PYBIND11_NOINLINE inline std::string error_string() {
132132
errorString += ": ";
133133
}
134134
if (scope.value)
135-
errorString += (std::string) handle(scope.value).str();
135+
errorString += (std::string) str(scope.value);
136136

137137
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
138138

@@ -1056,7 +1056,7 @@ template <typename T, typename SFINAE> type_caster<T, SFINAE> &load_type(type_ca
10561056
throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)");
10571057
#else
10581058
throw cast_error("Unable to cast Python instance of type " +
1059-
(std::string) handle.get_type().str() + " to C++ type '" + type_id<T>() + "''");
1059+
(std::string) str(handle.get_type()) + " to C++ type '" + type_id<T>() + "''");
10601060
#endif
10611061
}
10621062
return conv;
@@ -1070,16 +1070,20 @@ template <typename T> make_caster<T> load_type(const handle &handle) {
10701070

10711071
NAMESPACE_END(detail)
10721072

1073-
template <typename T> T cast(const handle &handle) {
1073+
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
1074+
T cast(const handle &handle) {
10741075
static_assert(!detail::cast_is_temporary_value_reference<T>::value,
10751076
"Unable to cast type to reference: value is local to type caster");
10761077
using type_caster = detail::make_caster<T>;
10771078
return detail::load_type<T>(handle).operator typename type_caster::template cast_op_type<T>();
10781079
}
10791080

1080-
template <typename T> object cast(const T &value,
1081-
return_value_policy policy = return_value_policy::automatic_reference,
1082-
handle parent = handle()) {
1081+
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
1082+
T cast(const handle &handle) { return {handle, true}; }
1083+
1084+
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
1085+
object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference,
1086+
handle parent = handle()) {
10831087
if (policy == return_value_policy::automatic)
10841088
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
10851089
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
10971101
throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references"
10981102
" (compile in debug mode for details)");
10991103
#else
1100-
throw cast_error("Unable to move from Python " + (std::string) obj.get_type().str() +
1104+
throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) +
11011105
" instance to C++ " + type_id<T>() + " instance: instance has multiple references");
11021106
#endif
11031107

@@ -1274,7 +1278,7 @@ class unpacking_collector {
12741278
int _[] = { 0, (process(args_list, std::forward<Ts>(values)), 0)... };
12751279
ignore_unused(_);
12761280

1277-
m_args = object(PyList_AsTuple(args_list.ptr()), false);
1281+
m_args = std::move(args_list);
12781282
}
12791283

12801284
const tuple &args() const & { return m_args; }
@@ -1336,7 +1340,7 @@ class unpacking_collector {
13361340
#if defined(NDEBUG)
13371341
multiple_values_error();
13381342
#else
1339-
multiple_values_error(k.first.str());
1343+
multiple_values_error(str(k.first));
13401344
#endif
13411345
}
13421346
m_kwargs[k.first] = k.second;

include/pybind11/common.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@
111111
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
112112
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
113113
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
114-
#define PYBIND11_BYTES_CHECK PyBytes_Check
115114
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
116115
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
117116
#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) PyLong_AsUnsignedLongLong(o)
@@ -130,7 +129,6 @@
130129
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
131130
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize
132131
#define PYBIND11_BYTES_AS_STRING PyString_AsString
133-
#define PYBIND11_BYTES_CHECK PyString_Check
134132
#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
135133
#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
136134
#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) (PyInt_Check(o) ? (unsigned long long) PyLong_AsUnsignedLong(o) : PyLong_AsUnsignedLongLong(o))

include/pybind11/numpy.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,10 +540,12 @@ class array : public buffer {
540540

541541
template <typename T, int ExtraFlags = array::forcecast> class array_t : public array {
542542
public:
543-
PYBIND11_OBJECT_CVT(array_t, array, is_non_null, m_ptr = ensure_(m_ptr));
544-
545543
array_t() : array() { }
546544

545+
array_t(handle h, bool borrowed) : array(h, borrowed) { m_ptr = ensure_(m_ptr); }
546+
547+
array_t(const object &o) : array(o) { m_ptr = ensure_(m_ptr); }
548+
547549
explicit array_t(const buffer_info& info) : array(info) { }
548550

549551
array_t(const std::vector<size_t> &shape,
@@ -588,8 +590,6 @@ template <typename T, int ExtraFlags = array::forcecast> class array_t : public
588590
return *(static_cast<T*>(array::mutable_data()) + byte_offset(size_t(index)...) / itemsize());
589591
}
590592

591-
static bool is_non_null(PyObject *ptr) { return ptr != nullptr; }
592-
593593
static PyObject *ensure_(PyObject *ptr) {
594594
if (ptr == nullptr)
595595
return nullptr;

include/pybind11/pybind11.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ class cpp_function : public function {
504504
msg += "\nInvoked with: ";
505505
tuple args_(args, true);
506506
for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) {
507-
msg += static_cast<std::string>(static_cast<object>(args_[ti]).str());
507+
msg += static_cast<std::string>(pybind11::str(args_[ti]));
508508
if ((ti + 1) != args_.size() )
509509
msg += ", ";
510510
}
@@ -665,11 +665,9 @@ class generic_type : public object {
665665
#endif
666666

667667
size_t num_bases = rec->bases.size();
668-
tuple bases(num_bases);
669-
for (size_t i = 0; i < num_bases; ++i)
670-
bases[i] = rec->bases[i];
668+
auto bases = tuple(rec->bases);
671669

672-
std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + rec->name)
670+
std::string full_name = (scope_module ? ((std::string) pybind11::str(scope_module) + "." + rec->name)
673671
: std::string(rec->name));
674672

675673
char *tp_doc = nullptr;
@@ -1470,7 +1468,7 @@ NAMESPACE_BEGIN(detail)
14701468
PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) {
14711469
auto strings = tuple(args.size());
14721470
for (size_t i = 0; i < args.size(); ++i) {
1473-
strings[i] = args[i].str();
1471+
strings[i] = str(args[i]);
14741472
}
14751473
auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" ");
14761474
auto line = sep.attr("join")(strings);
@@ -1654,7 +1652,7 @@ inline function get_type_overload(const void *this_ptr, const detail::type_info
16541652

16551653
/* Don't call dispatch code if invoked from overridden function */
16561654
PyFrameObject *frame = PyThreadState_Get()->frame;
1657-
if (frame && (std::string) pybind11::handle(frame->f_code->co_name).str() == name &&
1655+
if (frame && (std::string) str(frame->f_code->co_name) == name &&
16581656
frame->f_code->co_argcount > 0) {
16591657
PyFrame_FastToLocals(frame);
16601658
PyObject *self_caller = PyDict_GetItem(

0 commit comments

Comments
 (0)