aboutsummaryrefslogtreecommitdiff
path: root/optional
diff options
context:
space:
mode:
authorYaroslav de la Peña Smirnov <yps@yaroslavps.com>2023-07-22 03:03:09 +0300
committerYaroslav de la Peña Smirnov <yps@yaroslavps.com>2023-07-22 03:03:09 +0300
commitcb1a40859029f33184355475e51fec95afb79a73 (patch)
tree5aea859d3a3e10ddd058beac9d6734d17979d560 /optional
downloadc-wares-cb1a40859029f33184355475e51fec95afb79a73.tar.gz
c-wares-cb1a40859029f33184355475e51fec95afb79a73.zip
init
Just some C wares; hmap, list, optional, unit tests.
Diffstat (limited to 'optional')
-rw-r--r--optional/Makefile5
-rw-r--r--optional/README.md12
-rw-r--r--optional/optional-test.c55
-rw-r--r--optional/optional.h92
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