{"id":33003048,"url":"https://github.com/ph3at/libenvpp","last_synced_at":"2025-11-18T08:03:22.573Z","repository":{"id":65084168,"uuid":"521627510","full_name":"ph3at/libenvpp","owner":"ph3at","description":"A modern C++ library for type-safe environment variable parsing","archived":false,"fork":false,"pushed_at":"2025-10-25T10:48:07.000Z","size":207,"stargazers_count":150,"open_issues_count":3,"forks_count":13,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-10-25T12:27:39.224Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ph3at.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,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-08-05T12:20:56.000Z","updated_at":"2025-10-14T23:54:54.000Z","dependencies_parsed_at":"2024-05-02T00:53:30.580Z","dependency_job_id":"7f42865f-0804-4d8a-97e7-efe561363105","html_url":"https://github.com/ph3at/libenvpp","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/ph3at/libenvpp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph3at%2Flibenvpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph3at%2Flibenvpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph3at%2Flibenvpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph3at%2Flibenvpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ph3at","download_url":"https://codeload.github.com/ph3at/libenvpp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph3at%2Flibenvpp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285028340,"owners_count":27102545,"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","status":"online","status_checked_at":"2025-11-18T02:00:05.759Z","response_time":61,"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":[],"created_at":"2025-11-13T14:00:38.910Z","updated_at":"2025-11-18T08:03:22.554Z","avatar_url":"https://github.com/ph3at.png","language":"C++","readme":"# libenvpp - Modern C++ Library for Handling Environment Variables\n\nThis library provides a modern, platform independent, type-safe way of handling environment variables. It offers an easy to use interface for the most common use-cases, and is extensible and customizable to more complex use-cases, if and when required.\n\n- [Features](#features)\n- [Usage](#usage)\n  - [Simple Example](#simple-example)\n  - [Custom Type Parser](#custom-type-parser)\n  - [Custom Type Validator](#custom-type-validator)\n  - [Custom Variable Parser and Validator](#custom-variable-parser-and-validator)\n  - [Range Variables](#range-variables)\n  - [Option Variables](#option-variables)\n  - [Deprecated Variables](#deprecated-variables)\n  - [Prefixless Environment Variables](#prefixless-environment-variables)\n- [Error Handling](#error-handling)\n  - [Help Message](#help-message)\n  - [Warnings and Errors](#warnings-and-errors)\n- [Testing](#testing)\n  - [Global Testing Environment](#global-testing-environment)\n  - [Custom Environment](#custom-environment)\n  - [Set for Testing](#set-for-testing)\n- [Installation](#installation)\n  - [FetchContent](#fetchcontent)\n  - [Submodule](#submodule)\n  - [Cmake Options](#cmake-options)\n\n## Features\n\n- Platform independent\n- Type-safe parsing of environment variables\n- Support for required and optional environment variables, with specifiable default value\n- Automatic parsing of built-in types\n- User-defined types parsable with user-defined parser\n- Optional user-defined validation\n- Convenience range/option environment variable type\n- Parsing/validating possible per type, or per environment variable\n- Typo detection based on edit-distance\n- Unused environment variable detection\n\n## Usage\n\n### Simple Example\n\nTo use the library the first step would be to include the header:\n\n```cpp\n#include \u003clibenvpp/env.hpp\u003e\n```\n\nThe library is built around the idea of a `prefix`. The assumption is that all relevant environment variables used by a program share the same prefix, for example with the prefix `MYPROG`:\n\n```text\nMYPROG_LOG_FILE_PATH=\"/path/to/log\"\nMYPROG_NUM_THREADS=4\n```\n\nThis would be expressed as:\n\n```cpp\nauto pre = env::prefix(\"MYPROG\");\n\nconst auto log_path_id = pre.register_variable\u003cstd::filesystem::path\u003e(\"LOG_FILE_PATH\");\nconst auto num_threads_id = pre.register_required_variable\u003cunsigned int\u003e(\"NUM_THREADS\");\n```\n\nHere `MYPROG_LOG_FILE_PATH` is registered as an optional environment variable, which will not cause an error or warning when it is not specified in the environment. Whereas `MYPROG_NUM_THREADS` is registered as a required environment variable, which will cause an error if it is not found in the environment.\n\nAfter having registered all variables with the corresponding prefix it can be parsed and validated, which will retrieve the values from the environment and parse them into the type specified when registering:\n\n```cpp\nconst auto parsed_and_validated_pre = pre.parse_and_validate();\n```\n\nThis will invalidate the prefix `pre` which must not be used after this point, and can be safely destroyed.\n\nIf anything went wrong, like the required variable not being found, or the variable not being parsable, this can be queried through the `parsed_and_validated_pre`:\n\n```cpp\nif (!parsed_and_validated_pre.ok()) {\n    std::cout \u003c\u003c parsed_and_validated_pre.warning_message();\n    std::cout \u003c\u003c parsed_and_validated_pre.error_message();\n}\n```\n\nOtherwise the parsed values can be retrieved from the `parsed_and_validated_pre`:\n\n```cpp\nif (parsed_and_validated_pre.ok()) {\n    const auto log_path = parsed_and_validated_pre.get_or(log_path_id, \"/default/log/path\");\n    const auto num_threads = parsed_and_validated_pre.get(num_threads_id);\n}\n```\n\nOptional variables can be given a default value when getting them with `get_or`, which will return the default value if (and only if) the variable was not found in the environment. Parsing or validation errors of optional variables are also reported as errors. Additionally, optional variables can also be retrieved with `get` which will return a `std::optional` which is empty if the variable was not found in the environment.\n\nRequired variables can only be gotten with `get`, as they are required and therefore need no default value, which will return the type directly. If required variables were not found in the environment this will be reported as an error.\n\n#### Simple Example - Code\n\nPutting everything together:\n\n```cpp\n#include \u003ccstdlib\u003e\n#include \u003cfilesystem\u003e\n#include \u003ciostream\u003e\n\n#include \u003clibenvpp/env.hpp\u003e\n\nint main()\n{\n    auto pre = env::prefix(\"MYPROG\");\n\n    const auto log_path_id = pre.register_variable\u003cstd::filesystem::path\u003e(\"LOG_FILE_PATH\");\n    const auto num_threads_id = pre.register_required_variable\u003cunsigned int\u003e(\"NUM_THREADS\");\n\n    const auto parsed_and_validated_pre = pre.parse_and_validate();\n\n    if (parsed_and_validated_pre.ok()) {\n        const auto log_path = parsed_and_validated_pre.get_or(log_path_id, \"/default/log/path\");\n        const auto num_threads = parsed_and_validated_pre.get(num_threads_id);\n\n        std::cout \u003c\u003c \"Log path   : \" \u003c\u003c log_path \u003c\u003c std::endl;\n        std::cout \u003c\u003c \"Num threads: \" \u003c\u003c num_threads \u003c\u003c std::endl;\n    } else {\n        std::cout \u003c\u003c parsed_and_validated_pre.warning_message();\n        std::cout \u003c\u003c parsed_and_validated_pre.error_message();\n    }\n\n    return EXIT_SUCCESS;\n}\n```\n\n#### Simple Example - Output\n\nRunning the example without having any environment variable set:\n\n```console\n$ ./libenvpp_simple_usage_example\nError  : Environment variable 'MYPROG_NUM_THREADS' not set\n```\n\nRunning the example with a typo:\n\n```console\n$ MYPROG_LOG_FILEPATH=/var/log/file ./libenvpp_simple_usage_example\nWarning: Unrecognized environment variable 'MYPROG_LOG_FILEPATH' set, did you mean 'MYPROG_LOG_FILE_PATH'?\nError  : Environment variable 'MYPROG_NUM_THREADS' not set\n```\n\nRunning the example with only the required variable set:\n\n```console\n$ MYPROG_NUM_THREADS=4 ./libenvpp_simple_usage_example\nLog path   : \"/default/log/path\"\nNum threads: 4\n```\n\nRunning the example with both variables set:\n\n```console\n$ MYPROG_LOG_FILE_PATH=/var/log/file MYPROG_NUM_THREADS=4 ./libenvpp_simple_usage_example\nLog path   : \"/var/log/file\"\nNum threads: 4\n```\n\n### Custom Type Parser\n\nTo provide a parser for a user-defined type it is necessary to specialize the template struct `env::default_parser` for the specific type that should be parsed and provide a call operator that takes a `std::string_view` and returns the parsed type, for example:\n\n```cpp\nstruct program_data {\n    int number;\n    float percent;\n};\n\nnamespace env {\ntemplate \u003c\u003e\nstruct default_parser\u003cprogram_data\u003e {\n    program_data operator()(const std::string_view str) const\n    {\n        const auto split_str = split(str, ',');\n        if (split_str.size() != 2) {\n            // Report an error if the input does not have the expected format\n            throw parser_error{\"Expected 2 comma delimited values\"};\n        }\n\n        auto parsed_data = program_data{};\n\n        // Delegate parsing of primitive types to the default_parser\n        parsed_data.number = default_parser\u003cint\u003e{}(split_str[0]);\n        parsed_data.percent = default_parser\u003cfloat\u003e{}(split_str[1]);\n\n        return parsed_data;\n    }\n};\n} // namespace env\n```\n\n_Note:_ The `default_parser` already supports primitive types (and everything that can be constructed from string), so parsing should be delegated to the existing implementation whenever possible.\n\n#### Custom Type Parser - Code\n\nFor the entire code see [examples/libenvpp_custom_parser_example.cpp](examples/libenvpp_custom_parser_example.cpp).\n\n### Custom Type Validator\n\nSimilarly to the `env::default_parser` there is also an `env::default_validator` which can be specialized for any type that should be validated. The non-specialized implementation of `default_validator` does nothing, as everything that can be parsed is considered valid by default. When specializing `default_validator` for a type the `struct` must contain a call operator that takes the parsed type and returns `void`. Validation errors should be reported by throwing an `env::validation_error`:\n\n```cpp\nnamespace env {\ntemplate \u003c\u003e\nstruct default_validator\u003cstd::filesystem::path\u003e {\n    void operator()(const std::filesystem::path\u0026 path) const\n    {\n        if (!std::filesystem::exists(path)) {\n            throw validation_error{path.string() + \" path does not exist\"};\n        }\n    }\n};\n} // namespace env\n```\n\nThe validator example above shows a validator for the type `std::filesystem::path` which requires that environment variables of that type must exist on the filesystem, otherwise the specified environment variable is considered invalid, even if the string was a valid path.\n\n#### Custom Type Validator - Code\n\nFor the full example see [examples/libenvpp_custom_validator_example.cpp](examples/libenvpp_custom_validator_example.cpp).\n\n### Custom Variable Parser and Validator\n\nIf parsing/validator should be done in a specific way for a specific variable only, and not for every variable of the same type, it is possible to specify a parser and validator function when registering a variable:\n\n```cpp\nstd::filesystem::path path_parser_and_validator(const std::string_view str)\n{\n    const auto log_path = std::filesystem::path(str);\n\n    if (!std::filesystem::exists(log_path)) {\n        if (!std::filesystem::create_directory(log_path)) {\n            throw env::validation_error{\"Unable to create log directory\"};\n        }\n    } else if (!std::filesystem::is_directory(log_path)) {\n        throw env::validation_error{\"Log path is not a directory\"};\n    }\n\n    return log_path;\n}\n\nint main()\n{\n    auto pre = env::prefix(\"CUSTOM_PARSER_AND_VALIDATOR\");\n\n    const auto path_id = pre.register_required_variable\u003cstd::filesystem::path\u003e(\"LOG_PATH\", path_parser_and_validator);\n\n    /*...*/\n}\n```\n\nFor example, the parser and validator function above will make sure that the log path specified by the environment variable points to a directory, and will even create it if it doesn't exist. Errors are reported by throwing `env::parser_error` or `env::validation_error`.\n\n_Note:_ When specifying a custom parser and validator function, the `default_parser` and `default_validator` for the given type are not invoked automatically, however delegating to them is still possible and should be done if appropriate.\n\n_Note:_ The parser and validator function passed to `register_[required]_variable` can be any callable type, be it function, lambda, or functor.\n\n#### Custom Variable Parser and Validator - Code\n\nFor the full example see [examples/libenvpp_custom_parser_and_validator_example.cpp](examples/libenvpp_custom_parser_and_validator_example.cpp).\n\n### Range Variables\n\nBecause it is a frequent use-case that a value must be within a given range, environment variables can be registered with `register_range` which additionally takes a minimum and maximum value, and validates that the parsed value is within the given range. The minimum and maximum values are both **inclusive**. For example:\n\n```cpp\nauto pre = env::prefix(\"RANGE\");\nconst auto num_threads_id = pre.register_range\u003cunsigned int\u003e(\"NUM_THREADS\", 1, std::thread::hardware_concurrency());\nconst auto parsed_and_validated_pre = pre.parse_and_validate();\n\nif (parsed_and_validated_pre.ok()) {\n    const auto num_threads = parsed_and_validated_pre.get_or(num_threads_id, std::thread::hardware_concurrency());\n    std::cout \u003c\u003c \"Number of threads: \" \u003c\u003c num_threads \u003c\u003c std::endl;\n} else {\n    std::cout \u003c\u003c parsed_and_validated_pre.warning_message();\n    std::cout \u003c\u003c parsed_and_validated_pre.error_message();\n}\n```\n\nThis will enforce that the specified number of threads is between 1 and the maximum that the system this is run on supports, inclusive.\n\n_Note:_ Range variables, just like normal variables, can be optional or required. And the type is parsed with `default_parser` and validated with `default_validator`. It is not possible, however, to specify a custom parser and validator function for a range, if this is desired, a normal variable should be used and the range check done manually as part of the validation.\n\n_Note:_ When using `get_or` to set a default value for an optional range, the range requirement is **not** enforced on the default. This can be used for special case handling if desired.\n\n#### Range Variables - Code\n\nFor the complete code see [examples/libenvpp_range_example.cpp](examples/libenvpp_range_example.cpp).\n\n### Option Variables\n\nAnother frequent use-case is that a value is one of a given set of options. For this an environment variable can be registered with `register_[required]_option`, which takes a list of either pairs of strings and corresponding options, or just valid options, against which the value is checked. For example:\n\n```cpp\nenum class option {\n    first,\n    second,\n    fallback,\n};\n\nint main()\n{\n    auto pre = env::prefix(\"OPTION\");\n\n    const auto option_id =\n        pre.register_option\u003coption\u003e(\"CHOICE\", {{\"first\", option::first}, {\"second\", option::second}});\n\n    const auto parsed_and_validated_pre = pre.parse_and_validate();\n\n    if (parsed_and_validated_pre.ok()) {\n        const auto opt = parsed_and_validated_pre.get_or(option_id, option::fallback);\n    }\n}\n```\n\nThis registers an `enum class` option, where only a subset of all possible values is considered valid, so that `option::fallback` can be used as the value if the variable is not set.\n\n_Note:_ The list of options provided when registering must not be empty, and must not contain duplicates.\n\n_Note:_ As with range variables, the default value given with `get_or` is not enforced to be within the list of options given when registering the option variable.\n\n_Note:_ For the variant where no mapping to strings is provided, a specialized `default_parser` for the `enum class` type must exist.\n\n_Note:_ Options are mostly intended to be used with `enum class` types, but this is in no way a requirement. Any type can be used as an option, and `enum class` types can also just be normal environment variables.\n\n#### Option Variables - Code\n\nFor the full code, which features both the simple case shown above and a case with a custom parser, see [examples/libenvpp_option_example.cpp](examples/libenvpp_option_example.cpp).\n\n### Deprecated Variables\n\nSometimes, new releases of your software will deprecate environment variables. To provide users with helpful messages in this case, you can use the 'register_deprecated' feature.\n\n```cpp\n#include \u003clibenvpp/env.hpp\u003e\n\nint main()\n{\n    auto pre = env::prefix(\"APP\");\n    pre.register_deprecated(\"FEATURE\", \"The option 'APP_FEATURE' has been deprecated since version x.y. Please use SOMETHING instead.\");\n}\n```\n\n#### Deprecated Variables - Code\n\nFor a full code example see [examples/libenvpp_deprecated_variable_example.cpp](examples/libenvpp_deprecated_variable_example.cpp).\n\n### Prefixless Environment Variables\n\nEven though it is recommended to namespace environment variables with a prefix, and use the prefix mechanism of this library to parse those variables, sometimes it might be necessary to parse environment variables that don't have a prefix. To this end, this library also provides a mechanism for that:\n\n```cpp\n#include \u003ccstdlib\u003e\n#include \u003cfilesystem\u003e\n#include \u003ciostream\u003e\n\n#include \u003clibenvpp/env.hpp\u003e\n\nint main()\n{\n    const auto log_path = env::get_or\u003cstd::filesystem::path\u003e(\"LOG_FILE_PATH\", \"/default/log/path\");\n    const auto num_threads = env::get\u003cunsigned int\u003e(\"NUM_THREADS\");\n\n    if (num_threads.has_value()) {\n        std::cout \u003c\u003c \"Log path   : \" \u003c\u003c log_path \u003c\u003c std::endl;\n        std::cout \u003c\u003c \"Num threads: \" \u003c\u003c num_threads.value() \u003c\u003c std::endl;\n    } else {\n        std::cout \u003c\u003c num_threads.error().what() \u003c\u003c std::endl;\n    }\n\n    return EXIT_SUCCESS;\n}\n\n```\n\nThis is analogous to the simple usage example using a prefix. Required variables can be parsed using `env::get`, which either returns the parsed and validated value, or an error.\n\nOptional variables can be parsed and validated with `env::get_or` which will always return a value, either the successfully parsed and validated one from the environment, or the default value.\n\n_Note:_ These functions make use of the same `default_parser`/`default_validator` mechanism as the prefix, and so parsing/validating of user-defined types is also supported.\n\n#### Prefixless Environment Variables - Code\n\nFor the code of this example, see [examples/libenvpp_prefixless_get_example.cpp](examples/libenvpp_prefixless_get_example.cpp).\n\n## Error Handling\n\n### Help Message\n\nAfter having registered all variables with a prefix it is possible to generate a formatted help message for a specific prefix. This can be done by calling `help_message` on either a prefix, or a validated prefix. For example:\n\n```cpp\nauto pre = env::prefix(\"MYPROG\");\n\nconst auto log_path_id = pre.register_variable\u003cstd::filesystem::path\u003e(\"LOG_FILE_PATH\");\nconst auto num_threads_id = pre.register_required_variable\u003cunsigned int\u003e(\"NUM_THREADS\");\n\nstd::cout \u003c\u003c pre.help_message();\n```\n\nwill output:\n\n```text\nPrefix 'MYPROG' supports the following 2 environment variable(s):\n    'MYPROG_LOG_FILE_PATH' optional\n    'MYPROG_NUM_THREADS' required\n```\n\n### Warnings and Errors\n\nIf anything goes wrong when parsing and validating a prefix this can be queried through the following functions:\n\n| Function            | Return value                                                   |\n|---------------------|----------------------------------------------------------------|\n| `ok()`              | `bool` indicating whether there are any errors or warnings     |\n| `warning_message()` | `std::string` with a formatted message containing all warnings |\n| `error_message()`   | `std::string` with a formatted message containing all errors   |\n| `warnings()`        | `std::vector\u003cenv::error\u003e` containing all warnings              |\n| `errors()`          | `std::vector\u003cenv::error\u003e` containing all errors                |\n\n#### `error` Type\n\nThe `env::error` type is used to give more information on warnings and errors. The type supports:\n\n| Function     | Return value                                                                                                                                |\n|--------------|---------------------------------------------------------------------------------------------------------------------------------------------|\n| `get_id()`   | `std::size_t` ID of the variable that caused the error/warning. This ID can be compared to the ID returned from the `register_*` functions. |\n| `get_name()` | `std::string` containing the name of the variable that caused the warning/error.                                                            |\n| `what()`     | `std::string` containing the warning/error message.                                                                                         |\n\n#### Warnings\n\nThe following situations will generate a warning:\n\n- An optional variable is not set, but a similar (within edit distance configured for the prefix) unused variable is.\n- An unused variable with the same prefix is set.\n\n#### Errors\n\nThe following situations will generate an error:\n\n- A variable could not be parsed.\n- A variable could not be validated.\n- The value of a range variable is outside the specified range.\n- The value of an option variable is not a valid option.\n- An unexpected exception was thrown while parsing/validating a variable.\n- A required variable is not set, but a similar (within edit distance configured for the prefix) unused variable is.\n- A required variable is not set.\n\n### Warnings and Errors - Code\n\nFor an example of how the warning/error handling functions can be used, see [examples/libenvpp_error_handling_example.cpp](examples/libenvpp_error_handling_example.cpp).\n\n## Testing\n\nThere are a few ways that help facilitate (unit) testing.\n\n### Global Testing Environment\n\nLibenvpp provides an internal global testing environment where variables can be added as key/value pairs using `env::scoped_test_environment`:\n\n```cpp\nconst auto _ = env::scoped_test_environment({\n    {\"MYPROG_LOG_FILE_PATH\", \"/dev/null\"},\n    {\"MYPROG_NUM_THREADS\", \"8\"},\n});\n```\n\nOnce the scoped test environment instance goes out of scope, the variables it added to the global test environment are automatically removed.\n\nAny libenvpp function that usually retrieves variables from the system environment will first check if the global test environment contains a value for the requested variable and retrieve that instead. This can be used to facilitate unit testing without having to change the usage of libenvpp.\n\n_Note:_ Any value set in the global testing environment will take precedence over system/custom environment variables.\n\n_Note:_ Interacting with the global testing environment is not thread safe and the user must take care to synchronize any potentially conflicting accesses.\n\n#### Global Testing Environment - Code\n\nA complete example of how to use the global testing environment, see [examples/libenvpp_testing_example.cpp](examples/libenvpp_testing_example.cpp).\n\n### Custom Environment\n\nAnother mechanism that can be used for testing is the ability to pass a custom environment to `prefix::parse_and_validate` as the first parameter of type `std::unordered_map\u003cstd::string, std::string\u003e`.\n\nEnvironment variables will then be fetched from there instead of the system environment. Note that the global testing environment will still take precedence over the custom environment and if the variable is not found in the testing environment or the custom environment **no** fallback to the system environment will be performed.\n\n#### Custom Environment - Code\n\nA complete example of how to use a custom environment can be found here: [examples/libenvpp_custom_environment_example.cpp](examples/libenvpp_custom_environment_example.cpp)\n\n### Set for Testing\n\nAdditionally it is also possible to bypass parsing and validating completely and directly set the value for a registered variable on a prefix.\n\n```cpp\nauto pre = env::prefix(\"MYPROG\");\n\nconst auto log_path_id = pre.register_variable\u003cstd::filesystem::path\u003e(\"LOG_FILE_PATH\");\nconst auto num_threads_id = pre.register_required_variable\u003cunsigned int\u003e(\"NUM_THREADS\");\n\npre.set_for_testing(log_path_id, \"/dev/null\");\npre.set_for_testing(num_threads_id, 8);\n```\n\nThis will ignore any value for that variable in the testing/system/custom environment and not perform any parsing or validating and just directly return the set value when using `get`/`get_or` on the parsed and validated prefix.\n\n#### Set for Testing - Code\n\nA complete example of how to use `set_for_testing` can be found here: [examples/libenvpp_set_for_testing_example.cpp](examples/libenvpp_set_for_testing_example.cpp).\n\n## Installation\n\n### FetchContent\n\nLibenvpp can be integrated into a cmake project (`myproject`) using the FetchContent mechanism like this:\n\n`CMakeLists.txt`:\n\n```cmake\nset(LIBENVPP_INSTALL ON CACHE BOOL \"\" FORCE) # If installation is desired.\nFetchContent_Declare(libenvpp\n    GIT_REPOSITORY https://github.com/ph3at/libenvpp.git\n    GIT_TAG v1.5.1\n)\nFetchContent_MakeAvailable(libenvpp)\n\n# ...\n\ntarget_link_libraries(myproject PUBLIC\n    libenvpp::libenvpp\n)\n```\n\n`myproject-config.cmake.in` (only required if installation is desired):\n\n```cmake\n# ...\n\ninclude(CMakeFindDependencyMacro)\nfind_dependency(libenvpp REQUIRED)\n\n# ...\n```\n\n### Submodule\n\nThe library uses cmake as the build system and can be easily integrated into an existing cmake project by adding it with `add_subdirectory`, ideally used in combination with a git submodule of the library. The build system will detect this use-case and automatically disable the building of examples and tests.\n\n### Cmake Options\n\n| Option              | Default                                                                 | Description                                                                                                                                               |\n|---------------------|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `LIBENVPP_EXAMPLES` | `ON` if configured standalone, `OFF` if used as dependency              | Enables building of example programs.                                                                                                                     |\n| `LIBENVPP_TESTS`    | `ON` if configured standalone, `OFF` if used as dependency              | Enables building of unit tests.                                                                                                                           |\n| `LIBENVPP_CHECKS`   | `ON` in debug builds or when tests are enabled, `OFF` in release builds | Custom assertions that are not tied to `NDEBUG`, and are testable by throwing an exception instead of `std::abort`ing _iff_ `LIBENVPP_TESTS` are enabled. |\n| `LIBENVPP_INSTALL`  | `OFF`                                                                   | Adds an install target that can be used to install libenvpp as a library.                                                                                 |\n","funding_links":[],"categories":["Miscellaneous"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fph3at%2Flibenvpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fph3at%2Flibenvpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fph3at%2Flibenvpp/lists"}