diff options
| author | William A. Kennington III <wak@google.com> | 2018-11-16 19:56:10 -0800 |
|---|---|---|
| committer | William A. Kennington III <wak@google.com> | 2019-03-29 21:14:57 +0000 |
| commit | bff0b0fe903868a08f18d170a308e76d497b7fbb (patch) | |
| tree | 95af1ab6c7b68cdb932bcb28c6e6773fc904871f /src | |
| parent | e847ef8bbd5accd36a3287c686894418721c6798 (diff) | |
| download | stdplus-bff0b0fe903868a08f18d170a308e76d497b7fbb.tar.gz stdplus-bff0b0fe903868a08f18d170a308e76d497b7fbb.zip | |
util/cexec: Implement exception helpers
See src/stdplus/util/cexec.hpp callCheckErrno for the motivation behind
this change.
Change-Id: I0225b87398b632624f2ef8ccd6c00b5dd6b7e056
Signed-off-by: William A. Kennington III <wak@google.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/meson.build | 4 | ||||
| -rw-r--r-- | src/stdplus/util/cexec.hpp | 113 |
3 files changed, 119 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d45007e..2c1f835 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,3 +10,5 @@ nobase_include_HEADERS += stdplus/handle/managed.hpp nobase_include_HEADERS += stdplus/signal.hpp libstdplus_la_SOURCES += stdplus/signal.cpp + +nobase_include_HEADERS += stdplus/util/cexec.hpp diff --git a/src/meson.build b/src/meson.build index a6ff714..8744d33 100644 --- a/src/meson.build +++ b/src/meson.build @@ -23,3 +23,7 @@ install_headers( 'stdplus/handle/copyable.hpp', 'stdplus/handle/managed.hpp', subdir: 'stdplus/handle') + +install_headers( + 'stdplus/util/cexec.hpp', + subdir: 'stdplus/util') diff --git a/src/stdplus/util/cexec.hpp b/src/stdplus/util/cexec.hpp new file mode 100644 index 0000000..86dfe88 --- /dev/null +++ b/src/stdplus/util/cexec.hpp @@ -0,0 +1,113 @@ +#pragma once +#include <functional> +#include <system_error> +#include <type_traits> +#include <utility> + +namespace stdplus +{ +namespace util +{ + +/** @brief Common pattern used by default for constructing a system exception + * @details Most libc or system calls will want to return a generic + * system_error when detecting an error in a call. This function + * creates that error from the errno and message. + * + * @param[in] error - + * @param[in] msg - + * @return The exception passed to a `throw` call. + */ +inline auto makeSystemError(int error, const char* msg) +{ + return std::system_error(error, std::generic_category(), msg); +} + +/** @brief Wraps common c style error handling for exception throwing + * This requires the callee to set errno on error. + * @details We often have a pattern in our code for checking errors and + * propagating up exceptions: + * + * int c_call(const char* path); + * + * int our_cpp_call(const char* path) + * { + * int r = c_call(path); + * if (r < 0) + * { + * throw std::system_error(errno, std::generic_category(), + * "our msg"); + * } + * return r; + * } + * + * To make that more succinct, we can use callCheckErrno: + * + * int our_cpp_call(const char* path) + * { + * return callCheckErrno("our msg", c_call, path); + * } + * + * @param[in] msg - The error message displayed when errno is set. + * @param[in] func - The wrapped function we invoke + * @param[in] args... - The arguments passed to the function + * @throws std::system_error for an error case. + * @return A successful return value based on the function type + */ +template <auto (*makeError)(int, const char*) = makeSystemError, + typename... Args> +inline auto callCheckErrno(const char* msg, Args&&... args) +{ + using Ret = typename std::invoke_result<Args...>::type; + + if constexpr (std::is_integral_v<Ret> && std::is_signed_v<Ret>) + { + Ret r = std::invoke(std::forward<Args>(args)...); + if (r < 0) + throw makeError(errno, msg); + return r; + } + else if constexpr (std::is_pointer_v<Ret>) + { + Ret r = std::invoke(std::forward<Args>(args)...); + if (r == nullptr) + throw makeError(errno, msg); + return r; + } + else + { + static_assert(std::is_same_v<Ret, int>, "Unimplemented check routine"); + } +} + +/** @brief Wraps common c style error handling for exception throwing + * This requires the callee to provide error information in -r. + * See callCheckErrno() for details. + * + * @param[in] msg - The error message displayed when errno is set. + * @param[in] func - The wrapped function we invoke + * @param[in] args... - The arguments passed to the function + * @throws std::system_error for an error case. + * @return A successful return value based on the function type + */ +template <auto (*makeError)(int, const char*) = makeSystemError, + typename... Args> +inline auto callCheckRet(const char* msg, Args&&... args) +{ + using Ret = typename std::invoke_result<Args...>::type; + + if constexpr (std::is_integral_v<Ret> && std::is_signed_v<Ret>) + { + Ret r = std::invoke(std::forward<Args>(args)...); + if (r < 0) + throw makeError(-r, msg); + return r; + } + else + { + static_assert(std::is_same_v<Ret, int>, "Unimplemented check routine"); + } +} + +} // namespace util +} // namespace stdplus |

