mirror of
https://github.com/pybind/pybind11.git
synced 2026-05-13 01:36:21 +00:00
noexcept deduction, added in PR #555, doesn't work with clang's -std=c++1z; and while it works with g++, it isn't entirely clear to me that it is required to work in C++17. What should work, however, is that C++17 allows implicit conversion of a `noexcept(true)` function pointer to a `noexcept(false)` (i.e. default, noexcept-not-specified) function pointer. That was breaking in pybind11 because the cpp_function template used for lambdas provided a better match (i.e. without requiring an implicit conversion), but it then failed. This commit takes a different approach of using SFINAE on the lambda function to prevent it from matching a non-lambda object, which then gets implicit conversion from a `noexcept` function pointer to a `noexcept(false)` function pointer. This much nicer solution also gets rid of the C++17 NOEXCEPT macros, and works in both clang and g++.
82 lines
2.7 KiB
C++
82 lines
2.7 KiB
C++
/*
|
|
pybind11/functional.h: std::function<> support
|
|
|
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
|
|
|
All rights reserved. Use of this source code is governed by a
|
|
BSD-style license that can be found in the LICENSE file.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "pybind11.h"
|
|
#include <functional>
|
|
|
|
NAMESPACE_BEGIN(pybind11)
|
|
NAMESPACE_BEGIN(detail)
|
|
|
|
template <typename Return, typename... Args>
|
|
struct type_caster<std::function<Return(Args...)>> {
|
|
using type = std::function<Return(Args...)>;
|
|
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
|
using function_type = Return (*) (Args...);
|
|
|
|
public:
|
|
bool load(handle src_, bool) {
|
|
if (src_.is_none())
|
|
return true;
|
|
|
|
src_ = detail::get_function(src_);
|
|
if (!src_ || !PyCallable_Check(src_.ptr()))
|
|
return false;
|
|
|
|
/*
|
|
When passing a C++ function as an argument to another C++
|
|
function via Python, every function call would normally involve
|
|
a full C++ -> Python -> C++ roundtrip, which can be prohibitive.
|
|
Here, we try to at least detect the case where the function is
|
|
stateless (i.e. function pointer or lambda function without
|
|
captured variables), in which case the roundtrip can be avoided.
|
|
*/
|
|
if (PyCFunction_Check(src_.ptr())) {
|
|
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(src_.ptr()));
|
|
auto rec = (function_record *) c;
|
|
|
|
if (rec && rec->is_stateless && rec->data[1] == &typeid(function_type)) {
|
|
struct capture { function_type f; };
|
|
value = ((capture *) &rec->data)->f;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
auto src = reinterpret_borrow<object>(src_);
|
|
value = [src](Args... args) -> Return {
|
|
gil_scoped_acquire acq;
|
|
object retval(src(std::move(args)...));
|
|
/* Visual studio 2015 parser issue: need parentheses around this expression */
|
|
return (retval.template cast<Return>());
|
|
};
|
|
return true;
|
|
}
|
|
|
|
template <typename Func>
|
|
static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
|
|
if (!f_)
|
|
return none().inc_ref();
|
|
|
|
auto result = f_.template target<function_type>();
|
|
if (result)
|
|
return cpp_function(*result, policy).release();
|
|
else
|
|
return cpp_function(std::forward<Func>(f_), policy).release();
|
|
}
|
|
|
|
PYBIND11_TYPE_CASTER(type, _("Callable[[") +
|
|
argument_loader<Args...>::arg_names() + _("], ") +
|
|
make_caster<retval_type>::name() +
|
|
_("]"));
|
|
};
|
|
|
|
NAMESPACE_END(detail)
|
|
NAMESPACE_END(pybind11)
|