fixup! docs(c_test): Add a poc regarding results in c

This commit is contained in:
Benedikt Peetz 2024-05-20 15:15:07 +02:00
parent 644342070f
commit 4b7cd3a0b7
Signed by: bpeetz
GPG Key ID: B6139BCB07CE946D
2 changed files with 144 additions and 111 deletions

View File

@ -10,9 +10,9 @@
* reporting what happened. Alternatively, many C libraries allow a callback to * reporting what happened. Alternatively, many C libraries allow a callback to
* be logged in order to report a detailed description of any errors that * be logged in order to report a detailed description of any errors that
* occurred. However, the programming language known as Rust deliberately left * occurred. However, the programming language known as Rust deliberately left
* out exceptions; instead they opted for return values to report errors, with a * out exceptions; instead they opted for return values to report errors, with
* `RESULT` type. This type contains the actual value if there is one, or some * a `RESULT` type. This type contains the actual value if there is one, or
* error otherwise. * some error otherwise.
* *
* This is the approach that this file defines. * This is the approach that this file defines.
*/ */
@ -38,14 +38,17 @@
* *
* @see RESULT_M_TRY_DECL * @see RESULT_M_TRY_DECL
*/ */
#define RESULT_M_TRY(type, dest, result) \ #define RESULT_M_TRY(type, dest, result) \
do { \ do \
RESULT(type) _result_##type##_try_at_##__LINE__ = result; \ { \
if (_result_##type##_try_at_##__LINE__.err) { \ RESULT (type) _result_##type##_try_at_##__LINE__ = result; \
return _result_##type##_try_at_##__LINE__; \ if (_result_##type##_try_at_##__LINE__.err) \
} \ { \
dest = _result_##type##_try_at_##__LINE__.value; \ return _result_##type##_try_at_##__LINE__; \
} while (0) } \
dest = _result_##type##_try_at_##__LINE__.value; \
} \
while (0)
/** /**
* @brief A macro which gives the value of a `RESULT` if present, else returns * @brief A macro which gives the value of a `RESULT` if present, else returns
@ -74,13 +77,13 @@
* *
* @see RESULT_M_TRY * @see RESULT_M_TRY
*/ */
#define RESULT_M_TRY_DECL(type, dest, result) \ #define RESULT_M_TRY_DECL(type, dest, result) \
type dest; \ type dest; \
RESULT_M_TRY(type, dest, result) RESULT_M_TRY (type, dest, result)
/** /**
* @brief Template for a RESULT class (similar to the one that the language Rust * @brief Template for a RESULT class (similar to the one that the language
* has). This is the typename. * Rust has). This is the typename.
* @details This macro expands to a unique name per type, so `RESULT(int)` is * @details This macro expands to a unique name per type, so `RESULT(int)` is
* roughly equivalent to the C++ `RESULT<int>`. Note that * roughly equivalent to the C++ `RESULT<int>`. Note that
* `RESULT(int*)` will not compile; if you want to use something along * `RESULT(int*)` will not compile; if you want to use something along
@ -157,8 +160,8 @@
#define RESULT_EXPECT(type) Result_##type##_expect #define RESULT_EXPECT(type) Result_##type##_expect
/** /**
* @brief Unwraps the `RESULT` into a `type`, returning the supplied alternative * @brief Unwraps the `RESULT` into a `type`, returning the supplied
* otherwise * alternative otherwise
* @details If `RESULT_IS_OK`, this returns the `type` value of the `RESULT`. * @details If `RESULT_IS_OK`, this returns the `type` value of the `RESULT`.
* Otherwise, this returns the alternative supplied in the function * Otherwise, this returns the alternative supplied in the function
* arguments. * arguments.
@ -186,28 +189,29 @@
/** /**
* @brief Declares a RESULT type for use. * @brief Declares a RESULT type for use.
* @details This declares the struct and functions for a given type. Note that * @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 * the functions still need to be defined. The values inside the
* are implementation details, and may change at any time; do not use * struct are implementation details, and may change at any time; do not use
* them. * them.
* *
* @param template parameter * @param template parameter
* *
* @see RESULT_DEFINE * @see RESULT_DEFINE
*/ */
#define RESULT_DECLARE(type) \ #define RESULT_DECLARE(type) \
typedef struct RESULT(type) { \ typedef struct RESULT (type) \
type value; \ { \
/* string literals only */ \ type value; \
const char *err; \ /* string literals only */ \
} RESULT(type); \ const char *err; \
RESULT(type) RESULT_OK(type)(type value); \ } RESULT (type); \
RESULT(type) RESULT_ERR(type)(const char *err); \ RESULT (type) RESULT_OK (type) (type value); \
bool RESULT_IS_OK(type)(const RESULT(type) *); \ RESULT (type) RESULT_ERR (type) (const char *err); \
bool RESULT_IS_ERR(type)(const RESULT(type) *); \ bool RESULT_IS_OK (type) (const RESULT (type) *); \
type RESULT_UNWRAP(type)(const RESULT(type) *); \ bool RESULT_IS_ERR (type) (const RESULT (type) *); \
type RESULT_EXPECT(type)(const RESULT(type) *, const char *); \ type RESULT_UNWRAP (type) (const RESULT (type) *); \
type RESULT_UNWRAP_OR(type)(const RESULT(type) *, type); \ type RESULT_EXPECT (type) (const RESULT (type) *, const char *); \
const char *RESULT_UNWRAP_ERR(type)(const RESULT(type) *); 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)` * @brief Defines all the functions for the given `RESULT(type)`
@ -217,41 +221,49 @@
* *
* @see RESULT_DECLARE * @see RESULT_DECLARE
*/ */
#define RESULT_DEFINE(type) \ #define RESULT_DEFINE(type) \
RESULT(type) RESULT_OK(type)(type value) { \ RESULT (type) RESULT_OK (type) (type value) \
RESULT(type) res = {.value = value, .err = NULL}; \ { \
return res; \ RESULT (type) res = { .value = value, .err = NULL }; \
} \ return res; \
RESULT(type) RESULT_ERR(type)(const char *err) { \ } \
RESULT(type) res = {.err = err}; \ RESULT (type) RESULT_ERR (type) (const char *err) \
return res; \ { \
} \ RESULT (type) res = { .err = err }; \
bool RESULT_IS_OK(type)(const RESULT(type) * res) { \ return res; \
return res->err == NULL; \ } \
} \ bool RESULT_IS_OK (type) (const RESULT (type) * res) \
bool RESULT_IS_ERR(type)(const RESULT(type) * res) { \ { \
return res->err != NULL; \ return res->err == NULL; \
} \ } \
type RESULT_UNWRAP(type)(const RESULT(type) * res) { \ bool RESULT_IS_ERR (type) (const RESULT (type) * res) \
if (RESULT_IS_ERR(type)(res)) \ { \
panicf("Attempted to unwrap empty Result of type " #type \ return res->err != NULL; \
". Instead had error: %s", \ } \
res->err); \ type RESULT_UNWRAP (type) (const RESULT (type) * res) \
return res->value; \ { \
} \ if (RESULT_IS_ERR (type) (res)) \
type RESULT_EXPECT(type)(const RESULT(type) * res, \ panicf ("Attempted to unwrap empty Result of type " #type \
const char *message_on_err) { \ ". Instead had error: %s", \
if (RESULT_IS_ERR(type)(res)) \ res->err); \
panic(message_on_err); \ return res->value; \
return res->value; \ } \
} \ type RESULT_EXPECT (type) (const RESULT (type) * res, \
type RESULT_UNWRAP_OR(type)(const RESULT(type) * res, type else_val) { \ const char *message_on_err) \
if (RESULT_IS_ERR(type)(res)) \ { \
return else_val; \ if (RESULT_IS_ERR (type) (res)) \
return res->value; \ panic (message_on_err); \
} \ return res->value; \
const char *RESULT_UNWRAP_ERR(type)(const RESULT(type) * res) { \ } \
if (RESULT_IS_OK(type)(res)) \ type RESULT_UNWRAP_OR (type) (const RESULT (type) * res, type else_val) \
panic("Result was not an error; type: " #type); \ { \
return res->err; \ 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; \
} }

View File

@ -7,74 +7,95 @@
typedef const char *string; typedef const char *string;
enum ResultTag { enum ResultTag
{
OK, OK,
ERR, ERR,
}; };
Result(u_int32_t, string) { Result (u_int32_t, string)
{
enum ResultTag tag; enum ResultTag tag;
union { union
{
u_int32_t ok; u_int32_t ok;
string err; string err;
} value; } value;
}; };
Result(string, ResultTag) { Result (string, ResultTag)
{
enum ResultTag tag; enum ResultTag tag;
union { union
{
string ok; string ok;
enum ResultTag err; enum ResultTag err;
} value; } value;
}; };
Result(u_int32_t, string) get_a() { Result (u_int32_t, string) get_a ()
int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX. {
int r = rand (); // Returns a pseudo-random integer between 0 and RAND_MAX.
Result(u_int32_t, string) result; Result (u_int32_t, string) result;
if (r % 2 == 0) { if (r % 2 == 0)
result.value.ok = 32; {
result.tag = OK; result.value.ok = 32;
} else { result.tag = OK;
result.value.err = "Can't access a! Have you run the foo?"; }
result.tag = ERR; else
} {
result.value.err = "Can't access a! Have you run the foo?";
result.tag = ERR;
}
return result; return result;
} }
Result(string, ResultTag) get_b() { Result (string, ResultTag) get_b ()
int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX. {
int r = rand (); // Returns a pseudo-random integer between 0 and RAND_MAX.
Result(string, ResultTag) result; Result (string, ResultTag) result;
if (r % 2 == 0) { if (r % 2 == 0)
result.value.ok = "Wow could defudge the foo!"; {
result.tag = OK; result.value.ok = "Wow could defudge the foo!";
} else { result.tag = OK;
result.value.err = ERR; }
result.tag = ERR; else
} {
result.value.err = ERR;
result.tag = ERR;
}
return result; return result;
} }
int main() { int
srand(time(NULL)); // Initialization, should only be called once. main ()
printf("Hello World!\n"); {
srand (time (NULL)); // Initialization, should only be called once.
printf ("Hello World!\n");
Result(u_int32_t, string) result = get_a(); Result (u_int32_t, string) result = get_a ();
if (result.tag == OK) { if (result.tag == OK)
printf("Result is ok with value: '%d'\n", result.value.ok); {
} else { printf ("Result is ok with value: '%d'\n", result.value.ok);
printf("Result is err with value: '%s'\n", result.value.err); }
} else
{
printf ("Result is err with value: '%s'\n", result.value.err);
}
Result(string, ResultTag) result_b = get_b(); Result (string, ResultTag) result_b = get_b ();
if (result.tag == OK) { if (result.tag == OK)
printf("Result B is ok with value: '%s'\n", result_b.value.ok); {
} else { printf ("Result B is ok with value: '%s'\n", result_b.value.ok);
printf("Result B is err with value: '%i'\n", result_b.value.err); }
} else
{
printf ("Result B is err with value: '%i'\n", result_b.value.err);
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }