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