Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ set(PYBIND11_HEADERS
include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h
include/pybind11/detail/exception_translation.h
include/pybind11/detail/function_record_pyobject.h
include/pybind11/detail/function_ref.h
include/pybind11/detail/holder_caster_foreign_helpers.h
include/pybind11/detail/init.h
include/pybind11/detail/internals.h
Expand Down
39 changes: 26 additions & 13 deletions docs/advanced/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,10 @@ Certain argument types may support conversion from one type to another. Some
examples of conversions are:

* :ref:`implicit_conversions` declared using ``py::implicitly_convertible<A,B>()``
* Calling a method accepting a double with an integer argument
* Calling a ``std::complex<float>`` argument with a non-complex python type
(for example, with a float). (Requires the optional ``pybind11/complex.h``
header).
* Passing an argument that implements ``__float__`` or ``__index__`` to ``float`` or ``double``.
* Passing an argument that implements ``__int__`` or ``__index__`` to ``int``.
* Passing an argument that implements ``__complex__``, ``__float__``, or ``__index__`` to ``std::complex<float>``.
(Requires the optional ``pybind11/complex.h`` header).
* Calling a function taking an Eigen matrix reference with a numpy array of the
wrong type or of an incompatible data layout. (Requires the optional
``pybind11/eigen.h`` header).
Expand All @@ -452,24 +452,37 @@ object, such as:

.. code-block:: cpp
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
m.def("supports_float", [](double f) { return 0.5 * f; }, py::arg("f"));
m.def("only_float", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
Attempting the call the second function (the one without ``.noconvert()``) with
an integer will succeed, but attempting to call the ``.noconvert()`` version
will fail with a ``TypeError``:
``supports_float`` will accept any argument that implements ``__float__`` or ``__index__``.
``only_float`` will only accept a float or int argument. Anything else will fail with a ``TypeError``:

.. note::

The noconvert behaviour of float, double and complex has changed to match PEP 484.
A float/double argument marked noconvert will accept float or int.
A std::complex<float> argument will accept complex, float or int.

.. code-block:: pycon
>>> floats_preferred(4)
class MyFloat:
def __init__(self, value: float) -> None:
self._value = float(value)
def __repr__(self) -> str:
return f"MyFloat({self._value})"
def __float__(self) -> float:
return self._value
>>> supports_float(MyFloat(4))
2.0
>>> floats_only(4)
>>> only_float(MyFloat(4))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: floats_only(): incompatible function arguments. The following argument types are supported:
TypeError: only_float(): incompatible function arguments. The following argument types are supported:
1. (f: float) -> float
Invoked with: 4
Invoked with: MyFloat(4)
You may, of course, combine this with the :var:`_a` shorthand notation (see
:ref:`keyword_args`) and/or :ref:`default_args`. It is also permitted to omit
Expand Down
27 changes: 14 additions & 13 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,29 +244,28 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
return false;
}

#if !defined(PYPY_VERSION)
auto index_check = [](PyObject *o) { return PyIndex_Check(o); };
#else
// In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`,
// while CPython only considers the existence of `nb_index`/`__index__`.
auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); };
#endif

if (std::is_floating_point<T>::value) {
if (convert || PyFloat_Check(src.ptr())) {
if (convert || PyFloat_Check(src.ptr()) || PYBIND11_LONG_CHECK(src.ptr())) {
py_value = (py_type) PyFloat_AsDouble(src.ptr());
} else {
return false;
}
} else if (PyFloat_Check(src.ptr())
|| (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) {
|| !(convert || PYBIND11_LONG_CHECK(src.ptr())
|| PYBIND11_INDEX_CHECK(src.ptr()))) {
// Explicitly reject float → int conversion even in convert mode.
// This prevents silent truncation (e.g., 1.9 → 1).
// Only int → float conversion is allowed (widening, no precision loss).
// Also reject if none of the conversion conditions are met.
return false;
} else {
handle src_or_index = src;
// PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls.
#if defined(PYPY_VERSION)
object index;
if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr())
// If not a PyLong, we need to call PyNumber_Index explicitly on PyPy.
// When convert is false, we only reach here if PYBIND11_INDEX_CHECK passed above.
if (!PYBIND11_LONG_CHECK(src.ptr())) {
index = reinterpret_steal<object>(PyNumber_Index(src.ptr()));
if (!index) {
PyErr_Clear();
Expand All @@ -286,8 +285,10 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
}
}

// Python API reported an error
bool py_err = py_value == (py_type) -1 && PyErr_Occurred();
bool py_err = (PyErr_Occurred() != nullptr);
if (py_err) {
assert(py_value == static_cast<py_type>(-1));
}

// Check to see if the conversion is valid (integers should match exactly)
// Signed/unsigned checks happen elsewhere
Expand Down
4 changes: 3 additions & 1 deletion include/pybind11/complex.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ class type_caster<std::complex<T>> {
if (!src) {
return false;
}
if (!convert && !PyComplex_Check(src.ptr())) {
if (!convert
&& !(PyComplex_Check(src.ptr()) || PyFloat_Check(src.ptr())
|| PYBIND11_LONG_CHECK(src.ptr()))) {
return false;
}
handle src_or_index = src;
Expand Down
8 changes: 8 additions & 0 deletions include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@
# define PYBIND11_NOINLINE __attribute__((noinline)) inline
#endif

#if defined(_MSC_VER)
# define PYBIND11_ALWAYS_INLINE __forceinline
#elif defined(__GNUC__)
# define PYBIND11_ALWAYS_INLINE __attribute__((__always_inline__)) inline
#else
# define PYBIND11_ALWAYS_INLINE inline
#endif

#if defined(__MINGW32__)
// For unknown reasons all PYBIND11_DEPRECATED member trigger a warning when declared
// whether it is used or not
Expand Down
101 changes: 101 additions & 0 deletions include/pybind11/detail/function_ref.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

//===- llvm/ADT/STLFunctionalExtras.h - Extras for <functional> -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains a header-only class template that provides functionality
// similar to std::function but with non-owning semantics. It is a template-only
// implementation that requires no additional library linking.
//
//===----------------------------------------------------------------------===//

/// An efficient, type-erasing, non-owning reference to a callable. This is
/// intended for use as the type of a function parameter that is not used
/// after the function in question returns.
///
/// This class does not own the callable, so it is not in general safe to store
/// a FunctionRef.

// pybind11: modified again from executorch::runtime::FunctionRef
// - renamed back to function_ref
// - use pybind11 enable_if_t, remove_cvref_t, and remove_reference_t
// - lint suppressions

// torch::executor: modified from llvm::function_ref
// - renamed to FunctionRef
// - removed LLVM_GSL_POINTER and LLVM_LIFETIME_BOUND macro uses
// - use namespaced internal::remove_cvref_t

#pragma once

#include <pybind11/detail/common.h>

#include <cstdint>
#include <type_traits>
#include <utility>

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)

//===----------------------------------------------------------------------===//
// Features from C++20
//===----------------------------------------------------------------------===//

template <typename Fn>
class function_ref;

template <typename Ret, typename... Params>
class function_ref<Ret(Params...)> {
Ret (*callback)(intptr_t callable, Params... params) = nullptr;
intptr_t callable;

template <typename Callable>
// NOLINTNEXTLINE(performance-unnecessary-value-param)
static Ret callback_fn(intptr_t callable, Params... params) {
// NOLINTNEXTLINE(performance-no-int-to-ptr)
return (*reinterpret_cast<Callable *>(callable))(std::forward<Params>(params)...);
}

public:
function_ref() = default;
// NOLINTNEXTLINE(google-explicit-constructor)
function_ref(std::nullptr_t) {}

template <typename Callable>
// NOLINTNEXTLINE(google-explicit-constructor)
function_ref(
Callable &&callable,
// This is not the copy-constructor.
enable_if_t<!std::is_same<remove_cvref_t<Callable>, function_ref>::value> * = nullptr,
// Functor must be callable and return a suitable type.
enable_if_t<
std::is_void<Ret>::value
|| std::is_convertible<decltype(std::declval<Callable>()(std::declval<Params>()...)),
Ret>::value> * = nullptr)
: callback(callback_fn<remove_reference_t<Callable>>),
callable(reinterpret_cast<intptr_t>(&callable)) {}

// NOLINTNEXTLINE(performance-unnecessary-value-param)
Ret operator()(Params... params) const {
return callback(callable, std::forward<Params>(params)...);
}

explicit operator bool() const { return callback; }

bool operator==(const function_ref<Ret(Params...)> &Other) const {
return callable == Other.callable;
}
};
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
12 changes: 6 additions & 6 deletions include/pybind11/detail/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@
/// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version.
#ifndef PYBIND11_INTERNALS_VERSION
# define PYBIND11_INTERNALS_VERSION 11
# define PYBIND11_INTERNALS_VERSION 12
#endif

#if PYBIND11_INTERNALS_VERSION < 11
# error "PYBIND11_INTERNALS_VERSION 11 is the minimum for all platforms for pybind11v3."
#if PYBIND11_INTERNALS_VERSION < 12
# error "PYBIND11_INTERNALS_VERSION 12 is the minimum for all platforms for pybind11 v3.1.0"
#endif

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
Expand Down Expand Up @@ -191,7 +191,7 @@ inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) {
}

struct type_hash {
size_t operator()(const std::type_index &t) const {
size_t operator()(const std::type_index &t) const noexcept {
size_t hash = 5381;
const char *ptr = t.name();
while (auto c = static_cast<unsigned char>(*ptr++)) {
Expand All @@ -202,7 +202,7 @@ struct type_hash {
};

struct type_equal_to {
bool operator()(const std::type_index &lhs, const std::type_index &rhs) const {
bool operator()(const std::type_index &lhs, const std::type_index &rhs) const noexcept {
return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0;
}
};
Expand All @@ -218,7 +218,7 @@ template <typename value_type>
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;

struct override_hash {
size_t operator()(const std::pair<const PyObject *, const char *> &v) const {
size_t operator()(const std::pair<const PyObject *, const char *> &v) const noexcept {
size_t value = std::hash<const void *>()(v.first);
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value << 6) + (value >> 2);
return value;
Expand Down
Loading
Loading