Skip to content

Instantly share code, notes, and snippets.

@jirutka
Created June 3, 2022 20:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jirutka/12ff25d25d53bcb3132eab13396d2ba6 to your computer and use it in GitHub Desktop.
Save jirutka/12ff25d25d53bcb3132eab13396d2ba6 to your computer and use it in GitHub Desktop.
#include <stdbool.h>
typedef char* str;
#define GENERATE_OPTION_TYPES(Option) \
Option(int) \
Option(str)
#define GENERATE_RESULT_TYPES(Result) \
Result(str, int) \
Result(long, int)
#define GENERATE_RESULT_OK_TYPES(Ok, x) \
Ok(str, x) \
Ok(long, x)
#define GENERATE_RESULT_ERR_TYPES(Err, x) \
Err(x, int)
/////////////////////////
/** Returns type struct `Option(T)` for the specified "generic" type.
*
* @param T the value type.
*/
#define Option(T) struct Option_ ## T
/** Returns name of constructor function for `Option(T)` of variant `Some(T)`.
*
* @param T the value type.
*/
#define OptionSome(T) option_some_ ## T
/** Defines struct `Option(T)` and constructor function `OptionSome(T)` for
* the specified "generic" type.
*
* @param T the value type.
*/
#define DEFINE_OPTION_TYPE(T) \
Option(T) { \
bool tag; \
T value; \
}; \
static inline Option(T) OptionSome(T)(T value) { \
return (Option(T)) { .tag = true, .value = value }; \
}
#ifdef GENERATE_OPTION_TYPES
/** Creates struct `Option(T)` in variant `Some(T)` with the given value
* of type `T`.
*
* @param value the value to be wrapped in `Some(T)`.
*/
#define Some(value) _Generic((value) \
GENERATE_OPTION_TYPES(OPTION_SOME_GENERIC_ENTRY) \
)(value)
#define OPTION_SOME_GENERIC_ENTRY(T) , T: (OptionSome(T))
/** Creates struct `Option(T)` in variant `None`.
*
* @param T type of `Option(T)`.
*/
#define None(T) (Option(T)) { .tag = false }
GENERATE_OPTION_TYPES(DEFINE_OPTION_TYPE)
#endif
#define is_some(option) ((option).tag)
#define is_none(option) (!(option).tag)
////////////////
/** Returns type struct `Result(T, E)` for the specified "generic" types.
*
* @param T the value type.
* @param E the error type.
*/
#define Result(T, E) struct Result_ ## T ## __ ## E
/** Returns name of constructor function for struct `Result(T, E)` in variant
* `Ok(T)` for the specified "generic" types.
*
* @param T the value type.
* @param E the error type.
*/
#define ResultOk(T, E) result_ok_ ## T ## __ ## E
/** Returns name of constructor function for struct `Result(T, E)` in variant
* `Err(E)` for the specified "generic" types.
*
* @param T the value type.
* @param E the error type.
*/
#define ResultErr(T, E) result_err_ ## T ## __ ## E
/** Defines struct `Result(T, E)` and constructor functions `ResultOk(T, E)`
* and `ResultErr(T, E)` for the specified "generic" types.
*
* @param T the value type.
* @param E the error type.
*/
#define DEFINE_RESULT_TYPE(T, E) \
Result(T, E) { \
bool tag; \
union { \
T value; \
E err; \
}; \
}; \
static inline Result(T, E) ResultOk(T, E) (T value) { \
return (Result(T, E)) { .tag = true, .value = value }; \
} \
static inline Result(T, E) ResultErr(T, E) (E err) { \
return (Result(T, E)) { .tag = false, .err = err }; \
}
#ifdef GENERATE_RESULT_TYPES
/** Creates struct `Result(T, E)` in variant `Ok(T)` with the given value
* of "generic" type `T`.
*
* @param ok_value the value to be wrapped.
* @param E the error type.
*/
#define Ok(ok_value, E) _Generic((ok_value) \
GENERATE_RESULT_OK_TYPES(RESULT_OK_GENERIC_ENTRY, E) \
)(ok_value)
#define RESULT_OK_GENERIC_ENTRY(T, E) , T: (ResultOk(T, E))
/** Creates struct `Result(T, E)` in variant `Err(E)` with the given value
* of "generic" type `E`.
*
* @param T the value type.
* @param err_value the error value to be wrapped.
*/
#define Err(T, err_value) _Generic((err_value) \
GENERATE_RESULT_ERR_TYPES(RESULT_ERR_GENERIC_ENTRY, T) \
)(err_value)
#define RESULT_ERR_GENERIC_ENTRY(T, E) , E: (ResultErr(T, E))
GENERATE_RESULT_TYPES(DEFINE_RESULT_TYPE)
#endif
#define is_ok(result) ((result).tag)
#define is_err(result) (!(result).tag)
//////
/** A statement expression that returns the contained value or a default.
*
* Option(T):
* @code
* unwrap_or(Some(42), 100) # => 42
* unwrap_or(None(int), 100) # => 100
* @endcode
*
* Result(T, E):
* @code
* unwrap_or(Ok(42, str), 100) # => 42
* unwrap_or(Err(int, "NaN"), 100) # => 100
* @endcode
*
* @param cont container object (struct with `.tag` and `.value`).
* @param default_value the default value to be returned.
*/
#define unwrap_or(cont, default_value) ({ \
__typeof__(cont) _cont_##__LINE__ = (cont); \
_cont_##__LINE__.tag ? _cont_##__LINE__.value : (default_value); \
})
#include <stdio.h>
#include <stdlib.h>
#include "maybe.h"
#define unless(expr) if (!(expr))
#define between(value, min, max) (({ \
__typeof__(value) _value = (value); \
__typeof__(min) _min = (min); \
__typeof__(max) _max = (max); \
_value >= _min && _value <= _max; \
}))
#define unwrap_or_else(option, else_block) ({ \
__typeof__(option) _option_##__LINE__ = (option); \
if (!_option_##__LINE__.tag) else_block; \
_option_##__LINE__.value; \
})
#define unwrap_or_else2(option, else_block) ({ \
__typeof__(option) _option_##__LINE__ = (option); \
if (!_option_##__LINE__.tag) { \
__typeof__(option.err) err = _option_##__LINE__.err; \
else_block; \
} \
_option_##__LINE__.value; \
})
#define unwrap(option) ({ \
__typeof__(option) _option_##__LINE__ = (option); \
if (!_option_##__LINE__.tag) { \
fprintf(stderr, "FATAL: File \"%s\", line %d, in %s\n", __FILE__, __LINE__, __func__); abort(); \
} \
_option_##__LINE__.value; \
})
#define unwrap_err(result) ({ \
__typeof__(result) _result_##__LINE__ = (result); \
if (_result_##__LINE__.tag) { \
fprintf(stderr, "FATAL: File \"%s\", line %d, in %s\n", __FILE__, __LINE__, __func__); abort(); \
} \
_result_##__LINE__.err; \
})
#define result_map(result, func) ({ \
__typeof__(result) _result = (result); \
if (_result.tag) _result.value = func(_result.value); \
_result; \
})
#define result_map_err(result, func) ({ \
__typeof__(result) _result = (result); \
if (!_result.tag) _result.err = func(_result.err); \
_result; \
})
#define unwrap_or_else3(result, var, else_block) ({ \
__typeof__(result) _result = (result); \
var = _result.err; \
_result.tag ? _result.value : (else_block); \
})
Option(int) get_foo(bool a) {
printf("get_foo() called\n");
return a ? Some(5) : None(int);
}
Result(str, int) get_string(bool a) {
printf("get_string() called\n");
return a ? Ok("Hello, world!", int) : Err(str, 66);
}
int foo() {
puts("Computing foo");
return 5;
}
str hello(str value) {
printf("hello: %s\n", value);
return "foo!";
}
int main() {
/*
unless (i == 0 || i == 1) {
printf("Hello!\n");
}
*/
if between(foo(), 1, 8) {
printf("Hello!\n");
}
int x = unwrap_or(get_foo(false), 66);
printf("x = %d\n", x);
int z = unwrap_or_else(get_foo(true), {
return 1;
});
printf("z = %d\n", z);
int a = unwrap(get_foo(true));
printf("a %d\n", a);
Result(str, int) b = get_string(false);
int c = unwrap_err(b);
printf("c %d\n", c);
str d = unwrap_or(get_string(true), "foo!");
printf("d %s\n", d);
str e = unwrap_or_else2(get_string(false), {
printf("> %d\n", err);
});
Result(str, int) f = result_map(get_string(false), hello);
printf("f %d\n", f.err);
str g = unwrap_or_else3(get_string(true), int err, {
printf("?? %i\n", err);
goto error;
"";
});
//Result(str, int) xx = Ok(100, int);
//Result(str, int) l = Ok(5, int);
/*
int c;
if unwrap(get_foo(true), c) {
printf("Hello, world!\n%d\n", c);
}
*/
//int y = expect(get_foo(true), "the world is collapsing!");
/*
int i = unwrap_or( get_foo(true) ) {
printf("fuck!\n");
}
*/
ret:
return 0;
error:
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment