{"id":13424500,"url":"https://github.com/Sedeniono/tiny-optional","last_synced_at":"2025-03-15T18:35:18.145Z","repository":{"id":49331816,"uuid":"477105754","full_name":"Sedeniono/tiny-optional","owner":"Sedeniono","description":"Replacement for std::optional that does not waste memory unnecessarily","archived":false,"fork":false,"pushed_at":"2025-02-28T19:54:55.000Z","size":580,"stargazers_count":115,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-02-28T23:07:06.325Z","etag":null,"topics":["c-plus-plus","c-plus-plus-17","cache-friendly","cpp","header-only","memory-efficiency","optional"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sedeniono.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-02T16:11:36.000Z","updated_at":"2025-02-28T19:54:34.000Z","dependencies_parsed_at":"2024-03-28T19:01:42.410Z","dependency_job_id":"5d3c0322-0050-41a2-936a-90dcf4f0aa04","html_url":"https://github.com/Sedeniono/tiny-optional","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sedeniono%2Ftiny-optional","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sedeniono%2Ftiny-optional/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sedeniono%2Ftiny-optional/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sedeniono%2Ftiny-optional/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sedeniono","download_url":"https://codeload.github.com/Sedeniono/tiny-optional/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243775868,"owners_count":20346279,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-plus-plus","c-plus-plus-17","cache-friendly","cpp","header-only","memory-efficiency","optional"],"created_at":"2024-07-31T00:00:55.258Z","updated_at":"2025-03-15T18:35:13.119Z","avatar_url":"https://github.com/Sedeniono.png","language":"C++","readme":"# tiny::optional  \u003c!-- omit in toc --\u003e\n\n![tests of gcc on linux](https://github.com/Sedeniono/tiny-optional/actions/workflows/test_gcc_linux.yml/badge.svg)\n![tests of clang on linux](https://github.com/Sedeniono/tiny-optional/actions/workflows/test_clang_linux.yml/badge.svg)\n![tests of msvc on windows](https://github.com/Sedeniono/tiny-optional/actions/workflows/test_msvc_win.yml/badge.svg)\n![tests of clang on windows](https://github.com/Sedeniono/tiny-optional/actions/workflows/test_clang_win.yml/badge.svg)\n![tests of gcc on windows](https://github.com/Sedeniono/tiny-optional/actions/workflows/test_gcc_win.yml/badge.svg)\n![tests of clang on macOS](https://github.com/Sedeniono/tiny-optional/actions/workflows/test_clang_mac.yml/badge.svg)\n\n\n- [Introduction](#introduction)\n- [Motivation](#motivation)\n  - [Use case 1: Wasting no memory](#use-case-1-wasting-no-memory)\n  - [Use case 2: Using sentinel values](#use-case-2-using-sentinel-values)\n- [Requirements](#requirements)\n- [Limitations](#limitations)\n  - [Platform specific behavior](#platform-specific-behavior)\n  - [Compatibility with `std::optional`](#compatibility-with-stdoptional)\n- [Usage](#usage)\n  - [Installation](#installation)\n  - [Using `tiny::optional` as `std::optional` replacement](#using-tinyoptional-as-stdoptional-replacement)\n  - [Using a sentinel value](#using-a-sentinel-value)\n  - [Storing the empty state in a member variable](#storing-the-empty-state-in-a-member-variable)\n  - [The full signature of `tiny::optional`](#the-full-signature-of-tinyoptional)\n  - [Available non-member definitions](#available-non-member-definitions)\n  - [Helpers to distinguish types at compile-time (metaprogramming)](#helpers-to-distinguish-types-at-compile-time-metaprogramming)\n  - [Specifying a sentinel value via a type](#specifying-a-sentinel-value-via-a-type)\n  - [An optional type with automatic sentinels for integers and guarantee of in-place](#an-optional-type-with-automatic-sentinels-for-integers-and-guarantee-of-in-place)\n  - [Teaching `tiny::optional` about custom types (`tiny::optional_flag_manipulator`)](#teaching-tinyoptional-about-custom-types-tinyoptional_flag_manipulator)\n    - [Introduction](#introduction-1)\n    - [Example](#example)\n    - [Details](#details)\n    - [Storing the empty flag in only part of the payload](#storing-the-empty-flag-in-only-part-of-the-payload)\n    - [Enumerations](#enumerations)\n    - [Types that you have not authored](#types-that-you-have-not-authored)\n      - [Generic alternative](#generic-alternative)\n      - [Alternative for `static constexpr`](#alternative-for-static-constexpr)\n  - [Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)](#disabling-platform-specific-tricks-tiny_optional_use_separate_bool_instead_of_ub_tricks)\n  - [Natvis](#natvis)\n- [Performance results](#performance-results)\n  - [Runtime](#runtime)\n  - [Build time](#build-time)\n- [How the library exploits platform specific behavior](#how-the-library-exploits-platform-specific-behavior)\n- [Related work](#related-work)\n\n\n# Introduction\nThe goal of this library is to provide the functionality of [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional) while not wasting any memory unnecessarily for \n1. types with unused bits (currently `double`, `float`, `bool`, raw pointers; note that NaNs, `nullptr` etc. are still valid non-empty values!), or\n2. custom types with unused states, or \n3. where a specific programmer-defined sentinel value should be used (e.g., an optional of `int` where the value `0` should indicate \"no value\").\n\n\u003e ⚠️ **Warning:** This library exploits undefined/platform specific behavior on x86/x64 architectures to implement the first case. However, this first case can be disabled to allow using the second and third cases also on other architectures. See chapter \"[Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)](#disabling-platform-specific-tricks-tiny_optional_use_separate_bool_instead_of_ub_tricks)\" for more details.\n\nFor a quick start, see the following example, also available [live on godbolt](https://godbolt.org/z/83xo9hdxT):\n```C++\n//--------- Automatic exploitation of unused bit patterns ---------\n// Assume you have the following optional variable:\nstd::optional\u003cdouble\u003e stdOptional;\n\n// The size of it is 16 bytes due to padding; 7 bytes are wasted:\nstatic_assert(sizeof(stdOptional) == 16);\n\n// Replacing std::optional with tiny::optional does not waste space:\ntiny::optional\u003cdouble\u003e tinyOptional;\nstatic_assert(sizeof(tinyOptional) == 8);\n\n// This works automatically for bool, float, double and raw pointers.\n\n\n//--------- Usage of sentinel values ---------\n// But what about other types, such as integers? If you know that not the \n// whole value range is used, you can instruct the library to exploit this.\n// For example, assume you have an optional index into e.g. some array:\nstd::optional\u003cint\u003e stdIndex;\n// which has a size of 8 bytes due to padding:\nstatic_assert(sizeof(stdIndex) == 8);\n\n// Assume you know that negative values can never occur. Then you can instruct\n// the library to use e.g. -1 as \"sentinel\" and save half the memory:\ntiny::optional\u003cint, -1\u003e tinyIndex;\nstatic_assert(sizeof(tinyIndex) == 4);\n// This is more expressive and safe than using a raw variable with a comment:\nint poorMansTinyIndex = -1; // -1 value indicates emptiness\n\n// Of course, attempting to store the value -1 in tinyIndex is an error\n// and actually triggers an assertion (if not compiled with NDEBUG):\n//    tinyIndex = -1; // Uncomment to trigger assert\n\n// Note that without such a user supplied sentinel value, the optional is \n// not \"tiny\", because in principle the whole value range could be used:\nstatic_assert(sizeof(tiny::optional\u003cint\u003e) == sizeof(std::optional\u003cint\u003e));\n\n// You can also instruct the library to use a member variable of the\n// contained type to store the information about the empty state. See below.\n```\n\n\n# Motivation\n## Use case 1: Wasting no memory\n`std::optional` always uses a separate `bool` member to store the information if a value is set or not. A `bool` always has a size of at least 1 byte, and often implies several padding bytes afterwards. For example, a `double` has a size of 8 bytes.\nA `std::optional\u003cdouble\u003e` typically has a size of 16 bytes because the compiler inserts 7 padding bytes after the internal `bool`.\nBut for several types this is unnecessary because they have unused bit patterns.\nTherefore, `std::optional` often just wastes memory and is not very cache-friendly.\nThis library exploits these **unused** bit patterns to store the information if a value is set or not in-place within the payload itself, **without** loosing any \"valid\" values.\nTo emphasize this:\n* `sizeof(tiny::optional\u003cbool\u003e) == sizeof(bool)`, and both `true` and `false` can be stored.\n* `sizeof(tiny::optional\u003cdouble\u003e) == sizeof(double)`, and all \"normal\" values of `double` remain valid and can be stored in the `tiny::optional` without the optional becoming empty. This includes `std::numeric_limits\u003cdouble\u003e::quiet_NaN()` and `std::numeric_limits\u003cdouble\u003e::signaling_NaN()`, and of course infinities, subnormals, max. and lowest values, etc. Similar for `float`.\n* `sizeof(tiny::optional\u003cFoo*\u003e) == sizeof(Foo*)`; storing a `nullptr` results in a **non-empty** optional, so `nullptr` remains a valid value and is distinct from an empty optional!\n\nSee the end of the readme for details on how the library achieves this.\n\n`tiny::optional` can also be \"taught\" how it may store a custom payload type with an unused state without requiring an additional internal `bool`. See the chapter about `tiny::optional_flag_manipulator`.\n\nThe results below show that in memory bound applications this can result in a significant performance improvement.\nThis optimization is similar to what Rust is doing for [booleans](https://stackoverflow.com/a/73181003/3740047) and [references](https://stackoverflow.com/a/16515488/3740047).\n\n**Note:** The built-in exploitation of unused bit patterns is available only on x86/x64.\nIf you try to compile the library on any other platform you get a compiler error.\nTo allow the following use cases on other platforms, you can disable the platform specific tricks as explained in the chapter \"[Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)](#disabling-platform-specific-tricks-tiny_optional_use_separate_bool_instead_of_ub_tricks)\".\n\n\n\n## Use case 2: Using sentinel values\nSometimes one wants to use special \"sentinel\" or \"flag\" values to indicate that a certain variable does not contain any information. Think about a raw integer `int index` that stores an index into some array and where the special value `-1` should indicate that the index does not refer to anything. Looking at such a variable, it is not immediately clear that it can have such a special \"empty\" state. This makes code harder to understand and might introduce subtle bugs. \n\nThe present library can be used to provide more semantics: `tiny::optional\u003cint, -1\u003e` immediately tells the reader that the variable might be empty, and that the \"sentinel\" `-1` must not be within the set of valid values. At the same time, it does not waste additional memory (i.e. `sizeof(tiny::optional\u003cint, -1\u003e) == sizeof(int)`), in contrast to `std::optional\u003cint\u003e`.\n\nNote: In contrast to the first use case, the sentinel value is \"removed\" from the range of valid values of the type.\nSo `-1` cannot be stored in `tiny::optional\u003cint, -1\u003e`.\n\n\n# Requirements\nBesides the C++ standard library, there are no external dependencies.\nThe library requires at least C++17. The monadic operations `and_then()` and `transform()` are always defined (although the C++ standard introduced them starting only with C++23). When C++20 is enabled, the three-way comparison operator `operator\u003c=\u003e()` and the monadic operation `or_else()` are additionally implemented.\n\nThe full functionality of the library is supported only on **x64 and x86** architectures on Windows, Linux and Mac.\nBy disabling tricks relying on undefined behavior, as explained in \"[Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)](#disabling-platform-specific-tricks-tiny_optional_use_separate_bool_instead_of_ub_tricks)\", any standard conforming platform should work.\n\nThe library is regularly tested on MSVC, clang and gcc on Windows, Linux and Mac (see the github actions).\n\n\n# Limitations\n\n## Platform specific behavior\nThis library exploits **platform specific behavior** (i.e. undefined behavior). So if your own code also uses platform specific tricks, you might want to check that they are not incompatible. Compare the section below where the tricks employed by this library are explained. \n\nNote that you can disable them by defining `TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`, as explained in the chapter \"[Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)](#disabling-platform-specific-tricks-tiny_optional_use_separate_bool_instead_of_ub_tricks)\".\n\n\n## Compatibility with `std::optional`\nCurrently, the following components of the interface of `std::optional` are not yet supported:\n* No converting constructors and assignment operators implemented. The major issue here is to decide what to do with conversions such as `tiny::optional\u003cint, -1\u003e` to `tiny::optional\u003cunsigned, 42\u003e`: What if the source contains a `42`? Should an exception be thrown? Should this be asserted in debug? Should this specific conversion be forbidden?\n* Constructors and destructors are not trivial, even if the payload type `T` would allow it.\n* Methods and types are not `constexpr`. This will probably not be possible in C++17 because some of the tricks rely on `std::memcpy`, which is not `constexpr`. `std::bit_cast` might help here for C++20. Since the whole purpose of the library is to safe memory during runtime, a viable workaround is to simply use `std::optional` in `consteval` contexts.\n\nMoreover, the monadic operation `transform()` always returns a `tiny::optional\u003cT\u003e`, i.e. specification of a sentinel or some other optional as return type (`tiny::optional_sentinel_via_type` etc.) is not possible. As a workaround, you can use `and_then()`.\n\n\n# Usage\n\n## Installation\nThis is a header-only library. Just copy the folder from the include directory containing the header to your project. Include it via `#include \u003ctiny/optional\u003e`.\n\nThe library uses the standard [`assert()` macro](https://en.cppreference.com/w/cpp/error/assert) in a few places, which can be disabled as usual by defining `NDEBUG` for release builds.\n\n\n## Using `tiny::optional` as `std::optional` replacement\nInstead of writing `std::optional\u003cT\u003e`, use `tiny::optional\u003cT\u003e` in your code.\nIf the payload `T` is a `float`, `double`, `bool` or a pointer/function pointer (in the sense of `std::is_pointer`), the optional will not require additional space. E.g.: `sizeof(tiny::optional\u003cdouble\u003e) == sizeof(double)`.  \n**Notes:**\n* This is only true if `TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS` is not defined. See the chapter \"[Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)](#disabling-platform-specific-tricks-tiny_optional_use_separate_bool_instead_of_ub_tricks)\" for more information.\n* For pointers, `nullptr` remains a valid value! I.e. the optional `tiny::optional\u003cint*\u003e o = nullptr;` is **not** empty!\n* The type `long double` requires additional space at the moment, simply because the differing characteristics on the various supported platforms are not yet implemented.  \n\nFor other types (where the automatic \"tiny\" state is not possible), the size of `tiny::optional` is equal to that of `std::optional`. E.g. `sizeof(tiny::optional\u003cint\u003e) == sizeof(std::optional\u003cint\u003e)`, or `sizeof(tiny::optional\u003cSomeStruct\u003e) == sizeof(std::optional\u003cSomeStruct\u003e)`.  \nBut note that you can **teach** the library about custom types, see the chapter about `tiny::optional_flag_manipulator` below.\n\nBesides this, all standard operations such as assignment of `std::nullopt` are supported (with the exceptions listed above).\n\n## Using a sentinel value\n`tiny::optional` has a second optional template parameter: `tiny::optional\u003cT, sentinel\u003e`. \n`sentinel` is not a type but rather a [non-type template parameter (\"NTTP\")](https://en.cppreference.com/w/cpp/language/template_parameters).\nSetting this `sentinel` value instructs the library to assume that the value `sentinel` cannot occur as a valid value of the payload `T` and thus allows the library to use it to indicate the empty state. As a result: `sizeof(tiny::optional\u003cT, sentinel\u003e) == sizeof(T)`.\n\nThe `sentinel` should be of type `T`. Any value is possible that is supported by the compiler as a non-type template parameter. That means integers, and since C++20 also floating point types and literal class types (i.e. POD like types) that are equipped with an `operator==`.\n\nExamples: `tiny::optional\u003cunsigned int, MAX_UINT\u003e` and `tiny::optional\u003cint, -1\u003e`.\n\nNote: Attempting to store the sentinel value in the optional is illegal. If `NDEBUG` is **not** defined, an appropriate `assert()` gets triggered. For example, if you define `tiny::optional\u003cint, -1\u003e o;`, setting `o = -1;` is not allowed and triggers the assert.\n\n\n## Storing the empty state in a member variable\nImagine you have a simple POD-like data structure such as\n```C++\nstruct Data\n{\n    int var1;\n    double var2;\n    MoreData * var3;\n    // More stuff...\n}; \n```\nand you need an optional variable of `Data`. Writing `tiny::optional\u003cData\u003e` works but the optional requires an additional internal `bool`, so the size of `tiny::optional\u003cData\u003e` will be the same as `std::optional\u003cData\u003e`. \nThis is unnecessary since some members of `Data` have unused bit patterns, namely `var2` and `var3`.\nThe library allows to exploit this by specifying an accessible member where the emptiness flag can be stored: `tiny::optional\u003cData, \u0026Data::var2\u003e`. The resulting optional has the same size as `Data`. Using `tiny::optional\u003cData, \u0026Data::var3\u003e` works as well here. In fact, all the types mentioned above where the library stores the empty flag in-place can be specified.\nMoreover, all members for which a specialization of `tiny::optional_flag_manipulator` exist (see chapter below), work too.\n\nAdditionally, there is the option to use a sentinel value for the empty state and instruct the library to store it in one of the members. The sentinel value is specified as the third template parameter. For example, if you know that `Data::var1` can never be negative, you can instruct the library to use the value `-1` as sentinel: `tiny::optional\u003cData, \u0026Data::var1, -1\u003e`. Again the resulting `tiny::optional` will not require additional memory compared to a plain `Data`.\n\n**Note:** When storing the flag in a member variable, gcc with optimizations turned on likes to warn about possible uninitialized accesses (`-Wmaybe-uninitialized`).\nThese are false positives.\ngcc fails to figure out that certain branches that would lead to access of uninitialized memory cannot occur because these branches are protected by `has_value()` calls.\nUnfortunately, gcc sometimes still attributes the warnings to a location in the user code rather than the library, so although the library actually by itself disables the warning locally (in the `tiny/optional.h` header file), it might still occur.\nIn fact, even the standard stdlibc++ implementation of [`std::optional` at least until gcc 13 can trigger this warning](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635#c69).\nIf it happens to you, I suggest to [disable the warning locally](https://stackoverflow.com/a/26003732/3740047).\n\n**Note:** Using a member like this is actually undefined behavior. Hence it is available only on x86/x64. To allow compilation on other platforms, see the chapter \"[Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)](#disabling-platform-specific-tricks-tiny_optional_use_separate_bool_instead_of_ub_tricks)\". In this case the member pointer argument is ignored and a separate `bool` is used.\n\n\n## The full signature of `tiny::optional`\nGiven the explanations above, the full signature of `tiny::optional` is:\n```C++\nnamespace tiny {\n    template \u003c\n        class PayloadType, \n        auto sentinelOrMemPtr = UseDefaultValue, \n        auto irrelevantOrSentinel = UseDefaultValue\u003e\n    class optional;\n}\n```\nThe first template parameter specifies the type that should get stored in the optional.\nThe second and third parameters are optional.\nIf the second parameter is **not** a member pointer, the value is used as sentinel for the empty state.\nIf the second parameter is a member pointer, it has to point to a member of `PayloadType` in which case the emptiness flag is stored in that member. Only in this case the third parameter may be optionally specified to indicate a sentinel value to store in that member.\n\n\n## Available non-member definitions\nThe template function `tiny::make_optional()` can be used to create a `tiny::optional`. [Contrary to `std::make_optional()`](https://en.cppreference.com/w/cpp/utility/optional/make_optional), it can accept two additional optional template parameters corresponding to `sentinelOrMemPtr` and `irrelevantOrSentinel` explained above.\nExamples:\n```C++\ntiny::make_optional(42.0); // Constructs tiny::optional\u003cdouble\u003e(42.0)\ntiny::make_optional\u003cunsigned int, 0\u003e(42u); // Constructs tiny::optional\u003cunsigned int, 0\u003e(42)\n\nstruct Foo{\n    int v1;\n    double v2;\n    Foo(int v1, double v2);\n};\ntiny::make_optional\u003cFoo\u003e(2, 3.0); // tiny::optional\u003cFoo\u003e(2, 3.0), has size of std::optional\ntiny::make_optional\u003cFoo, \u0026Foo::v1, -1\u003e(2, 3.0); // tiny::optional\u003cFoo, \u0026Foo::v1, -1\u003e(2, 3.0)\n```\n\nAll the comparison operators `operator==`, `operator\u003c=`, etc. are provided, including the spaceship `operator\u003c=\u003e` [similar to the ones for `std::optional`](https://en.cppreference.com/w/cpp/utility/optional/operator_cmp).\n\nAdditionally, `std::hash` is specialized ([as for `std::optional`](https://en.cppreference.com/w/cpp/utility/optional/hash)) for the optional types defined by this library.\n\nAn appropriate deduction guide is also defined, allowing to write e.g. `tiny::optional{42}` to construct a `tiny::optional\u003cint\u003e{42}`. Note that this does not allow you to specify a sentinel.\n\n\n## Helpers to distinguish types at compile-time (metaprogramming)\nThere are a few helpers available which facilitate checks at compile-time:\n* `tiny::optional\u003cT\u003e::is_compressed` is true if the optional has the same size as the payload `T`, and false otherwise. It is also defined for all other optional types defined by this library.\n* `tiny::is_tiny_optional_v\u003cT\u003e` is true if `T` is an optional defined by this library (`tiny::optional`, `tiny::optional_inplace` or `tiny::optional_aip`, compare below). It is false for any other type, including `std::optional`.\n* Every optional defined by this library defines a static boolean member `is_tiny_optional` with value true. For example, `tiny::optional\u003cT\u003e::is_tiny_optional` is true for **every** type `T`. The value is never false. It can be used to determine whether something is a tiny optional. But it is most likely more convenient to use `tiny::is_tiny_optional_v`, which yields false for any type that is not a tiny optional instead of resulting in compilation error. Nevertheless, the member `is_tiny_optional` might be useful in SFINAE contexts.\n* The macro `TINY_OPTIONAL_VERSION` indicates the version of the library.\n\n\n## Specifying a sentinel value via a type\n`tiny::optional` accepts as second or third template parameter a value, i.e. they are non-type template parameters. \nEspecially in C++17, this can be restricting since e.g. floating point values cannot be used in templates.\nBut they can be static member constants.\nTo this end, the library provides an additional type \n```C++\nnamespace tiny {\n    template \u003c\n        class PayloadType, \n        class SentinelValue, \n        auto memPtr = UseDefaultValue\u003e\n    class optional_sentinel_via_type;\n}\n```\nwhere the sentinel value is expected to be given by `SentinelValue::value`.\nNote that this second template parameter is not optional. If you do not need a sentinel, just use `tiny::optional\u003cPayloadType\u003e`.\nThe third parameter is optional and can be a member pointer to instruct the library to store the sentinel value in that member, similar to `tiny::optional`. I.e. the `SentinelValue::value` gets stored in `memPtr`.\nContrary to `tiny::optional`, it has to be the third and not the second parameter.\nThis is for technical reasons (you cannot mix type and non-type template parameters, and having an optional parameter second and a mandatory third parameter makes no sense).\n\n\n## An optional type with automatic sentinels for integers and guarantee of in-place\nThe type `tiny::optional_aip` (\"aip\" for \"always in-place\") is similar to `tiny::optional` but with automatic \"swallowing\" of a value for integers to provide a sentinel, and a compilation error if no sentinel is automatically found. Hence, its size is **ALWAYS** the same as the size of the payload.\n\nIts declaration is basically `tiny::optional_aip\u003cPayloadType, SentinelValue = ...\u003e`. If you omit the `SentinelValue`, then:\n* If the `PayloadType` has unused bits, those get exploited. So for example `tiny::optional_aip\u003cdouble\u003e` behaves the same as `tiny::optional\u003cdouble\u003e`.\n* If `tiny::optional_flag_manipulator` (see chapter below) is specialized for `PayloadType`, then it is used.\n* If the `PayloadType` is an **unsigned integer**, the **maximal** integer value is used as sentinel. For example, `tiny::optional_aip\u003cunsigned\u003e` will use `UINT_MAX` as sentinel. This also means that it is no longer legal to attempt and store the value `UINT_MAX` in that optional!\n* Similar, if the `PayloadType` is a **signed integer**, the **minimum** integer value is used as sentinel. E.g. `tiny::optional_aip\u003cint\u003e` uses `INT_MIN`.\n* Note that for characters (`char`, `signed char` and `unsigned char`) and enumerations no automatic sentinel is provided.\n\nIn all other cases, you have to specify a sentinel yourself, e.g. `tiny::optional_aip\u003cchar, 'a'\u003e`. If you do not, then a compilation error occurs. Hence, `tiny::optional_aip` is guaranteed to have the same size as the payload.\n\nThe type has been suggested in [this issue](https://github.com/Sedeniono/tiny-optional/issues/1).\n\n\n## Teaching `tiny::optional` about custom types (`tiny::optional_flag_manipulator`)\n\n### Introduction\n\nThe library provides a customization point: By specializing `tiny::optional_flag_manipulator` for a custom type, you instruct the library to always place the emptiness flag within the payload type, and how to do that.\nThe method of customization by means of a specialization is the same as used by e.g. [`std::hash`](https://en.cppreference.com/w/cpp/utility/hash) and [`fmt::formatter`](https://fmt.dev/latest/api.html#formatting-user-defined-types).\n\n\n### Example\n\nAssume you have some custom type `MyNamespace::IndexPair` defined as\n```C++\nnamespace MyNamespace\n{\n    class IndexPair\n    {\n        // ...\n        // Both can never be negative at the same time.\n        int mIndex1;\n        int mIndex2;\n    };\n}\n```\nYou, as the author of `IndexPair`, know that not the whole theoretical range of values is used, i.e. there is some combination of values of member variables that are unused in your application: Both indices can never be negative simultaneously.\nTherefore, you want to exploit this, so that a `tiny::optional\u003cIndexPair\u003e` should always be in the \"tiny\" state (`sizeof(tiny::optional\u003cIndexPair\u003e) == sizeof(IndexPair)`).\nThis can be achieved by specializing `tiny::optional_flag_manipulator` as follows:\n```C++\n#include \u003ctiny/optional_flag_manipulator_fwd.h\u003e\n#include \u003cnew\u003e // for placement new\n\nnamespace MyNamespace\n{\n    class IndexPair\n    {\n    public:\n        void SetIndices(int idx1, int idx2)\n        {\n            mIndex1 = idx1;\n            mIndex2 = idx2;\n        }\n       \n        int GetIndex1() const { return mIndex1; }\n        int GetIndex2() const { return mIndex2; }\n    \n    private:\n        // Both can never be negative at the same time.\n        int mIndex1;\n        int mIndex2;\n    };\n}\n\ntemplate \u003c\u003e\nstruct tiny::optional_flag_manipulator\u003cMyNamespace::IndexPair\u003e\n{\n    static bool is_empty(MyNamespace::IndexPair const \u0026 payload) noexcept\n    {\n        // Needs to return true if the optional should be considered empty.\n        // I.e. if the given \"payload\" state indicates emptiness. It can be called after \n        // init_empty_flag() or invalidate_empty_flag() by the library.\n        return payload.GetIndex1() \u003c 0 \u0026\u0026 payload.GetIndex2() \u003c 0;\n    }\n    \n    static void init_empty_flag(MyNamespace::IndexPair \u0026 uninitializedPayloadMemory) noexcept\n    {\n        // uninitializedPayloadMemory is a reference to an **uninitialized** payload (i.e. \n        // the constructor of the payload has not been called, but the memory has been\n        // already allocated).\n        // This function is called when the optional is constructed in an empty state or\n        // once it should become empty. The function must initialize the memory such that \n        // the optional is considered empty, i.e. is_empty(uninitializedPayloadMemory) must\n        // return true afterwards.\n        ::new (\u0026uninitializedPayloadMemory) MyNamespace::IndexPair(); // Placement new\n        uninitializedPayloadMemory.SetIndices(-1, -1);\n    }\n    \n    static void invalidate_empty_flag(MyNamespace::IndexPair \u0026 emptyPayload) noexcept\n    {\n        // This function is called just before a (non-empty) value is stored in the\n        // optional. The given \"emptyPayload\" is currently indicating the empty state,\n        // i.e. is_empty(emptyPayload) returns true.\n        // The function must deconstruct the flag value in \"emptyPayload\" which was \n        // previously constructed by init_empty_flag(). After this function returns,\n        // the library constructs the payload. After that, is_empty() must return false.\n        // Note: The memory pointed to by \"emptyPayload\" must not be freed. It is handled\n        // by the library.\n        emptyPayload.~IndexPair();\n    }\n};\n```\nYou first need to include `\u003ctiny/optional_flag_manipulator_fwd.h\u003e` to make the primary `tiny::optional_flag_manipulator` template known to the compiler. \nAdditionally, the `\u003cnew\u003e` header is required to be able to use [placement new](https://en.cppreference.com/w/cpp/language/new).\n\n\n### Details\n\n\u003e ⚠️ It is quite crucial to place the definition of the specialization as close as possible below the definition of the payload type `IndexPair`, i.e. in the same header file:\nYou need to ensure that every time an instantiation of `tiny::optional\u003cIndexPair\u003e` happens, the compiler sees the specialization.\nIf the compiler sometimes sees it and sometimes it does not see it, [you have undefined behavior](https://stackoverflow.com/q/21112148/3740047). In practice, the result can be that it appears to work sometimes and sometimes not (see e.g. [this](https://stackoverflow.com/q/57614188/3740047) post), or that you have two `tiny::optional\u003cIndexPair\u003e` instances with different sizes, possibly causing access violations and other hard to track bugs because of mismatching memory layouts.\nThis is nothing special with the library: You have the exact same issues when specializing e.g. [`std::hash`](https://en.cppreference.com/w/cpp/utility/hash) or [`fmt::formatter`](https://fmt.dev/latest/api.html#formatting-user-defined-types).\nIf you put the specialization into a dedicated header file and forget to include it somewhere where `tiny::optional\u003cIndexPair\u003e` is used, you most likely already have undefined behavior.\nSo the best way to avoid all of this is to put the specialization right next to your type, in the same file.  \nSide note: Forward declaring `IndexPair` and using `tiny::optional\u003cIndexPair\u003e` as function parameter or return type is ok, since these [do not represent instantiation points](https://stackoverflow.com/q/76148731/3740047).\n\nThe `tiny::optional_flag_manipulator` specialization needs to happen in the `tiny` namespace. So if the `IndexPair` is in a namespace (like `MyNamespace` in the example), ensure that the specialization is not in the namespace `::MyNamespace::tiny`.\n\nThere are 3 functions that the specialization needs to define:\n* `is_empty()`: It receives a reference to the memory stored in the optional. The function must return `true` if that memory indicates that the optional is in the empty state, and false otherwise.\n* `init_empty_flag()`: This function receives a reference to already allocated \"raw\" memory, but without any \"object\" created in that memory. (After all, the whole point of an optional compared to e.g. a `std::unique_ptr` is to have the memory allocated statically, it never gets deleted or created after the initial creation of the optional.)\nIt must initialize the given memory such that afterwards `is_empty()` returns true.\nIn this example, we simply construct a complete `IndexPair` in the given memory via [placement new](https://en.cppreference.com/w/cpp/language/new). Roughly speaking, this is just like the ordinary `operator new` except that it does not perform dynamic memory allocation and instead constructs the object in the given memory.\nAfterwards, we set both indices to `-1`, which we **define** to indicate the empty state.\n* `invalidate_empty_flag()`: This function must destroy the object that was created in `init_empty_flag()`, but it must not free the associated memory! (As noted before, the whole point of an optional is to have the memory allocated statically and bound to the lifetime of the optional.) In C++ this means to call the destructor manually.\n\nIn other words, the tasks of the 3 functions are:\n* `is_empty()` must decide whether the current state represents the empty state.\n* `init_empty_flag()` has 2 tasks: First, it must create an object that is used for the emptiness flag, and second it must set the value of that created emptiness flag so that the optional is seen as empty.\n* `invalidate_empty_flag()` must destroy the emptiness flag that was created by `init_empty_flag()`.\n\nAll three functions must be `noexcept`. \nThis is necessary to satisfy the same exception guarantees as `std::optional`. Setting the optional into the empty state should always be possible. \nIf exceptions could be thrown from `init_empty_flag()`, the optional could be left in a weird in-between state. (So, requiring  `noexcept` avoids complications such as [`std::variant::valueless_by_exception`](https://en.cppreference.com/w/cpp/utility/variant/valueless_by_exception).)\nEspecially note that the constructor that you usually call in `init_empty_flag()` must therefore not throw exceptions.\n\n\u003e ⚠️ **As a guideline, ensure that the payload can transition to the state which indicates emptiness ONLY by calling `init_empty_flag()`. The empty state should not be constructable via the payload's default constructor, move constructor, move assignment operator and any other member function, except possible a single dedicated one that is used exclusively by the `init_empty_flag()` specialization.**\n\u003e\n\u003e Consider as a **bad** example:\n\u003e ```C++\n\u003e struct TypeBecomingEmptyAfterMove {\n\u003e   std::vector\u003cint\u003e v;\n\u003e };\n\u003e \n\u003e template \u003c\u003e \n\u003e struct tiny::optional_flag_manipulator\u003cTypeBecomingEmptyAfterMove\u003e {\n\u003e     static bool is_empty(TypeBecomingEmptyAfterMove const \u0026 payload) noexcept {\n\u003e         return payload.v.empty();\n\u003e     }\n\u003e \n\u003e     static void init_empty_flag(\n\u003e             TypeBecomingEmptyAfterMove \u0026 uninitializedPayloadMemory) noexcept {\n\u003e         ::new (\u0026uninitializedPayloadMemory) TypeBecomingEmptyAfterMove();\n\u003e     }\n\u003e \n\u003e     static void invalidate_empty_flag(\n\u003e             TypeBecomingEmptyAfterMove \u0026 emptyPayload) noexcept {\n\u003e         emptyPayload.~TypeBecomingEmptyAfterMove();\n\u003e     }\n\u003e };\n\u003e ```\n\u003e `tiny::optional\u003cTypeBecomingEmptyAfterMove\u003e` is treated as empty if `TypeBecomingEmptyAfterMove::v` is empty. Especially note that the move constructor and move assignment operator of `TypeBecomingEmptyAfterMove` empty `v`, and thus cause a transition to the empty state when stored in `tiny::optional`.\n\u003e Now consider:\n\u003e ```C++\n\u003e tiny::optional\u003cTypeBecomingEmptyAfterMove\u003e nonEmpty(std::vector\u003cint\u003e{1,2,3});\n\u003e tiny::optional\u003cTypeBecomingEmptyAfterMove\u003e o = std::move(nonEmpty);\n\u003e ```\n\u003e The `nonEmpty` optional would become empty due to the move because `nonEmpty.v` becomes empty.\n\u003e This happens \"implicitly\" from the point of view of `tiny::optional` rather than explicitly via e.g. `tiny::optional::reset()`.\n\u003e Thus, `tiny::optional` does not call the destructor of the payload `TypeBecomingEmptyAfterMove` stored in `nonEmpty` and then `init_empty_flag()` to initialize the empty flag cleanly.\n\u003e Instead, when `nonEmpty` is destroyed in the example, it ends up calling `invalidate_empty_flag()` rather than the destructor of `TypeBecomingEmptyAfterMove`.\n\u003e This can cause problems.\n\u003e \n\u003e In theory this is happens to be fine in the example because `invalidate_empty_flag()` happens to call the destructor of `TypeBecomingEmptyAfterMove`, too.\n\u003e Also, the move constructor and assignment operators of `tiny::optional` could detect this case and handle it properly. \n\u003e But for two 2 reasons, this is **not supported** at the moment:\n\u003e * In the general case outlined in the next section this is not fine (you might want to read that section, too!).\n\u003e * `tiny::optional` tries to follow the C++ standard (i.e. `std::optional`) as close as possible, which requires that a moved-from non-empty optional is non-empty afterwards. We could lift this restriction, of course, but for now we do not.\n\u003e \n\u003e As such, calling the move constructor or move assignment operator of `tiny::optional` performs a debug `assert()` that the emptiness state of the moved-from object does not change.\n\n\n### Storing the empty flag in only part of the payload\nThe above examples used the whole memory given in `init_empty_flag()` to initialize a full instance of the payload.\nWe just defined that a specific state of the full payload is to be interpreted as empty state.\nIn principle, one can also use just a part of the memory, e.g. the part where `IndexPair::mIndex2` is located, and to not initialize all the other members at all.\nIf the other member are expensive to initialize, this can improve performance.\nThis works in practice but it is actually undefined behavior according to the C++ standard.\nSo use this possibility at your own risk.\n\n\u003e ⚠️ If the `optional_flag_manipulator` specialization initializes only part of the payload, the payload's member functions really must not change the instance stored in the optional such that `tiny::optional` transitions from non-empty to empty or vice versa! As explained in the previous section, this is not supported anyway.\n\u003e But to explain the problems, consider the `TypeBecomingEmptyAfterMove` type again, and the code:\n\u003e ```C++\n\u003e tiny::optional\u003cTypeBecomingEmptyAfterMove\u003e nonEmpty(std::vector\u003cint\u003e{1,2,3});\n\u003e TypeBecomingEmptyAfterMove t = std::move(*nonEmpty);\n\u003e ```\n\u003e Notice that in contrast to the example from the previous section the code `TypeBecomingEmptyAfterMove t = std::move(*nonEmpty)` calls the move assignment operator of `TypeBecomingEmptyAfterMove` **directly** rather than the move assignment operator of `tiny::optional` (which would debug assert, as explained above).\n\u003e \n\u003e The `nonEmpty` optional will become empty due to the move because `nonEmpty.v` becomes empty.\n\u003e This happens \"magically\" from the point of view of `tiny::optional`; it happens **completely outside** of `tiny::optional`.\n\u003e There is no way for `tiny::optional` to detect the sudden change of emptiness.\n\u003e Thus, `tiny::optional` has no chance to make the transition \"clean\", even in theory.\n\u003e So, when `nonEmpty` is destroyed in the example, it ends up calling `invalidate_empty_flag()` rather than the destructor of `TypeBecomingEmptyAfterMove`.\n\u003e In the example this is happens to be fine because `invalidate_empty_flag()` happens to call the destructor of `TypeBecomingEmptyAfterMove`, too.\n\u003e But if your `optional_flag_manipulator` specialization does not call the constructor and destructor of the whole payload type, you get undefined behavior (i.e. some sort of memory leak), since `tiny::optional` originally constructed the whole payload but `invalidate_empty_flag()` destroys only part of it.\n\u003e Since there is no way to detect the problem, there is also no debug `assert()`.\n\u003e\n\u003e Also compare [this issue](https://github.com/Sedeniono/tiny-optional/issues/4).\n\n\n### Enumerations\nEnumerations typically do not exhaust their full value range, so they are an obvious choice for saving memory.\nUnfortunately, C++ does not provide any reflection mechanism with which the library could automatically figure out unused numeric values.\nInstead, the user of the library needs to specify the sentinel.\n\nAssume you have an enumeration\n```C++\nenum class MyEnum\n{\n    Value1,\n    Value2,\n    Max // Does not represent a valid value.\n};\n```\nThe `Max` element was introduced \"artificially\"; it is not used as a valid enumeration value by the application, but instead can be used to automatically deduce the number of values in the enumeration and, in our case, to get a nice named representation of a sentinel that can be used by `tiny::optional`.\n\nThere are now two options:\n1. Explicitly specify the sentinel value always: `tiny::optional\u003cMyEnum, MyEnum::Max\u003e` (also compare the chapters above).\n2. Specialize `tiny::optional_flag_manipulator` for `MyEnum` and then use `tiny::optional\u003cMyEnum\u003e`. ⚠️ BUT: USE WITH CARE! SEE THE WARNING BELOW!\n\nSince the second case might be quite common and the three functions required in the specialization always look the same, the library provides a helper `tiny::sentinel_flag_manipulator` which can be used to specialize `tiny::optional_flag_manipulator`.\nSimply inherit publicly from `tiny::sentinel_flag_manipulator` and specify the enumeration type as first template argument and the sentinel as second:\n```C++\ntemplate \u003c\u003e \nstruct tiny::optional_flag_manipulator\u003cMyEnum\u003e\n    : tiny::sentinel_flag_manipulator\u003cMyEnum, MyEnum::Max\u003e\n{ };\n```\nThis \"registers\" `MyEnum::Max` as sentinel for the empty state whenever you write `tiny::optional\u003cMyEnum\u003e`.\n\n\u003e ⚠️ As noted above, you need to ensure that the specialization is consistently seen by instantiations of `tiny::optional`.\nFor enumerations, there is a pitfall regarding forward declarations.  \nFirst, consider what happens if you attempt to instantiate `tiny::optional` with a class type that is just forward declared: you get a compiler error (since `tiny::optional` requires a complete type). So by putting the specialization into the same file as the payload type, you guarantee that at every instantiation with your class type `tiny::optional` sees the specialization.  \nProblem for enumerations: C++ allows to [forward declare (most) enumerations](https://stackoverflow.com/a/1280969/3740047), and forward declared enumerations are complete since the forward declaration contains the underlying type.\nSo if you use `tiny::optional` with a forward declared enumeration `MyEnum`, you will not get a compiler error. \nThat by itself is nice and fine.\nBut if somewhere in your code you have the `tiny::optional_flag_manipulator` specialization for `MyEnum`, but the specialization is not seen everywhere where a forward declared `MyEnum` is used to instantiate `tiny::optional`, you get undefined behavior, as explained already above.\n\nSo, what to do?\nForbidding forward declarations of specific enumerations by convention is brittle and most likely futile.\nMy advice would be to use the `tiny::optional_flag_manipulator` specialization method only for enumerations defined in structs/classes, since [those cannot be forward declared](https://stackoverflow.com/q/27019292/3740047).\nThis forces users of the enumeration to include the header where the enumeration is defined, together with the `tiny::optional_flag_manipulator` specialization (which of course should be defined in the same header).\n\n\n### Types that you have not authored\nAs explained above, the `tiny::optional_flag_manipulator` specialization should be placed right next to the targeted payload type.\nSo, what about types from other libraries such as the standard library (`std::vector`,...), boost, etc, where you cannot modify the header files which define these types?\nIn this case there is no sensible places to specialize `tiny::optional_flag_manipulator`: The only two valid places are the header file of the type, or the `tiny/optional.h` file, both of which you should not change.\nThus, there is simply no good way to achieve what you want.\nA hacky workaround would be to create a dedicated header file, say `my_vector.h`, that includes e.g. `\u003cvector\u003e` and specializes `tiny::optional_flag_manipulator` for `std::vector`, and then to always include `my_vector.h` everywhere instead of `\u003cvector\u003e`. \nOr the other way around, create a custom `my_tiny_optional.h` header file, and always include it.\nI really do not recommend either of these solutions.\n\n#### Generic alternative\n\nInstead of relying on the automatism of `tiny::optional`, you can instead use `tiny::optional_inplace` which accepts a `FlagManipulator`:\n```C++\nnamespace tiny {\n    template \u003cclass PayloadType, class FlagManipulator\u003e\n    class optional_inplace;\n}\n```\nAs before, `PayloadType` is the type to store in the optional.\nThe expected interface of `FlagManipulator` is identical to the one expected from the `tiny::optional_flag_manipulator` specialization.\n\n**Example:** Assume you have an iterator class from some library, and that iterator knows if it is still valid:\n```C++\nclass Iterator\n{\npublic:\n    Iterator() noexcept\n        : mIsValid(false), mIndex(0)\n    { }\n\n    Iterator(std::size_t index) noexcept\n        : mIsValid(true), mIndex(index) \n    { }\n\n    bool IsValid() const noexcept {\n        return mIsValid;\n    }\n\n    // More members, e.g. operator++().\n\nprivate:\n    bool mIsValid;\n    std::size_t mIndex;\n    // Maybe more members that \n};\n```\nNow, assume you have some function that must return either a valid iterator or none at all.\nYou could define that an invalid iterator indicates \"no iterator\", but this might not be obvious to readers.\nYou could also return a `std::optional\u003cIterator\u003e` or `tiny::optional\u003cIterator\u003e` and guarantee that the iterator is always valid if the optional is not empty. But this will waste some space.\nIn principle, you would like to write `tiny::optional\u003cIterator, \u0026Iterator::mIsValid, false\u003e` (which would cause the library to store the emptiness by means of a `mIsValid=false` value).\nBut the members are private, so this does not work.\nInstead you can use `tiny::optional_inplace\u003cIterator, FlagManipulator\u003e` with a custom manipulator definition:\n```C++\nstruct FlagManipulator\n{\n    static bool is_empty(Iterator const \u0026 iter) noexcept\n    {\n        return !iter.IsValid();\n    }\n\n    static void init_empty_flag(Iterator \u0026 uninitializedIteratorMemory) noexcept\n    {\n        // Placement new because memory is already allocated.\n        // Default constructor of Iterator will set mIsValid=false.\n        ::new (\u0026uninitializedIteratorMemory) Iterator();\n    }\n\n    static void invalidate_empty_flag(Iterator \u0026 emptyIterator) noexcept\n    {\n        // Deconstruct the iterator constructed in init_empty_flag().\n        // The memory itself will be handled by the library.\n        // After this function returns, the library will construct the new valid iterator.\n        // That the new iterator will always have IsValid()==true was one of the basic\n        // assumptions in this example.\n        emptyIterator.~Iterator();\n    }\n};\n```\nUsage example:\n```C++\nint main()\n{\n    tiny::optional_inplace\u003cIterator, FlagManipulator\u003e o;\n    static_assert(sizeof(o) == sizeof(Iterator));\n    assert(o.empty());\n    \n    // Construct some valid iterator and store it.\n    Iterator iter{5};\n    o = iter;\n\n    // Note: Attempting to store an invalid iterator is illegal and causes a debug assert:\n    //o = Iterator{};\n}\n```\n\n#### Alternative for `static constexpr`\nA simpler alternative is possible via `tiny::optional_sentinel_via_type` if the sentinel can be stored in a `static constexpr` variable of a class, the sentinel value can be compared with `operator==` and it can be assigned with `operator=`.\nFor example:\n```C++\nstruct MicroSecondsSentinel\n{\n  // The sentinel value.\n  static constexpr auto value = std::chrono::microseconds::min();\n};\n\ntiny::optional_sentinel_via_type\u003cstd::chrono::microseconds, MicroSecondsSentinel\u003e o;\nstatic_assert(sizeof(o) == sizeof(std::chrono::microseconds));\n```\nIn this example the value `std::chrono::microseconds::min()` is used to indicate an empty optional.\nHence, it is no longer a valid value to store in the optional.\n\nNote: E.g. `tiny::optional\u003cstd::chrono::microseconds, std::chrono::microseconds::min()\u003e` would be invalid C++20 because `std::chrono::duration` (and thus `std::chrono::microseconds::min()`) is not a literal class type.\n\n\n## Disabling platform specific tricks (`TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`)\nThe built-in exploitation of unused bit patterns and the built-in use of member variables as location for the empty state is only available on x86/x64.\nThe reason is that I have not enough experience for e.g. ARM to be confident that similar exploitations of undefined behavior works reliably there.\nIf you try to compile the library on any other platform you get a compiler error.\n\nHowever, because the custom sentinel functionality and custom specializations of `tiny::optional_flag_manipulator` do not rely on undefined behavior, you can disable any tricks that rely on undefined behavior by compiling your program with `TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`.\nThe code is then completely C++ standard compliant and works on any platform.\n\nDefining `TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS` will have the following effects:\n* Unused bit patterns are no longer exploited. This means that `tiny::optional\u003cbool\u003e`, `tiny::optional\u003cdouble\u003e`, `tiny::optional\u003cfloat\u003e` and `tiny::optional\u003cT*\u003e` will use a separate `bool` internally to store the empty state. Consequently, their sizes will be the same as their `std::optional` counterpart.\n* The member pointer template parameter of `tiny::optional` will be ignored. In most cases this will mean that a separate `bool` will be used to store the empty state. Thus, the size will be the same as the `std::optional` counterpart.\n\nIf `TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS` is defined, you can still specify custom sentinels via the template parameter of `tiny::optional`.\nMoreover, specializations of `tiny::optional_flag_manipulator` still work. (Any undefined behavior you might exploit in these specializations, such as storing the empty state in a member, is your responsibility!)\n\nMixing code that was compiled with and without `TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS` would lead to incorrect behavior because the involved types have different sizes.\nTo prevent this, the types defined by the library are implemented in an [inline namespace](https://en.cppreference.com/w/cpp/language/namespace#Inline_namespaces) that encodes the configuration. If you try to mix different configurations, you will get errors related to missing or mismatching symbols.\n\n\n## Natvis\nThe `include` directory contains a Natvis file which improves the display of the optionals in the Visual Studio debugger considerably.\nCopy and add the Natvis file to your project, or append its content to your existing Natvis file.\nSee the [official Microsoft documentation](https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects) for more information on Natvis.\n\nThe Natvis visualizers show whether the optional contains a value or not, and if it does, the contained value.\nUnfortunately, when you specialize `tiny::optional_flag_manipulator` (see above), it is not reasonably possible to write a generic Natvis visualizer because Natvis does not allow to call any functions (even if they are `constexpr`).\nSo you need to add additional visualizers for each custom specialization yourself.\nThe same holds true for `tiny::optional_inplace`.\n\nNotes:\n* If you update to a more recent version of `tiny::optional`, you also need to update your copy of the Natvis file. Reason: It contains the name of the inline namespace in which all types are defined, and the name includes the version number.\n* If you compile with `TINY_OPTIONAL_USE_SEPARATE_BOOL_INSTEAD_OF_UB_TRICKS`, the types are defined in an inline namespace with a different name than the one expected by default by Natvis. So you need to replace all occurrences of the inline namespace name with the new one. See the top of the Natvis file for more information.\n\n\n# Performance results\n\n## Runtime\nFirst note that one goal of the library has no runtime performance impact: Namely it provides a more semantically expressive way to have variables where a special value indicates the empty state, i.e. instead of\n```C++\nint oneBasedIndex; // 0 means \"does not reference anything\"\n```\none can write \n```C++\ntiny::optional\u003cint, 0\u003e oneBasedIndex;\n```\n\nThe second goal of the library is get rid of the additional `bool` required to store the empty state where possible. This reduces the size and thus should improve runtime performance in applications where memory bandwidth is the limiting factor.\nBut as always, you have to profile it in your specific application if `tiny::optional` results in a performance improvement.\nAs a somewhat contrived example that highlights a rather extreme case, consider the type\n```C++\nstruct WeirdVector\n{\n    tiny::optional\u003cdouble\u003e x, y, z;\n    tiny::optional\u003cdouble\u003e length2;\n};\n```\nwhich has a size of 32 bytes. Using `std::optional\u003cdouble\u003e` instead, it is twice as large, i.e. has a size of 64 bytes.\nThe following piece of test code is rather memory bound:\n```C++\ntemplate \u003cclass T\u003e\nconstexpr T Sqr(T v) { return v*v; }\n\ndouble PerformTest(std::vector\u003cWeirdVector\u003e \u0026 values) \n{\n    for (WeirdVector \u0026 vec : values) {\n        if (vec.x || vec.y || vec.z) {\n            vec.length2.emplace(Sqr(vec.x.value_or(0)) + Sqr(vec.y.value_or(0)) + Sqr(vec.z.value_or(0)));\n        }\n    }\n\n    double totalLength = 0;\n    for (WeirdVector \u0026 vec : values) {\n        totalLength += vec.length2.value_or(0);\n    }\n    return totalLength;\n}\n```\nRunning this code with `tiny::optional` and with `std::optional` on an Intel Core i7-4770K results in the following:\n![Performance results](performance/results_relative.png)\nclang 13 and gcc 11 were compiled with `-O3 -DNDEBUG -mavx` on WSL, MSVC 19.29 used `/O2 /arch:AVX /GS- /sdl-`.\nThe vertical axis shows the ratio of the execution time for `std::optional` divided by the execution time of the version with `tiny::optional`. \nA value of 1 thus means that they are equally fast, a value \u003e1 means that `tiny::optional` is faster. The horizontal axis depicts the number of values in the input container `values`.\nThe three vertical lines show when the values using `tiny::optional` completely fill the L1 (64kiB per core), L2 (256kiB per core) and L3 cache (8.0MiB) of the Intel i7-4770K.\n\nIf the data of both the std and the tiny version fit completely into the L1 cache, there is not much performance difference. That `tiny::optional` is slightly faster for MSVC is apparently because MSVC is [better at optimizing `tiny::optional` than `std::optional`](https://stackoverflow.com/q/72755405/3740047).\nOnce the the std data no longer fits in the L1 cache, the tiny case is always faster compared to the std case by roughly a factor of 1.5x. The std case needs to load more data from the slower caches.\nThe peak improvement is reached once most of the tiny data still fits into the L3 cache but the std data does not (meaning that a significant part of the std data needs to be retrieved from RAM), in which case the tiny version is up to roughly 3x faster.\nOnce most of the data in both cases no longer fit, the improvement converges to a factor of roughly 2x.\nThe reason is that most data needs to be streamed from the RAM and the amount of data in the tiny case is half of that of the std case.\n\n\n## Build time\nTo benchmark the time it takes to compile code using `tiny::optional` rather than `std::optional`, the following bit of generated C++ code is used:\n```C++\nstruct S0\n{\n    struct impl{};\n\n    void test() {\n        o = std::nullopt;\n        [[maybe_unused]] bool v1 = o.has_value();\n        o = impl{};\n        [[maybe_unused]] auto v2 = o.value();\n        v2 = *o;\n        o.reset();\n        o.emplace(v2);\n        [[maybe_unused]] bool v3 = static_cast\u003cbool\u003e(o);\n    }\n    \n    tiny::optional\u003cimpl\u003e o; // or std::optional\n};\n\nint main()\n{\n    S0 s0;\n    s0.test();\n}\n```\nHowever, not only a single class `S0` but additional ones `S1`, `S2`, etc. get generated and used to achieve meaningful build times.\nThe same code but with `tiny::optional` replaced with `std::optional` is also measured.\nThe ratio of the times (build time of `tiny::optional` divided by the build time of `std::optional`) is shown in the following figure:\n![Build times](buildtimes//unique_types/result.png)\nclang 13 and gcc 11 are used on WSL, and `cl.exe` means MSVC 19.29.\nclang without the `-stdlib=libc++` flag uses gcc's stdlibc++.\n\nObviously, `tiny::optional` takes roughly ~1.5x-2x longer to compile than `std::optional`.\nThe more generic interface of `tiny::optional` requires additional template meta-programming, which apparently takes its toll.\nBut note that this is a rather extreme example since here the optional dominates the build time completely.\nIn larger real world projects (where build times actually matter) one would expect that the overwhelming majority of all variables are not optionals, meaning that the usage of `tiny::optional` should not have a noticeable impact.\nIndeed, replacing all occurrences of `std::optional` in a commercial application with several million lines of code did not result in a measurable slowdown of the build.\n\n\n# How the library exploits platform specific behavior\n\nThe library exploits **platform specific behavior** (that is not guaranteed by the C++ standard) to construct optionals that have the same size as the payload. Specifically:\n\n* Booleans: A `bool` has a size of at least 1 byte (so that addresses to it can be formed). But only 1 bit is necessary to store the information if the value is `true` or `false`. The remaining 7 or more bits are unused. More precisely, the numerical value of `true` is `1` and for `false` it is `0` on the supported platforms. Any other numerical value results in undefined behavior. `tiny::optional\u003cbool\u003e` will store the numerical value `0xfe` in the `bool` to indicate an empty state.\n\n* Floating point types (`float` and `double`): There are two types of \"not a numbers\" (NaNs) defined by the IEEE754 standard: Quiet and signaling NaNs. However, there is a wide range of bit patterns that represent a quite or a signaling NaN. For example, for `float` **any** bit pattern in `[0x7f800001, 0x7fbfffff]` and `[0xff800001, 0xffbfffff]` represents a signaling NaN, and **any** bit pattern in `[0x7fc00000, 0x7fffffff]` and `[0xffc00000, 0xffffffff]` represents a quiet NaN. However, on the supported platforms only a **single** specific quiet NaN and a **single** specific signaling NaN bit pattern is used by the supported compilers and standard libraries (e.g. for linux clang x64 `float`: `0x7fc00000` for quiet and `0x7fa00000` for signaling NaNs). \nAlso see e.g. the paper [\"Floating point exception tracking and NAN propagation\" by Agner Fog](https://www.agner.org/optimize/nan_propagation.pdf).\nThis holds of course only as long as a program does not do any tricks by itself. This library exploits this assumption and uses the quiet NaN `0x7fedcba9` as sentinel value for `float` and `0x7ff8fedcba987654` for `double`.  \n**Note 1:** To emphasize with an example, `tiny::optional\u003cdouble\u003e{std::numeric_limits\u003cdouble\u003e::quiet_NaN()}` and `tiny::optional\u003cdouble\u003e{std::numeric_limits\u003cdouble\u003e::signaling_NaN()}` are **not** empty optionals!  \n**Note 2:** `long double` is not (yet) supported and a `tiny::optional\u003clong double\u003e` instead uses a separate `bool`.\n\n* Pointers: For pointers the library uses the sentinel values `0xffff'ffff - 8` (32 bit) and `0xffff'8000'0000'0000 - 1` (64 bit) to indicate an empty state. In short, these values avoid [pseudo-handles on Windows](https://devblogs.microsoft.com/oldnewthing/20210105-00/?p=104667), and for 64 bit lies in the gap of [non-canonical addresses](https://read.seas.harvard.edu/cs161/2018/doc/memory-layout/). See the explanation in the source code at `SentinelForExploitingUnusedBits\u003cT*\u003e` for more details. Thanks to the reddit users \"compiling\" and \"ra-zor\" for [pointing this out](https://www.reddit.com/r/cpp/comments/ybc4lf/comment/itjvkmc/?utm_source=share\u0026utm_medium=web2x\u0026context=3).  \n**Note 1:** Only pointers in the sense of `std::is_pointer` (i.e. ordinary pointers and function pointers) are supported that way; member pointers and member function pointers require an additional `bool` since they are not \"ordinary\" pointers).  \n**Note 2:** Having a `tiny::optional\u003cT*\u003e` is probably not that often useful. But if you have a POD like type with a pointer in it as member, you can instruct `tiny::optional` to use that member as storage for the sentinel value (see above) and save the memory of the additional `bool`. To this end, the library implements the trick for pointers.  \n**Note 3:** The `nullptr` is not used as sentinel, and thus remains a valid value. So assigning `nullptr` to a `tiny::optional` results in a non-empty optional!\n\n* Members: Storing the empty state in a member variable is also exploiting undefined behavior because the code writes and reads from memory locations where no \"proper\" C++ object has been constructed yet (only the raw memory has been allocated).\n\n\nAdditional ideas (not yet implemented!):\n\n* Polymorphic types (`std::is_polymorphic`) have a vtable pointer at the beginning. As for ordinary pointers, it could be set to `0xffffffffffffffff`. This would allow to store **any** polymorphic type within the optional without requiring additional space. But having optionals of polymorphic types is probably rare. Also need to research how the layout is in case of multiple inheritance.\n* Padding bytes in types could be exploited to store the emptiness. The closest in the standard we have is probably `std::has_unique_object_representations`. If this is `true`, there are either floating point types or padding bytes involved. But this type trait only works with trivially copyable types. But maybe one could use `boost::pfr` to get a `std::tuple` of the members, and by subtracting the addresses and comparing this difference with the actual size of the types one could identify the padding.\n* For POD-like types, `boost::pfr` could be used to get a `std::tuple` to the members. Then, at compile time, we could iterate over the members and check for any type with unused bit patterns. This would make the explicit specification of a member pointer by the user unnecessary. However, it would introduce a dependency on `boost`.\n* References: Similar to pointers. But references in optionals are currently forbidden by the C++ standard.\n* Enums: If there were a way to automatically get the min. or max. value in an enumeration, we could find an unused value as sentinel automatically.\n* Nested `tiny::optional\u003ctiny::optional\u003cT\u003e\u003e` could be optimized. But something like this is probably rare?\n* Strings: Idea from [reddit](https://www.reddit.com/r/cpp/comments/ybc4lf/comment/itftn0y/?utm_source=share\u0026utm_medium=web2x\u0026context=3): Use `\"\\0\\0\"` for std strings, or maybe better `\"\\0\\r\\a...\"`. The size of the sentinel string should not be larger than the internal storage used for the short string optimization (SSO).\n\n\n# Related work\nThe [discussion on reddit](https://www.reddit.com/r/cpp/comments/ybc4lf/tinyoptional_a_c_optional_that_does_not_waste/?utm_source=share\u0026utm_medium=web2x\u0026context=3) has shown that some other libraries with similar intent exist:\n* [`compact_optional`](https://github.com/akrzemi1/compact_optional) and his successors [`markable`](https://github.com/akrzemi1/markable). A major difference to `tiny::optional` is that `tiny::optional` attempts to be a drop-in replacement for `std::optional` while providing automatic optimization for floats etc. The sentinel functionality is opt-in (by specifying a second template argument). On the other hand, `markable` is not designed to be a direct replacement of `std::optional`. To get an optional of some generic type (where an additional internal boolean must be used to represent the empty state), `markable` needs to be told about this (so in a sense, it is opt-out): `markable\u003cmark_optional\u003cboost::optional\u003cint\u003e\u003e\u003e`. On the other hand, `tiny::optional\u003cint\u003e` does this automatically.\n* The [talk by Arthur O'Dwyer “The Best Type Traits that C++ Doesn't Have”](https://youtu.be/MWBfmmg8-Yo) from C++Now 2018 ([github repository](https://github.com/Quuxplusone/from-scratch)) introduces `tombstone_traits`, which exposes unused bit patterns. It is exploited by his [own implementation of optional](https://github.com/Quuxplusone/from-scratch/tree/master/include/scratch/bits/optional).\n* [LibCat](https://github.com/Cons-Cat/libCat/blob/main/src/libraries/optional/cat/optional): A C++20 library that includes a similar optional where you can specify a lambda as non-type template parameter that handles the sentinel. It seems to be conceptionally similar to `tiny::optional_inplace` described above.\n* [foonathan/tiny](https://github.com/foonathan/tiny): Seems to be abandoned and to not implement a fully fledged `std::optional` replacement.\n\n\nAlso, Rust's `Option` implements some magic for [references](https://stackoverflow.com/q/16504643/3740047) and [bools](https://stackoverflow.com/q/73180983/3740047).\n","funding_links":[],"categories":["Containers and Algorithms"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSedeniono%2Ftiny-optional","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSedeniono%2Ftiny-optional","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSedeniono%2Ftiny-optional/lists"}