Thread System 0.3.1
High-performance C++20 thread pool with work stealing and DAG scheduling
Loading...
Searching...
No Matches
error_handling.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
5#pragma once
6
15#include <string>
16#include <optional>
17#include <type_traits>
18#include <string_view>
19#include <ostream>
20#include <functional>
21#include <stdexcept>
22
23namespace kcenon::thread {
24
39enum class error_code {
40 // General errors (-100 to -109)
41 success = 0,
42 unknown_error = -101,
43 operation_canceled = -102,
44 operation_timeout = -103,
45 not_implemented = -104,
46 invalid_argument = -105,
47
48 // Thread errors (-110 to -119)
50 thread_not_running = -111,
53
54 // Queue errors (-120 to -129)
55 queue_full = -120,
56 queue_empty = -121,
57 queue_stopped = -122,
58 queue_busy = -123, // Queue is temporarily busy with concurrent operations
59
60 // Job errors (-130 to -139)
63 job_invalid = -132,
64
65 // Resource errors (-140 to -149)
68
69 // Synchronization errors (-150 to -159)
70 mutex_error = -150,
71 deadlock_detected = -151,
73
74 // IO errors (-160 to -169)
75 io_error = -160,
76 file_not_found = -161
77};
78
79// Compile-time validation for error code range
80static_assert(static_cast<int>(error_code::unknown_error) >= -199 &&
81 static_cast<int>(error_code::unknown_error) <= -100,
82 "error_code values must be in range [-199, -100]");
83static_assert(static_cast<int>(error_code::file_not_found) >= -199 &&
84 static_cast<int>(error_code::file_not_found) <= -100,
85 "error_code values must be in range [-199, -100]");
86
92inline std::string error_code_to_string(error_code code) {
93 using map_entry = std::pair<error_code, std::string_view>;
94 static constexpr map_entry kMap[] = {
95 {error_code::success, "Success"},
96 {error_code::unknown_error, "Unknown error"},
97 {error_code::operation_canceled, "Operation canceled"},
98 {error_code::operation_timeout, "Operation timed out"},
99 {error_code::not_implemented, "Not implemented"},
100 {error_code::invalid_argument, "Invalid argument"},
101 {error_code::thread_already_running, "Thread is already running"},
102 {error_code::thread_not_running, "Thread is not running"},
103 {error_code::thread_start_failure, "Failed to start thread"},
104 {error_code::thread_join_failure, "Failed to join thread"},
105 {error_code::queue_full, "Queue is full"},
106 {error_code::queue_empty, "Queue is empty"},
107 {error_code::queue_stopped, "Queue is stopped"},
108 {error_code::queue_busy, "Queue is busy"},
109 {error_code::job_creation_failed, "Failed to create job"},
110 {error_code::job_execution_failed, "Failed to execute job"},
111 {error_code::job_invalid, "Invalid job"},
112 {error_code::resource_allocation_failed, "Failed to allocate resource"},
113 {error_code::resource_limit_reached, "Resource limit reached"},
114 {error_code::mutex_error, "Mutex error"},
115 {error_code::deadlock_detected, "Deadlock detected"},
116 {error_code::condition_variable_error, "Condition variable error"},
117 {error_code::io_error, "I/O error"},
118 {error_code::file_not_found, "File not found"},
119 };
120 for (const auto& [k, v] : kMap) {
121 if (k == code) return std::string(v);
122 }
123 return std::string("Unknown error code");
124}
125
132class error {
133public:
139 explicit error(error_code code, std::string message = {})
140 : code_(code), message_(std::move(message)) {}
141
146 [[nodiscard]] error_code code() const noexcept { return code_; }
147
152 [[nodiscard]] const std::string& message() const noexcept { return message_; }
153
158 [[nodiscard]] std::string to_string() const {
159 if (message_.empty()) {
161 }
162 return error_code_to_string(code_) + ": " + message_;
163 }
164
168 operator std::string() const {
169 return to_string();
170 }
171
172private:
174 std::string message_;
175};
176
177// Forward declaration of result template
178template <typename T>
179class result;
180
188public:
192 result_void() = default;
193
198 result_void(const error& err) : has_error_(true), error_(err) {}
199
204 [[nodiscard]] bool has_error() const noexcept { return has_error_; }
205
211 [[nodiscard]] bool has_value() const noexcept { return !has_error_; }
212
218 [[nodiscard]] bool is_ok() const noexcept { return !has_error_; }
219
225 [[nodiscard]] bool is_error() const noexcept { return has_error_; }
226
231 [[nodiscard]] const error& get_error() const { return error_; }
232
237 explicit operator bool() const noexcept { return !has_error_; }
238
239private:
240 bool has_error_ = false;
242};
243
251template <typename T>
252class result {
253public:
254 using value_type = T;
256
262
267 result(error err) : has_value_(false), error_(std::move(err)) {}
268
273 [[nodiscard]] explicit operator bool() const noexcept {
274 return has_value_;
275 }
276
281 [[nodiscard]] bool has_value() const noexcept {
282 return has_value_;
283 }
284
290 [[nodiscard]] bool is_ok() const noexcept {
291 return has_value_;
292 }
293
299 [[nodiscard]] bool is_error() const noexcept {
300 return !has_value_;
301 }
302
308 [[nodiscard]] T& value() & {
309 if (!has_value_) {
310 throw std::runtime_error("Cannot access value of an error result");
311 }
312 return value_;
313 }
314
320 [[nodiscard]] const T& value() const & {
321 if (!has_value_) {
322 throw std::runtime_error("Cannot access value of an error result");
323 }
324 return value_;
325 }
326
332 [[nodiscard]] T&& value() && {
333 if (!has_value_) {
334 throw std::runtime_error("Cannot access value of an error result");
335 }
336 return std::move(value_);
337 }
338
344 [[nodiscard]] error& get_error() & {
345 if (has_value_) {
346 throw std::runtime_error("Cannot get error from successful result");
347 }
348 return error_;
349 }
350
356 [[nodiscard]] const error& get_error() const & {
357 if (has_value_) {
358 throw std::runtime_error("Cannot get error from successful result");
359 }
360 return error_;
361 }
362
368 [[nodiscard]] error&& get_error() && {
369 if (has_value_) {
370 throw std::runtime_error("Cannot get error from successful result");
371 }
372 return std::move(error_);
373 }
374
380 template <typename U>
381 [[nodiscard]] T value_or(U&& default_value) const & {
382 if (has_value_) {
383 return value_;
384 }
385 return static_cast<T>(std::forward<U>(default_value));
386 }
387
393 template <typename U>
394 [[nodiscard]] T value_or(U&& default_value) && {
395 if (has_value_) {
396 return std::move(value_);
397 }
398 return static_cast<T>(std::forward<U>(default_value));
399 }
400
406 [[nodiscard]] T value_or_throw() const & {
407 if (has_value_) {
408 return value_;
409 }
410 throw std::runtime_error(error_.to_string());
411 }
412
418 [[nodiscard]] T value_or_throw() && {
419 if (has_value_) {
420 return std::move(value_);
421 }
422 throw std::runtime_error(error_.to_string());
423 }
424
430 template <typename Fn>
431 [[nodiscard]] auto map(Fn&& fn) const -> result<std::invoke_result_t<Fn, const T&>> {
433
434 if (has_value_) {
435 return ResultType(std::invoke(std::forward<Fn>(fn), value_));
436 }
437 return ResultType(error_);
438 }
439
445 template <typename Fn>
446 [[nodiscard]] auto and_then(Fn&& fn) const -> std::invoke_result_t<Fn, const T&> {
447 using ResultType = std::invoke_result_t<Fn, const T&>;
448 static_assert(std::is_same_v<typename ResultType::error_type, error>, "Function must return a result with the same error type");
449
450 if (has_value_) {
451 return std::invoke(std::forward<Fn>(fn), value_);
452 }
453 return ResultType(error_);
454 }
455
456private:
457 bool has_value_ = false;
460};
461
465template <>
466class result<void> {
467public:
468 using value_type = void;
470
474 result() : success_(true) {}
475
479 result(const result_void& r) : success_(!r.has_error()), error_(r.has_error() ? r.get_error() : error{error_code::success, ""}) {}
480
485 result(error err) : success_(false), error_(std::move(err)) {}
486
491 [[nodiscard]] explicit operator bool() const noexcept {
492 return success_;
493 }
494
499 [[nodiscard]] bool has_value() const noexcept {
500 return success_;
501 }
502
508 [[nodiscard]] bool is_ok() const noexcept {
509 return success_;
510 }
511
517 [[nodiscard]] bool is_error() const noexcept {
518 return !success_;
519 }
520
526 [[nodiscard]] error& get_error() & {
527 if (success_) {
528 throw std::runtime_error("Cannot get error from successful result");
529 }
530 return error_;
531 }
532
538 [[nodiscard]] const error& get_error() const & {
539 if (success_) {
540 throw std::runtime_error("Cannot get error from successful result");
541 }
542 return error_;
543 }
544
550 [[nodiscard]] error&& get_error() && {
551 if (success_) {
552 throw std::runtime_error("Cannot get error from successful result");
553 }
554 return std::move(error_);
555 }
556
561 void value_or_throw() const {
562 if (!success_) {
563 throw std::runtime_error(error_.to_string());
564 }
565 }
566
572 template <typename Fn>
573 [[nodiscard]] auto map(Fn&& fn) const -> result<std::invoke_result_t<Fn>> {
574 using ResultType = result<std::invoke_result_t<Fn>>;
575
576 if (success_) {
577 return ResultType(std::invoke(std::forward<Fn>(fn)));
578 }
579 return ResultType(error_);
580 }
581
587 template <typename Fn>
588 [[nodiscard]] auto and_then(Fn&& fn) const -> std::invoke_result_t<Fn> {
589 using ResultType = std::invoke_result_t<Fn>;
590 static_assert(std::is_same_v<typename ResultType::error_type, error>, "Function must return a result with the same error type");
591
592 if (success_) {
593 return std::invoke(std::forward<Fn>(fn));
594 }
595 return ResultType(error_);
596 }
597
598private:
599 bool success_ = false;
601};
602
603// Type aliases for common result types
604template <typename T>
606
607// Helper to convert std::optional<std::string> to result<T>
608template <typename T>
609result<T> optional_error_to_result(const std::optional<std::string>& error, T&& value) {
610 if (error) {
612 }
613 return result<T>(std::forward<T>(value));
614}
615
616// Helper to convert std::optional<std::string> to result<void>
617inline result<void> optional_error_to_result(const std::optional<std::string>& error) {
618 if (error) {
620 }
621 return result<void>();
622}
623
624// Compatibility layer for existing code
625inline std::optional<std::string> result_to_optional_error(const result<void>& res) {
626 if (res) {
627 return std::nullopt;
628 }
629 return res.get_error().to_string();
630}
631
632template <typename T>
633std::pair<std::optional<T>, std::optional<std::string>> result_to_pair(const result<T>& res) {
634 if (res) {
635 return {res.value(), std::nullopt};
636 }
637 return {std::nullopt, res.get_error().to_string()};
638}
639
640} // namespace kcenon::thread
Represents an error in the thread system.
error_code code() const noexcept
Gets the error code.
error(error_code code, std::string message={})
Constructs an error with a code and optional message.
const std::string & message() const noexcept
Gets the error message.
std::string to_string() const
Converts the error to a string representation.
auto map(Fn &&fn) const -> result< std::invoke_result_t< Fn > >
Maps the result to another type using a function.
error & get_error() &
Gets the error.
const error & get_error() const &
Gets the error.
error && get_error() &&
Gets the error.
result(error err)
Constructs a result with an error.
bool has_value() const noexcept
Checks if the result is successful.
auto and_then(Fn &&fn) const -> std::invoke_result_t< Fn >
Maps the result to another type using a function that returns a result.
result()
Constructs a successful void result.
void value_or_throw() const
Throws an exception if the result contains an error.
result(const result_void &r)
Constructs a void result from a result_void.
bool is_ok() const noexcept
Checks if the result is successful.
bool is_error() const noexcept
Checks if the result contains an error.
Wrapper for void result.
bool has_value() const noexcept
Checks if the result is successful (has a value)
result_void()=default
Constructs a successful result.
bool is_error() const noexcept
Checks if the result contains an error.
bool has_error() const noexcept
Checks if the result contains an error.
result_void(const error &err)
Constructs a result with an error.
bool is_ok() const noexcept
Checks if the result is successful.
const error & get_error() const
Gets the error.
A template class representing either a value or an error.
auto map(Fn &&fn) const -> result< std::invoke_result_t< Fn, const T & > >
Maps the result to another type using a function.
error & get_error() &
Gets the error.
bool is_error() const noexcept
Checks if the result contains an error.
result(error err)
Constructs a result with an error.
T && value() &&
Gets the value.
auto and_then(Fn &&fn) const -> std::invoke_result_t< Fn, const T & >
Maps the result to another type using a function that returns a result.
T & value() &
Gets the value.
T value_or(U &&default_value) &&
Gets the value or a default.
bool has_value() const noexcept
Checks if the result contains a value.
T value_or_throw() &&
Gets the value or throws an exception.
error && get_error() &&
Gets the error.
bool is_ok() const noexcept
Checks if the result is successful.
const T & value() const &
Gets the value.
T value_or(U &&default_value) const &
Gets the value or a default.
T value_or_throw() const &
Gets the value or throws an exception.
const error & get_error() const &
Gets the error.
result(T value)
Constructs a result with a value.
Core threading foundation of the thread system library.
Definition thread_impl.h:17
error_code
Strongly typed error codes for thread system operations.
result< T > optional_error_to_result(const std::optional< std::string > &error, T &&value)
std::optional< std::string > result_to_optional_error(const result< void > &res)
@ error
Error events that might still allow continuation.
std::string error_code_to_string(error_code code)
Converts an error_code to a string representation.
std::pair< std::optional< T >, std::optional< std::string > > result_to_pair(const result< T > &res)
STL namespace.