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)
/// 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
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)
template <typename T>
struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
static void init(const arg_t<T> &a, function_record *r) {
template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
static void init(const arg_v &a, function_record *r) {
if (r->class_ && r->args.empty())
r->args.emplace_back("self", nullptr, handle());
/* Convert keyword value into a Python object */
auto o = object(detail::make_caster<T>::cast(
*a.value, return_value_policy::automatic, handle()), false);
if (!o) {
if (!a.value) {
#if !defined(NDEBUG)
std::string descr(typeid(T).name());
detail::clean_type_id(descr);
descr = "'" + std::string(a.name) + ": " + descr + "'";
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 + "'";
@ -264,7 +233,7 @@ struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
"Compile in debug mode for more information.");
#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;
}
/// 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(constexpr_impl)
/// Implementation details for constexpr functions
@ -1044,8 +1082,7 @@ private:
}
}
template <typename T>
void process(list &/*args_list*/, arg_t<T> &&a) {
void process(list &/*args_list*/, arg_v a) {
if (m_kwargs[a.name]) {
#if defined(NDEBUG)
multiple_values_error();
@ -1053,15 +1090,14 @@ private:
multiple_values_error(a.name);
#endif
}
auto o = object(detail::make_caster<T>::cast(*a.value, policy, nullptr), false);
if (!o) {
if (!a.value) {
#if defined(NDEBUG)
argument_cast_error();
#else
argument_cast_error(a.name, type_id<T>());
argument_cast_error(a.name, a.type);
#endif
}
m_kwargs[a.name] = o;
m_kwargs[a.name] = a.value;
}
void process(list &/*args_list*/, detail::kwargs_proxy kp) {

View File

@ -17,7 +17,7 @@ NAMESPACE_BEGIN(pybind11)
/* A few forward declarations */
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; }
/// Holds a reference to a Python object (no reference counting)