{"id":47892210,"url":"https://github.com/kevinchannon/wite","last_synced_at":"2026-04-04T03:09:28.660Z","repository":{"id":59958149,"uuid":"526092644","full_name":"kevinchannon/wite","owner":"kevinchannon","description":"Wite is a small collection of routines and classes that aims to make easy things that should be easy in C++ but are not, for one reason or another.","archived":false,"fork":false,"pushed_at":"2023-01-17T19:35:44.000Z","size":919,"stargazers_count":19,"open_issues_count":9,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-11-27T13:18:14.736Z","etag":null,"topics":["buffer","collections","geometry","io","strings","types","uuid","uuid-generator","uuid-v4","uuid4","uuidv4"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kevinchannon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null}},"created_at":"2022-08-18T06:53:53.000Z","updated_at":"2025-04-11T07:08:02.000Z","dependencies_parsed_at":"2023-02-10T11:31:56.990Z","dependency_job_id":null,"html_url":"https://github.com/kevinchannon/wite","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/kevinchannon/wite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinchannon%2Fwite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinchannon%2Fwite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinchannon%2Fwite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinchannon%2Fwite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kevinchannon","download_url":"https://codeload.github.com/kevinchannon/wite/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinchannon%2Fwite/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31385988,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T01:22:39.193Z","status":"online","status_checked_at":"2026-04-04T02:00:07.569Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["buffer","collections","geometry","io","strings","types","uuid","uuid-generator","uuid-v4","uuid4","uuidv4"],"created_at":"2026-04-04T03:09:28.029Z","updated_at":"2026-04-04T03:09:28.651Z","avatar_url":"https://github.com/kevinchannon.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿![Ubuntu | GCC12 | x64 | Debug](https://github.com/kevinchannon/wite/actions/workflows/build_and_test_ubuntu_gcc12_x64_debug.yml/badge.svg)\u003cbr\u003e\n![Ubuntu | GCC12 | x64 | Release](https://github.com/kevinchannon/wite/actions/workflows/build_and_test_ubuntu_gcc12_x64_release.yml/badge.svg)\u003cbr\u003e\n![Windows | MSVC++ | x64 | Debug](https://github.com/kevinchannon/wite/actions/workflows/build_and_test_windows_msvc_x64_debug.yml/badge.svg)\u003cbr\u003e\n![Windows | MSVC++ | x64 | Release](https://github.com/kevinchannon/wite/actions/workflows/build_and_test_windows_msvc_x64_release.yml/badge.svg)\n \n### Table of Contents\n\n* [Wite](https://github.com/kevinchannon/wite#Wite)\n  * [Prerequisites](https://github.com/kevinchannon/wite#Prerequisites)\n  * [Getting Wite](https://github.com/kevinchannon/wite#getting-wite)\n  * [Building](https://github.com/kevinchannon/wite#Building)\n  * [Get Started](https://github.com/kevinchannon/wite#Get-Started)\n* [Core](https://github.com/kevinchannon/wite#Core)\n* [Collections](https://github.com/kevinchannon/wite#Collections)\n* [IO](https://github.com/kevinchannon/wite#IO)\n* [Binascii](https://github.com/kevinchannon/wite#Binascii)\n* [String](https://github.com/kevinchannon/wite#String)\n* [Fragment String](https://github.com/kevinchannon/wite#Fragment-String)\n* [Geometry](https://github.com/kevinchannon/wite#Geometry)\n* [Maths](https://github.com/kevinchannon/wite#Maths)\n    * [Numeric Helpers](https://github.com/kevinchannon/wite#Numeric-Helpers)\n    * [Value Range](https://github.com/kevinchannon/wite#value_range)\n    * [Bounded Value](https://github.com/kevinchannon/wite#bounded_value)\n* [Compiler Macros](https://github.com/kevinchannon/wite#Compiler-Macros)\n\n\u003e **Note**\n\u003e\n\u003e Version 1.0.0 is now ready! If you try it and find any problems, please do make create and issue so that we can fix it for the next version and make Wite more useful on more platforms and environments :)\n\n# Wite\nWite stands for \"**W**hy **i**sn't **t**his **e**asy!?\". It's a collection of routines and classes that aims to make easy things that should be easy in C++ but are not, for one reason or another. The aim is to make a small library of things that can just be dropped into a project to make things a little easier. So, if you don't want the weight of introducing a dependency on Boost, or something, then maybe there's something here to help you. At the moment, Wite is header only, so good times! Just plop the files into your source tree and rock on.\n\nThe aim here is not to produce necessarily the most complete set of features, nor the most generic, nor the most performant, nor the most memory-efficient, nor, even, the safest implementations. If we were to make all those considerations, then we'd miss out on 99% of the usefulness, for the sake of 1% of the considerations. This is not to say that those considerations are not made, just that they're not the focus.\n\nThe focus is to provide things that are *simple at the point of use*, with implementations that are *intuitive* and can almost be guessed and just *do the right thing* in \u003e~95% cases. That is **Wite should be**:\n1. Easy to obtain and include in a project\n2. Easy to call in the project's code\n\n**If you're a C++ beginner**, then you should be able to acquire and use Wite in your project without too much of a headache. That's the aim.  If this is not the case, then let me know, and we'll see what we can do to make it even easier :)\n \n`string::split` is a good example. There isn't (currently) a standard library function for this and every codebase rolls its own, essentially. Sure, I could get it from [Boost](https://www.boost.org/doc/libs/1_80_0/doc/html/string_algo/usage.html#id-1.3.3.5.9), but then I've pulled in quite a heavyweight dependency (or, I've had to exercise a relatively large amount of understanding in order *not* to pull in a large dependency). Wite's `string::split` takes one thing (a string-like thing) and returns a `std::vector\u003cstd::string\u003e` with the result in it. Is this the most efficient thing in all cases? No. Is it *good enough* in 99% of cases? Probably.\n\nIf you're looking at some implementation inside Wite and thinking \"this is definitely NOT the best way to do this!\" and have an idea how to make it better, safer or more generic (without harming the most common case usability, of course) then go for it and raise an issue, or send a pull request, or something :)\n\nRight, on with instructions on how to get going...\n\n## Prerequisites\nWite doesn't have any dependencies, so you don't have to worry about that sort of thing. If you're developing new features for  Wite, then the tests have a dependency on [Catch2](https://github.com/catchorg/Catch2), but that is handled by CMake [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html), so it should take care of itself (as long as you're connected to the internet). Wite pretty much requires that you're using **C++20**, so I guess that's a prerequisite of sorts.\n\n## Getting Wite\nThere are a bunch of ways you could choose to consume Wite, here are some possibilities\n\n### CMake FetchContent\nIf your project is CMake-based, then the preferred way to get going is by using **CMake FetchContent**.  You can find an example project that does this in the [wite-cmake-example](https://github.com/kevinchannon/wite-cmake-example) repository.\n\n### NuGet\nIf you use NuGet, then see [NuGet.org](https://www.nuget.org/packages/wite) for more information on acquiring Wite via NuGet.  If you're using Visual Studio for your project, then you can consume Wite via the NuGet integration it has. So, you right-click on your project, or solution, and then choose \"Manage NuGet packages...\" (or something like that) and then you should be able to search for Wite and include it in your project.  For an example project that has Wite included this way, see the [wite-vs-example](https://github.com/kevinchannon/wite-vs-example) repo.\n\n### Ain't nobody got time for package management!! Just tell me where to download it!\nOne of the above options should really be your first call, but if you're in a hurry and don't care about controlling the version, or getting updates and stuff like that, then just straight up downloading it and putting it in your source tree will also do the business. You can get it from here: [Releases](https://github.com/kevinchannon/wite/releases).\n\nYou'll want the \"wite-src.zip\" file for the version of your choice.\n\n## Building\nWite is header only, so there's no \"building\" of Wite by itself. The relevant bits will get built when you `#include` them in your files and then build your own project.\n\nSome features of Wite are tunable by the user at compilation time. For a list of the macros that you can define as command-line parameters to the compiler, see the [Compiler Macros](https://github.com/kevinchannon/wite#Compiler-Macros) section at the end of this readme.\n\n## Get Started\nOnce downloaded, or however you installed things, then you should be able to just `#include` the bit you want to use and get going. If you don't know which bit you want, then just `#include \u003cwite/wite.hpp\u003e` and you'll get everything.\n\nEverything in Wite is in the `wite` namespace, and the various sub-parts are in their own sub-namespaces within this. So, the string stuff is all in `wite::string` and the maths stuff is all in `wite::maths`, and so on.\n\n# Core\n```c++\n#include \u003cwite/core.hpp\u003e\n```\n\nThese are some small and very basic classes and functions that can be used in a wide variety of situations.\n\n## `result`\n```c++\n#include \u003cwite/core/result.hpp\u003e\n```\nIn a world where exceptions are not the mechanism for handling errors, then \"result codes\" are basically the go-to alternative. These often take the form of an `int` value that's returned by a function that indicates the error state. Then you define a special value of that code to mean OK and you have a lot of code that looks like this:\n```c++\n// Result-code-returning function; takes a value to populate\nint string_populator(std::string\u0026);\n\n...\n\nauto my_string = std::string{};\nauto rc = string_populator(my_string);\nif (RC_OK != rc) {\n    // Handle the error gracefully\n}\n\n// Use `my_string` for something\n```\n\nThis code is a little annoying because:\n1. `my_string` is first defined and default constructed and then passed the function to be populated via an ugly \"output\" argument.\n2. If `string_populator` returns an error, there has to be some statement somewhere about the state of `my_string` in this case. Is it left as it was when you passed it in? Is its state undefined? does it get cleared?\n3. There is nothing to prevent you accidentally using `my_string` if you ignore the result code (purposely, or not)\n\n`result` is intended to be an alternative to this pattern that does not allow you to accidentally ignore the error state. So, the example above would look like this:\n```c++\nconst auto string_result = string_maker();\nif (string_result.is_error()) { // Alternatively call do `not string_result.ok()`\n    // Handle the error by calling string_result.error() to get at the error code\n}\n\n// Use the result by calling string_result.value() to get the string.\n```\n\n### Defining a result type\n`result` is a class template that looks like this:\n```c++\ntemplate \u003ctypename Value_T, typename Error_T\u003e class result;\n```\nSo, you need to tell it what the good result looks like (i.e. a `std::string` in the example above) and what the bad result looks like, which could just be an `int` , or you could define some `error_code` enum, or even some specific class for the errors. Say we had some `enum` for the errors like:\n```c++\nenum class string_maker_error {\n    error_1,\n    error_2,\n    error_3,\n    ...\n    error_N\n};\n```\nThen we would define an alias for the result in this situation:\n```c++\nusing string_maker_result = wite::result\u003cstd::string, string_maker_error\u003e;\n```\nand then the prototype for `string_maker` just looks like:\n```c++\nstring_maker_result string_maker();\n```\nAnd now we have a strongly-typed result type that won't allow us to ignore errors. If you call `value()` on the result when it's actually an error, then it will call `abort` and kill your app... probably.  \n\u003e **Warning**\n\u003e \n\u003e All the methods on `result` are declared `noexcept`, but it's based on `std::variant`, so it will try to throw exceptions. Without the proper things built by the compiler for handling the exceptions, this will probably have some undefined behaviour, but in my experience it just aborts execution of the thing pretty quickly.  What the hell!? I can hear you saying. Well, this is in an error case, where you have ignored the error and tried to use the result anyway, so all bets are off, in my opinion. If you're using the \"traditional\" error code mechanism, then it may let your app stumble on in a pretty undefined way for some time, but it's still fundamentally in an undefined state (since the error should have been handled properly, but someone failed to write the code to do that). To me, all undefined states are equivalent ;)\n\nSo, yeah. Use `result`, make sure you check `ok()`, or `is_error()` on it before you make your next move and then use either `value()` or `error()` to get at the details. That's about it.\n\n\u003e **Note**\n\u003e \n\u003e The interface of `result` includes much of the functionality of [`std::expected`](https://en.cppreference.com/w/cpp/utility/expected). The template function specificity and complexity is probably much lower in `result` than in `std::expected`, but it should do roughly the same thing in most situations. `std::expected` has a lot of additional constructors that `result` doesn't have and there is no analogue to `std::unexpected` in Wite. If `std::expected` wasn't just around the corner, then it would probably be worth doing a better job on the alignment, but life is too short for that :) I would say that, if you have access to `std::expected`, then you should really use that instead. However, the implementation of `result` is provided here for those that don't have `std::expected` (since it's in C++23).\n\n## Scope Exit Runners\n```c++\n#include \u003cwite/core/scope.hpp\u003e\n```\n\n\u003e **Note**:\n\u003e \n\u003eThese are supposed to be in the C++ standard, but they're stuck in the \"experimental\" state and not many compilers seem to implement them. This is probably because it's hard to define and implement them in a way that is guaranteed to be safe in all situations. This is the remit of the standards committee, but it is not our remit here :)  So, Wite has simple scope exit runners, but the usual caveats apply: they might not be the MOST memory and performance efficient implementations possible, and it's possible to get yourself into trouble if you use them wrongly!  So, use with care :)\n\nSo, the use-case is simple, these are for doing clean-up stuff that is guaranteed to execute when the thing goes out of scope:\n```c++\nvoid must_be_run_at_the_end_no_matter_what(){\n    std::cout \u003c\u003c \"Phew! I ran\" \u003c\u003c std::endl;\n}\n\nvoid some_fn(){\n    auto tidyup = wite::scope_exit{must_be_run_at_the_end_no_matter_what};\n\n    // Insert code that needs to be run on exit.\n\n}   // \u003c-- `must_be_run_at_the_end_no_matter_what` is run somewhere around here.\n```\n\nThat's about it. Some cautionary points:\n1. Make sure that all the things that the exit function needs to run are still in scope and valid at the point that it's going to run.\n   1. Things captured by lambdas (by reference).\n   2. Member variables, if you're in some class' member function\n   3. Locks and mutexes\n\nThat's about it.\n\nIn addition to `scope_exit`, you can also use `scope_success` to run a function only in the case that the scope was exited normally and, conversely, `scope_fail` to run a function if the scope is being exited due to an exception being thrown.\n```c++\nvoid some_fn() {\n    try {\n        auto tidyup = wite::scope_exit{[](){\n            std::cout \u003c\u003c \"This is always done!\" \u003c\u003c std::endl; }\n        };\n        auto success_tidyup = wite::scope_success{[](){\n            std::cout \u003c\u003c \"Success things were done!\" \u003c\u003c std::endl; }\n        };\n        auto failure_tidyup = wite::scope_fail{[](){\n            std::cout \u003c\u003c \"Failure things were done!\" \u003c\u003c std::endl; }\n        };\n    } catch (...) {}\n}\n```\nIn the above example, you should see the \"Success things were done!\" message, because no exceptions are thrown in the scope. The failure message should not appear.\n\nNow for the other case:\n```c++\nvoid some_fn_that_goes_wrong_and_throws(){\n    throw std::runtime_error{\"Boom!\"}\n}\n\nvoid some_fn() {\n    try {\n        auto tidyup = wite::scope_exit{[](){\n            std::cout \u003c\u003c \"This is always done!\" \u003c\u003c std::endl; }\n        };\n        auto success_tidyup = wite::scope_success{[](){\n            std::cout \u003c\u003c \"Success things were done!\" \u003c\u003c std::endl; }\n        };\n        auto failure_tidyup = wite::scope_fail{[](){\n            std::cout \u003c\u003c \"Failure things were done!\" \u003c\u003c std::endl; }\n        };\n        \n        some_fn_that_goes_wrong_and_throws();\n    } catch (...) {}\n}\n```\nHere, we will see the failure message, but not the success one. In both cases, you will see the message \"This is always done!\", because that is in a `scope_exit`, which doesn't care about exceptions.\n\n\u003e **Warning**\n\u003e\n\u003e If you're compiling with the **WITE_NO_EXCEPTIONS** macro defined, then `scope_success` and `scope_fail` are not defined, and you'll get a compilation failure if you try to use them.\n\n## `overloaded`\nThis is a thing that allows you to do a kind of type-switching on a parameter pack, which is basically directly lifted from [here](https://en.cppreference.com/w/cpp/utility/variant/visit).  So, if you have a function that looks like this:\n```c++\ntemplate\u003ctypename... Arg_Ts\u003e\nvoid fn(Arg_Ts... args){\n...\n}\n```\n\nAnd you want to handle a bunch of different argument types and combinations, then you can use `overloaded` in a fold-expression to handle any and all of a specific set of types in the pack. This is how the `make_vector` function works, for example.\n```c++\ntemplate \u003ctypename T, typename... Arg_Ts\u003e\nstd::vector\u003cT\u003e make_vector(Arg_Ts... args) {\n  auto out = std::vector\u003cT\u003e{};\n\n  (overloaded{[\u0026out](arg::reserve arg) { out.reserve(arg.value); },\n              [\u0026out](arg::size\u003cT\u003e arg) { out.resize(arg.value, arg.initialise_to); },\n              [](auto arg) { static_assert(always_false_v\u003cdecltype(arg)\u003e, \"Invalid make_vector arg type\"); }}(\n       std::forward\u003cArg_Ts\u003e(args)),\n   ...);\n\n  return out;\n}\n```\nSo, you make lambdas with the types that you want to handle in your pack and then pass them all to `overloaded` as arguments and then use that whole thing in a fold-expression to handle each of the parameters in turn. In this case, it's going to process the params in the reverse order that they appear in the argument list, but you could change that by reversing the fold-expression.\n\nAnyway, that's overload. It's useful in some niche situations.\n\n## UUID\n```c++\n#include \u003cwite/core/uuid.hpp\u003e\n```\nWe all know what UUIDs are, right? On Windows they're generally called GUIDs. But, for some reason, they don't appear in the C++ standard. I think they should just be a type in the standard library, but I don't get to make decisions about this sort of thing :)\n\nAnyway, Wite has a ready-baked UUID type that you can use:\n```c++\nconst auto id = wite::make_uuid();\n```\nThis will get you a UUID that I would describe as \"pretty random\".  It's probably fine for what you want, but I would probably look into something else if you want really strong guarantees about the randomness and such. Actually, if you have a random number generator that you know to be sufficient for your application, then you can make a random UUID using it:\n```c++\nauto rng = MyMagicRandomEngine{};\nconst auto id = wite::uuid{rng};\n```\nThis will generate the values for the bytes with your engine.\n\n### NULL UUIDs\nThere is a `wite::nulluuid` for those times when you want a NULL UUID\n\n### UUIDs from strings\nYou can initialise your UUID from a string, like this:\n```c++\nconst auto id = wite::uuid{\"01234567-89AB-CDEF-0123-456789ABCDEF\"};\n```\nThis does what it says on the tin. It also turns out that there are a bunch of different formats for UUID strings. Wite supports some popular ones that are also supported in .NET:\n\n* D-format: `01234567-89AB-CDEF-0123-456789ABCDEF`\n* N-format: `0123456789ABCDEF0123456789ABCDEF`\n* B-format: `{01234567-89AB-CDEF-0123-456789ABCDEF}`\n* P-format: `(01234567-89AB-CDEF-0123-456789ABCDEF)`\n* X-format: `{0x01234567,0x89AB,0xCDEF,{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}}`\n\nYou can create a UUID from a given format by passing its format specifier to the constructor of `uuid`:\n```c++\nconst auto id = wite::uuid{\"{01234567-89AB-CDEF-0123-456789ABCDEF}\", 'B'};\n```\nIf the format specifier is invalid, then `uuid` will throw `std::invalid_argument`. If you're not using exceptions in your project, then you can use `try_make_uuid` to generate a UUID:\n```c++\nconst auto id_result = wite::try_make_uuid(\"{0x01234567,0x89AB,0xCDEF,{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}}\", 'X');\n```\nIf this fails, then it will return an `enum` indicating the error (usually `make_uuid_error::invalid_uuid_format`).\n\n### Outputting UUIDs\nIf you want to get a UUID as a string, then you can use `wite::to_string` for that:\n```c++\nconst auto id = wite::make_uuid();\n\n// id_str \u003c-- \"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\"\nconst auto id_str = wite::to_string(id);\n```\nAgain, if you want the string in one of the other formats, then pass the format specifier to `to_string`:\n```c++\nwite::to_string(id, 'P');\n```\nBy default, the output is in UPPER-CASE hex. If you want a lower-case version, then use a lower-case version letter for the format specifier:\n```c++\nconst auto id = wite::uuid{\"01234567-89AB-CDEF-0123-456789ABCDEF\"};\n\n// id_str \u003c-- \"0123456789abcdef0123456789abcdef\"\nconst auto id_str = wite::to_string(id, 'n');\n```\n\nFor convenience, `uuid` has a `str()` and `wstr()` member functions that do the same as `to_string()` and `to_wstring()`, respectively:\n```c++\nconst auto id = wite::make_uuid();\n\n// id_str \u003c-- L\"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\nconst auto id_str = id.wstr('N')\n```\nIn this case, the wide-string version of the function is \n\nStream insertion is also overloaded for UUID:\n```c++\nstd::cout \u003c\u003c \"my UUID = \" \u003c\u003c wite::make_uuid() \u003c\u003c std::endl;\n```\n### Macros\nThe default format for UUIDs is 'D'. If you want to change this, then you can compile with `WITE_DEFAULT_UUID_FMT`set to one of the other values.\n\n### `basic_uuid`\nIf you don't want, or can't use, the additional conveniences of `uuid`, for some reason, then you can use `basic_uuid` to just get the bare minimum data struct for holding a UUID:\n```c++\n#include \u003cwite/core/uuid/basic_uuid.hpp\u003e\n```\n`basic_uuid` has a `data` member that holds the UUID data in a 16-byte array and also the comparison operators.\n\n\n## Typed-ID\n```c++\n#include \u003cwite/core/id.hpp\u003e\n```\nWite has a typed ID thing that you can use as an ID value for your classes. This ID is \"typed\", which means that it will produce a compilation error if you try and use it in the wrong context. So, for example, if you have some system of widgets in a UI, or something, then they're often identified by some kind of ID. You might have some things like this:\n```c++\nclass Button { ... };\nusing Buttons = std::vector\u003cButton\u003e;\n\nclass ScrollBar { ... };\nusing ScrollBars = std::vector\u003cScrollBar\u003e;\n\nclass MenuItem { ... };\nusing MenuItems = std::vector\u003cMenuItem\u003e;\n```\nCool. In your code, you probably want to do things with particular items in these collections. So, each will have some kind of ID and you want to find the things by their ID in order to use them. You could replace the vectors above with maps from ID to the item, or something, or you could make some convenience function that gives you the thing, given its ID:\n```c++\ntemplate\u003ctypename Container_T\u003e\nconst typename Container_T::value_type* const find_item(const Container_T\u0026 items, const int id) {\n    const auto item = std::find_if(items.begin(), items.end(), [id](auto\u0026\u0026 item){\n        return id == item.id();\n    });\n\n    return items.end() != item ? \u0026(*item) : nullptr; \n}\n```\nGreat! Now we can find the things we need using their ID :) However, in a larger codebase, it's not too difficult to make this kind of mistake:\n```c++\nconst auto id = button1.id();\n\n/*\n... Insert lots of code here, so that you don't easily see the definition of `id`.\n*/\n\nauto menu_item = find_item(menu_items, id);  // D'oh! This is not the menu item you a looking for.\n```\nThis is a bit of a noddy example, but these kinds of error can be annoying and difficult to diagnose if you have lots of collections of lots of types. Typed IDs are a way to prevent this kind of error at compile-time.\n### Defining a typed ID\nThe `id` class template definition looks like this:\n```c++\ntemplate \u003ctypename Obj_T, typename Id_T\u003e\nstruct id;\n```\nThe first template parameter is the type of object that is identified by this kind of ID, so `Button`, `ScrollBar` or `MenuItem` from the example above. The second template parameter is the type of the ID value itself. In the example above, that would be an `int`, but it could be a `uuid`, a `std::string`, or some other type. It doesn't really matter too much.\n\nTo make use of the template for our items, we can just alias a specific set of types into the template for our class. So, we might define the previous UI classes to have an ID aliased in them like this:\n```c++\nclass Button {\n    public:\n    \n    using id_type = wite::id\u003cButton, int\u003e;\n    \n    ...\n};\n\nclass ScrollBar {\n    public:\n    \n    using id_type = wite::id\u003cScrollBar, int\u003e;\n};\n\nclass MenuItem {\n    public:\n    \n    using id_type = wite::id\u003cMenuItem, int\u003e;\n};\n```\nComparison functions (==, !=, \u003c, etc.) for `wite::id` are only defined for types that have equal `Obj_T` template parameters. This means that the compiler doesn't know how to compare a `Button` ID to a `MenuItem` ID, for example. And, if you try and write this comparison in your code, then it will complain at you loudly:\n```c++\nconst auto id = button1.id();\n\n/*\n... Insert lots of code here, so that you don't easily see the definition of `id`.\n*/\n\n// This is now a compilation error and not a bug that your users will complain about!\nauto menu_item = find_item(menu_items, id);\n```\n\nBecause of the type-safety thing, to find a particular item, you need to specify the exact type of ID that you're trying to compare to. So, the `find_item` function might be edited to look like:\n```c++\ntemplate\u003ctypename Container_T\u003e\nconst typename Container_T::value_type* const find_item(\n        const Container_T\u0026 items,\n        const wite::id\u003cstd::decay_t\u003cdecltype(*items.begin())\u003e, int\u003e id) {\n    const auto item = std::find_if(items.begin(), items.end(), [id](auto\u0026\u0026 item){\n        return id == item.id();\n    });\n\n    return items.end() != item ? \u0026(*item) : nullptr; \n}\n```\nLooks a bit of a mouthful, but it's better than confusing the types and generating a runtime error!\n\nThat's about all there is to it. In addition, ID values don't have integer-like semantics, so you can't do something like `id++`, or `id_1 + id_2`, but then that's seldom something that makes sense for an ID.\n\nIf you want the raw value out of the ID in its original type, then you can use `value()`, or `operator*` on the ID:\n```c++\nconst auto id = wite::id\u003cMyType, int\u003e{100};\n\n// x \u003c-- int(100)\nconst auto x = id.value();\n\n// y \u003c-- int(100)\nconst auto y = *id;\n```\n\n### Default ID type\nIf you don't care what the actual ID type is, then you can just not specify it. In this case, the ID will have a value type of whatever the default ID type is. By default, the default ID type is UUID, so in the preceding examples, we might have simply done:\n```c++\nusing id_type = wite::id\u003cButton\u003e;\n```\nand our `Button` type would be using `wite::uuid` for its IDs.  If you don't like the idea of using UUIDs as your default ID type, for some reason, then it is possible to set it the default type at compilation time with the `WITE_DEFAULT_ID_TYPE` compilation flag.\n\nThat's about it for typed-IDs.\n\n## Typed-Index\n```c++\n#include \u003cwite/core/index.hpp\u003e\n```\nImagine all the same stuff that I just said about IDs, but for indices into some collection. That's what `wite::index` is for. It's basically the same thing, but it's got index-like semantics for incrementing and decrementing it.\n\n\n# Collections\n\n## `make_vector`\n```c++\n#include \u003cwite/collections/make_vector.hpp\u003e\n```\nHow much do you hate the aesthetic of having to declare a vector with its default constructor and then immediately on the next line call `reserve` on it, before passing it to some algorithm, or something (which will typically be using `push_back`, or something, on the vector)?  I know I do:\n```c++\nauto v = std::vector\u003cint\u003e{};\nv.reserve(1000);   // What an annoying extra line!\n```\nAnyway, `make_vector` solves this issue:\n```c++\nusing namespace wite;\n\nauto v = make_vector\u003cint\u003e(arg::reseve{1000});\n```\nNow, `v` is reserved with space for 1000 elements on one line. Nice.\n\nYou can also specify a size for the vector too, if you like:\n```c++\n// v \u003c- [ 1, 1, ... , 1]\nauto v = make_vector\u003cint\u003e(arg::size{1000, 1});\n```\nIf you want to specify a size and also reserve space for more things, then you can:\n```c++\nauto v = make_vector\u003cfloat\u003e(arg::reserve{1000}, arg::size{10, 3.14f});\n```\nThis gives a `v` that has 10 values initialised to 3.14, with space reserved for 1000 `float` elements in total. The order of the `arg` parameters is not important; you could have `size` first in the list if you like.  I don't know what the name for this \"argument adapter object\" pattern, but I quite like it.\n\n## `static_vector`\n```c++\n#include \u003cwite/collections/static_vector.hpp\u003e\n```\n\nThis is a vector with a compile-time capacity, but a run-time size. It stores its data on the stack, so don't put too many huge things in it.  Other than not being able to call `reserve` on it, the interface is pretty much like that of `std::vector`. For example, you can do something like:\n\n```c++\nauto v = wite::collections::static_vector\u003cint, 20\u003e{};\n\nv.push_back(1);\n\n```\n\n## `static_lookup`\n```c++\n#include \u003cwite/collections/static_lookup.hpp\u003e\n```\nThis is a simple lookup table. It works a bit like a `std::map`; you fill it with key-value pairs, and you can look up the values based on the keys (in addition, you can also look up the keys from the values too, which you can't do with a `std::map`). The primary usage for this thing is to provide a low-overhead way to convert an `enum` to a string without writing a switch-case statement.\n\nSo, say you had some `enum` that you wanted to serialise, or print out in text, or something:\n```c++\nenum class fruit { apple, orange, mango, banana, papaya };\n```\nYou can make a lookup table that converts these values to strings (assuming `namespace wc = wite::collections`):\n\n```c++\n// This string literal helps readability, I think.\nusing namespace std::string_view_literals;\n\nconstexpr auto fruit_text_lookup = wc::static_lookup{\n    std::pair{fruit::apple,  \"apple\"sv},\n    std::pair{fruit::orange, \"orange\"sv},\n    std::pair{fruit::mango,  \"mango\"sv},\n    std::pair{fruit::banana, \"banana\"sv},\n    std::pair{fruit::papaya, \"papaya\"sv}\n};\n```\nSo now we have the lookup table, we can use it to, you know, look stuff up:\n```c++\nconstexpr auto f = fruit::mango;\n\nstd::cout \u003c\u003c \"Your fruit is a \" \u003c\u003c fruit_text_lookup.at(f) \u003c\u003c std::endl;\n```\nOr, in reverse:\n```c++\n// b \u003c-- fruit::banana\nconst auto b = fruit_text_lookup.with(\"banana\");\n```\nOne of the cool things about `static_lookup` is that lots of the parts of it are `constexpr`. So, for example, the definition of `fruit_text_lookup` above is actually all evaluated at compilation time. If you've declared it in global scope, then there's no run-time overhead to it being defined at all. Even the lookups themselves are `constexpr`, so they will also be evaluated by the compiler, if possible. Note: the items in the lookup need to be `constexpr` constructable in order to have the whole thing be `constexpr`. So, if you have `std::string`s in it, then it won't be `constexpr`, for example.\n\nThese examples are aimed at this use case, but you should be able to put many other types of things in it.\n\n\u003e **Warning**\n\u003e \n\u003e `static_lookup` doesn't work well with `const char*` things at the moment (hence the usage of `std::string_view` above :) ).  I'll work on fixing that, at some point. If you're reading this and feel enraged by my sloth, please feel free to fix and submit a PR, or something :)\n\n## Identifiable Item Collection\n```c++\n#include \u003cwite/collections/identifiable_item_collection.hpp\u003e\n```\nIf you're dealing with things that have IDs, then it's common to have a bunch of collections of those things floating about. If you just chuck them all in a `std::vector`, then you have to also have a load of functions like the `find_item` function shown in the typed-ID section, above. Anyway, Wite provides `wite::identifiable_item_collection\u003c\u003e` to store things that have IDs in:\n```c++\n/// UI button element from above; models the `identifiable` concept.\nclass Button {\n    public:\n    \n    using id_type = wite::id\u003cButton, int\u003e;\n    \n    const id_type\u0026 id() const { return _id; }\n    \n    ...\n};\n\nusing Buttons = wite::identifiable_item_collection\u003cButton\u003e;\n```\nSo now `Buttons` is an Identifiable Item Collection of `Button` objects, and we can insert and retrieve items using their ID:\n```c++\nauto buttons = Buttons{};\n\n// Add some buttons\nauto button_1 = Button{1};\nbuttons.insert(std::move(button_1));\n\nbuttons.insert(Button{2});\n\n// Find a button.\nconst auto b = buttons.find(Button::id_type{1});\n```\nThat's about it. There are quite a few operations that you can do on an `identifiable_item_collection`, so check out the example in [**$/examples/collections/identifiable_item_collection.hpp**](https://github.com/kevinchannon/wite/blob/master/examples/collections/identifiable_item_collection.cpp) for more things.\n\n# IO\n```c++\n#include \u003cwite/io/bytes_buffer.hpp\u003e\n```\nA small collection of routines for doing IO to buffers and things like that. The type of \"buffer\" isn't defined, so you should be able to use vectors, arrays, pointers, whatever.  The main restriction is that a \"buffer\" is a thing of std::bytes.\n\n## Buffer operations\nTo write to a buffer, then you can do:\n\n```c++\nauto buffer_data = std::vector\u003cio::byte\u003e(100, io::byte{0x00});\n\n{\n  const auto d = double{3.142};\n  const auto i32 = int32_t{-1234};\n  const auto u16 = uint16_t{100};\n\n  // Write something to the buffer\n  auto writer = io::bytes_write_buffer_view{buffer_data};\n\n  writer.write(d);\n  writer.write(i32);\n  writer.write(u16);\n}\n\n// Read things from the buffer\n{\n  auto reader = io::byte_read_buffer_view{buffer_data};\n\n  const auto d = reader.read\u003cdouble\u003e();\n  const auto i32 = reader.read\u003cint32_t\u003e();\n  const auto u16 = reader.read\u003cuint16_t\u003e();\n}\n```\nThis example is using `byte_read_buffer_view` and `byte_write_buffer_view` to manipulate the bytes in the buffer. This does things like increment the read/write position and things like that.\n\nBecause it's not specified above, then the write operations are using the native platform endianness.  The endianness may be specified using an \"encoding adapter\" (if you know it at compile time): \n```c++\nconst auto x = reader.read\u003cio::big_endian\u003cuint32_t\u003e\u003e();\n```\nor as an additional parameter if it's only known at runtime, for some reason.\n```c++\nconst auto x = reader.read\u003cuint32_t\u003e(io::endian::big);\n```\n\nYou can also write ranges of values (like vectors, arrays and lists):\n```c++\nconst auto my_values = std::vector\u003cdouble\u003e{ 1.0, 2.0, 3.0 };\nconst auto bytes_written = writer.write(my_values);\n```\nBecause we don't know how the allocator works for your chosen range, in general, the read interface for ranges is a little different. You call the `read_range` method and pass it a range to use for the output data. This needs to have the right size and type for the data you expect. It's moved in if it's an R-value, so you can do something like this:\n```c++\nconst auto v = reader.read_range(std::vector\u003cdouble\u003e(3, 0.0));\n```\nThis will read a vector from the buffer into `v`.\n\n### Multiple values\nOften, you will know what things you expect to read and write at compile time.  If so, then you can write/read a bunch of values in one operation, like this:\n```c++\nconst auto a = uint32_t{0x12345678};\nconst auto b = uint16_t{0xABCD};\nconst auto c = true;\nconst auto d = uint32_t{0xFEDCBA98};\n\nconst auto bytes_written = writer.write(a, io::big_endian{b}, c, d);\n\n...\n\nconst auto [ w, x, y, z ] =\n    reader.read\u003cuint32_t, io::big_endian\u003cuint16_t\u003e, bool, uint32_t\u003e();\n```\nThe advantage of this approach is that the various checks on the buffer capacity are only done once at the start, rather than for each value. Notice that you can use encoding adapters in the arguments. It's also possible to write ranges in this way too. So, you might serialise the size of a vector ahead of the vector itself. So, for a vector, `v`:\n```c++\nwriter.write(uint64_t{v.size()}, v);\n```\n\n## Reading/writing bytes from/to files\n```c++\n#include \u003cwite/io/byte_buffer_file.hpp\u003e\n```\n`io::read` and `io::write` can be used with a file path to write bytes to file. So, to read some bytes from a file with path `my/great/bytes.bin` you'd do:\n```c++\n// Read all the bytes\nconst auto all_the_bytes = io::read(\"my/great/bytes.bin\");\n\n// The first 100 bytes\nconst auto some_bytes = io::read(\"my/great/bytes.bin\", 100);\n```\n\nIf you specify a number of bytes, but the file is smaller than that, then all the bytes will be read (however many that is).  You can also use `try_read` if you don't want the call to throw exceptions (if the file doesn't exist, for example).\n\nWriting to a file is similar:\n```c++\nconst auto bytes = io::dynamic_byte_buffer{};\n\n// Fill the buffer with things...\n\n// Write the bytes to a file.\nio::write(\"my/file.bin\", bytes);\n```\nIf you only want to write the first N bytes from the buffer to file, you can pass that into `write` too:\n```c++\nio::write(\"my/file.bin\", 100, bytes);\n```\n\n## Simple byte conversions\nIf you have a value, and you want to get it as an array of `std::bytes`, then you can simply do:\n```c++\n// \"bytes\" \u003c- wite::io::static_byte_buffer\u003csizeof(my_value)\u003e\nconst auto bytes = wite::io::to_bytes(my_value);\n```\nof course, you can do the opposite too:\n```c++\nconst auto bytes = std::array\u003cio::byte, sizeof(uint32_t)\u003e{\n    io::byte{0x12}, io::byte{0x34}, io::byte{0x56}, io::byte{078}};\n\nconst auto i = wite::io::from_bytes\u003cint\u003e(bytes);\n```\n\n## Fancier usage\n### Controlling endianness\nSometimes you want to write the bytes of a value with a particular endianness.  If you know the endianness that you're going to need at build time, then you can specify it using either of the two endian encoding adapters `wite::io::little_endian` or `wite::io::big_endian`. So, to write some `int` called `my_int` to a buffer as a big endian value, then:\n```c++\nwite::io::write(buffer, wite::io::big_endian{my_int});\n```\nYou can also specify the endianness when reading:\n```c++\nconst auto my_int = wite::io::read\u003cwite::io::big_endian\u003cint\u003e\u003e(buffer);\n```\n\nThe endianness adapters also work in an equivalent way with the `to_bytes` and `from_bytes` functions.\n\nIf you only know the endianness at runtime, for some reason, then you can provide a final argument to `read` and `write` to specify the endianness:\n```c++\nconst auto my_int = wite::io::read\u003cint\u003e(buffer, std::endian::little);\n```\n# Binascii\n```c++\n#include \u003cwite/binascii/hexlify.hpp\u003e\n```\n\nThis component is basically two functions, `hexlify` and `unhexlify` that are based on the [Python functions of the same name](https://docs.python.org/3/library/binascii.html). `hexlify` takes a range containing byte values and returns a `std::string` with the hexadecimal values of the bytes in it:\n```c++\nconst auto bytes = std::vector\u003cstd::byte\u003e{ ... };\n\nconst auto str = binascii::hexlify(bytes);\n```\nIf your `bytes` vector is `[ 0x01, 0x02, 0xFE, 0xFF ]`, then the resulting `str` would be \"0102FEFF\". The input just has to be a range containing bytes, so it could be a `std::list`, or `std::array`, or whatever you have.\n\n`unhexlify` does the opposite of `hexlify`. `unhexlify` takes a `std::string_view` and returns a `wite::io::dynamic_byte_buffer`, which is basically a `std::vector` of `wite::io::byte` values.\n\n# String\nA small collection of string functions.  The aim is that the most common use case is the simplest thing to do and just does what you want without too much fuss. It might not be the most efficient way to do it, but it should be good enough for 99% of use cases. If you want to do something a bit more fancy, then there might be a way to do that using this library, or there might not.\n\n## Basic usage\n\nAll of these examples assume that you've done something like `namespace ws = wite::string;` somewhere.\n### `join`\n```c++\n#include \u003cwite/string/join.hpp\u003e\n```\nTake a collection of strings and make them into a single string, separated by a specified character\n\n```c++\nconst auto strings = {\"One\", \"small\", \"step\", \"for\", \"a\", \"man...\"};\n\n// \"sentence\" \u003c- std::string{ \"One small step for a man...\" }\nconst auto sentence = ws::join(string);\n```\n\n### `split`\n```c++\n#include \u003cwite/string/slit.hpp\u003e\n```\nThe opposite of `join`.\n\n```c++\nconst auto sentence = \"Some long string\";\n\n// \"words\" \u003c- std::vector\u003cstd::string\u003e{ \"Some\", \"long\", \"string\" }\nconst auto words = ws::split(sentence);\n```\n\n### `trim_left`, `trim_right`\n```c++\n#include \u003cwite/string/trim.hpp\u003e\n```\nRemove white-space from either the left, or right, side of a string\n\n```c++\nconst auto messy = \"   \\t\\n \u003c-- Messy bit here, and here -\u003e \\t\\t\\r\\v\\n  \";\n\n// \"tidier\" \u003c- std::string{ \"\u003c-- Messy bit here, and here -\u003e \\t\\t\\r\\v\\n  \" }\nconst auto tidier = ws::trim_left(messy);\n\n// \"tidy\" \u003c- std::string{ \"\u003c-- Messy bit here, and here -\u003e\" }\nconst auto tidy = ws::trim_right(tidier);\n```\n\n### `strip`\n```c++\n#include \u003cwite/string/strip.hpp\u003e\n```\nRemove all white space from both ends of a string.\n\n```c++\nconst auto messy = \"   \\t\\n \u003c-- Messy bit here, and here -\u003e \\t\\t\\r\\v\\n  \";\n\n// \"tidy\" \u003c- std::string{ \"\u003c-- Messy bit here, and here -\u003e\" }\nconst auto tidy = ws::strip(messy);\n```\n\n## Fancier usage\n### `join`\nJoin also accepts a second argument, which is the character that the strings will be joined with:\n\n```c++\nconst auto values = { \"1.618\", \"2.718\", \"3.142\" };\n\nconst auto csv_row = ws::join(values, ',');\n```\n\n### `split`\nYou can tell `split` a specific character that you want to split on:\n\n```c++\nconst auto csv_row = { \"1.618,2.718,3.142\" };\n\nconst auto values = ws::split(csv_row, ',');\n```\n\nYou can also tell it to ignore empty items:\n```c++\nconst auto csv_row = { \"1.618,,,,2.718,3.142\" };\n\nconst auto values = ws::split(csv_row, ',', ws::split_behaviour::drop_empty);\n```\n\n#### `split_to`\nIf you really want to control the thing, you can call `split_to`, which allows you to specify how the output is returned.  So, if you know that the string that you're splitting is going to out-live the result of the split, and it's not going to change, then you can do something like:\n```c++\nconst auto long_lived_values = { \"1.618,2.718,3.142\" };\n\nconst auto views_of_pieces = ws::split_to\u003cstd::vector\u003cstd::string_view\u003e\u003e(long_lived_values, ',');\n```\nIn this case, no additional memory allocation happens for the strings in the split result, the string_views are just pointers into the original strings.\n\n# Fragment String\n\n```c++\n#include \u003cwite/string/fragment_string.hpp\u003e\n```\n\n`wite::basic_fragment_string\u003cChar_T, FRAGMENT_COUNT\u003e` is a part of the wite strings library.  It allows you to compose literal strings into something that appears to be a single string. The interface is pretty much the same as the non-mutating parts of std::string. Some examples:\n\n```c++\nconst auto fs_1 = fragment_string{\"Hello\"};\n\nconst auto fs_2 = fs_1 + \", world!\";\n\n// Prints \"Hello, World!\"\nstd::cout \u003c\u003c fs_2 \u003c\u003c std::endl;\n```\n\nYou can use `wite::fragment_wstring` for wide chars.\n\nThere is also a user-defined string literal in the `wite::string_literals` namespace:\n\n```c++\nusing wite::string_literals;\n\nconst auto fs = \"first fragment\"_fs;\nconst auto wfs = L\"wide fragment string\"_wfs;\n```\n\n# Geometry\n\n```c++\n#include \u003cwite/geometry.hpp\u003e\n```\n\nSome basic geometry things; points, lines, shapes, that sort of thing.\n\n## point\n\n```c++\n#include \u003cwite/geometry/point.hpp\u003e\n```\n\n`point` is a template that represents a point in an N-dimensional space:\n```c++\nusing namespace wite::geometry;\n\nconst auto p = point\u003c2, int\u003e{1, 2};\n\n// Access the dimensions of the point by templated 'get', or a square-bracket operator.\nconst auto x = p.get\u003cdim::x\u003e();\nconst auto y = p[1];\n```\n\nCompare points:\n```c++\nconst auto p = point\u003c3, double\u003e{1.5, 2.0, 2.5};\n\nconst auto q = point\u003c3, double\u003e{1.5, 2.0, 2.5};\nconst auto r = point\u003c3, double\u003e{1.5, 2.0, 2.4};\nconst auto s = point\u003c3, double\u003e{1.5, 2.1, 2.5};\n\nstd::cout \u003c\u003c \"p == q: \" \u003c\u003c std::boolalpha \u003c\u003c p == q \u003c\u003c std::endl;\nstd::cout \u003c\u003c \"p != r: \" \u003c\u003c std::boolalpha \u003c\u003c p != r \u003c\u003c std::endl;\nstd::cout \u003c\u003c \"p  \u003c r: \" \u003c\u003c std::boolalpha \u003c\u003c p  \u003c r \u003c\u003c std::endl;\nstd::cout \u003c\u003c \"p  \u003c s: \" \u003c\u003c std::boolalpha \u003c\u003c p  \u003c s \u003c\u003c std::endl;\n```\nInitializer-lists:\n```c++\nauto p = point\u003c4, uint8_t\u003e;\n\n...\n\np = {0x02, 0x03, 0x04};\n```\n\n### Specializations\nSome kinds of point are very common, so there are specializations for those:\n```c++\npoint_2d\u003cT\u003e ==\u003e point\u003c2, T\u003e\npoint_3d\u003cT\u003e ==\u003e point\u003c3, T\u003e\npoint_4d\u003cT\u003e ==\u003e point\u003c4, T\u003e\n```\nSo, you can do thins like:\n```c++\nconst auto p = point_2d{10, 100};\n```\nwhich is nice and concise. This is using class type-deduction to work out the type of the values in the point.\n\n### IO\n```c++\n#include \u003cwite/geometry/io.hpp\u003e\n```\n\nIf you want to output a point to a stream easily, then include `geometry/io.hpp`:\n```c++\nconst auto p = point_2d{2.718, 3.142};\n\n...\n\nstd::cout \u003c\u003c \"p = \" \u003c\u003c p \u003c\u003c std::endl;\n```\nwill print `[ 2.718, 3.142 ]`.\n\n# Maths\n```c++\n#include \u003cwite/maths.hpp\u003e\n```\n\nA bunch of basic maths-y routines and classes\n\n## Numeric helpers\n```c++\n#include \u003cwite/maths/numeric.hpp\n```\n\nSome small functions to help with basic number things...\n\n### `next_value`, `prev_value`\n\nUse these to get at the next representable value after (`next_value`) or before (`prev_value`) the specified value:\n```c++\nconst auto next = wite::maths::next_value(1.23e45);\nconst auto prev = wite::maths::prev_value(1.23e45);\n```\nThese functions will take floating-point and integer types.\n\n### Variadic `min`, `max` and `minmax`\n\nThese take arbitrary numbers of values and give you the min or max (or both) of them. Kind of obvious really.\n```c++\n// min_val \u003c-- -1.23\nconst auto min_val = wite::maths::min(1.0, 10.0, -1.23, 5.0, 1000.0);\n\n// max_val \u003c-- 45\nconst auto max_val = wite::maths::max(21, 32, 1, 0, -10, 45, 19);\n\n// a \u003c-- -10, b \u003c-- 45\nconst auto [a, b] = wite::maths::minmax(21, 32, 1, 0, -10, 45, 19);\n```\nThese functions work with anything that is [`std::totally_ordered`](https://en.cppreference.com/w/cpp/concepts/totally_ordered).  If the input values are not trivially copyable, then they will be passed in by reference-to-const and the result will also be a reference-to-const, so watch out for that. It's a bit weird, but it mirrors the behaviour of `std::min` and `std::max`.\n\n\n### `interpolate` and `fraction`\nThese allow you to calculate the value of some fraction of the distance between two end points. So, if you have two extrema, like `10` and `20`, and you want to get the number that's 35% of the way between them, you can do:\n```c++\n// x \u003c-- 13.5\nconst auto x = wite::maths::interpolate(0.35, 10.0, 20.0);\n```\nIf your fraction is greater than one, or less than zero, then `interpolate` will linearly extrapolate beyond the end of the range:\n```c++\n// x \u003c-- 35.0\nconst auto x = wite::maths::interpolate(2.5, 10.0, 2.00);\n```\n`fraction` basically doe the inverse of `interpolate`; if you know the value and the extrema, then what would be the fraction through the range that gives that fraction:\n```c++\n// f \u003c-- 0.6\nconst auto f = wite::maths::fraction(16.0, 10.0, 20.0);\n```\nThese functions are templated, so the result has the same type as the extrema that you pass in. So, `interpolate(0.35, 0, 10)` will return `3`.\n\n\n## `value_range`\n\n```c++\n#include \u003cwith/maths/value_range.hpp\u003e\n```\n\nA small class that expresses an interval, in the mathematical sense. You give it the min and max of the range in its constructor, and you can test where values are inside or outside the range, etc.\n\n```c++\nconst auto val_rng = maths::value_range{-10, 10};\n\nconst auto a = val_rng.min();   // -10\nconst auto b = val_rng.max();   //  10\n\nconst auto width = val_rng.size();  // 20\n\nconst auto mid_point = val_rng.mid();   // 0\n```\nIn this example, we're relying on class template deduction to guess the type for the `value_range`. So, it's deduced as `int` in this case. We could have used `const auto val_rng = maths::value_range\u003cint\u003e{-10, 10}` if we wanted to be explicit. Similarly, `maths::value_range{0.0, std::numbers::pi}` would return a `double` range.\n\nTo test whether points are in or out of the range, you can use `contains`:\n```c++\nif (val_rng.contains(5)){\n    std::cout \u003c\u003c \"5 is in range\" \u003c\u003c std::endl;\n}\n\nif (not val_rng.contains(11)) {\n    std::cout \u003c\u003c \"11 is not in range\" \u003c\u003c std::endl;\n}\n```\n\nYou can get the overlap of a range with another range by using `overlap`:\n```c++\n// *common_rng \u003c-- [-10, -2]\nconst auto common_rng = val_rng.overlap(maths::value_range{-100, -2});\n```\n`overlap` returns an optional, because ranges aren't guaranteed to overlap. So, if there's no overlap, then the result of `overlap` is `std::nullopt`:\n```c++\n// no_overlap \u003c-- std::nullopt\nconst auto no_overlap = val_rng.overlap(maths::value_range{20, 40});\n```\n\nIf you have a value and a range, and you want to clamp the value into the range, then you can use `clamp` for that:\n```c++\nconst auto r = maths::value_range{0.0, std::numbers::pi};\n\nconst auto x = r.clamp(-0.5);   // x \u003c-- 0.0\nconst auto y = r.clamp(1.0);    // y \u003c-- 1.0\nconst auto z = r.clamp(4.0);    // z \u003c-- π\n```\n\nThere are also `interpolate` and `fraction` methods that do the same thing as the free functions in `numeric.hpp`:\n```c++\nconst auto r = maths::value_range{10.0, 20.0};\n\n// x \u003c-- 13.5\nconst auto x = r.interpolate(0.35);\n\n// f \u003c-- 0.35\nconst auto f = r.fraction(13.5)\n```\nCalculating the fraction of an empty range returns NaN.\n\n\n## Open and closed ranges\n\nBy default, `value_range`s are closed (i.e. the min and max values are defined as being inside the range). If you need an open range, then you can specify it as a template parameter:\n```c++\n// These three value_ranges will all compare equal.\n// [0.0, 1.0]\nconst auto closed_closed_1 = maths::value_range{0.0, 1.0};\nconst auto closed_closed_2 = maths::closed_value_range{0.0, 1.0};\nconst auto closed_closed_3 = maths::value_range\u003cdouble, maths::range::boundary::closed, maths::range::boundary::closed\u003e{0.0, 1.0};\n\n// These two value_ranges will both compare equal.\n// (0.0, 1.0)\nconst auto open_open_1 = maths::open_value_range{0.0, 1.0};\nconst auto open_open_2 = maths::value_range\u003cdouble, maths::range::boundary::open, maths::range::boundary::open\u003e{0.0, 1.0};\n\n// [0.0, 1.0)\nconst auto closed_open = maths::value_range\u003cdouble, maths::range::boundary::closed, maths::range::boundary::open\u003e{0.0, 1.0};\n\n// (0.0, 1.0]\nconst auto open_closed = maths::value_range\u003cdouble, maths::range::boundary::open, maths::range::boundary::closed\u003e{0.0, 1.0};\n```\nNote: there are specific classes for the common \"all-open\" and \"all-closed\" cases, `maths::open_value_range` and `maths::closed_value_range`, respectively.\n\n### IO\n\n```c++\n#include \u003cwite/maths/io.hpp\u003e\n```\n\nYou can insert a `value_range` into a `std::ostream` as expected:\n```c++\n// \"My range = [0.1,2.0]\"\nstd::cout \u003c\u003c \"My range = \" \u003c\u003c maths::value_range{0.1, 2.0} \u003c\u003c std::endl;\n\nconst auto rng = maths::value_range\u003cdouble, maths::range::boundary::closed, maths::range::boundary::open\u003e{0.1, 2.0};\n\n// \"My range = [0.1,2.0)\"\nstd::cout \u003c\u003c \"My range = \" \u003c\u003c rng \u003c\u003c std::endl;\n```\n\n### Free functions\n\nIf you have a bunch of values, and you want to get their range, you can use `envelope` for that:\n```c++\n// val_rng \u003c-- [-4.0, 11.2]\nconst auto val_rng = maths::envelope(0.0, 0.1, -1.2, 10.0, -4.0, 11.2);\n\nconst auto values = std::vector\u003cint\u003e{0, 1, 2, 3, 4, 5, 10};\n\n// val_rng_from_collection \u003c-- [0, 10]\nconst auto val_rng_from_collection = maths::envelope(values);\n```\n`envelope` also works with value_ranges as inputs. It will return a new `value_range` the min of the range mins and the max of range maxes.\n\n`maths::min` and `maths::max` are overloaded to take `value_range`. So, if you have a bunch of `value_range`s, then you can find the min of the mins, or the max of the maxes. \n\n## `bounded_value`\n```c++\n#include \u003cwite/maths/bounded_value.hpp\u003e\n```\nThis is a small class that bundles a value and its range together.\n```c++\nnamespace wite::maths = wm;\n\nconst auto val = wm::bounded_value{2, {0, 10}};\n```\n`val` is has a value of `2` and a range of `[0,10]`.  You can set the bounds and value to any value you like; there's no constraints applied by the class itself. However, `bounded_value` lets you check if the value is in range, or push it into range:\n```c++\nauto param = wm::bounded_value{2, {0, 10}};\n\nparam.value = 20;\n\n// in_range \u003c-- false.\nconst auto in_range = param.is_in_bounds();\n\n// param.value \u003c-- 10.\nparam.clamp();\n```\n\n# Compiler Macros\nYou might want to do things a bit differently, so here are some build-time specializations that you can make.\n\n## Global\n\n### `WITE_NO_NODISCARD`\nMany of the functions in Wite are marked with the `[[nodiscard]]` attribute, if the compiler supports it, because it makes code safer.  If your compiler does support `[[nodiscard]]` but you don't want it, for some reason, then compile with `WITE_NO_NODISCARD` defined.\n\n### `WITE_NO_EXCEPTIONS`\nIn general, things that go wrong throw exceptions in Wite. However, if you don't want exceptions being thrown from Wite code in your project, then define this command line variable in compilation. It doesn't guarantee that no exceptions will be emitted, but it does guarantee that Wite won't throw any. If you define this value, some features of the library will not be available to you.\n\n## Core\n### WITE_DEFAULT_UUID_FMT\nSets the default format for IO of UUIDs. Set to one of 'D', 'd', 'N', 'n', 'B', 'b', 'P', 'p', 'X' or 'x'.\n### WITE_DEFAULT_ID_TYPE\nSets the default type that will be used when `wite::id` types are created without specifying a type for the ID value itself.\n\n## IO \n### `WITE_USER_DEFINED_BYTE_TYPE_`\nBy default, and if the compiler supports it, Wite will use `std::byte` as its native byte type for IO. If your compiler doesn't support `std::byte`, then WIte will detect this at build time and use `unsigned char` as its native byte type. If you have a bunch of pre-existing code that uses some other type for bytes, then you can choose some other options at compile-time.\n* `WITE_USER_DEFINED_BYTE_TYPE_CHAR` - sets the byte type to `char`\n* `WITE_USER_DEFINED_BYTE_TYPE_U8` - sets the byte type to `std::uint8_t`\n* `WITE_USER_DEFINED_BYTE_TYPE_I8` - sets the byte type to `std::int8_t`\n\nIt is an error to specify more than one of these options. \n\n### `WITE_LITTLE_ENDIAN` and `WITE_BIG_ENDIAN`\nBy default, Wite will try to use the system's native endianness (if `std::endian` is available in your compiler). If `std::endian` is not available, then the default endianness will default to little-endian.  If you want to override what Wite considers to be the native system endianness, then you can specify `WITE_BIG_ENDIAN`, or `WITE_LITTLE_ENDIAN` to the compiler.\n\nIt is an error to specify both of these options at the same time.\n\n## Geometry\n\n### `WITE_DEFAULT_POINT_TYPE`\nIf you're using `wite::geometry::point`, then the *default* default value type for points is `double`.  If you're concerned mainly with pixel positions, or something, and you want the default type for points to be some integer type, then you can use `WITE_DEFAULT_POINT_TYPE` to define the default type.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevinchannon%2Fwite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkevinchannon%2Fwite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevinchannon%2Fwite/lists"}