fixup! docs(c_test): Add a poc regarding results in c
This commit is contained in:
parent
644342070f
commit
4b7cd3a0b7
154
c_test/result.h
154
c_test/result.h
|
@ -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; \
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue