Often a function needs to return (or accept as parameter) an object that
may be absent. For pointers, C provides the NULL pointers, like Lisp provides
NIL. But for small struct objects, that are supposed to be lightweight, it
would be overkill to heap-allocate them, just in order to be able to
distinguish them from NULL.

The traditional way in C has been that the function returns a boolean
(by value) and the object (in memory, via pointer). Or vice versa:
it may return the object (as value) and a boolean (in memory, via pointer).
But I've come to dislike this solution, because it's not symmetric:
one of the return values is treated differently than the other one.
And gcc's analyzer often enough gets confused about this idiom too.

So, the modern solution is to return both the value and a boolean as
part of a struct.

The following module is an implementation of this idea. Loosely inspired by
  Rust Option        <https://doc.rust-lang.org/std/option/>
  C++ std::optional  <https://en.cppreference.com/cpp/utility/optional>
  metalang99 maybe   <https://metalang99.readthedocs.io/en/latest/maybe.html>


2026-11-01  Bruno Haible  <[email protected]>

        optional: Add tests.
        * tests/test-optional.c: New file.
        * modules/optional-tests: New file.

        optional: New module.
        * lib/optional.h: New file.
        * modules/optional: New file.

>From 3e9fd8706dad6a59e257559f870475685ee11c8f Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 2 Nov 2026 01:36:12 +0100
Subject: [PATCH 1/2] optional: New module.

* lib/optional.h: New file.
* modules/optional: New file.
---
 ChangeLog        |   6 +++
 lib/optional.h   | 113 +++++++++++++++++++++++++++++++++++++++++++++++
 modules/optional |  21 +++++++++
 3 files changed, 140 insertions(+)
 create mode 100644 lib/optional.h
 create mode 100644 modules/optional

diff --git a/ChangeLog b/ChangeLog
index 8cd6ae11d6..4db71502bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2026-11-01  Bruno Haible  <[email protected]>
+
+	optional: New module.
+	* lib/optional.h: New file.
+	* modules/optional: New file.
+
 2026-10-31  Bruno Haible  <[email protected]>
 
 	string-desc: Avoid a gcc -Wshadow=compatible-local warning.
diff --git a/lib/optional.h b/lib/optional.h
new file mode 100644
index 0000000000..570aa2c5b7
--- /dev/null
+++ b/lib/optional.h
@@ -0,0 +1,113 @@
+/* An Optional type wrapper.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation, either version 3 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <[email protected]>, 2026.  */
+
+#ifndef _OPTIONAL_H
+#define _OPTIONAL_H 1
+
+/* Pointers in C can be NULL; this is typically used to indicate the absence
+   of a valid value.  The same feature is not present in structs or unions.
+
+   This file defines a facility to define an Optional type wrapper for a given
+   type.  This is a type whose values may be of two kinds:
+     SOME (value)     is a value,
+     NONE             denotes the absence of a value.
+
+   Loosely modeled after
+     Rust Option        <https://doc.rust-lang.org/std/option/>
+     C++ std::optional  <https://en.cppreference.com/cpp/utility/optional>
+     metalang99 maybe   <https://metalang99.readthedocs.io/en/latest/maybe.html>
+ */
+
+#include <stdbool.h>
+
+/* Get abort().  */
+#include <stdlib.h>
+
+/* Whether the compiler supports statement-expressions.
+   Test program:
+     int f (int x) { return ({ x; x; }); }
+ */
+#if defined __GNUC__ /* both C and C++ mode */ \
+    || defined __clang__ /* both C and C++ mode */ \
+    || (defined __SUNPRO_C && __SUNPRO_C >= 0x5150) /* C mode */ \
+    || (defined __SUNPRO_CC && __SUNPRO_CC >= 0x5150) /* C++ mode */
+# define HAVE_STATEMENT_EXPRESSIONS 1
+#endif
+
+/* Whether the compiler supports typeof.
+   Test program:
+     int f (int x) { typeof (x) y = x; return y; }
+ */
+#if (((defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 1) > 3) /* both C and C++ mode */ \
+      || (defined __clang__ && __clang_major__ >= 3 /* both C and C++ mode */ \
+          && !(defined __cplusplus && !defined __GNUC__))) /* except for clang-cl in C++ mode */ \
+     && !defined __STRICT_ANSI__) /* but not with -std=c99 or -std=c11 */ \
+    || (defined __SUNPRO_C && __SUNPRO_C >= 0x5110) /* C mode */ \
+    || __STDC_VERSION__ >= 202311L /* C mode */
+# define HAVE_TYPEOF 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* A type expression for the Optional wrapper.
+   Usually used in a 'typedef'.  */
+#define OPTIONAL_TYPE(_TYPE_) \
+  struct \
+    { \
+      /* The fields of this struct should be considered private.  */ \
+      _TYPE_ _value; \
+      bool _valid; \
+    }
+
+/* An instance with a value.
+   Usually used within a compound literal.  */
+#define SOME(...) \
+  { ._value = (__VA_ARGS__), ._valid = true }
+
+/* An instance without a value.
+   Usually used within a compound literal.  */
+#define NONE \
+  { ._valid = false }
+
+/* Returns true for an instance with a value,
+   false for an instance without a value.  */
+#define has_value(instance) \
+  ((instance)._valid)
+
+/* Returns the value of an instance with a value.  */
+#if HAVE_STATEMENT_EXPRESSIONS && HAVE_TYPEOF
+# define get_value(instance) \
+    ({typeof (instance) _instance_ = (instance); \
+      if (!_instance_._valid) \
+        abort (); \
+      _instance_._value; \
+    })
+#else
+# define get_value(instance) \
+    ((instance)._value)
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OPTIONAL_H */
diff --git a/modules/optional b/modules/optional
new file mode 100644
index 0000000000..b747f6cb32
--- /dev/null
+++ b/modules/optional
@@ -0,0 +1,21 @@
+Description:
+Optional type wrapper.
+
+Files:
+lib/optional.h
+
+Depends-on:
+bool
+
+configure.ac:
+
+Makefile.am:
+
+Include:
+"optional.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.53.0

>From 9feb794ca59449e32a859a2617e9e0971ad85c54 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 2 Nov 2026 01:37:24 +0100
Subject: [PATCH 2/2] optional: Add tests.

* tests/test-optional.c: New file.
* modules/optional-tests: New file.
---
 ChangeLog              |  4 +++
 modules/optional-tests | 11 ++++++++
 tests/test-optional.c  | 58 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+)
 create mode 100644 modules/optional-tests
 create mode 100644 tests/test-optional.c

diff --git a/ChangeLog b/ChangeLog
index 4db71502bc..23dbd842bf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2026-11-01  Bruno Haible  <[email protected]>
 
+	optional: Add tests.
+	* tests/test-optional.c: New file.
+	* modules/optional-tests: New file.
+
 	optional: New module.
 	* lib/optional.h: New file.
 	* modules/optional: New file.
diff --git a/modules/optional-tests b/modules/optional-tests
new file mode 100644
index 0000000000..a714558415
--- /dev/null
+++ b/modules/optional-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-optional.c
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-optional
+check_PROGRAMS += test-optional
diff --git a/tests/test-optional.c b/tests/test-optional.c
new file mode 100644
index 0000000000..318ed8215f
--- /dev/null
+++ b/tests/test-optional.c
@@ -0,0 +1,58 @@
+/* Test of Optional wrapper type.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <[email protected]>, 2026.  */
+
+#include <config.h>
+
+#include "optional.h"
+
+#include "macros.h"
+
+/* A point in 3D.  */
+struct point { double x, y, z; };
+
+/* A type that may contain a point or none.  */
+typedef OPTIONAL_TYPE(struct point) optional_point;
+
+/* A function that returns an optional_point.  */
+static optional_point
+spherical_inversion (struct point p)
+{
+  double r = p.x * p.x + p.y * p.y + p.z * p.z;
+  if (r == 0)
+    /* We cannot represent the point at infinity as a point in the
+       affine space.  */
+    return (optional_point) NONE;
+  else
+    return (optional_point) SOME ((struct point) { .x = p.x / r, .y = p.y / r, .z = p.z / r });
+}
+
+int
+main ()
+{
+  optional_point a = spherical_inversion ((struct point) { .x = 3, .y = 4, .z = 12 });
+  optional_point b = spherical_inversion ((struct point) { .x = 0, .y = 0, .z = 0 });
+  ASSERT (has_value (a));
+  ASSERT (!has_value (b));
+
+  struct point p = get_value (a);
+  ASSERT (p.x > 0.0177 && p.x < 0.0178);
+  ASSERT (p.y > 0.0236 && p.y < 0.0237);
+  ASSERT (p.z > 0.0710 && p.z < 0.0711);
+
+  return test_exit_status;
+}
-- 
2.53.0

Reply via email to