Skip to content

Instantly share code, notes, and snippets.

@jevinskie
Last active October 15, 2024 03:57
Show Gist options
  • Save jevinskie/43c32ff4772007281e34c83fd1703afd to your computer and use it in GitHub Desktop.
Save jevinskie/43c32ff4772007281e34c83fd1703afd to your computer and use it in GitHub Desktop.
C++23 checked libc function wrappers
// clang-format: off
#include "common_internal.hpp"
// clang-format: on
#include "gsnr/utils.hpp"
#include <sys/mman.h>
#include <unistd.h>
namespace gsnr {
int checked_open(const char *GSNR_NONNULL filename, const int flags, const mode_t mode,
const std::source_location &loc) {
return posix_call_impl(loc, ::open, is_negative<int>,
fmt::format("::open(path = \"{:s}\", flags = {:#x}, mode = {:#x})", filename, flags, mode),
filename, flags, mode);
}
int checked_close(const int fildes, const std::source_location &loc) {
return posix_call_impl(loc, ::close, is_non_zero<int>, fmt::format("::close(fildes = {:d})", fildes), fildes);
}
struct stat checked_fstat(const int fildes, const std::source_location &loc) {
struct stat buf;
posix_call_impl(loc, ::fstat, is_non_zero<int>,
fmt::format("::fstat(fildes = {:d}, buf = {})", fildes, fmt::ptr(&buf)), fildes, &buf);
return buf;
}
std::FILE *GSNR_RETURNS_NONNULL checked_fopen(const char *GSNR_NONNULL GSNR_RESTRICT filename,
const char *GSNR_NONNULL GSNR_RESTRICT mode,
const std::source_location &loc) {
return posix_call_impl(loc, std::fopen, is_nullptr<std::FILE *>,
fmt::format("std::fopen(path = \"{:s}\", mode = \"{:s}\")", filename, mode), filename,
mode);
}
int checked_fseek(std::FILE *GSNR_NONNULL stream, const long offset, const int origin,
const std::source_location &loc) {
return posix_call_impl(
loc, std::fseek, is_non_zero<int>,
fmt::format("std::fseek(stream = {}, offset = {:d}, origin = {:d})", fmt::ptr(stream), offset, origin), stream,
offset, origin);
}
void checked_rewind(std::FILE *GSNR_NONNULL stream, const std::source_location &loc) {
posix_call_impl(loc, std::rewind, returns_void, fmt::format("std::rewind(stream = {})", fmt::ptr(stream)), stream);
}
int checked_fclose(std::FILE *GSNR_NONNULL stream, const std::source_location &loc) {
return posix_call_impl(loc, std::fclose, is_non_zero<int>,
fmt::format("std::fclose(stream = {})", fmt::ptr(stream)), stream);
}
int checked_ftell(std::FILE *GSNR_NONNULL stream, const std::source_location &loc) {
return posix_call_impl(loc, std::ftell, is_negative<long>, fmt::format("std::ftell(stream = {})", fmt::ptr(stream)),
stream);
}
void *GSNR_RETURNS_NONNULL checked_mmap(const void *GSNR_NULLABLE addr, const size_t len, const int prot,
const int flags, const int fildes, const off_t off,
const std::source_location &loc) {
return posix_call_impl(
loc, ::mmap, is_nullptr<void *>,
fmt::format("::mmap(addr = {}, len = {:d}, prot = {:#x}, flags = {:#x}, filedes = {:d}, off = {:d})",
fmt::ptr(addr), len, prot, flags, fildes, off),
const_cast<void *>(addr), len, prot, flags, fildes, off);
}
int checked_munmap(const void *GSNR_NONNULL addr, const size_t len, const std::source_location &loc) {
return posix_call_impl(loc, ::munmap, is_non_zero<int>,
fmt::format("::munmap(addr = {}, len = {:d})", fmt::ptr(addr), len),
const_cast<void *>(addr), len);
}
} // namespace gsnr
#pragma once
#include "common.hpp"
#include <cerrno> // For errno
#include <cstdio>
#include <cstring> // For strerror_r
#include <expected> // For std::expected
#include <fcntl.h>
#include <limits>
#include <source_location> // For std::source_location
#include <string> // For std::string
#include <sys/mman.h>
#include <sys/stat.h>
#include <system_error> // For std::system_error
#include <type_traits> // For std::is_pointer_v
#include <debug_assert.hpp>
#include <fmt/format.h> // For fmt::format()
namespace gsnr {
struct utils_module
: debug_assert::default_handler, // use the default handler
debug_assert::set_level<std::numeric_limits<unsigned>{}.max()> // level -1, i.e. all assertions, 0 would mean
// none, 1 would be level 1, 2 level 2 or lower,...
{};
template <typename Func, typename Predicate, typename... Args>
auto posix_call_impl(const std::source_location &loc, Func &&func, Predicate failure_condition,
const std::string &msg = "", Args &&...args) -> decltype(func(args...)) {
using real_res_t = decltype(func(args...));
const auto returns_void = std::is_void_v<real_res_t>;
const auto returns_ptr = std::is_pointer_v<real_res_t>;
using res_t = std::conditional_t<returns_void, std::string_view, real_res_t>;
res_t res;
if constexpr (std::is_void_v<decltype(func(args...))>) {
res = "void"sv;
errno = 0;
func(std::forward<Args>(args)...);
} else {
errno = 0;
res = func(std::forward<Args>(args)...);
}
const auto res_errno = errno;
bool failed = true;
if constexpr (returns_void) {
failed = res_errno != 0;
} else {
failed = failure_condition(res) || res_errno != 0;
}
// Check for the failure condition using the provided predicate
if (failed) {
char buf[256];
const auto strerr_errno = strerror_r(res_errno, buf, sizeof(buf));
if (strerr_errno) {
if constexpr (returns_ptr) {
GSNR_THROW(std::system_error(
res_errno, std::system_category(),
fmt::format("\nLocation: {:s}:{:d}:{:d} (in function {:s})\nPOSIX call failed: (strerror_r itself "
"failed and returned {:d}){:s} (result: {} errno: {:d})",
loc.file_name(), loc.line(), loc.column(), loc.function_name(), strerr_errno,
msg.empty() ? msg : " " + msg, fmt::ptr(res), res_errno)));
} else {
GSNR_THROW(std::system_error(
res_errno, std::system_category(),
fmt::format("\nLocation: {:s}:{:d}:{:d} (in function {:s})\nPOSIX call failed: (strerror_r itself "
"failed and returned {:d}){:s} (result: {} errno: {:d})",
loc.file_name(), loc.line(), loc.column(), loc.function_name(), strerr_errno,
msg.empty() ? msg : " " + msg, res, res_errno)));
}
} else {
if constexpr (returns_ptr) {
GSNR_THROW(
std::system_error(res_errno, std::system_category(),
fmt::format("\nLocation: {:s}:{:d}:{:d} (in function {:s})\nPOSIX call failed: "
"{:s}{:s} (result: {} errno: {:d})",
loc.file_name(), loc.line(), loc.column(), loc.function_name(), buf,
msg.empty() ? msg : " " + msg, fmt::ptr(res), res_errno)));
} else {
GSNR_THROW(std::system_error(res_errno, std::system_category(),
fmt::format("\nLocation: {:s}:{:d}:{:d} (in function {:s})\nPOSIX call "
"failed: {:s}{:s} (result: {} errno: {:d})",
loc.file_name(), loc.line(), loc.column(), loc.function_name(),
buf, msg.empty() ? msg : " " + msg, res, res_errno)));
}
}
}
if constexpr (!returns_void) {
return res;
}
}
#define posix_call(func, failure_condition, ...) \
::gsnr::posix_call_impl(std::source_location::current(), func, failure_condition, " " #func " ", __VA_ARGS__)
#define posix_call_msg(func, failure_condition, msg, ...) \
::gsnr::posix_call_impl(std::source_location::current(), func, failure_condition, msg, __VA_ARGS__)
// Predicates for common failure conditions (using raw function pointers)
template <typename T>
requires(std::is_integral_v<T> && std::is_signed_v<T>)
constexpr bool is_negative(const T result) {
return result < 0;
}
template <typename T>
requires(std::is_integral_v<T> && std::is_signed_v<T>)
constexpr bool is_non_positive(const T result) {
return result <= 0;
}
template <typename T>
requires(std::is_integral_v<T>)
constexpr bool is_non_zero(const T result) {
return result != 0;
}
template <typename T>
requires(std::is_pointer_v<T>)
constexpr bool is_nullptr(const T GSNR_NULLABLE result) {
return result == nullptr;
}
constexpr bool returns_void([[maybe_unused]] const std::string_view res) {
return true;
}
int checked_open(const char *GSNR_NONNULL filename, const int flags, const mode_t mode = 0,
const std::source_location &loc = std::source_location::current());
int checked_close(const int filedes, const std::source_location &loc = std::source_location::current());
struct stat checked_fstat(const int fildes, const std::source_location &loc = std::source_location::current());
std::FILE *GSNR_RETURNS_NONNULL checked_fopen(const char *GSNR_NONNULL GSNR_RESTRICT filename,
const char *GSNR_NONNULL GSNR_RESTRICT mode,
const std::source_location &loc = std::source_location::current());
int checked_fseek(std::FILE *GSNR_NONNULL stream, const long offset, const int origin,
const std::source_location &loc = std::source_location::current());
void checked_rewind(std::FILE *GSNR_NONNULL stream, const std::source_location &loc = std::source_location::current());
int checked_fclose(std::FILE *GSNR_NONNULL stream, const std::source_location &loc = std::source_location::current());
int checked_ftell(std::FILE *GSNR_NONNULL stream, const std::source_location &loc = std::source_location::current());
void *GSNR_RETURNS_NONNULL checked_mmap(const void *GSNR_NULLABLE addr, const size_t len, const int prot,
const int flags, const int fildes, const off_t off,
const std::source_location &loc = std::source_location::current());
int checked_munmap(const void *GSNR_NONNULL addr, const size_t len,
const std::source_location &loc = std::source_location::current());
} // namespace gsnr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment