diff options
Diffstat (limited to 'optional')
-rw-r--r-- | optional/Makefile | 5 | ||||
-rw-r--r-- | optional/README.md | 12 | ||||
-rw-r--r-- | optional/optional-test.c | 55 | ||||
-rw-r--r-- | optional/optional.h | 92 |
4 files changed, 164 insertions, 0 deletions
diff --git a/optional/Makefile b/optional/Makefile new file mode 100644 index 0000000..32e87b2 --- /dev/null +++ b/optional/Makefile @@ -0,0 +1,5 @@ +CC?=gcc +CFLAGS+=-Wall -Werror -Wno-unused -std=gnu11 -Og -g + +all: + $(CC) $(CFLAGS) -o optional-test optional-test.c diff --git a/optional/README.md b/optional/README.md new file mode 100644 index 0000000..c039b85 --- /dev/null +++ b/optional/README.md @@ -0,0 +1,12 @@ +# Optional + +Poor man's optional "type templates" for C. Just for fun. + +In its current form it's intended to be used with "single token" types. Might +or might not "make it compatible" with structs, enums, unions, etc. in the +future, if the need arises. + +## Example usage + +If you want to see an example on how to use optional, take a look at +`optional-test.c`. diff --git a/optional/optional-test.c b/optional/optional-test.c new file mode 100644 index 0000000..88577d3 --- /dev/null +++ b/optional/optional-test.c @@ -0,0 +1,55 @@ +#include "optional.h" +#include "../utest/utest.h" + +#include <stdio.h> + +OPTIONALDEC(int); +OPTIONALDEC(char); + +struct my_struct { + OPTIONAL(int) a; + OPTIONAL(int) b; +}; + +void optput(OPTIONAL(char) * dst, char src) +{ + opt_set_some(*dst, src); +} + +TEST_BEGIN(test_optional_example) +{ + int a, b; + + struct my_struct my = { + .a = OPTSOME(69), + .b = OPTNONE, + }; + + OPTIONAL(char) y = OPTNONE; + OPTIONAL(char) z = OPTSOME('z'); + float xy, xz; + + expect(opt_has(my.a), "expected to contain something"); + expect(opt_unwrap(my.a, a), "expected to contain something"); + asserteq(a, 69); + + expect(!opt_unwrap(my.b, b), "expected to contain nothing"); + + optput(&y, 'y'); + opt_set_none(z); + + expect(opt_unwrap(y, xy), "expected to contain something"); + asserteq(xy, 'y'); + + expect(!opt_unwrap(z, xz), "expected to contain nothing"); + + xy = opt_default(y, 'd'), xz = opt_default(z, 'd'); + + asserteq(xy, 'y'); + asserteq(xz, 'd'); + + TEST_OUT +} +TEST_END + +RUN_TESTS(test_optional_example) diff --git a/optional/optional.h b/optional/optional.h new file mode 100644 index 0000000..6dcc3e2 --- /dev/null +++ b/optional/optional.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: MIT */ +/** + * optional.h - Macros for optional types. v0.1.0 + * + * Copyright (C) 2023 Yaroslav de la Peña Smirnov + * + * Documentation + * ============= + * + * Poor man's optional "type templates" for C. + * + * An object of optional type is an object that may or may not hold an object of + * a certain type inside itself at any time. Before declaring a variable of a + * certain OPTIONAL type, said OPTIONAL type should be declared with the + * OPTIONALDEC macro. + * + * Currently only supports types that are composed of one token, i.e. no + * structs, enums, unions, etc. Might expand it in the future to support them. + */ + +#ifndef OPTIONAL_H +#define OPTIONAL_H + +#include <stdbool.h> + +/** + * OPTIONALDEC - declare OPTIONAL type that holds data of type @T + * @T: the type of data to hold. + */ +#define OPTIONALDEC(T) struct __OPTIONAL_##T { bool has; T data; } + +/** + * OPTIONAL - declare an object that optionally holds type @T + * @T: the type of data to hold. + */ +#define OPTIONAL(T) struct __OPTIONAL_##T + +/** + * OPTNONE - initialize an OPTIONAL object to hold nothing. + */ +#define OPTNONE { .has = false } + +/** + * OPTSOME - initialize an OPTIONAL object to hold something. + * @d: data to hold. + */ +#define OPTSOME(d) { .has = true, .data = d } + +/** + * opt_set_none - set an OPTIONAL object to hold nothing. + * @opt: OPTIONAL object. + */ +#define opt_set_none(opt) opt.has = false + +/** + * opt_set_some - set an OPTIONAL object to hold something. + * @opt: OPTIONAL object. + * @d: data to hold. + */ +#define opt_set_some(opt, d) \ + ((opt).has = true, (opt).data = d) + +/** + * opt_has - true if an OPTIONAL object has something. + * @opt: OPTIONAL object. + */ +#define opt_has(opt) ((opt).has) + +/** + * opt_hasnt - true if an OPTIONAL object has nothing. + * @opt: OPTIONAL object. + */ +#define opt_hasnt(opt) (!(opt).has) + +/** + * opt_unwrap - if there's something in @src, copy it to @dst. + * @src: OPTIONAL object to copy data of type T from. + * @dst: variable of type T to copy to. + * + * Returns true if there was something, false if there wasn't. + */ +#define opt_unwrap(src, dst) \ + (opt_has(src) ? (dst = (src).data, true) : (false)) + +/** + * opt_default - get something from @src or default to @def. + * @src: OPTIONAL object to get data from. + * @def: default value if there's nothing. + */ +#define opt_default(src, def) opt_has(src) ? (src).data : def + +#endif |