From 4b7cd3a0b7240f39b7c1fd9a4dbd1ddb9ed19514 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Mon, 20 May 2024 15:15:07 +0200 Subject: [PATCH] fixup! docs(c_test): Add a poc regarding results in c --- c_test/result.h | 154 +++++++++++++++++++++++++--------------------- c_test/src/main.c | 101 ++++++++++++++++++------------ 2 files changed, 144 insertions(+), 111 deletions(-) diff --git a/c_test/result.h b/c_test/result.h index 21de6b4..465fbea 100644 --- a/c_test/result.h +++ b/c_test/result.h @@ -10,9 +10,9 @@ * reporting what happened. Alternatively, many C libraries allow a callback to * be logged in order to report a detailed description of any errors that * occurred. However, the programming language known as Rust deliberately left - * out exceptions; instead they opted for return values to report errors, with a - * `RESULT` type. This type contains the actual value if there is one, or some - * error otherwise. + * out exceptions; instead they opted for return values to report errors, with + * a `RESULT` type. This type contains the actual value if there is one, or + * some error otherwise. * * This is the approach that this file defines. */ @@ -38,14 +38,17 @@ * * @see RESULT_M_TRY_DECL */ -#define RESULT_M_TRY(type, dest, result) \ - do { \ - RESULT(type) _result_##type##_try_at_##__LINE__ = result; \ - if (_result_##type##_try_at_##__LINE__.err) { \ - return _result_##type##_try_at_##__LINE__; \ - } \ - dest = _result_##type##_try_at_##__LINE__.value; \ - } while (0) +#define RESULT_M_TRY(type, dest, result) \ + do \ + { \ + RESULT (type) _result_##type##_try_at_##__LINE__ = result; \ + if (_result_##type##_try_at_##__LINE__.err) \ + { \ + return _result_##type##_try_at_##__LINE__; \ + } \ + dest = _result_##type##_try_at_##__LINE__.value; \ + } \ + while (0) /** * @brief A macro which gives the value of a `RESULT` if present, else returns @@ -74,13 +77,13 @@ * * @see RESULT_M_TRY */ -#define RESULT_M_TRY_DECL(type, dest, result) \ - type dest; \ - RESULT_M_TRY(type, dest, result) +#define RESULT_M_TRY_DECL(type, dest, result) \ + type dest; \ + RESULT_M_TRY (type, dest, result) /** - * @brief Template for a RESULT class (similar to the one that the language Rust - * has). This is the typename. + * @brief Template for a RESULT class (similar to the one that the language + * Rust has). This is the typename. * @details This macro expands to a unique name per type, so `RESULT(int)` is * roughly equivalent to the C++ `RESULT`. Note that * `RESULT(int*)` will not compile; if you want to use something along @@ -157,8 +160,8 @@ #define RESULT_EXPECT(type) Result_##type##_expect /** - * @brief Unwraps the `RESULT` into a `type`, returning the supplied alternative - * otherwise + * @brief Unwraps the `RESULT` into a `type`, returning the supplied + * alternative otherwise * @details If `RESULT_IS_OK`, this returns the `type` value of the `RESULT`. * Otherwise, this returns the alternative supplied in the function * arguments. @@ -186,28 +189,29 @@ /** * @brief Declares a RESULT type for use. * @details This declares the struct and functions for a given type. Note that - * the functions still need to be defined. The values inside the struct - * are implementation details, and may change at any time; do not use + * the functions still need to be defined. The values inside the + * struct are implementation details, and may change at any time; do not use * them. * * @param template parameter * * @see RESULT_DEFINE */ -#define RESULT_DECLARE(type) \ - typedef struct RESULT(type) { \ - type value; \ - /* string literals only */ \ - const char *err; \ - } RESULT(type); \ - RESULT(type) RESULT_OK(type)(type value); \ - RESULT(type) RESULT_ERR(type)(const char *err); \ - bool RESULT_IS_OK(type)(const RESULT(type) *); \ - bool RESULT_IS_ERR(type)(const RESULT(type) *); \ - type RESULT_UNWRAP(type)(const RESULT(type) *); \ - type RESULT_EXPECT(type)(const RESULT(type) *, const char *); \ - type RESULT_UNWRAP_OR(type)(const RESULT(type) *, type); \ - const char *RESULT_UNWRAP_ERR(type)(const RESULT(type) *); +#define RESULT_DECLARE(type) \ + typedef struct RESULT (type) \ + { \ + type value; \ + /* string literals only */ \ + const char *err; \ + } RESULT (type); \ + RESULT (type) RESULT_OK (type) (type value); \ + RESULT (type) RESULT_ERR (type) (const char *err); \ + bool RESULT_IS_OK (type) (const RESULT (type) *); \ + bool RESULT_IS_ERR (type) (const RESULT (type) *); \ + type RESULT_UNWRAP (type) (const RESULT (type) *); \ + type RESULT_EXPECT (type) (const RESULT (type) *, const char *); \ + type RESULT_UNWRAP_OR (type) (const RESULT (type) *, type); \ + const char *RESULT_UNWRAP_ERR (type) (const RESULT (type) *); /** * @brief Defines all the functions for the given `RESULT(type)` @@ -217,41 +221,49 @@ * * @see RESULT_DECLARE */ -#define RESULT_DEFINE(type) \ - RESULT(type) RESULT_OK(type)(type value) { \ - RESULT(type) res = {.value = value, .err = NULL}; \ - return res; \ - } \ - RESULT(type) RESULT_ERR(type)(const char *err) { \ - RESULT(type) res = {.err = err}; \ - return res; \ - } \ - bool RESULT_IS_OK(type)(const RESULT(type) * res) { \ - return res->err == NULL; \ - } \ - bool RESULT_IS_ERR(type)(const RESULT(type) * res) { \ - return res->err != NULL; \ - } \ - type RESULT_UNWRAP(type)(const RESULT(type) * res) { \ - if (RESULT_IS_ERR(type)(res)) \ - panicf("Attempted to unwrap empty Result of type " #type \ - ". Instead had error: %s", \ - res->err); \ - return res->value; \ - } \ - type RESULT_EXPECT(type)(const RESULT(type) * res, \ - const char *message_on_err) { \ - if (RESULT_IS_ERR(type)(res)) \ - panic(message_on_err); \ - return res->value; \ - } \ - type RESULT_UNWRAP_OR(type)(const RESULT(type) * res, type else_val) { \ - if (RESULT_IS_ERR(type)(res)) \ - return else_val; \ - return res->value; \ - } \ - const char *RESULT_UNWRAP_ERR(type)(const RESULT(type) * res) { \ - if (RESULT_IS_OK(type)(res)) \ - panic("Result was not an error; type: " #type); \ - return res->err; \ +#define RESULT_DEFINE(type) \ + RESULT (type) RESULT_OK (type) (type value) \ + { \ + RESULT (type) res = { .value = value, .err = NULL }; \ + return res; \ + } \ + RESULT (type) RESULT_ERR (type) (const char *err) \ + { \ + RESULT (type) res = { .err = err }; \ + return res; \ + } \ + bool RESULT_IS_OK (type) (const RESULT (type) * res) \ + { \ + return res->err == NULL; \ + } \ + bool RESULT_IS_ERR (type) (const RESULT (type) * res) \ + { \ + return res->err != NULL; \ + } \ + type RESULT_UNWRAP (type) (const RESULT (type) * res) \ + { \ + if (RESULT_IS_ERR (type) (res)) \ + panicf ("Attempted to unwrap empty Result of type " #type \ + ". Instead had error: %s", \ + res->err); \ + return res->value; \ + } \ + type RESULT_EXPECT (type) (const RESULT (type) * res, \ + const char *message_on_err) \ + { \ + if (RESULT_IS_ERR (type) (res)) \ + panic (message_on_err); \ + return res->value; \ + } \ + type RESULT_UNWRAP_OR (type) (const RESULT (type) * res, type else_val) \ + { \ + if (RESULT_IS_ERR (type) (res)) \ + return else_val; \ + return res->value; \ + } \ + const char *RESULT_UNWRAP_ERR (type) (const RESULT (type) * res) \ + { \ + if (RESULT_IS_OK (type) (res)) \ + panic ("Result was not an error; type: " #type); \ + return res->err; \ } diff --git a/c_test/src/main.c b/c_test/src/main.c index 89e3f8a..924e15b 100644 --- a/c_test/src/main.c +++ b/c_test/src/main.c @@ -7,74 +7,95 @@ typedef const char *string; -enum ResultTag { +enum ResultTag +{ OK, ERR, }; -Result(u_int32_t, string) { +Result (u_int32_t, string) +{ enum ResultTag tag; - union { + union + { u_int32_t ok; string err; } value; }; -Result(string, ResultTag) { +Result (string, ResultTag) +{ enum ResultTag tag; - union { + union + { string ok; enum ResultTag err; } value; }; -Result(u_int32_t, string) get_a() { - int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX. +Result (u_int32_t, string) get_a () +{ + int r = rand (); // Returns a pseudo-random integer between 0 and RAND_MAX. - Result(u_int32_t, string) result; - if (r % 2 == 0) { - result.value.ok = 32; - result.tag = OK; - } else { - result.value.err = "Can't access a! Have you run the foo?"; - result.tag = ERR; - } + Result (u_int32_t, string) result; + if (r % 2 == 0) + { + result.value.ok = 32; + result.tag = OK; + } + else + { + result.value.err = "Can't access a! Have you run the foo?"; + result.tag = ERR; + } return result; } -Result(string, ResultTag) get_b() { - int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX. +Result (string, ResultTag) get_b () +{ + int r = rand (); // Returns a pseudo-random integer between 0 and RAND_MAX. - Result(string, ResultTag) result; - if (r % 2 == 0) { - result.value.ok = "Wow could defudge the foo!"; - result.tag = OK; - } else { - result.value.err = ERR; - result.tag = ERR; - } + Result (string, ResultTag) result; + if (r % 2 == 0) + { + result.value.ok = "Wow could defudge the foo!"; + result.tag = OK; + } + else + { + result.value.err = ERR; + result.tag = ERR; + } return result; } -int main() { - srand(time(NULL)); // Initialization, should only be called once. - printf("Hello World!\n"); +int +main () +{ + srand (time (NULL)); // Initialization, should only be called once. + printf ("Hello World!\n"); - Result(u_int32_t, string) result = get_a(); - if (result.tag == OK) { - printf("Result is ok with value: '%d'\n", result.value.ok); - } else { - printf("Result is err with value: '%s'\n", result.value.err); - } + Result (u_int32_t, string) result = get_a (); + if (result.tag == OK) + { + printf ("Result is ok with value: '%d'\n", result.value.ok); + } + else + { + printf ("Result is err with value: '%s'\n", result.value.err); + } - Result(string, ResultTag) result_b = get_b(); - if (result.tag == OK) { - printf("Result B is ok with value: '%s'\n", result_b.value.ok); - } else { - printf("Result B is err with value: '%i'\n", result_b.value.err); - } + Result (string, ResultTag) result_b = get_b (); + if (result.tag == OK) + { + printf ("Result B is ok with value: '%s'\n", result_b.value.ok); + } + else + { + printf ("Result B is err with value: '%i'\n", result_b.value.err); + } return EXIT_SUCCESS; }