A modern C++23 library providing robust error handling utilities
and abstractions built around std::expected
and std::error_code
.
This library provides tools to simplify error handling, improve code readability, and ensure robust error management.
- Error handling with context using
Result<T>
type (based onstd::expected
). - System error code integration with
std::error_code
andstd::error_condition
. - Exception handling utilities with automatic conversion to error codes.
- Convenient error handling for C system calls and errno.
- Custom error categories and conditions.
- Support for chaining error operations.
- Zero overhead abstractions.
- Support for exception-safe code.
- Monadic operations for functional composition on
Result<T>
, fromstd::expected
. - Lightweight and easy to integrate into existing projects.
This library is designed to be thread-safe.
However, the thread safety of your code depends on how you use the library.
If you share Result<T>
objects across threads, ensure proper synchronization.
- C++23 compatible compiler (GCC 14.2.0+ or Clang 19+) with a supported implementation of the C++ standard library, such as libstdc++ 14.2.0 (bundled with GCC C++ compiler).
The library currently supports only UNIX-like systems (Linux, macOS, etc.). Windows support is not guaranteed but will be added in the future.
The library is header-only, so you can easily include it in your project.
- Download the header files from the include/ directory of the repository.
- Place them in your project's include directory.
- Include the headers in your source files as needed.
If your project uses CMake, you can use the FetchContent module to include this library as a dependency.
In your CMakeLists.txt
, add the following lines:
include(FetchContent)
FetchContent_Declare(
cpp_error_utils
GIT_REPOSITORY https://github.com./dr8co/cpp_error_utils.git
GIT_TAG main
)
FetchContent_MakeAvailable(cpp_error_utils)
target_link_libraries(your_target_name PRIVATE cpp_error_utils::cpp_error_utils)
Prebuilt binaries are available at the release page.
You can download the latest release and install it with your package manager.
If you want to manually build and install the library system-wide, you can do so by following these steps:
-
Clone the repository:
git clone https://github.com./dr8co/cpp_error_utils.git
-
Navigate into the cloned directory:
cd cpp_error_utils
-
Build and install the library:
mkdir build && cd build cmake .. cmake --build . --config Release cmake --install . --config Release # might need sudo
The following CMake options are available:
CPP_ERR_BUILD_EXAMPLES
- Build example programs (ON by default)CPP_ERR_BUILD_TESTING
- Build tests (ON by default)CPP_ERR_BUILD_DOC
- Build documentation (OFF by default)CPP_ERR_PACKAGE
- Create installation package (OFF by default)
Include the necessary headers in your C++ files:
#include "error_utils.hpp"
// or, depending on your installation method:
#include <error_utils.hpp>
#include <error_utils.hpp>
#include <string_view>
#include <print>
#include <filesystem>
namespace fs = std::filesystem;
Result<void> print_file_details(std::string_view file_name) {
std::error_code ec{};
const fs::path file_path{file_name};
const auto status = fs::status(file_path, ec);
if (ec) {
// error_utils::Error can be constructed with std::error_code directly
return std::unexpected(error_utils::Error{
ec, std::format("Failed to determine the attributes of '{}'", file_name)
});
}
if (!fs::exists(status)) {
// std::errc constants can also be used, as well as error_utils::ExtraError
return std::unexpected(error_utils::Error{
std::errc::no_such_file_or_directory, std::format("File '{}' does not exist.", file_name)
});
}
std::print("{}", fs::absolute(file_path).string());
switch (status.type()) {
case fs::file_type::regular:
std::println(" is a regular file.");
break;
case fs::file_type::directory:
std::println(" is a directory.");
break;
case fs::file_type::fifo:
std::println(" is a named IPC pipe.");
break;
case fs::file_type::block:
std::println(" is a block device.");
break;
case fs::file_type::socket:
std::println(" is a named IPC socket.");
break;
case fs::file_type::symlink:
std::println(" is a symbolic link.");
break;
case fs::file_type::character:
std::println(" is a character device.");
break;
case fs::file_type::unknown:
std::println(" has 'unknown' type.");
break;
case fs::file_type::none:
std::println(" has 'not-evaluated-yet' type");
break;
default:
std::println(" has an implementation-defined type.");
}
std::print("File permissions: ");
const auto file_perms = status.permissions();
// Adapted from https://en.cppreference.com/w/cpp/filesystem/file_status/permissions
auto show = [=](const char op, const fs::perms perm) {
std::print("{}", fs::perms::none == (perm & file_perms) ? '-' : op);
};
show('r', fs::perms::owner_read);
show('w', fs::perms::owner_write);
show('x', fs::perms::owner_exec);
show('r', fs::perms::group_read);
show('w', fs::perms::group_write);
show('x', fs::perms::group_exec);
show('r', fs::perms::others_read);
show('w', fs::perms::others_write);
show('x', fs::perms::others_exec);
std::println();
return {}; // Success value for Result<void>
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::println("Usage: {} <file>", argv[0]);
return 1;
}
// monadic operations
auto res = print_file_details(argv[1])
.or_else(
[](const error_utils::Error &err) -> Result<void> {
std::println(stderr, "An error occurred: {}", err.message());
return {};
}
);
return 0;
}
// Convert exceptions to Result<T>
#include <error_utils.hpp>
int main() {
auto result = error_utils::try_catch([]() -> error_utils::IntResult {
// Your code that might throw exceptions
return 42;
});
if (result) {
std::println("Success: {}", result.value());
} else {
std::println("Error: {}", result.error().message());
}
return 0;
}
// Handle errno-based C API calls
IntResult create_directory(const std::string& path) {
return error_utils::invoke_with_syscall_api(
[&] noexcept { return mkdir(path.c_str(), 0755); },
std::format("Failed to create directory '{}'", path)
);
}
Check out more examples for additional usage patterns.
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new branch for your feature or bug fix.
- Submit a pull request with a detailed description of your changes.
This project is licensed under the MIT License. See the LICENSE file for details.
For questions or feedback, feel free to open an issue or contact me directly at [email protected].