aboutsummaryrefslogtreecommitdiff
path: root/optional/optional.h
blob: 4eb0e873ad8be8e0228ba69ca02526936cd67bb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* SPDX-License-Identifier: MIT */
/**
 * optional.h - Macros for optional types. v0.2.0
 *
 * Copyright (C) 2023-2024 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.
 */

#ifndef OPTIONAL_H
#define OPTIONAL_H

#include <stdbool.h>

/**
 * DECLARE_NAMED_OPTIONAL - declare OPTIONAL that holds type @T called @optname
 * @optname:	name of Optional type.
 * @T:			type to hold.
 *
 * This macro is used to give a custom name to the optional type. This is,
 * unfortunately, needed due to the nature of C's type system and the fact that
 * we have types in C that are made up of several tokens, which does not bode
 * well for these kinds of macros.
 */
#define DECL_NAMED_OPTIONAL(optname, T) \
	struct __OPTIONAL_##optname {       \
		T	 data;                      \
		bool has;                       \
	}

/**
 * DECLARE_OPTIONAL - declare OPTIONAL "type" that holds data of type @T
 * @T:		the type of data to hold.
 *
 * The type @T also becomes the "name" of the optional type.
 */
#define DECL_OPTIONAL(T) DECL_NAMED_OPTIONAL(T, T)

/**
 * 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