Created
January 8, 2023 18:06
-
-
Save SuperSonicHub1/1c6c4c59e89ea7df0e18a1a113e146fb to your computer and use it in GitHub Desktop.
Rust's Result and Option monads implemented in C macros
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <dirent.h> | |
| #include <errno.h> | |
| #include "monads.h" | |
| // Demonstrate use of Result | |
| enum division_error { | |
| DIVIDE_BY_ZERO, | |
| }; | |
| struct division_result DEFINE_RESULT(double, enum division_error); | |
| struct division_result divide(double a, double b) { | |
| struct division_result result; | |
| if (b == 0) | |
| ERR(result, DIVIDE_BY_ZERO); | |
| else | |
| OK(result, a / b); | |
| return result; | |
| } | |
| void display_division_result(struct division_result result) { | |
| if (result.ok) { | |
| printf("OK(%f)\n", result.value.ok); | |
| } else { | |
| switch (result.value.error) { | |
| case DIVIDE_BY_ZERO: | |
| printf("ERR(DIVIDE_BY_ZERO)\n"); | |
| break; | |
| } | |
| } | |
| } | |
| // Demonstrate a more practical use of Result | |
| extern int errno; | |
| struct opendir_result DEFINE_RESULT(DIR*, int); | |
| struct opendir_result opendirr(const char *dirname) { | |
| struct opendir_result result; | |
| DIR* directory = opendir(dirname); | |
| if (directory == NULL) ERR(result, errno); | |
| else OK(result, directory); | |
| return result; | |
| } | |
| int main() { | |
| display_division_result(divide(10, 5)); | |
| display_division_result(divide(10, 0)); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Implementations of Rust's most popular monads through macros | |
| // and non-standard C extensions [1]. Makes use of statement | |
| // expressions as seen in SerenityOS' TRY macro [2]. | |
| // [1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html | |
| // [2]: https://github.com/SerenityOS/serenity/blob/master/AK/Try.h | |
| #pragma once | |
| #include <stdbool.h> | |
| // Rust's Result: https://doc.rust-lang.org/std/result/enum.Result.html | |
| #define DEFINE_RESULT(result_type, error_type) \ | |
| { \ | |
| bool ok; \ | |
| union { \ | |
| result_type ok; \ | |
| error_type error; \ | |
| } value; \ | |
| } | |
| #define OK(variable, value_to_set) \ | |
| ({ \ | |
| variable.ok = true; \ | |
| variable.value.ok = value_to_set; \ | |
| }) | |
| #define ERR(variable, value_to_set) \ | |
| ({ \ | |
| variable.ok = false; \ | |
| variable.value.error = value_to_set; \ | |
| }) | |
| // Rust's Option: https://doc.rust-lang.org/std/option/enum.Option.html | |
| #define DEFINE_OPTION(value_type) \ | |
| { \ | |
| bool some; \ | |
| value_type value; \ | |
| } | |
| #define SOME(variable, value_to_set) \ | |
| ({ \ | |
| variable.ok = true; \ | |
| variable.value = value_to_set; \ | |
| }) | |
| #define NONE(variable) ({ variable.some = false; }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment