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>
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/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 @@
'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