mirror of
https://github.com/pybind/pybind11.git
synced 2025-02-22 08:29:23 +00:00
redesigned cpp_function constructor; significant space savings
This commit is contained in:
parent
07ef518bbb
commit
5984baafd0
@ -95,10 +95,10 @@ struct function_record {
|
|||||||
handle (*impl) (function_record *, handle, handle) = nullptr;
|
handle (*impl) (function_record *, handle, handle) = nullptr;
|
||||||
|
|
||||||
/// Storage for the wrapped function pointer and captured data, if any
|
/// 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)
|
/// 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 associated with this function
|
||||||
return_value_policy policy = return_value_policy::automatic;
|
return_value_policy policy = return_value_policy::automatic;
|
||||||
|
@ -33,77 +33,32 @@ NAMESPACE_BEGIN(pybind11)
|
|||||||
|
|
||||||
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
|
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
|
||||||
class cpp_function : public function {
|
class cpp_function : public function {
|
||||||
protected:
|
|
||||||
/// Picks a suitable return value converter from cast.h
|
|
||||||
template <typename T> using return_value_caster =
|
|
||||||
detail::type_caster<typename std::conditional<
|
|
||||||
std::is_void<T>::value, detail::void_type, typename detail::intrinsic_type<T>::type>::type>;
|
|
||||||
|
|
||||||
/// Picks a suitable argument value converter from cast.h
|
|
||||||
template <typename... T> using arg_value_caster =
|
|
||||||
detail::type_caster<typename std::tuple<T...>>;
|
|
||||||
public:
|
public:
|
||||||
cpp_function() { }
|
cpp_function() { }
|
||||||
|
|
||||||
/// Vanilla function pointers
|
/// Construct a cpp_function from a vanilla function pointer
|
||||||
template <typename Return, typename... Args, typename... Extra>
|
template <typename Return, typename... Args, typename... Extra>
|
||||||
cpp_function(Return (*f)(Args...), const Extra&... extra) {
|
cpp_function(Return (*f)(Args...), const Extra&... extra) {
|
||||||
auto rec = new detail::function_record();
|
initialize(f, f, extra...);
|
||||||
rec->data = (void *) f;
|
|
||||||
|
|
||||||
typedef arg_value_caster<Args...> cast_in;
|
|
||||||
typedef return_value_caster<Return> 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<Extra...>::precall(pyArgs);
|
|
||||||
|
|
||||||
/* Do the call and convert the return value back into the Python domain */
|
|
||||||
handle result = cast_out::cast(
|
|
||||||
args.template call<Return>((Return (*) (Args...)) rec->data),
|
|
||||||
rec->policy, parent);
|
|
||||||
|
|
||||||
/* Invoke call policy post-call hook */
|
|
||||||
detail::process_attributes<Extra...>::postcall(pyArgs, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Process any user-provided function attributes */
|
|
||||||
detail::process_attributes<Extra...>::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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delegating helper constructor to deal with lambda functions
|
/// Construct a cpp_function from a lambda function (possibly with internal state)
|
||||||
template <typename Func, typename... Extra> cpp_function(Func &&f, const Extra&... extra) {
|
template <typename Func, typename... Extra> cpp_function(Func &&f, const Extra&... extra) {
|
||||||
initialize(std::forward<Func>(f),
|
initialize(std::forward<Func>(f),
|
||||||
(typename detail::remove_class<decltype(
|
(typename detail::remove_class<decltype(
|
||||||
&std::remove_reference<Func>::type::operator())>::type *) nullptr, extra...);
|
&std::remove_reference<Func>::type::operator())>::type *) nullptr, extra...);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delegating helper constructor to deal with class methods (non-const)
|
/// Construct a cpp_function from a class method (non-const)
|
||||||
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
|
template <typename Return, typename Class, typename... Arg, typename... Extra>
|
||||||
Return (Class::*f)(Arg...), const Extra&... extra) {
|
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
|
||||||
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
|
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
|
||||||
(Return (*) (Class *, Arg...)) nullptr, extra...);
|
(Return (*) (Class *, Arg...)) nullptr, extra...);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delegating helper constructor to deal with class methods (const)
|
/// Construct a cpp_function from a class method (const)
|
||||||
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
|
template <typename Return, typename Class, typename... Arg, typename... Extra>
|
||||||
Return (Class::*f)(Arg...) const, const Extra&... extra) {
|
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
|
||||||
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
|
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
|
||||||
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
|
(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) */
|
/* Store the function including any extra state it might have (e.g. a lambda capture object) */
|
||||||
auto rec = new detail::function_record();
|
auto rec = new detail::function_record();
|
||||||
rec->data = new capture { std::forward<Func>(f) };
|
|
||||||
|
|
||||||
/* Create a cleanup handler, but only if we have to (less generated code) */
|
/* Store the capture object directly in the function record if there is enough space */
|
||||||
if (!std::is_trivially_destructible<Func>::value)
|
if (sizeof(capture) <= sizeof(rec->data)) {
|
||||||
rec->free_data = [](void *ptr) { delete (capture *) ptr; };
|
new ((capture *) &rec->data) capture { std::forward<Func>(f) };
|
||||||
else
|
if (!std::is_trivially_destructible<Func>::value)
|
||||||
rec->free_data = operator delete;
|
rec->free_data = [](detail::function_record *r) { ((capture *) &r->data)->~capture(); };
|
||||||
|
} else {
|
||||||
|
rec->data[0] = new capture { std::forward<Func>(f) };
|
||||||
|
rec->free_data = [](detail::function_record *r) { delete ((capture *) r->data[0]); };
|
||||||
|
}
|
||||||
|
|
||||||
typedef arg_value_caster<Args...> cast_in;
|
/* Type casters for the function arguments and return value */
|
||||||
typedef return_value_caster<Return> cast_out;
|
typedef detail::type_caster<typename std::tuple<Args...>> cast_in;
|
||||||
|
typedef detail::type_caster<typename std::conditional<
|
||||||
|
std::is_void<Return>::value, detail::void_type,
|
||||||
|
typename detail::intrinsic_type<Return>::type>::type> cast_out;
|
||||||
|
|
||||||
/* Dispatch code which converts function arguments and performs the actual function call */
|
/* Dispatch code which converts function arguments and performs the actual function call */
|
||||||
rec->impl = [](detail::function_record *rec, handle pyArgs, handle parent) -> handle {
|
rec->impl = [](detail::function_record *rec, handle args, handle parent) -> handle {
|
||||||
cast_in args;
|
cast_in args_converter;
|
||||||
|
|
||||||
/* Try to cast the function arguments into the C++ domain */
|
/* 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;
|
return PYBIND11_TRY_NEXT_OVERLOAD;
|
||||||
|
|
||||||
/* Invoke call policy pre-call hook */
|
/* Invoke call policy pre-call hook */
|
||||||
detail::process_attributes<Extra...>::precall(pyArgs);
|
detail::process_attributes<Extra...>::precall(args);
|
||||||
|
|
||||||
/* Do the call and convert the return value back into the Python domain */
|
/* Get a pointer to the capture object */
|
||||||
handle result = cast_out::cast(
|
capture *cap = (capture *) (sizeof(capture) <= sizeof(rec->data)
|
||||||
args.template call<Return>(((capture *) rec->data)->f),
|
? &rec->data : rec->data[0]);
|
||||||
rec->policy, parent);
|
|
||||||
|
/* Perform the functionc all */
|
||||||
|
handle result = cast_out::cast(args_converter.template call<Return>(cap->f),
|
||||||
|
rec->policy, parent);
|
||||||
|
|
||||||
/* Invoke call policy post-call hook */
|
/* Invoke call policy post-call hook */
|
||||||
detail::process_attributes<Extra...>::postcall(pyArgs, result);
|
detail::process_attributes<Extra...>::postcall(args, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@ -160,12 +124,12 @@ protected:
|
|||||||
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name();
|
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name();
|
||||||
|
|
||||||
/* Register the function with Python from generic (non-templated) code */
|
/* 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)
|
/// Register a function call with Python (generic non-templated code goes here)
|
||||||
void initialize(detail::function_record *rec, const char *text,
|
void initialize_generic(detail::function_record *rec, const char *text,
|
||||||
const std::type_info *const *types, int args) {
|
const std::type_info *const *types, int args) {
|
||||||
|
|
||||||
/* Create copies of all referenced C-style strings */
|
/* Create copies of all referenced C-style strings */
|
||||||
rec->name = strdup(rec->name ? rec->name : "");
|
rec->name = strdup(rec->name ? rec->name : "");
|
||||||
@ -336,7 +300,7 @@ protected:
|
|||||||
while (rec) {
|
while (rec) {
|
||||||
detail::function_record *next = rec->next;
|
detail::function_record *next = rec->next;
|
||||||
if (rec->free_data)
|
if (rec->free_data)
|
||||||
rec->free_data(rec->data);
|
rec->free_data(rec);
|
||||||
std::free((char *) rec->name);
|
std::free((char *) rec->name);
|
||||||
std::free((char *) rec->doc);
|
std::free((char *) rec->doc);
|
||||||
std::free((char *) rec->signature);
|
std::free((char *) rec->signature);
|
||||||
|
Loading…
Reference in New Issue
Block a user