| // Copyright (C) 2020 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| #pragma once |
| #include "dctv.h" |
| #include "pyutil.h" |
| |
| namespace dctv { |
| |
| // "Interface" for an exception that can set the current |
| // Python exception. |
| struct PyException : virtual std::exception { |
| // Set the stored exception as the Python current exception. |
| // Must be repeatable. |
| virtual void set_pyexception() const noexcept = 0; |
| // std::exception::what implementation that formats the |
| // Python exception. |
| const char* what() const noexcept override; |
| private: |
| // Cache of the what() message; C++17 gives us noexcept init. |
| mutable String msg; |
| }; |
| |
| struct PyExceptionInfo { |
| unique_pyref type, value, traceback; |
| |
| static PyExceptionInfo fetch() noexcept; |
| void normalize() noexcept; |
| void restore() && noexcept; |
| PyExceptionInfo dup() const noexcept; |
| }; |
| |
| // AutoErrorSentinel means _exceptions_to_pyerr should use the |
| // value-initialized version of the return type of functor, e.g., if |
| // functor returns a PyObject*, then the error value is PyObject*(), |
| // i.e., (PyObject*)nullptr. If functor returns void, |
| // _exceptions_to_pyerr also returns void. We use an enum class so |
| // that it's legal for AutoErrorSentinel to be a non-type template |
| // parameter before C++20. |
| enum class AutoErrorSentinel {}; |
| |
| // Set the currently-pending C++ exception as a Python exception. |
| // Type erasure for exception setting: keeps the generated |
| // _exceptions_to_pyerr code small. |
| void _set_pending_cxx_exception_as_pyexception() noexcept; |
| |
| // Call FUNCTOR with no arguments and return whatever it returns. |
| // If FUNCTOR throws instead of returning, convert the exception to a |
| // Python exception, set it as current, and return the error sentinel. |
| // The error sentinel defaults to the value-initialized version of |
| // FUNCTOR's return value, but can be overridden by passing the |
| // ERROR_SENTINEL parameter. |
| template<typename Functor, typename ErrorSentinel = AutoErrorSentinel> |
| auto _exceptions_to_pyerr( |
| Functor&& functor, |
| ErrorSentinel error_sentinel = ErrorSentinel()) |
| noexcept; |
| |
| // Figure out the plain-C type we should accept in the wrapper to |
| // build objects of type T. |
| template<typename T, typename = void> |
| struct py_fn_arg_decay; |
| |
| template<typename T> |
| struct py_fn_arg_decay< |
| T, |
| typename std::enable_if_t<std::is_arithmetic_v<T> || |
| std::is_pointer_v<T>>> |
| { |
| using plain_c_type = T; |
| static T convert(plain_c_type value) { return value; } |
| }; |
| |
| template<typename T> |
| struct py_fn_arg_decay< |
| obj_pyref<T>, |
| typename std::enable_if_t<is_safe_pycast<PyObject, T>()>> |
| { |
| using plain_c_type = PyObject*; |
| static obj_pyref<T> convert(plain_c_type value) { |
| return pyref(value).as<T>(); |
| } |
| }; |
| |
| // Abstract over the difference between function pointers and member |
| // function pointers and provide invocation wrappers. If Munge1st is |
| // true, treat the first argument as a self pointer. |
| // TODO(dancol): rename Munge1st to Arg1IsSelf. |
| template<bool Munge1st, typename R, typename T, typename... Args> |
| struct base_py_fn_info { |
| using first_argument_type = |
| typename std::conditional_t<Munge1st, PyObject*, T*>; |
| using args_tuple_type = std::tuple<T*, Args...>; |
| using delegate_type = R(*)(args_tuple_type) noexcept; |
| template<delegate_type Delegate> |
| static auto plain_c_wrapper( |
| first_argument_type obj, |
| typename py_fn_arg_decay<Args>::plain_c_type... args) noexcept; |
| }; |
| |
| template<bool Munge1st, typename Function> |
| struct py_fn_info; |
| |
| template<bool Munge1st, typename R, typename T, typename... Args> |
| struct py_fn_info<Munge1st, R(*)(T*, Args...)> |
| : base_py_fn_info<Munge1st, R, T, Args...> { |
| static constexpr bool is_noexcept = false; |
| }; |
| |
| template<bool Munge1st, typename R, typename T, typename... Args> |
| struct py_fn_info<Munge1st, R(*)(T*, Args...) noexcept> |
| : base_py_fn_info<Munge1st, R, T, Args...> { |
| static constexpr bool is_noexcept = true; |
| }; |
| |
| template<bool Munge1st, typename R, typename T, typename... Args> |
| struct py_fn_info<Munge1st, R(T::*)(Args...)> |
| : base_py_fn_info<Munge1st, R, T, Args...> { |
| static constexpr bool is_noexcept = false; |
| }; |
| |
| template<bool Munge1st, typename R, typename T, typename... Args> |
| struct py_fn_info<Munge1st, R(T::*)(Args...) noexcept> |
| : base_py_fn_info<Munge1st, R, T, Args...> { |
| static constexpr bool is_noexcept = true; |
| }; |
| |
| template<bool Munge1st, typename R, typename T, typename... Args> |
| struct py_fn_info<Munge1st, R(T::*)(Args...) const > |
| : base_py_fn_info<Munge1st, R, T, Args...> { |
| static constexpr bool is_noexcept = false; |
| }; |
| |
| template<bool Munge1st, typename R, typename T, typename... Args> |
| struct py_fn_info<Munge1st, R(T::*)(Args...) const noexcept> |
| : base_py_fn_info<Munge1st, R, T, Args...> { |
| static constexpr bool is_noexcept = true; |
| }; |
| |
| template<typename FunctionType, |
| typename ErrorSentinelType, |
| bool Munge1st> |
| struct _py_error_wrapper { |
| using info = py_fn_info<Munge1st, FunctionType>; |
| template<FunctionType Function, ErrorSentinelType ErrorSentinel> |
| struct wrapper { |
| static auto delegate(typename info::args_tuple_type args_tuple) noexcept; |
| static constexpr auto function = &info::template plain_c_wrapper<delegate>; |
| }; |
| }; |
| |
| template<auto Function, |
| auto ErrorSentinel = AutoErrorSentinel(), |
| bool Munge1st = true> |
| constexpr auto wraperr(); |
| |
| struct PyExceptionSaver final : NoCopy, NoMove { |
| inline PyExceptionSaver() noexcept; |
| inline ~PyExceptionSaver() noexcept; |
| private: |
| PyExceptionInfo info; |
| }; |
| |
| inline void init_pyerr(pyref m) {}; |
| |
| } // namespace dctv |
| |
| #include "pyerr-inl.h" |