Make keyword argument hold a py::object instead of T*

With this change arg_t is no longer a template, but it must remain so
for backward compatibility. Thus, a non-template arg_v is introduced,
while a dummy template alias arg_t is there to keep old code from
breaking. This can be remove in the next major release.

The implementation of arg_v also needed to be placed a little earlier in
the headers because it's not a template any more and unpacking_collector
needs more than a forward declaration.
This commit is contained in:
Dean Moldovan 2016-09-06 00:49:21 +02:00
parent 8fe13b8896
commit 60b26802fd
3 changed files with 48 additions and 43 deletions

View File

@ -14,30 +14,6 @@
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
/// Annotation for keyword arguments
struct arg {
constexpr explicit arg(const char *name) : name(name) { }
template <typename T>
constexpr arg_t<T> operator=(const T &value) const { return {name, value}; }
const char *name;
};
/// Annotation for keyword arguments with default values
template <typename T> struct arg_t : public arg {
constexpr arg_t(const char *name, const T &value, const char *descr = nullptr)
: arg(name), value(&value), descr(descr) { }
const T *value;
const char *descr;
};
inline namespace literals {
/// String literal version of arg
constexpr arg operator"" _a(const char *name, size_t) { return arg(name); }
}
/// Annotation for methods /// Annotation for methods
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
@ -233,21 +209,14 @@ template <> struct process_attribute<arg> : process_attribute_default<arg> {
}; };
/// Process a keyword argument attribute (*with* a default value) /// Process a keyword argument attribute (*with* a default value)
template <typename T> template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> { static void init(const arg_v &a, function_record *r) {
static void init(const arg_t<T> &a, function_record *r) {
if (r->class_ && r->args.empty()) if (r->class_ && r->args.empty())
r->args.emplace_back("self", nullptr, handle()); r->args.emplace_back("self", nullptr, handle());
/* Convert keyword value into a Python object */ if (!a.value) {
auto o = object(detail::make_caster<T>::cast(
*a.value, return_value_policy::automatic, handle()), false);
if (!o) {
#if !defined(NDEBUG) #if !defined(NDEBUG)
std::string descr(typeid(T).name()); auto descr = "'" + std::string(a.name) + ": " + a.type + "'";
detail::clean_type_id(descr);
descr = "'" + std::string(a.name) + ": " + descr + "'";
if (r->class_) { if (r->class_) {
if (r->name) if (r->name)
descr += " in method '" + (std::string) r->class_.str() + "." + (std::string) r->name + "'"; descr += " in method '" + (std::string) r->class_.str() + "." + (std::string) r->name + "'";
@ -264,7 +233,7 @@ struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
"Compile in debug mode for more information."); "Compile in debug mode for more information.");
#endif #endif
} }
r->args.emplace_back(a.name, a.descr, o.release()); r->args.emplace_back(a.name, a.descr, a.value.inc_ref());
} }
}; };

View File

@ -949,6 +949,44 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
return result; return result;
} }
/// Annotation for keyword arguments
struct arg {
constexpr explicit arg(const char *name) : name(name) { }
template <typename T> arg_v operator=(T &&value) const;
const char *name;
};
/// Annotation for keyword arguments with values
struct arg_v : arg {
template <typename T>
arg_v(const char *name, T &&x, const char *descr = nullptr)
: arg(name),
value(detail::make_caster<T>::cast(x, return_value_policy::automatic, handle()), false),
descr(descr)
#if !defined(NDEBUG)
, type(type_id<T>())
#endif
{ }
object value;
const char *descr;
#if !defined(NDEBUG)
std::string type;
#endif
};
template <typename T>
arg_v arg::operator=(T &&value) const { return {name, std::forward<T>(value)}; }
/// Alias for backward compatibility -- to be remove in version 2.0
template <typename /*unused*/> using arg_t = arg_v;
inline namespace literals {
/// String literal version of arg
constexpr arg operator"" _a(const char *name, size_t) { return arg(name); }
}
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
NAMESPACE_BEGIN(constexpr_impl) NAMESPACE_BEGIN(constexpr_impl)
/// Implementation details for constexpr functions /// Implementation details for constexpr functions
@ -1044,8 +1082,7 @@ private:
} }
} }
template <typename T> void process(list &/*args_list*/, arg_v a) {
void process(list &/*args_list*/, arg_t<T> &&a) {
if (m_kwargs[a.name]) { if (m_kwargs[a.name]) {
#if defined(NDEBUG) #if defined(NDEBUG)
multiple_values_error(); multiple_values_error();
@ -1053,15 +1090,14 @@ private:
multiple_values_error(a.name); multiple_values_error(a.name);
#endif #endif
} }
auto o = object(detail::make_caster<T>::cast(*a.value, policy, nullptr), false); if (!a.value) {
if (!o) {
#if defined(NDEBUG) #if defined(NDEBUG)
argument_cast_error(); argument_cast_error();
#else #else
argument_cast_error(a.name, type_id<T>()); argument_cast_error(a.name, a.type);
#endif #endif
} }
m_kwargs[a.name] = o; m_kwargs[a.name] = a.value;
} }
void process(list &/*args_list*/, detail::kwargs_proxy kp) { void process(list &/*args_list*/, detail::kwargs_proxy kp) {

View File

@ -17,7 +17,7 @@ NAMESPACE_BEGIN(pybind11)
/* A few forward declarations */ /* A few forward declarations */
class object; class str; class iterator; class object; class str; class iterator;
struct arg; template <typename T> struct arg_t; struct arg; struct arg_v;
namespace detail { class accessor; class args_proxy; class kwargs_proxy; } namespace detail { class accessor; class args_proxy; class kwargs_proxy; }
/// Holds a reference to a Python object (no reference counting) /// Holds a reference to a Python object (no reference counting)