diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 86face3a9..8aa428216 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -95,10 +95,10 @@ struct function_record { handle (*impl) (function_record *, handle, handle) = nullptr; /// Storage for the wrapped function pointer and captured data, if any - void *data = nullptr; + void *data[3] = { }; /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (void *ptr) = nullptr; + void (*free_data) (function_record *ptr) = nullptr; /// Return value policy associated with this function return_value_policy policy = return_value_policy::automatic; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index efa2fa898..e7e07d609 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -33,77 +33,32 @@ NAMESPACE_BEGIN(pybind11) /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object class cpp_function : public function { -protected: - /// Picks a suitable return value converter from cast.h - template using return_value_caster = - detail::type_caster::value, detail::void_type, typename detail::intrinsic_type::type>::type>; - - /// Picks a suitable argument value converter from cast.h - template using arg_value_caster = - detail::type_caster>; public: cpp_function() { } - /// Vanilla function pointers + /// Construct a cpp_function from a vanilla function pointer template cpp_function(Return (*f)(Args...), const Extra&... extra) { - auto rec = new detail::function_record(); - rec->data = (void *) f; - - typedef arg_value_caster cast_in; - typedef return_value_caster cast_out; - - /* Dispatch code which converts function arguments and performs the actual function call */ - rec->impl = [](detail::function_record *rec, handle pyArgs, handle parent) -> handle { - cast_in args; - - /* Try to cast the function arguments into the C++ domain */ - if (!args.load(pyArgs, true)) - return PYBIND11_TRY_NEXT_OVERLOAD; - - /* Invoke call policy pre-call hook */ - detail::process_attributes::precall(pyArgs); - - /* Do the call and convert the return value back into the Python domain */ - handle result = cast_out::cast( - args.template call((Return (*) (Args...)) rec->data), - rec->policy, parent); - - /* Invoke call policy post-call hook */ - detail::process_attributes::postcall(pyArgs, result); - - return result; - }; - - /* Process any user-provided function attributes */ - detail::process_attributes::init(extra..., rec); - - /* Generate a readable signature describing the function's arguments and return value types */ - using detail::descr; - PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); - - /* Register the function with Python from generic (non-templated) code */ - initialize(rec, signature.text(), signature.types(), sizeof...(Args)); + initialize(f, f, extra...); } - /// Delegating helper constructor to deal with lambda functions + /// Construct a cpp_function from a lambda function (possibly with internal state) template cpp_function(Func &&f, const Extra&... extra) { initialize(std::forward(f), (typename detail::remove_class::type::operator())>::type *) nullptr, extra...); } - /// Delegating helper constructor to deal with class methods (non-const) - template cpp_function( - Return (Class::*f)(Arg...), const Extra&... extra) { + /// Construct a cpp_function from a class method (non-const) + template + cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*) (Class *, Arg...)) nullptr, extra...); } - /// Delegating helper constructor to deal with class methods (const) - template cpp_function( - Return (Class::*f)(Arg...) const, const Extra&... extra) { + /// Construct a cpp_function from a class method (const) + template + cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*)(const Class *, Arg ...)) nullptr, extra...); } @@ -119,35 +74,44 @@ protected: /* Store the function including any extra state it might have (e.g. a lambda capture object) */ auto rec = new detail::function_record(); - rec->data = new capture { std::forward(f) }; - /* Create a cleanup handler, but only if we have to (less generated code) */ - if (!std::is_trivially_destructible::value) - rec->free_data = [](void *ptr) { delete (capture *) ptr; }; - else - rec->free_data = operator delete; + /* Store the capture object directly in the function record if there is enough space */ + if (sizeof(capture) <= sizeof(rec->data)) { + new ((capture *) &rec->data) capture { std::forward(f) }; + if (!std::is_trivially_destructible::value) + rec->free_data = [](detail::function_record *r) { ((capture *) &r->data)->~capture(); }; + } else { + rec->data[0] = new capture { std::forward(f) }; + rec->free_data = [](detail::function_record *r) { delete ((capture *) r->data[0]); }; + } - typedef arg_value_caster cast_in; - typedef return_value_caster cast_out; + /* Type casters for the function arguments and return value */ + typedef detail::type_caster> cast_in; + typedef detail::type_caster::value, detail::void_type, + typename detail::intrinsic_type::type>::type> cast_out; /* Dispatch code which converts function arguments and performs the actual function call */ - rec->impl = [](detail::function_record *rec, handle pyArgs, handle parent) -> handle { - cast_in args; + rec->impl = [](detail::function_record *rec, handle args, handle parent) -> handle { + cast_in args_converter; /* Try to cast the function arguments into the C++ domain */ - if (!args.load(pyArgs, true)) + if (!args_converter.load(args, true)) return PYBIND11_TRY_NEXT_OVERLOAD; /* Invoke call policy pre-call hook */ - detail::process_attributes::precall(pyArgs); + detail::process_attributes::precall(args); - /* Do the call and convert the return value back into the Python domain */ - handle result = cast_out::cast( - args.template call(((capture *) rec->data)->f), - rec->policy, parent); + /* Get a pointer to the capture object */ + capture *cap = (capture *) (sizeof(capture) <= sizeof(rec->data) + ? &rec->data : rec->data[0]); + + /* Perform the functionc all */ + handle result = cast_out::cast(args_converter.template call(cap->f), + rec->policy, parent); /* Invoke call policy post-call hook */ - detail::process_attributes::postcall(pyArgs, result); + detail::process_attributes::postcall(args, result); return result; }; @@ -160,12 +124,12 @@ protected: PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); /* Register the function with Python from generic (non-templated) code */ - initialize(rec, signature.text(), signature.types(), sizeof...(Args)); + initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args)); } /// Register a function call with Python (generic non-templated code goes here) - void initialize(detail::function_record *rec, const char *text, - const std::type_info *const *types, int args) { + void initialize_generic(detail::function_record *rec, const char *text, + const std::type_info *const *types, int args) { /* Create copies of all referenced C-style strings */ rec->name = strdup(rec->name ? rec->name : ""); @@ -336,7 +300,7 @@ protected: while (rec) { detail::function_record *next = rec->next; if (rec->free_data) - rec->free_data(rec->data); + rec->free_data(rec); std::free((char *) rec->name); std::free((char *) rec->doc); std::free((char *) rec->signature);