blob: 16b9db15df370b275f4e722b321c52def6f665d8 [file] [log] [blame]
// 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"