Skip to content

Commit ca1245d

Browse files
committed
Simplify implementation
This simplifies the PR implementation by adding a `static cpp_function method()` to `cpp_function` that takes care of generating the derived-type lambdas when given a base pointer function (and otherwise just forwards arguments to the cpp_function constructor).
1 parent 04214b8 commit ca1245d

File tree

4 files changed

+58
-42
lines changed

4 files changed

+58
-42
lines changed

include/pybind11/attr.h

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,7 @@ NAMESPACE_BEGIN(pybind11)
1818
/// @{
1919

2020
/// Annotation for methods
21-
template <typename CppClass>
22-
struct is_method {
23-
handle class_;
24-
is_method(const handle &c) : class_(c) { }
25-
using Class = CppClass;
26-
template <typename DeducedClass>
27-
using BindClass = detail::conditional_t<std::is_base_of<DeducedClass, Class>::value,
28-
Class, DeducedClass>;
29-
};
21+
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
3022

3123
/// Annotation for operators
3224
struct is_operator { };
@@ -329,8 +321,8 @@ template <> struct process_attribute<sibling> : process_attribute_default<siblin
329321
};
330322

331323
/// Process an attribute which indicates that this function is a method
332-
template <typename Class> struct process_attribute<is_method<Class>> : process_attribute_default<is_method<Class>> {
333-
static void init(const is_method<Class> &s, function_record *r) { r->is_method = true; r->scope = s.class_; }
324+
template <> struct process_attribute<is_method> : process_attribute_default<is_method> {
325+
static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; }
334326
};
335327

336328
/// Process an attribute which indicates the parent scope of a method
@@ -470,7 +462,7 @@ using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extr
470462
/// Check the number of named arguments at compile time
471463
template <typename... Extra,
472464
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
473-
size_t self = constexpr_sum(is_instantiation<is_method, Extra>::value...)>
465+
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
474466
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
475467
return named == 0 || (self + named + has_args + has_kwargs) == nargs;
476468
}

include/pybind11/common.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,11 @@ template <typename T> struct remove_class { };
511511
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
512512
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...) const> { typedef R type(A...); };
513513

514+
/// Extract the class from a method type
515+
template <typename T> struct extract_class { };
516+
template <typename C, typename R, typename... A> struct extract_class<R (C::*)(A...)> { typedef C type; };
517+
template <typename C, typename R, typename... A> struct extract_class<R (C::*)(A...) const> { typedef C type; };
518+
514519
/// Helper template to strip away type modifiers
515520
template <typename T> struct intrinsic_type { typedef T type; };
516521
template <typename T> struct intrinsic_type<const T> { typedef typename intrinsic_type<T>::type type; };
@@ -582,6 +587,11 @@ using exactly_one_t = typename exactly_one<Predicate, Default, Ts...>::type;
582587
template <typename T, typename... /*Us*/> struct deferred_type { using type = T; };
583588
template <typename T, typename... Us> using deferred_t = typename deferred_type<T, Us...>::type;
584589

590+
/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of<T, T>::value == false`,
591+
/// unlike `std::is_base_of`)
592+
template <typename Base, typename Derived> using is_strict_base_of = bool_constant<
593+
std::is_base_of<Base, Derived>::value && !std::is_base_of<Derived, Base>::value>;
594+
585595
template <template<typename...> class Base>
586596
struct is_template_base_of_impl {
587597
template <typename... Us> static std::true_type check(Base<Us...> *);

include/pybind11/pybind11.h

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ NAMESPACE_BEGIN(pybind11)
4444

4545
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
4646
class cpp_function : public function {
47-
private:
48-
template <typename Extra> using is_method_annotation = detail::is_instantiation<is_method, Extra>;
4947
public:
5048
cpp_function() { }
5149

@@ -68,27 +66,42 @@ class cpp_function : public function {
6866
(FuncType *) nullptr, extra...);
6967
}
7068

71-
/// Construct a cpp_function from a class method (non-const)
69+
/// Construct a cpp_function from a class method (non-const); the exposed class argument type is
70+
/// given by BindClass.
7271
template <typename Return, typename Class, typename... Arg, typename... Extra>
7372
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
74-
using ClassArg = typename detail::exactly_one_t<is_method_annotation, is_method<Class>, Extra...>
75-
::template BindClass<Class>;
76-
initialize([f](ClassArg *c, Arg... args) -> Return { return (c->*f)(args...); },
77-
(Return (*) (ClassArg *, Arg...)) nullptr, extra...);
73+
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
74+
(Return (*) (Class *, Arg...)) nullptr, extra...);
7875
}
7976

8077
/// Construct a cpp_function from a class method (const)
8178
template <typename Return, typename Class, typename... Arg, typename... Extra>
8279
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
83-
using ClassArg = typename detail::exactly_one_t<is_method_annotation, is_method<Class>, Extra...>
84-
::template BindClass<Class>;
85-
initialize([f](const ClassArg *c, Arg... args) -> Return { return (c->*f)(args...); },
86-
(Return (*)(const ClassArg *, Arg ...)) nullptr, extra...);
80+
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
81+
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
8782
}
8883

8984
/// Return the function name
9085
object name() const { return attr("__name__"); }
9186

87+
/// Used to construct a cpp_function that might be a method of a base type; in such a case, this
88+
/// wraps the method in a lambda accepting the given type (rather than the derived type) as the
89+
/// `self` argument. Otherwise this simply forwards to the constructors above.
90+
template <typename Derived, typename Func, typename... Extra>
91+
static cpp_function method(Func &&f, const Extra&... extra) {
92+
return {std::forward<Func>(f), extra...};
93+
}
94+
template <typename Derived, typename Return, typename Class, typename... Arg, typename... Extra,
95+
detail::enable_if_t<detail::is_strict_base_of<Class, Derived>::value, int> = 0>
96+
static cpp_function method(Return (Class::*f)(Arg...), const Extra&... extra) {
97+
return {[f](Derived *c, Arg... args) -> Return { return (c->*f)(args...); }, extra...};
98+
}
99+
template <typename Derived, typename Return, typename Class, typename... Arg, typename... Extra,
100+
detail::enable_if_t<detail::is_strict_base_of<Class, Derived>::value, int> = 0>
101+
static cpp_function method(Return (Class::*f)(Arg...) const, const Extra&... extra) {
102+
return {[f](const Derived *c, Arg... args) -> Return { return (c->*f)(args...); }, extra...};
103+
}
104+
92105
protected:
93106
/// Space optimization: don't inline this frequently instantiated fragment
94107
PYBIND11_NOINLINE detail::function_record *make_function_record() {
@@ -917,8 +930,8 @@ NAMESPACE_END(detail)
917930
template <typename type_, typename... options>
918931
class class_ : public detail::generic_type {
919932
template <typename T> using is_holder = detail::is_holder_type<type_, T>;
920-
template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>;
921-
template <typename T> using is_base = detail::bool_constant<std::is_base_of<T, type_>::value && !std::is_same<T, type_>::value>;
933+
template <typename T> using is_subtype = detail::is_strict_base_of<type_, T>;
934+
template <typename T> using is_base = detail::is_strict_base_of<T, type_>;
922935
// struct instead of using here to help MSVC:
923936
template <typename T> struct is_valid_class_option :
924937
detail::any_of<is_holder<T>, is_subtype<T>, is_base<T>> {};
@@ -984,8 +997,9 @@ class class_ : public detail::generic_type {
984997

985998
template <typename Func, typename... Extra>
986999
class_ &def(const char *name_, Func&& f, const Extra&... extra) {
987-
cpp_function cf(std::forward<Func>(f), name(name_), is_method<type>(*this),
988-
sibling(getattr(*this, name_, none())), extra...);
1000+
auto cf = cpp_function::method<type>(
1001+
std::forward<Func>(f), name(name_), is_method(*this),
1002+
sibling(getattr(*this, name_, none())), extra...);
9891003
attr(cf.name()) = cf;
9901004
return *this;
9911005
}
@@ -1048,18 +1062,18 @@ class class_ : public detail::generic_type {
10481062

10491063
template <typename C, typename D, typename... Extra>
10501064
class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) {
1051-
using BindC = typename is_method<type>::template BindClass<C>;
1052-
def_property(name,
1053-
[pm](const BindC &c) -> const D &{ return c.*pm; },
1054-
[pm](BindC &c, const D &value) { c.*pm = value; },
1055-
extra...);
1065+
static_assert(std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
1066+
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)),
1067+
fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this));
1068+
def_property(name, fget, fset, return_value_policy::reference_internal, extra...);
10561069
return *this;
10571070
}
10581071

10591072
template <typename C, typename D, typename... Extra>
10601073
class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) {
1061-
using BindC = typename is_method<type>::template BindClass<C>;
1062-
def_property_readonly(name, [pm](const BindC &c) -> const D &{ return c.*pm; }, extra...);
1074+
static_assert(std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
1075+
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this));
1076+
def_property_readonly(name, fget, return_value_policy::reference_internal, extra...);
10631077
return *this;
10641078
}
10651079

@@ -1081,7 +1095,8 @@ class class_ : public detail::generic_type {
10811095
/// Uses return_value_policy::reference_internal by default
10821096
template <typename Getter, typename... Extra>
10831097
class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) {
1084-
return def_property_readonly(name, cpp_function(fget, is_method<type>(*this)), return_value_policy::reference_internal, extra...);
1098+
return def_property_readonly(name, cpp_function::method<type>(fget),
1099+
return_value_policy::reference_internal, extra...);
10851100
}
10861101

10871102
/// Uses cpp_function's return_value_policy by default
@@ -1105,21 +1120,17 @@ class class_ : public detail::generic_type {
11051120
/// Uses return_value_policy::reference_internal by default
11061121
template <typename Getter, typename Setter, typename... Extra>
11071122
class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) {
1108-
return def_property(name, fget,
1109-
cpp_function(fset, is_method<type>(*this), return_value_policy::reference_internal),
1110-
extra...);
1123+
return def_property(name, fget, cpp_function::method<type>(fset, return_value_policy::reference_internal), extra...);
11111124
}
11121125
template <typename Getter, typename... Extra>
11131126
class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) {
1114-
return def_property(name,
1115-
cpp_function(fget, is_method<type>(*this), return_value_policy::reference_internal),
1116-
fset, extra...);
1127+
return def_property(name, cpp_function::method<type>(fget), fset, return_value_policy::reference_internal, extra...);
11171128
}
11181129

11191130
/// Uses cpp_function's return_value_policy by default
11201131
template <typename... Extra>
11211132
class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
1122-
return def_property_static(name, fget, fset, is_method<type>(*this), extra...);
1133+
return def_property_static(name, fget, fset, is_method(*this), extra...);
11231134
}
11241135

11251136
/// Uses return_value_policy::reference by default

tests/test_methods_and_attributes.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,9 @@ test_initializer methods_and_attributes([](py::module &m) {
371371
.def("increase_value", &RegisteredDerived::increase_value)
372372
.def_readwrite("rw_value", &RegisteredDerived::rw_value)
373373
.def_readonly("ro_value", &RegisteredDerived::ro_value)
374+
// These should trigger a static_assert if uncommented
375+
//.def_readwrite("fails", &SimpleValue::value) // should trigger a static_assert if uncommented
376+
//.def_readonly("fails", &SimpleValue::value) // should trigger a static_assert if uncommented
374377
.def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int)
375378
.def_property_readonly("ro_value_prop", &RegisteredDerived::get_double)
376379
// This one is in the registered class:

0 commit comments

Comments
 (0)