#ifndef PYTHONWRAPPERUTILITIES_H #define PYTHONWRAPPERUTILITIES_H #include #include #include #include #include "error.h" #include "gilock.h" namespace details { /** * @brief Common stuffs for all basicWrapperFunction methods. */ template ReturnType wrapperFunctionImplementation(WrapperTypePtr wrapper, Fn fn, boost::python::object* objPtr, const char *methodName, Args... args) { GILock lock; boost::python::override implementation = wrapper->get_override(methodName); if (!implementation) { if constexpr (std::is_same_v) { throw pyexcept::MissingImplementation(wrapper->className, methodName); } else { return std::invoke(fn, wrapper, args...); } } try { boost::python::object result = implementation(args...); if (objPtr) { *objPtr = result; } if constexpr (!std::is_same_v) { return boost::python::extract(result)(); } } catch (const boost::python::error_already_set&) { throw pyexcept::PythonError(); } catch (...) { throw pyexcept::UnknownException(); } } } /** * @brief Call the given method on the wrapper with the given arguments, with proper * exception handling. * * @param wrapper The wrapper object to use to retrieve the python method. Must have a publicly * available `className` attribute. * @param methodName The name of the method. * @param args... Arguments for the method. * * @return the result of calling the given Python method on the wrapper. * * @throw pyexcept::MissingImplementation if the method does not exist. * @throw pyexcept::PythonError if an error occurs while executing the python method. * @throw pyexecpt::UnknownException if an unknown error occurs. */ template ReturnType basicWrapperFunctionImplementation(const WrapperType *wrapper, const char *methodName, Args... args) { return details::wrapperFunctionImplementation(wrapper, nullptr, nullptr, methodName, args...); } /** * @brief Call the given method on the wrapper with the given arguments, with proper * exception handling, and store the intermediate result in the given python object. * * @param wrapper The wrapper object to use to retrieve the python method. Must have a publicly * available `className` attribute. * @param ref Python object to which the result of `get_override()` should be stored. * @param methodName The name of the method. * @param args... Arguments for the method. * * @return the result of calling the given Python method on the wrapper. * * @throw pyexcept::MissingImplementation if the method does not exist. * @throw pyexcept::PythonError if an error occurs while executing the python method. * @throw pyexecpt::UnknownException if an unknown error occurs. */ template ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, boost::python::object &ref, const char* methodName, Args... args) { return details::wrapperFunctionImplementation(wrapper, nullptr, &ref, methodName, args...); } /** * @brief Call the given method on the wrapper with the given arguments, with proper * exception handling, falling back to the given function if the method does not exist. * * @param wrapper The wrapper object to use to retrieve the python method. Must have a publicly * available `className` attribute. * @param fn The function to call if the method does not exists. * @param methodName The name of the method. * @param args... Arguments for the method. * * Note: `fn` does not have to be a member-function of `wrapper` but `std::invoke(fn, wrapper, args...)` must be valid. * * @return the result of calling the given Python method on the wrapper. * * @throw pyexcept::PythonError if an error occurs while executing the python method. * @throw pyexecpt::UnknownException if an unknown error occurs. */ template ReturnType basicWrapperFunctionImplementationWithDefault(WrapperTypePtr wrapper, Fn fn, const char* methodName, Args... args) { return details::wrapperFunctionImplementation(wrapper, fn, nullptr, methodName, args...); } #endif // PYTHONWRAPPERUTILITIES_H