pax_global_header00006660000000000000000000000064143761007560014523gustar00rootroot0000000000000052 comment=3a1209de8370bf5fe16362934956144b49591565 TartanLlama-optional-3a1209d/000077500000000000000000000000001437610075600160735ustar00rootroot00000000000000TartanLlama-optional-3a1209d/.appveyor.yml000066400000000000000000000002261437610075600205410ustar00rootroot00000000000000os: - Visual Studio 2015 - Visual Studio 2017 build_script: - cmake -Bbuild -S. - cmake --build build - cmake --build build --target RUN_TESTS TartanLlama-optional-3a1209d/.clang-format000066400000000000000000000000231437610075600204410ustar00rootroot00000000000000BasedOnStyle: LLVM TartanLlama-optional-3a1209d/.gitignore000066400000000000000000000000361437610075600200620ustar00rootroot00000000000000\#* .\#* /build/ .idea cmake-*TartanLlama-optional-3a1209d/.travis.yml000066400000000000000000000134541437610075600202130ustar00rootroot00000000000000language: cpp dist: xenial matrix: include: - compiler: gcc addons: { apt: { sources: ubuntu-toolchain-r-test, packages: g++-4.8 } } env: COMPILER=g++-4.8 CXXSTD=11 - compiler: gcc addons: { apt: { sources: ubuntu-toolchain-r-test, packages: g++-4.9 } } env: COMPILER=g++-4.9 CXXSTD=11 - &gcc-5 compiler: gcc addons: { apt: { sources: ubuntu-toolchain-r-test, packages: g++-5 } } env: COMPILER=g++-5 CXXSTD=11 - &gcc-6 compiler: gcc addons: { apt: { sources: ubuntu-toolchain-r-test, packages: g++-6 } } env: COMPILER=g++-6 CXXSTD=11 - &gcc-7 compiler: gcc addons: { apt: { sources: ubuntu-toolchain-r-test, packages: g++-7 } } env: COMPILER=g++-7 CXXSTD=11 - &gcc-8 compiler: gcc addons: { apt: { sources: ubuntu-toolchain-r-test, packages: g++-8 } } env: COMPILER=g++-8 CXXSTD=11 - &gcc-9 compiler: gcc addons: { apt: { sources: ubuntu-toolchain-r-test, packages: g++-9 } } env: COMPILER=g++-9 CXXSTD=11 - &clang-35 compiler: clang addons: apt: sources: [llvm-toolchain-precise-3.5, ubuntu-toolchain-r-test] packages: [clang++-3.5, libc++-dev] env: COMPILER=clang++-3.5 CXXSTD=11 - &clang-36 compiler: clang addons: apt: sources: [llvm-toolchain-precise-3.6, ubuntu-toolchain-r-test] packages: [clang++-3.6, libc++-dev] env: COMPILER=clang++-3.6 CXXSTD=11 - &clang-37 compiler: clang addons: apt: sources: [llvm-toolchain-precise-3.7, ubuntu-toolchain-r-test] packages: [clang++-3.7, libc++-dev] env: COMPILER=clang++-3.7 CXXSTD=11 - &clang-38 compiler: clang addons: apt: sources: - &xenial-clang sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.8 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: [clang++-3.8, libc++-dev] env: COMPILER=clang++-3.8 CXXSTD=11 - &clang-39 compiler: clang addons: apt: sources: - <<: *xenial-clang sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main' - ubuntu-toolchain-r-test packages: [clang++-3.9, libc++-dev] env: COMPILER=clang++-3.9 CXXSTD=11 - &clang-4 compiler: clang addons: apt: sources: - <<: *xenial-clang sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main' - ubuntu-toolchain-r-test packages: [clang++-4.0, libc++-dev] env: COMPILER=clang++-4.0 CXXSTD=11 - &clang-5 compiler: clang addons: apt: sources: - <<: *xenial-clang sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-5.0 main' - ubuntu-toolchain-r-test packages: [clang++-5.0, libc++-dev] env: COMPILER=clang++-5.0 CXXSTD=11 - &clang-6 compiler: clang addons: apt: sources: [llvm-toolchain-xenial-6.0, ubuntu-toolchain-r-test] packages: [clang++-6.0, libc++-dev] env: COMPILER=clang++-6.0 CXXSTD=11 - &clang-7 compiler: clang addons: apt: sources: [llvm-toolchain-xenial-7, ubuntu-toolchain-r-test] packages: [clang++-7, libc++-7-dev, libc++abi-7-dev] env: COMPILER=clang++-7 CXXSTD=11 - &clang-8 compiler: clang addons: apt: sources: [llvm-toolchain-xenial-8, ubuntu-toolchain-r-test] packages: [clang++-8, libc++-8-dev, libc++abi-8-dev] env: COMPILER=clang++-8 CXXSTD=11 - &clang-9 compiler: clang addons: apt: sources: - <<: *xenial-clang sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' - ubuntu-toolchain-r-test packages: [clang++-9, libc++-9-dev, libc++abi-9-dev] env: COMPILER=clang++-9 CXXSTD=11 - { <<: *gcc-5, env: COMPILER=g++-5 CXXSTD=14 } - { <<: *gcc-6, env: COMPILER=g++-6 CXXSTD=14 } - { <<: *gcc-7, env: COMPILER=g++-7 CXXSTD=14 } - { <<: *gcc-8, env: COMPILER=g++-8 CXXSTD=14 } - { <<: *gcc-9, env: COMPILER=g++-9 CXXSTD=14 } - { <<: *gcc-7, env: COMPILER=g++-7 CXXSTD=17 } - { <<: *gcc-8, env: COMPILER=g++-8 CXXSTD=17 } - { <<: *gcc-9, env: COMPILER=g++-9 CXXSTD=17 } - { <<: *clang-35, env: COMPILER=clang++-3.5 CXXSTD=14 } - { <<: *clang-36, env: COMPILER=clang++-3.6 CXXSTD=14 } - { <<: *clang-37, env: COMPILER=clang++-3.7 CXXSTD=14 } - { <<: *clang-38, env: COMPILER=clang++-3.8 CXXSTD=14 } - { <<: *clang-39, env: COMPILER=clang++-3.9 CXXSTD=14 } - { <<: *clang-4, env: COMPILER=clang++-4.0 CXXSTD=14 } - { <<: *clang-5, env: COMPILER=clang++-5.0 CXXSTD=14 } - { <<: *clang-6, env: COMPILER=clang++-6.0 CXXSTD=14 } - { <<: *clang-7, env: COMPILER=clang++-7 CXXSTD=14 } - { <<: *clang-8, env: COMPILER=clang++-8 CXXSTD=14 } - { <<: *clang-9, env: COMPILER=clang++-9 CXXSTD=14 } - { <<: *clang-7, env: COMPILER=clang++-7 CXXSTD=17 } - { <<: *clang-8, env: COMPILER=clang++-8 CXXSTD=17 } - { <<: *clang-9, env: COMPILER=clang++-9 CXXSTD=17 } before_install: - sudo apt update - sudo apt install -y apt-transport-https ca-certificates gnupg software-properties-common - curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | sudo apt-key add - - sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ xenial main' - sudo apt update install: - sudo apt install -y cmake script: - /usr/bin/cmake -B build -S . "-DCMAKE_CXX_STANDARD=$CXXSTD" "-DCMAKE_CXX_COMPILER=$COMPILER" - /usr/bin/cmake --build build - /usr/bin/cmake --build build --target test TartanLlama-optional-3a1209d/CMakeLists.txt000066400000000000000000000105711437610075600206370ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.14) project(tl-optional HOMEPAGE_URL https://tl.tartanllama.xyz DESCRIPTION "C++11/14/17 std::optional with functional-style extensions and reference support" VERSION 1.0.0 LANGUAGES CXX) include(CMakePackageConfigHelpers) include(CMakeDependentOption) include(GNUInstallDirs) include(FetchContent) include(CTest) find_program(DPKG_BUILDPACKAGE_FOUND dpkg-buildpackage) find_program(RPMBUILD_FOUND rpmbuild) if (NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() option(OPTIONAL_BUILD_PACKAGE "Build packages" ON) cmake_dependent_option(OPTIONAL_BUILD_TESTS "Enable tl::optional tests" ON "BUILD_TESTING" OFF) cmake_dependent_option(OPTIONAL_BUILD_PACKAGE_DEB "Create DEB Package (${PROJECT_NAME})" ON "OPTIONAL_BUILD_PACKAGE;DPKG_BUILDPACKAGE_FOUND" OFF) cmake_dependent_option(OPTIONAL_BUILD_PACKAGE_RPM "Create RPM Package (${PROJECT_NAME})" ON "OPTIONAL_BUILD_PACKAGE;RPMBUILD_FOUND" OFF) cmake_dependent_option(OPTIONAL_BUILD_PACKAGE_MSI "Create MSI (${PROJECT_NAME})" ON "OPTIONAL_BUILD_PACKAGE;CMAKE_HOST_WIN32" OFF) add_library(optional INTERFACE) target_include_directories(optional INTERFACE $ $) if (NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) add_library(tl::optional ALIAS optional) endif() configure_package_config_file( "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake.in" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}") write_basic_package_version_file( "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT) install(TARGETS optional EXPORT ${PROJECT_NAME}-targets) install(EXPORT ${PROJECT_NAME}-targets DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}" NAMESPACE tl:: FILE "${PROJECT_NAME}-targets.cmake") install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}") install(DIRECTORY "include/" TYPE INCLUDE) if (OPTIONAL_BUILD_TESTS) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CATCH_INSTALL_HELPERS OFF) set(CATCH_BUILD_TESTING OFF) set(CATCH_INSTALL_DOCS OFF) FetchContent_Declare(Catch2 URL https://github.com/catchorg/Catch2/archive/v2.9.2.zip) FetchContent_MakeAvailable(Catch2) file(GENERATE OUTPUT catch.main.cpp CONTENT [[ #define CATCH_CONFIG_MAIN #include ]]) set_property(SOURCE ${PROJECT_BINARY_DIR}/catch.main.cpp PROPERTY GENERATED ON) add_library(${PROJECT_NAME}-catch-main OBJECT) target_sources(${PROJECT_NAME}-catch-main PRIVATE ${PROJECT_BINARY_DIR}/catch.main.cpp) target_compile_options(${PROJECT_NAME}-catch-main PUBLIC $<$,$>:-stdlib=libc++> $<$>:-Wall -Wextra>) target_link_options(${PROJECT_NAME}-catch-main PUBLIC $<$,$>:-stdlib=libc++>) target_link_libraries(${PROJECT_NAME}-catch-main PUBLIC Catch2::Catch2 optional) file(GLOB test-sources CONFIGURE_DEPENDS tests/*.cpp) foreach (source IN LISTS test-sources) get_filename_component(name "${source}" NAME_WE) set(test "${PROJECT_NAME}-test-${name}") add_executable(${test} "${source}" $) target_link_libraries(${test} PRIVATE ${PROJECT_NAME}-catch-main) add_test(NAME ${PROJECT_NAME}::test::${name} COMMAND ${test}) endforeach() endif() if (NOT OPTIONAL_BUILD_PACKAGE) return() endif() list(APPEND source-generators TBZ2 TGZ TXZ ZIP) if (OPTIONAL_BUILD_PACKAGE_MSI) list(APPEND binary-generators "WIX") endif() if (OPTIONAL_BUILD_PACKAGE_DEB) list(APPEND binary-generators "DEB") endif() if (OPTIONAL_BUILD_PACKAGE_RPM) list(APPEND binary-generators "RPM") endif() set(CPACK_SOURCE_GENERATOR ${source-generators}) set(CPACK_GENERATOR ${binary-generators}) set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILENAME}") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sy Brand") list(APPEND CPACK_SOURCE_IGNORE_FILES /.git/ /build/ .gitignore .DS_Store) include(CPack) TartanLlama-optional-3a1209d/COPYING000066400000000000000000000156101437610075600171310ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. TartanLlama-optional-3a1209d/README.md000066400000000000000000000104221437610075600173510ustar00rootroot00000000000000# optional Single header implementation of `std::optional` with functional-style extensions and support for references. [![Documentation Status](https://readthedocs.org/projects/tl-docs/badge/?version=latest)](https://tl.tartanllama.xyz/en/latest/?badge=latest) Clang + GCC: [![Linux Build Status](https://travis-ci.org/TartanLlama/optional.png?branch=master)](https://travis-ci.org/TartanLlama/optional) MSVC: [![Windows Build Status](https://ci.appveyor.com/api/projects/status/k5x00xa11y3s5wsg?svg=true)](https://ci.appveyor.com/project/TartanLlama/optional) `std::optional` is the preferred way to represent an object which may or may not have a value. Unfortunately, chaining together many computations which may or may not produce a value can be verbose, as empty-checking code will be mixed in with the actual programming logic. This implementation provides a number of utilities to make coding with `optional` cleaner. For example, instead of writing this code: ```c++ std::optional get_cute_cat (const image& img) { auto cropped = crop_to_cat(img); if (!cropped) { return std::nullopt; } auto with_tie = add_bow_tie(*cropped); if (!with_tie) { return std::nullopt; } auto with_sparkles = make_eyes_sparkle(*with_tie); if (!with_sparkles) { return std::nullopt; } return add_rainbow(make_smaller(*with_sparkles)); } ``` You can do this: ```c++ tl::optional get_cute_cat (const image& img) { return crop_to_cat(img) .and_then(add_bow_tie) .and_then(make_eyes_sparkle) .map(make_smaller) .map(add_rainbow); } ``` The interface is the same as `std::optional`, but the following member functions are also defined. Explicit types are for clarity. - `map`: carries out some operation on the stored object if there is one. * `tl::optional s = opt_string.map(&std::string::size);` - `and_then`: like `map`, but for operations which return a `tl::optional`. * `tl::optional stoi (const std::string& s);` * `tl::optional i = opt_string.and_then(stoi);` - `or_else`: calls some function if there is no value stored. * `opt.or_else([] { throw std::runtime_error{"oh no"}; });` - `map_or`: carries out a `map` if there is a value, otherwise returns a default value. * `tl::optional s = opt_string.map_or(&std::string::size, 0);` - `map_or_else`: carries out a `map` if there is a value, otherwise returns the result of a given default function. * `std::size_t get_default();` * `tl::optional s = opt_string.map_or_else(&std::string::size, get_default);` - `conjunction`: returns the argument if a value is stored in the optional, otherwise an empty optional. * `tl::make_optional(42).conjunction(13); //13` * `tl::optional{}.conjunction(13); //empty` - `disjunction`: returns the argument if the optional is empty, otherwise the current value. * `tl::make_optional(42).disjunction(13); //42` * `tl::optional{}.disjunction(13); //13` - `take`: returns the current value, leaving the optional empty. * `opt_string.take().map(&std::string::size); //opt_string now empty;` In addition to those member functions, optional references are also supported: ```c++ int i = 42; tl::optional o = i; *o == 42; //true i = 12; *o == 12; //true &*o == &i; //true ``` Assignment has rebind semantics rather than assign-through semantics: ```c++ int j = 8; o = j; &*o == &j; //true ``` ### Compiler support Tested on: - Linux * clang 6.0.1 * clang 5.0.2 * clang 4.0.1 * clang 3.9 * clang 3.8 * clang 3.7 * clang 3.6 * clang 3.5 * g++ 8.0.1 * g++ 7.3 * g++ 6.4 * g++ 5.5 * g++ 4.9 * g++ 4.8 - Windows * MSVC 2015 * MSVC 2017 ### Standards Proposal This library also serves as an implementation of WG21 standards paper [P0798R0: Monadic operations for std::optional](https://wg21.tartanllama.xyz/monadic-optional). This paper proposes adding `map`, `and_then`, and `or_else` to `std::optional`. ---------- [![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)]("http://creativecommons.org/publicdomain/zero/1.0/") To the extent possible under law, [Sy Brand](https://twitter.com/TartanLlama) has waived all copyright and related or neighboring rights to the `optional` library. This work is published from: United Kingdom. TartanLlama-optional-3a1209d/cmake/000077500000000000000000000000001437610075600171535ustar00rootroot00000000000000TartanLlama-optional-3a1209d/cmake/tl-optional-config.cmake.in000066400000000000000000000001201437610075600242600ustar00rootroot00000000000000@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/tl-optional-targets.cmake")TartanLlama-optional-3a1209d/include/000077500000000000000000000000001437610075600175165ustar00rootroot00000000000000TartanLlama-optional-3a1209d/include/tl/000077500000000000000000000000001437610075600201355ustar00rootroot00000000000000TartanLlama-optional-3a1209d/include/tl/optional.hpp000066400000000000000000002175601437610075600225060ustar00rootroot00000000000000 /// // optional - An implementation of std::optional with extensions // Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) // // Documentation available at https://tl.tartanllama.xyz/ // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to the // public domain worldwide. This software is distributed without any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. If not, see // . /// #ifndef TL_OPTIONAL_HPP #define TL_OPTIONAL_HPP #define TL_OPTIONAL_VERSION_MAJOR 1 #define TL_OPTIONAL_VERSION_MINOR 1 #define TL_OPTIONAL_VERSION_PATCH 0 #include #include #include #include #include #if (defined(_MSC_VER) && _MSC_VER == 1900) #define TL_OPTIONAL_MSVC2015 #endif #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) #define TL_OPTIONAL_GCC49 #endif #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ !defined(__clang__)) #define TL_OPTIONAL_GCC54 #endif #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ !defined(__clang__)) #define TL_OPTIONAL_GCC55 #endif #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) // GCC < 5 doesn't support overloading on const&& for member functions #define TL_OPTIONAL_NO_CONSTRR // GCC < 5 doesn't support some standard C++11 type traits #define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ std::has_trivial_copy_constructor::value #define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign::value // This one will be different for GCC 5.7 if it's ever supported #define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value // GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector // for non-copyable types #elif (defined(__GNUC__) && __GNUC__ < 8 && \ !defined(__clang__)) #ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX #define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX namespace tl { namespace detail { template struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{}; #ifdef _GLIBCXX_VECTOR template struct is_trivially_copy_constructible> : std::is_trivially_copy_constructible{}; #endif } } #endif #define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ tl::detail::is_trivially_copy_constructible::value #define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable::value #define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value #else #define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ std::is_trivially_copy_constructible::value #define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable::value #define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value #endif #if __cplusplus > 201103L #define TL_OPTIONAL_CXX14 #endif // constexpr implies const in C++11, not C++14 #if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || \ defined(TL_OPTIONAL_GCC49)) #define TL_OPTIONAL_11_CONSTEXPR #else #define TL_OPTIONAL_11_CONSTEXPR constexpr #endif namespace tl { #ifndef TL_MONOSTATE_INPLACE_MUTEX #define TL_MONOSTATE_INPLACE_MUTEX /// Used to represent an optional with no data; essentially a bool class monostate {}; /// A tag type to tell optional to construct its value in-place struct in_place_t { explicit in_place_t() = default; }; /// A tag to tell optional to construct its value in-place static constexpr in_place_t in_place{}; #endif template class optional; namespace detail { #ifndef TL_TRAITS_MUTEX #define TL_TRAITS_MUTEX // C++14-style aliases for brevity template using remove_const_t = typename std::remove_const::type; template using remove_reference_t = typename std::remove_reference::type; template using decay_t = typename std::decay::type; template using enable_if_t = typename std::enable_if::type; template using conditional_t = typename std::conditional::type; // std::conjunction from C++17 template struct conjunction : std::true_type {}; template struct conjunction : B {}; template struct conjunction : std::conditional, B>::type {}; #if defined(_LIBCPP_VERSION) && __cplusplus == 201103L #define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND #endif // In C++11 mode, there's an issue in libc++'s std::mem_fn // which results in a hard-error when using it in a noexcept expression // in some cases. This is a check to workaround the common failing case. #ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND template struct is_pointer_to_non_const_member_func : std::false_type{}; template struct is_pointer_to_non_const_member_func : std::true_type{}; template struct is_pointer_to_non_const_member_func : std::true_type{}; template struct is_pointer_to_non_const_member_func : std::true_type{}; template struct is_pointer_to_non_const_member_func : std::true_type{}; template struct is_pointer_to_non_const_member_func : std::true_type{}; template struct is_pointer_to_non_const_member_func : std::true_type{}; template struct is_const_or_const_ref : std::false_type{}; template struct is_const_or_const_ref : std::true_type{}; template struct is_const_or_const_ref : std::true_type{}; #endif // std::invoke from C++17 // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround template ::value && is_const_or_const_ref::value)>, #endif typename = enable_if_t>::value>, int = 0> constexpr auto invoke(Fn &&f, Args &&... args) noexcept( noexcept(std::mem_fn(f)(std::forward(args)...))) -> decltype(std::mem_fn(f)(std::forward(args)...)) { return std::mem_fn(f)(std::forward(args)...); } template >::value>> constexpr auto invoke(Fn &&f, Args &&... args) noexcept( noexcept(std::forward(f)(std::forward(args)...))) -> decltype(std::forward(f)(std::forward(args)...)) { return std::forward(f)(std::forward(args)...); } // std::invoke_result from C++17 template struct invoke_result_impl; template struct invoke_result_impl< F, decltype(detail::invoke(std::declval(), std::declval()...), void()), Us...> { using type = decltype(detail::invoke(std::declval(), std::declval()...)); }; template using invoke_result = invoke_result_impl; template using invoke_result_t = typename invoke_result::type; #if defined(_MSC_VER) && _MSC_VER <= 1900 // TODO make a version which works with MSVC 2015 template struct is_swappable : std::true_type {}; template struct is_nothrow_swappable : std::true_type {}; #else // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept namespace swap_adl_tests { // if swap ADL finds this then it would call std::swap otherwise (same // signature) struct tag {}; template tag swap(T &, T &); template tag swap(T (&a)[N], T (&b)[N]); // helper functions to test if an unqualified swap is possible, and if it // becomes std::swap template std::false_type can_swap(...) noexcept(false); template (), std::declval()))> std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), std::declval()))); template std::false_type uses_std(...); template std::is_same(), std::declval())), tag> uses_std(int); template struct is_std_swap_noexcept : std::integral_constant::value && std::is_nothrow_move_assignable::value> {}; template struct is_std_swap_noexcept : is_std_swap_noexcept {}; template struct is_adl_swap_noexcept : std::integral_constant(0))> {}; } // namespace swap_adl_tests template struct is_swappable : std::integral_constant< bool, decltype(detail::swap_adl_tests::can_swap(0))::value && (!decltype(detail::swap_adl_tests::uses_std(0))::value || (std::is_move_assignable::value && std::is_move_constructible::value))> {}; template struct is_swappable : std::integral_constant< bool, decltype(detail::swap_adl_tests::can_swap(0))::value && (!decltype( detail::swap_adl_tests::uses_std(0))::value || is_swappable::value)> {}; template struct is_nothrow_swappable : std::integral_constant< bool, is_swappable::value && ((decltype(detail::swap_adl_tests::uses_std(0))::value &&detail::swap_adl_tests::is_std_swap_noexcept::value) || (!decltype(detail::swap_adl_tests::uses_std(0))::value && detail::swap_adl_tests::is_adl_swap_noexcept::value))> { }; #endif #endif // std::void_t from C++17 template struct voider { using type = void; }; template using void_t = typename voider::type; // Trait for checking if a type is a tl::optional template struct is_optional_impl : std::false_type {}; template struct is_optional_impl> : std::true_type {}; template using is_optional = is_optional_impl>; // Change void to tl::monostate template using fixup_void = conditional_t::value, monostate, U>; template > using get_map_return = optional>>; // Check if invoking F for some Us returns void template struct returns_void_impl; template struct returns_void_impl>, U...> : std::is_void> {}; template using returns_void = returns_void_impl; template using enable_if_ret_void = enable_if_t::value>; template using disable_if_ret_void = enable_if_t::value>; template using enable_forward_value = detail::enable_if_t::value && !std::is_same, in_place_t>::value && !std::is_same, detail::decay_t>::value>; template using enable_from_other = detail::enable_if_t< std::is_constructible::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value>; template using enable_assign_forward = detail::enable_if_t< !std::is_same, detail::decay_t>::value && !detail::conjunction, std::is_same>>::value && std::is_constructible::value && std::is_assignable::value>; template using enable_assign_from_other = detail::enable_if_t< std::is_constructible::value && std::is_assignable::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value && !std::is_assignable &>::value && !std::is_assignable &&>::value && !std::is_assignable &>::value && !std::is_assignable &&>::value>; // The storage base manages the actual storage, and correctly propagates // trivial destruction from T. This case is for when T is not trivially // destructible. template ::value> struct optional_storage_base { TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} template TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) : m_value(std::forward(u)...), m_has_value(true) {} ~optional_storage_base() { if (m_has_value) { m_value.~T(); m_has_value = false; } } struct dummy {}; union { dummy m_dummy; T m_value; }; bool m_has_value; }; // This case is for when T is trivially destructible. template struct optional_storage_base { TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} template TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) : m_value(std::forward(u)...), m_has_value(true) {} // No destructor, so this class is trivially destructible struct dummy {}; union { dummy m_dummy; T m_value; }; bool m_has_value = false; }; // This base class provides some handy member functions which can be used in // further derived classes template struct optional_operations_base : optional_storage_base { using optional_storage_base::optional_storage_base; void hard_reset() noexcept { get().~T(); this->m_has_value = false; } template void construct(Args &&... args) { new (std::addressof(this->m_value)) T(std::forward(args)...); this->m_has_value = true; } template void assign(Opt &&rhs) { if (this->has_value()) { if (rhs.has_value()) { this->m_value = std::forward(rhs).get(); } else { this->m_value.~T(); this->m_has_value = false; } } else if (rhs.has_value()) { construct(std::forward(rhs).get()); } } bool has_value() const { return this->m_has_value; } TL_OPTIONAL_11_CONSTEXPR T &get() & { return this->m_value; } TL_OPTIONAL_11_CONSTEXPR const T &get() const & { return this->m_value; } TL_OPTIONAL_11_CONSTEXPR T &&get() && { return std::move(this->m_value); } #ifndef TL_OPTIONAL_NO_CONSTRR constexpr const T &&get() const && { return std::move(this->m_value); } #endif }; // This class manages conditionally having a trivial copy constructor // This specialization is for when T is trivially copy constructible template struct optional_copy_base : optional_operations_base { using optional_operations_base::optional_operations_base; }; // This specialization is for when T is not trivially copy constructible template struct optional_copy_base : optional_operations_base { using optional_operations_base::optional_operations_base; optional_copy_base() = default; optional_copy_base(const optional_copy_base &rhs) : optional_operations_base() { if (rhs.has_value()) { this->construct(rhs.get()); } else { this->m_has_value = false; } } optional_copy_base(optional_copy_base &&rhs) = default; optional_copy_base &operator=(const optional_copy_base &rhs) = default; optional_copy_base &operator=(optional_copy_base &&rhs) = default; }; // This class manages conditionally having a trivial move constructor // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it // doesn't implement an analogue to std::is_trivially_move_constructible. We // have to make do with a non-trivial move constructor even if T is trivially // move constructible #ifndef TL_OPTIONAL_GCC49 template ::value> struct optional_move_base : optional_copy_base { using optional_copy_base::optional_copy_base; }; #else template struct optional_move_base; #endif template struct optional_move_base : optional_copy_base { using optional_copy_base::optional_copy_base; optional_move_base() = default; optional_move_base(const optional_move_base &rhs) = default; optional_move_base(optional_move_base &&rhs) noexcept( std::is_nothrow_move_constructible::value) { if (rhs.has_value()) { this->construct(std::move(rhs.get())); } else { this->m_has_value = false; } } optional_move_base &operator=(const optional_move_base &rhs) = default; optional_move_base &operator=(optional_move_base &&rhs) = default; }; // This class manages conditionally having a trivial copy assignment operator template struct optional_copy_assign_base : optional_move_base { using optional_move_base::optional_move_base; }; template struct optional_copy_assign_base : optional_move_base { using optional_move_base::optional_move_base; optional_copy_assign_base() = default; optional_copy_assign_base(const optional_copy_assign_base &rhs) = default; optional_copy_assign_base(optional_copy_assign_base &&rhs) = default; optional_copy_assign_base &operator=(const optional_copy_assign_base &rhs) { this->assign(rhs); return *this; } optional_copy_assign_base & operator=(optional_copy_assign_base &&rhs) = default; }; // This class manages conditionally having a trivial move assignment operator // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it // doesn't implement an analogue to std::is_trivially_move_assignable. We have // to make do with a non-trivial move assignment operator even if T is trivially // move assignable #ifndef TL_OPTIONAL_GCC49 template ::value &&std::is_trivially_move_constructible::value &&std::is_trivially_move_assignable::value> struct optional_move_assign_base : optional_copy_assign_base { using optional_copy_assign_base::optional_copy_assign_base; }; #else template struct optional_move_assign_base; #endif template struct optional_move_assign_base : optional_copy_assign_base { using optional_copy_assign_base::optional_copy_assign_base; optional_move_assign_base() = default; optional_move_assign_base(const optional_move_assign_base &rhs) = default; optional_move_assign_base(optional_move_assign_base &&rhs) = default; optional_move_assign_base & operator=(const optional_move_assign_base &rhs) = default; optional_move_assign_base & operator=(optional_move_assign_base &&rhs) noexcept( std::is_nothrow_move_constructible::value &&std::is_nothrow_move_assignable::value) { this->assign(std::move(rhs)); return *this; } }; // optional_delete_ctor_base will conditionally delete copy and move // constructors depending on whether T is copy/move constructible template ::value, bool EnableMove = std::is_move_constructible::value> struct optional_delete_ctor_base { optional_delete_ctor_base() = default; optional_delete_ctor_base(const optional_delete_ctor_base &) = default; optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; optional_delete_ctor_base & operator=(const optional_delete_ctor_base &) = default; optional_delete_ctor_base & operator=(optional_delete_ctor_base &&) noexcept = default; }; template struct optional_delete_ctor_base { optional_delete_ctor_base() = default; optional_delete_ctor_base(const optional_delete_ctor_base &) = default; optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; optional_delete_ctor_base & operator=(const optional_delete_ctor_base &) = default; optional_delete_ctor_base & operator=(optional_delete_ctor_base &&) noexcept = default; }; template struct optional_delete_ctor_base { optional_delete_ctor_base() = default; optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; optional_delete_ctor_base & operator=(const optional_delete_ctor_base &) = default; optional_delete_ctor_base & operator=(optional_delete_ctor_base &&) noexcept = default; }; template struct optional_delete_ctor_base { optional_delete_ctor_base() = default; optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; optional_delete_ctor_base & operator=(const optional_delete_ctor_base &) = default; optional_delete_ctor_base & operator=(optional_delete_ctor_base &&) noexcept = default; }; // optional_delete_assign_base will conditionally delete copy and move // constructors depending on whether T is copy/move constructible + assignable template ::value && std::is_copy_assignable::value), bool EnableMove = (std::is_move_constructible::value && std::is_move_assignable::value)> struct optional_delete_assign_base { optional_delete_assign_base() = default; optional_delete_assign_base(const optional_delete_assign_base &) = default; optional_delete_assign_base(optional_delete_assign_base &&) noexcept = default; optional_delete_assign_base & operator=(const optional_delete_assign_base &) = default; optional_delete_assign_base & operator=(optional_delete_assign_base &&) noexcept = default; }; template struct optional_delete_assign_base { optional_delete_assign_base() = default; optional_delete_assign_base(const optional_delete_assign_base &) = default; optional_delete_assign_base(optional_delete_assign_base &&) noexcept = default; optional_delete_assign_base & operator=(const optional_delete_assign_base &) = default; optional_delete_assign_base & operator=(optional_delete_assign_base &&) noexcept = delete; }; template struct optional_delete_assign_base { optional_delete_assign_base() = default; optional_delete_assign_base(const optional_delete_assign_base &) = default; optional_delete_assign_base(optional_delete_assign_base &&) noexcept = default; optional_delete_assign_base & operator=(const optional_delete_assign_base &) = delete; optional_delete_assign_base & operator=(optional_delete_assign_base &&) noexcept = default; }; template struct optional_delete_assign_base { optional_delete_assign_base() = default; optional_delete_assign_base(const optional_delete_assign_base &) = default; optional_delete_assign_base(optional_delete_assign_base &&) noexcept = default; optional_delete_assign_base & operator=(const optional_delete_assign_base &) = delete; optional_delete_assign_base & operator=(optional_delete_assign_base &&) noexcept = delete; }; } // namespace detail /// A tag type to represent an empty optional struct nullopt_t { struct do_not_use {}; constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {} }; /// Represents an empty optional static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, nullopt_t::do_not_use{}}; class bad_optional_access : public std::exception { public: bad_optional_access() = default; const char *what() const noexcept { return "Optional has no value"; } }; /// An optional object is an object that contains the storage for another /// object and manages the lifetime of this contained object, if any. The /// contained object may be initialized after the optional object has been /// initialized, and may be destroyed before the optional object has been /// destroyed. The initialization state of the contained object is tracked by /// the optional object. template class optional : private detail::optional_move_assign_base, private detail::optional_delete_ctor_base, private detail::optional_delete_assign_base { using base = detail::optional_move_assign_base; static_assert(!std::is_same::value, "instantiation of optional with in_place_t is ill-formed"); static_assert(!std::is_same, nullopt_t>::value, "instantiation of optional with nullopt_t is ill-formed"); public: // The different versions for C++14 and 11 are needed because deduced return // types are not SFINAE-safe. This provides better support for things like // generic lambdas. C.f. // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) /// Carries out some operation which returns an optional on the stored /// object if there is one. template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } template constexpr auto and_then(F &&f) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr auto and_then(F &&f) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } #endif #else /// Carries out some operation which returns an optional on the stored /// object if there is one. template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } template constexpr detail::invoke_result_t and_then(F &&f) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr detail::invoke_result_t and_then(F &&f) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } #endif #endif #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { return optional_map_impl(*this, std::forward(f)); } template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { return optional_map_impl(std::move(*this), std::forward(f)); } template constexpr auto map(F &&f) const & { return optional_map_impl(*this, std::forward(f)); } template constexpr auto map(F &&f) const && { return optional_map_impl(std::move(*this), std::forward(f)); } #else /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), std::declval())) map(F &&f) & { return optional_map_impl(*this, std::forward(f)); } template TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), std::declval())) map(F &&f) && { return optional_map_impl(std::move(*this), std::forward(f)); } template constexpr decltype(optional_map_impl(std::declval(), std::declval())) map(F &&f) const & { return optional_map_impl(*this, std::forward(f)); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr decltype(optional_map_impl(std::declval(), std::declval())) map(F &&f) const && { return optional_map_impl(std::move(*this), std::forward(f)); } #endif #endif #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & { return optional_map_impl(*this, std::forward(f)); } template TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && { return optional_map_impl(std::move(*this), std::forward(f)); } template constexpr auto transform(F&& f) const & { return optional_map_impl(*this, std::forward(f)); } template constexpr auto transform(F&& f) const && { return optional_map_impl(std::move(*this), std::forward(f)); } #else /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), std::declval())) transform(F&& f) & { return optional_map_impl(*this, std::forward(f)); } template TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), std::declval())) transform(F&& f) && { return optional_map_impl(std::move(*this), std::forward(f)); } template constexpr decltype(optional_map_impl(std::declval(), std::declval())) transform(F&& f) const & { return optional_map_impl(*this, std::forward(f)); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr decltype(optional_map_impl(std::declval(), std::declval())) transform(F&& f) const && { return optional_map_impl(std::move(*this), std::forward(f)); } #endif #endif /// Calls `f` if the optional is empty template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { if (has_value()) return *this; std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { return has_value() ? *this : std::forward(f)(); } template * = nullptr> optional or_else(F &&f) && { if (has_value()) return std::move(*this); std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { return has_value() ? std::move(*this) : std::forward(f)(); } template * = nullptr> optional or_else(F &&f) const & { if (has_value()) return *this; std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { return has_value() ? *this : std::forward(f)(); } #ifndef TL_OPTIONAL_NO_CONSTRR template * = nullptr> optional or_else(F &&f) const && { if (has_value()) return std::move(*this); std::forward(f)(); return nullopt; } template * = nullptr> optional or_else(F &&f) const && { return has_value() ? std::move(*this) : std::forward(f)(); } #endif /// Maps the stored value with `f` if there is one, otherwise returns `u`. template U map_or(F &&f, U &&u) & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); } template U map_or(F &&f, U &&u) && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); } template U map_or(F &&f, U &&u) const & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); } #ifndef TL_OPTIONAL_NO_CONSTRR template U map_or(F &&f, U &&u) const && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); } #endif /// Maps the stored value with `f` if there is one, otherwise calls /// `u` and returns the result. template detail::invoke_result_t map_or_else(F &&f, U &&u) & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); } template detail::invoke_result_t map_or_else(F &&f, U &&u) && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); } template detail::invoke_result_t map_or_else(F &&f, U &&u) const & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); } #ifndef TL_OPTIONAL_NO_CONSTRR template detail::invoke_result_t map_or_else(F &&f, U &&u) const && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); } #endif /// Returns `u` if `*this` has a value, otherwise an empty optional. template constexpr optional::type> conjunction(U &&u) const { using result = optional>; return has_value() ? result{u} : result{nullopt}; } /// Returns `rhs` if `*this` is empty, otherwise the current value. TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { return has_value() ? *this : rhs; } constexpr optional disjunction(const optional &rhs) const & { return has_value() ? *this : rhs; } TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { return has_value() ? std::move(*this) : rhs; } #ifndef TL_OPTIONAL_NO_CONSTRR constexpr optional disjunction(const optional &rhs) const && { return has_value() ? std::move(*this) : rhs; } #endif TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { return has_value() ? *this : std::move(rhs); } constexpr optional disjunction(optional &&rhs) const & { return has_value() ? *this : std::move(rhs); } TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { return has_value() ? std::move(*this) : std::move(rhs); } #ifndef TL_OPTIONAL_NO_CONSTRR constexpr optional disjunction(optional &&rhs) const && { return has_value() ? std::move(*this) : std::move(rhs); } #endif /// Takes the value out of the optional, leaving it empty optional take() { optional ret = std::move(*this); reset(); return ret; } using value_type = T; /// Constructs an optional that does not contain a value. constexpr optional() noexcept = default; constexpr optional(nullopt_t) noexcept {} /// Copy constructor /// /// If `rhs` contains a value, the stored value is direct-initialized with /// it. Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default; /// Move constructor /// /// If `rhs` contains a value, the stored value is direct-initialized with /// it. Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; /// Constructs the stored value in-place using the given arguments. template constexpr explicit optional( detail::enable_if_t::value, in_place_t>, Args &&... args) : base(in_place, std::forward(args)...) {} template TL_OPTIONAL_11_CONSTEXPR explicit optional( detail::enable_if_t &, Args &&...>::value, in_place_t>, std::initializer_list il, Args &&... args) { this->construct(il, std::forward(args)...); } /// Constructs the stored value with `u`. template < class U = T, detail::enable_if_t::value> * = nullptr, detail::enable_forward_value * = nullptr> constexpr optional(U &&u) : base(in_place, std::forward(u)) {} template < class U = T, detail::enable_if_t::value> * = nullptr, detail::enable_forward_value * = nullptr> constexpr explicit optional(U &&u) : base(in_place, std::forward(u)) {} /// Converting copy constructor. template < class U, detail::enable_from_other * = nullptr, detail::enable_if_t::value> * = nullptr> optional(const optional &rhs) { if (rhs.has_value()) { this->construct(*rhs); } } template * = nullptr, detail::enable_if_t::value> * = nullptr> explicit optional(const optional &rhs) { if (rhs.has_value()) { this->construct(*rhs); } } /// Converting move constructor. template < class U, detail::enable_from_other * = nullptr, detail::enable_if_t::value> * = nullptr> optional(optional &&rhs) { if (rhs.has_value()) { this->construct(std::move(*rhs)); } } template < class U, detail::enable_from_other * = nullptr, detail::enable_if_t::value> * = nullptr> explicit optional(optional &&rhs) { if (rhs.has_value()) { this->construct(std::move(*rhs)); } } /// Destroys the stored value if there is one. ~optional() = default; /// Assignment to empty. /// /// Destroys the current value if there is one. optional &operator=(nullopt_t) noexcept { if (has_value()) { this->m_value.~T(); this->m_has_value = false; } return *this; } /// Copy assignment. /// /// Copies the value from `rhs` if there is one. Otherwise resets the stored /// value in `*this`. optional &operator=(const optional &rhs) = default; /// Move assignment. /// /// Moves the value from `rhs` if there is one. Otherwise resets the stored /// value in `*this`. optional &operator=(optional &&rhs) = default; /// Assigns the stored value from `u`, destroying the old value if there was /// one. template * = nullptr> optional &operator=(U &&u) { if (has_value()) { this->m_value = std::forward(u); } else { this->construct(std::forward(u)); } return *this; } /// Converting copy assignment operator. /// /// Copies the value from `rhs` if there is one. Otherwise resets the stored /// value in `*this`. template * = nullptr> optional &operator=(const optional &rhs) { if (has_value()) { if (rhs.has_value()) { this->m_value = *rhs; } else { this->hard_reset(); } } else if (rhs.has_value()) { this->construct(*rhs); } return *this; } // TODO check exception guarantee /// Converting move assignment operator. /// /// Moves the value from `rhs` if there is one. Otherwise resets the stored /// value in `*this`. template * = nullptr> optional &operator=(optional &&rhs) { if (has_value()) { if (rhs.has_value()) { this->m_value = std::move(*rhs); } else { this->hard_reset(); } } else if (rhs.has_value()) { this->construct(std::move(*rhs)); } return *this; } /// Constructs the value in-place, destroying the current one if there is /// one. template T &emplace(Args &&... args) { static_assert(std::is_constructible::value, "T must be constructible with Args"); *this = nullopt; this->construct(std::forward(args)...); return value(); } template detail::enable_if_t< std::is_constructible &, Args &&...>::value, T &> emplace(std::initializer_list il, Args &&... args) { *this = nullopt; this->construct(il, std::forward(args)...); return value(); } /// Swaps this optional with the other. /// /// If neither optionals have a value, nothing happens. /// If both have a value, the values are swapped. /// If one has a value, it is moved to the other and the movee is left /// valueless. void swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value &&detail::is_nothrow_swappable::value) { using std::swap; if (has_value()) { if (rhs.has_value()) { swap(**this, *rhs); } else { new (std::addressof(rhs.m_value)) T(std::move(this->m_value)); this->m_value.T::~T(); } } else if (rhs.has_value()) { new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); rhs.m_value.T::~T(); } swap(this->m_has_value, rhs.m_has_value); } /// Returns a pointer to the stored value constexpr const T *operator->() const { return std::addressof(this->m_value); } TL_OPTIONAL_11_CONSTEXPR T *operator->() { return std::addressof(this->m_value); } /// Returns the stored value TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } constexpr const T &operator*() const & { return this->m_value; } TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { return std::move(this->m_value); } #ifndef TL_OPTIONAL_NO_CONSTRR constexpr const T &&operator*() const && { return std::move(this->m_value); } #endif /// Returns whether or not the optional has a value constexpr bool has_value() const noexcept { return this->m_has_value; } constexpr explicit operator bool() const noexcept { return this->m_has_value; } /// Returns the contained value if there is one, otherwise throws bad_optional_access TL_OPTIONAL_11_CONSTEXPR T &value() & { if (has_value()) return this->m_value; throw bad_optional_access(); } TL_OPTIONAL_11_CONSTEXPR const T &value() const & { if (has_value()) return this->m_value; throw bad_optional_access(); } TL_OPTIONAL_11_CONSTEXPR T &&value() && { if (has_value()) return std::move(this->m_value); throw bad_optional_access(); } #ifndef TL_OPTIONAL_NO_CONSTRR TL_OPTIONAL_11_CONSTEXPR const T &&value() const && { if (has_value()) return std::move(this->m_value); throw bad_optional_access(); } #endif /// Returns the stored value if there is one, otherwise returns `u` template constexpr T value_or(U &&u) const & { static_assert(std::is_copy_constructible::value && std::is_convertible::value, "T must be copy constructible and convertible from U"); return has_value() ? **this : static_cast(std::forward(u)); } template TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && { static_assert(std::is_move_constructible::value && std::is_convertible::value, "T must be move constructible and convertible from U"); return has_value() ? std::move(**this) : static_cast(std::forward(u)); } /// Destroys the stored value if one exists, making the optional empty void reset() noexcept { if (has_value()) { this->m_value.~T(); this->m_has_value = false; } } }; // namespace tl /// Compares two optional objects template inline constexpr bool operator==(const optional &lhs, const optional &rhs) { return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); } template inline constexpr bool operator!=(const optional &lhs, const optional &rhs) { return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); } template inline constexpr bool operator<(const optional &lhs, const optional &rhs) { return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); } template inline constexpr bool operator>(const optional &lhs, const optional &rhs) { return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); } template inline constexpr bool operator<=(const optional &lhs, const optional &rhs) { return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); } template inline constexpr bool operator>=(const optional &lhs, const optional &rhs) { return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); } /// Compares an optional to a `nullopt` template inline constexpr bool operator==(const optional &lhs, nullopt_t) noexcept { return !lhs.has_value(); } template inline constexpr bool operator==(nullopt_t, const optional &rhs) noexcept { return !rhs.has_value(); } template inline constexpr bool operator!=(const optional &lhs, nullopt_t) noexcept { return lhs.has_value(); } template inline constexpr bool operator!=(nullopt_t, const optional &rhs) noexcept { return rhs.has_value(); } template inline constexpr bool operator<(const optional &, nullopt_t) noexcept { return false; } template inline constexpr bool operator<(nullopt_t, const optional &rhs) noexcept { return rhs.has_value(); } template inline constexpr bool operator<=(const optional &lhs, nullopt_t) noexcept { return !lhs.has_value(); } template inline constexpr bool operator<=(nullopt_t, const optional &) noexcept { return true; } template inline constexpr bool operator>(const optional &lhs, nullopt_t) noexcept { return lhs.has_value(); } template inline constexpr bool operator>(nullopt_t, const optional &) noexcept { return false; } template inline constexpr bool operator>=(const optional &, nullopt_t) noexcept { return true; } template inline constexpr bool operator>=(nullopt_t, const optional &rhs) noexcept { return !rhs.has_value(); } /// Compares the optional with a value. template inline constexpr bool operator==(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs == rhs : false; } template inline constexpr bool operator==(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs == *rhs : false; } template inline constexpr bool operator!=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs != rhs : true; } template inline constexpr bool operator!=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs != *rhs : true; } template inline constexpr bool operator<(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs < rhs : true; } template inline constexpr bool operator<(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs < *rhs : false; } template inline constexpr bool operator<=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs <= rhs : true; } template inline constexpr bool operator<=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs <= *rhs : false; } template inline constexpr bool operator>(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs > rhs : false; } template inline constexpr bool operator>(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs > *rhs : true; } template inline constexpr bool operator>=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs >= rhs : false; } template inline constexpr bool operator>=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs >= *rhs : true; } template ::value> * = nullptr, detail::enable_if_t::value> * = nullptr> void swap(optional &lhs, optional &rhs) noexcept(noexcept(lhs.swap(rhs))) { return lhs.swap(rhs); } namespace detail { struct i_am_secret {}; } // namespace detail template ::value, detail::decay_t, T>> inline constexpr optional make_optional(U &&v) { return optional(std::forward(v)); } template inline constexpr optional make_optional(Args &&... args) { return optional(in_place, std::forward(args)...); } template inline constexpr optional make_optional(std::initializer_list il, Args &&... args) { return optional(in_place, il, std::forward(args)...); } #if __cplusplus >= 201703L template optional(T)->optional; #endif /// \exclude namespace detail { #ifdef TL_OPTIONAL_CXX14 template (), *std::declval())), detail::enable_if_t::value> * = nullptr> constexpr auto optional_map_impl(Opt &&opt, F &&f) { return opt.has_value() ? detail::invoke(std::forward(f), *std::forward(opt)) : optional(nullopt); } template (), *std::declval())), detail::enable_if_t::value> * = nullptr> auto optional_map_impl(Opt &&opt, F &&f) { if (opt.has_value()) { detail::invoke(std::forward(f), *std::forward(opt)); return make_optional(monostate{}); } return optional(nullopt); } #else template (), *std::declval())), detail::enable_if_t::value> * = nullptr> constexpr auto optional_map_impl(Opt &&opt, F &&f) -> optional { return opt.has_value() ? detail::invoke(std::forward(f), *std::forward(opt)) : optional(nullopt); } template (), *std::declval())), detail::enable_if_t::value> * = nullptr> auto optional_map_impl(Opt &&opt, F &&f) -> optional { if (opt.has_value()) { detail::invoke(std::forward(f), *std::forward(opt)); return monostate{}; } return nullopt; } #endif } // namespace detail /// Specialization for when `T` is a reference. `optional` acts similarly /// to a `T*`, but provides more operations and shows intent more clearly. template class optional { public: // The different versions for C++14 and 11 are needed because deduced return // types are not SFINAE-safe. This provides better support for things like // generic lambdas. C.f. // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) /// Carries out some operation which returns an optional on the stored /// object if there is one. template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template constexpr auto and_then(F &&f) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr auto and_then(F &&f) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } #endif #else /// Carries out some operation which returns an optional on the stored /// object if there is one. template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template constexpr detail::invoke_result_t and_then(F &&f) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr detail::invoke_result_t and_then(F &&f) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } #endif #endif #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { return detail::optional_map_impl(*this, std::forward(f)); } template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } template constexpr auto map(F &&f) const & { return detail::optional_map_impl(*this, std::forward(f)); } template constexpr auto map(F &&f) const && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } #else /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F &&f) & { return detail::optional_map_impl(*this, std::forward(f)); } template TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F &&f) && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } template constexpr decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F &&f) const & { return detail::optional_map_impl(*this, std::forward(f)); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F &&f) const && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } #endif #endif #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & { return detail::optional_map_impl(*this, std::forward(f)); } template TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } template constexpr auto transform(F&& f) const & { return detail::optional_map_impl(*this, std::forward(f)); } template constexpr auto transform(F&& f) const && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } #else /// Carries out some operation on the stored object if there is one. template TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), std::declval())) transform(F&& f) & { return detail::optional_map_impl(*this, std::forward(f)); } /// \group map /// \synopsis template auto transform(F &&f) &&; template TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), std::declval())) transform(F&& f) && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } template constexpr decltype(detail::optional_map_impl(std::declval(), std::declval())) transform(F&& f) const & { return detail::optional_map_impl(*this, std::forward(f)); } #ifndef TL_OPTIONAL_NO_CONSTRR template constexpr decltype(detail::optional_map_impl(std::declval(), std::declval())) transform(F&& f) const && { return detail::optional_map_impl(std::move(*this), std::forward(f)); } #endif #endif /// Calls `f` if the optional is empty template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { if (has_value()) return *this; std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { return has_value() ? *this : std::forward(f)(); } template * = nullptr> optional or_else(F &&f) && { if (has_value()) return std::move(*this); std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { return has_value() ? std::move(*this) : std::forward(f)(); } template * = nullptr> optional or_else(F &&f) const & { if (has_value()) return *this; std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { return has_value() ? *this : std::forward(f)(); } #ifndef TL_OPTIONAL_NO_CONSTRR template * = nullptr> optional or_else(F &&f) const && { if (has_value()) return std::move(*this); std::forward(f)(); return nullopt; } template * = nullptr> optional or_else(F &&f) const && { return has_value() ? std::move(*this) : std::forward(f)(); } #endif /// Maps the stored value with `f` if there is one, otherwise returns `u` template U map_or(F &&f, U &&u) & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); } template U map_or(F &&f, U &&u) && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); } template U map_or(F &&f, U &&u) const & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); } #ifndef TL_OPTIONAL_NO_CONSTRR template U map_or(F &&f, U &&u) const && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); } #endif /// Maps the stored value with `f` if there is one, otherwise calls /// `u` and returns the result. template detail::invoke_result_t map_or_else(F &&f, U &&u) & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); } template detail::invoke_result_t map_or_else(F &&f, U &&u) && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); } template detail::invoke_result_t map_or_else(F &&f, U &&u) const & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); } #ifndef TL_OPTIONAL_NO_CONSTRR template detail::invoke_result_t map_or_else(F &&f, U &&u) const && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); } #endif /// Returns `u` if `*this` has a value, otherwise an empty optional. template constexpr optional::type> conjunction(U &&u) const { using result = optional>; return has_value() ? result{u} : result{nullopt}; } /// Returns `rhs` if `*this` is empty, otherwise the current value. TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { return has_value() ? *this : rhs; } constexpr optional disjunction(const optional &rhs) const & { return has_value() ? *this : rhs; } TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { return has_value() ? std::move(*this) : rhs; } #ifndef TL_OPTIONAL_NO_CONSTRR constexpr optional disjunction(const optional &rhs) const && { return has_value() ? std::move(*this) : rhs; } #endif TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { return has_value() ? *this : std::move(rhs); } constexpr optional disjunction(optional &&rhs) const & { return has_value() ? *this : std::move(rhs); } TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { return has_value() ? std::move(*this) : std::move(rhs); } #ifndef TL_OPTIONAL_NO_CONSTRR constexpr optional disjunction(optional &&rhs) const && { return has_value() ? std::move(*this) : std::move(rhs); } #endif /// Takes the value out of the optional, leaving it empty optional take() { optional ret = std::move(*this); reset(); return ret; } using value_type = T &; /// Constructs an optional that does not contain a value. constexpr optional() noexcept : m_value(nullptr) {} constexpr optional(nullopt_t) noexcept : m_value(nullptr) {} /// Copy constructor /// /// If `rhs` contains a value, the stored value is direct-initialized with /// it. Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) noexcept = default; /// Move constructor /// /// If `rhs` contains a value, the stored value is direct-initialized with /// it. Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; /// Constructs the stored value with `u`. template >::value> * = nullptr> constexpr optional(U &&u) noexcept : m_value(std::addressof(u)) { static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); } template constexpr explicit optional(const optional &rhs) noexcept : optional(*rhs) {} /// No-op ~optional() = default; /// Assignment to empty. /// /// Destroys the current value if there is one. optional &operator=(nullopt_t) noexcept { m_value = nullptr; return *this; } /// Copy assignment. /// /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise /// resets the stored value in `*this`. optional &operator=(const optional &rhs) = default; /// Rebinds this optional to `u`. template >::value> * = nullptr> optional &operator=(U &&u) { static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); m_value = std::addressof(u); return *this; } /// Converting copy assignment operator. /// /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise /// resets the stored value in `*this`. template optional &operator=(const optional &rhs) noexcept { m_value = std::addressof(rhs.value()); return *this; } /// Rebinds this optional to `u`. template >::value> * = nullptr> optional &emplace(U &&u) noexcept { return *this = std::forward(u); } void swap(optional &rhs) noexcept { std::swap(m_value, rhs.m_value); } /// Returns a pointer to the stored value constexpr const T *operator->() const noexcept { return m_value; } TL_OPTIONAL_11_CONSTEXPR T *operator->() noexcept { return m_value; } /// Returns the stored value TL_OPTIONAL_11_CONSTEXPR T &operator*() noexcept { return *m_value; } constexpr const T &operator*() const noexcept { return *m_value; } constexpr bool has_value() const noexcept { return m_value != nullptr; } constexpr explicit operator bool() const noexcept { return m_value != nullptr; } /// Returns the contained value if there is one, otherwise throws bad_optional_access TL_OPTIONAL_11_CONSTEXPR T &value() { if (has_value()) return *m_value; throw bad_optional_access(); } TL_OPTIONAL_11_CONSTEXPR const T &value() const { if (has_value()) return *m_value; throw bad_optional_access(); } /// Returns the stored value if there is one, otherwise returns `u` template constexpr T value_or(U &&u) const & noexcept { static_assert(std::is_copy_constructible::value && std::is_convertible::value, "T must be copy constructible and convertible from U"); return has_value() ? **this : static_cast(std::forward(u)); } /// \group value_or template TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && noexcept { static_assert(std::is_move_constructible::value && std::is_convertible::value, "T must be move constructible and convertible from U"); return has_value() ? **this : static_cast(std::forward(u)); } /// Destroys the stored value if one exists, making the optional empty void reset() noexcept { m_value = nullptr; } private: T *m_value; }; // namespace tl } // namespace tl namespace std { // TODO SFINAE template struct hash> { ::std::size_t operator()(const tl::optional &o) const { if (!o.has_value()) return 0; return std::hash>()(*o); } }; } // namespace std #endif TartanLlama-optional-3a1209d/tests/000077500000000000000000000000001437610075600172355ustar00rootroot00000000000000TartanLlama-optional-3a1209d/tests/assignment.cpp000066400000000000000000000022171437610075600221130ustar00rootroot00000000000000#include #include TEST_CASE("Assignment value", "[assignment.value]") { tl::optional o1 = 42; tl::optional o2 = 12; tl::optional o3; o1 = o1; REQUIRE(*o1 == 42); o1 = o2; REQUIRE(*o1 == 12); o1 = o3; REQUIRE(!o1); o1 = 42; REQUIRE(*o1 == 42); o1 = tl::nullopt; REQUIRE(!o1); o1 = std::move(o2); REQUIRE(*o1 == 12); tl::optional o4 = 42; o1 = o4; REQUIRE(*o1 == 42); o1 = std::move(o4); REQUIRE(*o1 == 42); } TEST_CASE("Assignment reference", "[assignment.ref]") { auto i = 42; auto j = 12; tl::optional o1 = i; tl::optional o2 = j; tl::optional o3; o1 = o1; REQUIRE(*o1 == 42); REQUIRE(&*o1 == &i); o1 = o2; REQUIRE(*o1 == 12); o1 = o3; REQUIRE(!o1); auto k = 42; o1 = k; REQUIRE(*o1 == 42); REQUIRE(*o1 == i); REQUIRE(*o1 == k); REQUIRE(&*o1 != &i); REQUIRE(&*o1 == &k); k = 12; REQUIRE(*o1 == 12); o1 = tl::nullopt; REQUIRE(!o1); o1 = std::move(o2); REQUIRE(*o1 == 12); } TartanLlama-optional-3a1209d/tests/bases.cpp000066400000000000000000000077541437610075600210530ustar00rootroot00000000000000#include #include // Old versions of GCC don't have the correct trait names. Could fix them up if needs be. #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) // nothing for now #else TEST_CASE("Triviality", "[bases.triviality]") { REQUIRE(std::is_trivially_copy_constructible>::value); REQUIRE(std::is_trivially_copy_assignable>::value); REQUIRE(std::is_trivially_move_constructible>::value); REQUIRE(std::is_trivially_move_assignable>::value); REQUIRE(std::is_trivially_destructible>::value); { struct T { T(const T&) = default; T(T&&) = default; T& operator=(const T&) = default; T& operator=(T&&) = default; ~T() = default; }; REQUIRE(std::is_trivially_copy_constructible>::value); REQUIRE(std::is_trivially_copy_assignable>::value); REQUIRE(std::is_trivially_move_constructible>::value); REQUIRE(std::is_trivially_move_assignable>::value); REQUIRE(std::is_trivially_destructible>::value); } { struct T { T(const T&){} T(T&&) {}; T& operator=(const T&) { return *this; } T& operator=(T&&) { return *this; }; ~T(){} }; REQUIRE(!std::is_trivially_copy_constructible>::value); REQUIRE(!std::is_trivially_copy_assignable>::value); REQUIRE(!std::is_trivially_move_constructible>::value); REQUIRE(!std::is_trivially_move_assignable>::value); REQUIRE(!std::is_trivially_destructible>::value); } } TEST_CASE("Deletion", "[bases.deletion]") { REQUIRE(std::is_copy_constructible>::value); REQUIRE(std::is_copy_assignable>::value); REQUIRE(std::is_move_constructible>::value); REQUIRE(std::is_move_assignable>::value); REQUIRE(std::is_destructible>::value); { struct T { T(const T&) = default; T(T&&) = default; T& operator=(const T&) = default; T& operator=(T&&) = default; ~T() = default; }; REQUIRE(std::is_copy_constructible>::value); REQUIRE(std::is_copy_assignable>::value); REQUIRE(std::is_move_constructible>::value); REQUIRE(std::is_move_assignable>::value); REQUIRE(std::is_destructible>::value); } { struct T { T(const T&)=delete; T(T&&)=delete; T& operator=(const T&)=delete; T& operator=(T&&)=delete; }; REQUIRE(!std::is_copy_constructible>::value); REQUIRE(!std::is_copy_assignable>::value); REQUIRE(!std::is_move_constructible>::value); REQUIRE(!std::is_move_assignable>::value); } { struct T { T(const T&)=delete; T(T&&)=default; T& operator=(const T&)=delete; T& operator=(T&&)=default; }; REQUIRE(!std::is_copy_constructible>::value); REQUIRE(!std::is_copy_assignable>::value); REQUIRE(std::is_move_constructible>::value); REQUIRE(std::is_move_assignable>::value); } { struct T { T(const T&)=default; T(T&&)=delete; T& operator=(const T&)=default; T& operator=(T&&)=delete; }; REQUIRE(std::is_copy_constructible>::value); REQUIRE(std::is_copy_assignable>::value); } } #endif TartanLlama-optional-3a1209d/tests/constexpr.cpp000066400000000000000000000023321437610075600217660ustar00rootroot00000000000000#include #include TEST_CASE("Constexpr", "[constexpr]") { #if !defined(TL_OPTIONAL_MSVC2015) && defined(TL_OPTIONAL_CXX14) SECTION("empty construct") { constexpr tl::optional o2{}; constexpr tl::optional o3 = {}; constexpr tl::optional o4 = tl::nullopt; constexpr tl::optional o5 = {tl::nullopt}; constexpr tl::optional o6(tl::nullopt); STATIC_REQUIRE(!o2); STATIC_REQUIRE(!o3); STATIC_REQUIRE(!o4); STATIC_REQUIRE(!o5); STATIC_REQUIRE(!o6); } SECTION("value construct") { constexpr tl::optional o1 = 42; constexpr tl::optional o2{42}; constexpr tl::optional o3(42); constexpr tl::optional o4 = {42}; constexpr int i = 42; constexpr tl::optional o5 = std::move(i); constexpr tl::optional o6{std::move(i)}; constexpr tl::optional o7(std::move(i)); constexpr tl::optional o8 = {std::move(i)}; STATIC_REQUIRE(*o1 == 42); STATIC_REQUIRE(*o2 == 42); STATIC_REQUIRE(*o3 == 42); STATIC_REQUIRE(*o4 == 42); STATIC_REQUIRE(*o5 == 42); STATIC_REQUIRE(*o6 == 42); STATIC_REQUIRE(*o7 == 42); STATIC_REQUIRE(*o8 == 42); } #endif } TartanLlama-optional-3a1209d/tests/constructors.cpp000066400000000000000000000020571437610075600225150ustar00rootroot00000000000000#include #include #include struct foo { foo() = default; foo(foo&) = delete; foo(foo&&) {}; }; TEST_CASE("Constructors", "[constructors]") { tl::optional o1; REQUIRE(!o1); tl::optional o2 = tl::nullopt; REQUIRE(!o2); tl::optional o3 = 42; REQUIRE(*o3 == 42); tl::optional o4 = o3; REQUIRE(*o4 == 42); tl::optional o5 = o1; REQUIRE(!o5); tl::optional o6 = std::move(o3); REQUIRE(*o6 == 42); tl::optional o7 = 42; REQUIRE(*o7 == 42); tl::optional o8 = o7; REQUIRE(*o8 == 42); tl::optional o9 = std::move(o7); REQUIRE(*o9 == 42); { tl::optional o; REQUIRE(!o); tl::optional oo = o; REQUIRE(!oo); } { auto i = 42; tl::optional o = i; REQUIRE(o); REQUIRE(*o == 42); tl::optional oo = o; REQUIRE(oo); REQUIRE(*oo == 42); } std::vector v; v.emplace_back(); tl::optional> ov = std::move(v); REQUIRE(ov->size() == 1); } TartanLlama-optional-3a1209d/tests/emplace.cpp000066400000000000000000000011511437610075600213450ustar00rootroot00000000000000#include #include #include #include TEST_CASE("Emplace", "[emplace]") { tl::optional, std::pair>> i; i.emplace(std::piecewise_construct, std::make_tuple(0,2), std::make_tuple(3,4)); REQUIRE(i->first.first == 0); REQUIRE(i->first.second == 2); REQUIRE(i->second.first == 3); REQUIRE(i->second.second == 4); } struct A { A() { throw std::exception(); } }; TEST_CASE("Emplace with exception thrown", "[emplace]") { tl::optional a; REQUIRE_THROWS(a.emplace()); } TartanLlama-optional-3a1209d/tests/extensions.cpp000066400000000000000000000352371437610075600221520ustar00rootroot00000000000000#include #include #include constexpr int get_int(int) { return 42; } TL_OPTIONAL_11_CONSTEXPR tl::optional get_opt_int(int) { return 42; } // What is Clang Format up to?! TEST_CASE("Monadic operations", "[monadic]") { SECTION("map") { // lhs is empty tl::optional o1; auto o1r = o1.map([](int i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o1r); // lhs has value tl::optional o2 = 40; auto o2r = o2.map([](int i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o2r.value() == 42); struct rval_call_map { double operator()(int) && { return 42.0; }; }; // ensure that function object is forwarded tl::optional o3 = 42; auto o3r = o3.map(rval_call_map{}); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o3r.value() == 42); // ensure that lhs is forwarded tl::optional o4 = 40; auto o4r = std::move(o4).map([](int &&i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o4r.value() == 42); // ensure that lhs is const-propagated const tl::optional o5 = 40; auto o5r = o5.map([](const int &i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o5r.value() == 42); // test void return tl::optional o7 = 40; auto f7 = [](const int &) { return; }; auto o7r = o7.map(f7); STATIC_REQUIRE( (std::is_same>::value)); REQUIRE(o7r.has_value()); // test each overload in turn tl::optional o8 = 42; auto o8r = o8.map([](int) { return 42; }); REQUIRE(*o8r == 42); tl::optional o9 = 42; auto o9r = o9.map([](int) { return; }); REQUIRE(o9r); tl::optional o12 = 42; auto o12r = std::move(o12).map([](int) { return 42; }); REQUIRE(*o12r == 42); tl::optional o13 = 42; auto o13r = std::move(o13).map([](int) { return; }); REQUIRE(o13r); const tl::optional o16 = 42; auto o16r = o16.map([](int) { return 42; }); REQUIRE(*o16r == 42); const tl::optional o17 = 42; auto o17r = o17.map([](int) { return; }); REQUIRE(o17r); const tl::optional o20 = 42; auto o20r = std::move(o20).map([](int) { return 42; }); REQUIRE(*o20r == 42); const tl::optional o21 = 42; auto o21r = std::move(o21).map([](int) { return; }); REQUIRE(o21r); tl::optional o24 = tl::nullopt; auto o24r = o24.map([](int) { return 42; }); REQUIRE(!o24r); tl::optional o25 = tl::nullopt; auto o25r = o25.map([](int) { return; }); REQUIRE(!o25r); tl::optional o28 = tl::nullopt; auto o28r = std::move(o28).map([](int) { return 42; }); REQUIRE(!o28r); tl::optional o29 = tl::nullopt; auto o29r = std::move(o29).map([](int) { return; }); REQUIRE(!o29r); const tl::optional o32 = tl::nullopt; auto o32r = o32.map([](int) { return 42; }); REQUIRE(!o32r); const tl::optional o33 = tl::nullopt; auto o33r = o33.map([](int) { return; }); REQUIRE(!o33r); const tl::optional o36 = tl::nullopt; auto o36r = std::move(o36).map([](int) { return 42; }); REQUIRE(!o36r); const tl::optional o37 = tl::nullopt; auto o37r = std::move(o37).map([](int) { return; }); REQUIRE(!o37r); // callable which returns a reference tl::optional o38 = 42; auto o38r = o38.map([](int &i) -> const int & { return i; }); REQUIRE(o38r); REQUIRE(*o38r == 42); int i = 42; tl::optional o39 = i; o39.map([](int& x){x = 12;}); REQUIRE(i == 12); } SECTION("map constexpr") { #if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14) // test each overload in turn constexpr tl::optional o16 = 42; constexpr auto o16r = o16.map(get_int); STATIC_REQUIRE(*o16r == 42); constexpr tl::optional o20 = 42; constexpr auto o20r = std::move(o20).map(get_int); STATIC_REQUIRE(*o20r == 42); constexpr tl::optional o32 = tl::nullopt; constexpr auto o32r = o32.map(get_int); STATIC_REQUIRE(!o32r); constexpr tl::optional o36 = tl::nullopt; constexpr auto o36r = std::move(o36).map(get_int); STATIC_REQUIRE(!o36r); #endif } SECTION("transform") { // lhs is empty tl::optional o1; auto o1r = o1.transform([](int i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o1r); // lhs has value tl::optional o2 = 40; auto o2r = o2.transform([](int i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o2r.value() == 42); struct rval_call_transform { double operator()(int) && { return 42.0; }; }; // ensure that function object is forwarded tl::optional o3 = 42; auto o3r = o3.transform(rval_call_transform{}); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o3r.value() == 42); // ensure that lhs is forwarded tl::optional o4 = 40; auto o4r = std::move(o4).transform([](int&& i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o4r.value() == 42); // ensure that lhs is const-propagated const tl::optional o5 = 40; auto o5r = o5.transform([](const int& i) { return i + 2; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o5r.value() == 42); // test void return tl::optional o7 = 40; auto f7 = [](const int&) { return; }; auto o7r = o7.transform(f7); STATIC_REQUIRE( (std::is_same>::value)); REQUIRE(o7r.has_value()); // test each overload in turn tl::optional o8 = 42; auto o8r = o8.transform([](int) { return 42; }); REQUIRE(*o8r == 42); tl::optional o9 = 42; auto o9r = o9.transform([](int) { return; }); REQUIRE(o9r); tl::optional o12 = 42; auto o12r = std::move(o12).transform([](int) { return 42; }); REQUIRE(*o12r == 42); tl::optional o13 = 42; auto o13r = std::move(o13).transform([](int) { return; }); REQUIRE(o13r); const tl::optional o16 = 42; auto o16r = o16.transform([](int) { return 42; }); REQUIRE(*o16r == 42); const tl::optional o17 = 42; auto o17r = o17.transform([](int) { return; }); REQUIRE(o17r); const tl::optional o20 = 42; auto o20r = std::move(o20).transform([](int) { return 42; }); REQUIRE(*o20r == 42); const tl::optional o21 = 42; auto o21r = std::move(o21).transform([](int) { return; }); REQUIRE(o21r); tl::optional o24 = tl::nullopt; auto o24r = o24.transform([](int) { return 42; }); REQUIRE(!o24r); tl::optional o25 = tl::nullopt; auto o25r = o25.transform([](int) { return; }); REQUIRE(!o25r); tl::optional o28 = tl::nullopt; auto o28r = std::move(o28).transform([](int) { return 42; }); REQUIRE(!o28r); tl::optional o29 = tl::nullopt; auto o29r = std::move(o29).transform([](int) { return; }); REQUIRE(!o29r); const tl::optional o32 = tl::nullopt; auto o32r = o32.transform([](int) { return 42; }); REQUIRE(!o32r); const tl::optional o33 = tl::nullopt; auto o33r = o33.transform([](int) { return; }); REQUIRE(!o33r); const tl::optional o36 = tl::nullopt; auto o36r = std::move(o36).transform([](int) { return 42; }); REQUIRE(!o36r); const tl::optional o37 = tl::nullopt; auto o37r = std::move(o37).transform([](int) { return; }); REQUIRE(!o37r); // callable which returns a reference tl::optional o38 = 42; auto o38r = o38.transform([](int& i) -> const int& { return i; }); REQUIRE(o38r); REQUIRE(*o38r == 42); int i = 42; tl::optional o39 = i; o39.transform([](int& x) {x = 12; }); REQUIRE(i == 12); } SECTION("transform constexpr") { #if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14) // test each overload in turn constexpr tl::optional o16 = 42; constexpr auto o16r = o16.transform(get_int); STATIC_REQUIRE(*o16r == 42); constexpr tl::optional o20 = 42; constexpr auto o20r = std::move(o20).transform(get_int); STATIC_REQUIRE(*o20r == 42); constexpr tl::optional o32 = tl::nullopt; constexpr auto o32r = o32.transform(get_int); STATIC_REQUIRE(!o32r); constexpr tl::optional o36 = tl::nullopt; constexpr auto o36r = std::move(o36).transform(get_int); STATIC_REQUIRE(!o36r); #endif } SECTION("and_then") { // lhs is empty tl::optional o1; auto o1r = o1.and_then([](int) { return tl::optional{42}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o1r); // lhs has value tl::optional o2 = 12; auto o2r = o2.and_then([](int) { return tl::optional{42}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o2r.value() == 42.f); // lhs is empty, rhs returns empty tl::optional o3; auto o3r = o3.and_then([](int) { return tl::optional{}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o3r); // rhs returns empty tl::optional o4 = 12; auto o4r = o4.and_then([](int) { return tl::optional{}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o4r); struct rval_call_and_then { tl::optional operator()(int) && { return tl::optional(42.0); }; }; // ensure that function object is forwarded tl::optional o5 = 42; auto o5r = o5.and_then(rval_call_and_then{}); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o5r.value() == 42); // ensure that lhs is forwarded tl::optional o6 = 42; auto o6r = std::move(o6).and_then([](int &&i) { return tl::optional(i); }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o6r.value() == 42); // ensure that function object is const-propagated const tl::optional o7 = 42; auto o7r = o7.and_then([](const int &i) { return tl::optional(i); }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o7r.value() == 42); // test each overload in turn tl::optional o8 = 42; auto o8r = o8.and_then([](int) { return tl::make_optional(42); }); REQUIRE(*o8r == 42); tl::optional o9 = 42; auto o9r = std::move(o9).and_then([](int) { return tl::make_optional(42); }); REQUIRE(*o9r == 42); const tl::optional o10 = 42; auto o10r = o10.and_then([](int) { return tl::make_optional(42); }); REQUIRE(*o10r == 42); const tl::optional o11 = 42; auto o11r = std::move(o11).and_then([](int) { return tl::make_optional(42); }); REQUIRE(*o11r == 42); tl::optional o16 = tl::nullopt; auto o16r = o16.and_then([](int) { return tl::make_optional(42); }); REQUIRE(!o16r); tl::optional o17 = tl::nullopt; auto o17r = std::move(o17).and_then([](int) { return tl::make_optional(42); }); REQUIRE(!o17r); const tl::optional o18 = tl::nullopt; auto o18r = o18.and_then([](int) { return tl::make_optional(42); }); REQUIRE(!o18r); const tl::optional o19 = tl::nullopt; auto o19r = std::move(o19).and_then([](int) { return tl::make_optional(42); }); REQUIRE(!o19r); int i = 3; tl::optional o20{i}; std::move(o20).and_then([](int& r){return tl::optional{++r};}); REQUIRE(o20); REQUIRE(i == 4); } SECTION("constexpr and_then") { #if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14) constexpr tl::optional o10 = 42; constexpr auto o10r = o10.and_then(get_opt_int); REQUIRE(*o10r == 42); constexpr tl::optional o11 = 42; constexpr auto o11r = std::move(o11).and_then(get_opt_int); REQUIRE(*o11r == 42); constexpr tl::optional o18 = tl::nullopt; constexpr auto o18r = o18.and_then(get_opt_int); REQUIRE(!o18r); constexpr tl::optional o19 = tl::nullopt; constexpr auto o19r = std::move(o19).and_then(get_opt_int); REQUIRE(!o19r); #endif } SECTION("or else") { tl::optional o1 = 42; REQUIRE(*(o1.or_else([] { return tl::make_optional(13); })) == 42); tl::optional o2; REQUIRE(*(o2.or_else([] { return tl::make_optional(13); })) == 13); } SECTION("disjunction") { tl::optional o1 = 42; tl::optional o2 = 12; tl::optional o3; REQUIRE(*o1.disjunction(o2) == 42); REQUIRE(*o1.disjunction(o3) == 42); REQUIRE(*o2.disjunction(o1) == 12); REQUIRE(*o2.disjunction(o3) == 12); REQUIRE(*o3.disjunction(o1) == 42); REQUIRE(*o3.disjunction(o2) == 12); } SECTION("conjunction") { tl::optional o1 = 42; REQUIRE(*o1.conjunction(42.0) == 42.0); REQUIRE(*o1.conjunction(std::string{"hello"}) == std::string{"hello"}); tl::optional o2; REQUIRE(!o2.conjunction(42.0)); REQUIRE(!o2.conjunction(std::string{"hello"})); } SECTION("map_or") { tl::optional o1 = 21; REQUIRE((o1.map_or([](int x) { return x * 2; }, 13)) == 42); tl::optional o2; REQUIRE((o2.map_or([](int x) { return x * 2; }, 13)) == 13); } SECTION("map_or_else") { tl::optional o1 = 21; REQUIRE((o1.map_or_else([](int x) { return x * 2; }, [] { return 13; })) == 42); tl::optional o2; REQUIRE((o2.map_or_else([](int x) { return x * 2; }, [] { return 13; })) == 13); } SECTION("take") { tl::optional o1 = 42; REQUIRE(*o1.take() == 42); REQUIRE(!o1); tl::optional o2; REQUIRE(!o2.take()); REQUIRE(!o2); } struct foo { void non_const() {} }; #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) SECTION("Issue #1") { tl::optional f = foo{}; auto l = [](auto &&x) { x.non_const(); }; f.map(l); } #endif struct overloaded { tl::optional operator()(foo &) { return 0; } tl::optional operator()(const foo &) { return ""; } }; SECTION("Issue #2") { tl::optional f = foo{}; auto x = f.and_then(overloaded{}); } }; TartanLlama-optional-3a1209d/tests/hash.cpp000066400000000000000000000001321437610075600206600ustar00rootroot00000000000000#include #include TEST_CASE("Hashing", "[hash]") {} TartanLlama-optional-3a1209d/tests/in_place.cpp000066400000000000000000000020241437610075600215110ustar00rootroot00000000000000#include #include #include #include struct takes_init_and_variadic { std::vector v; std::tuple t; template takes_init_and_variadic(std::initializer_list l, Args &&... args) : v(l), t(std::forward(args)...) {} }; TEST_CASE("In place", "[in_place]") { tl::optional o1{tl::in_place}; tl::optional o2(tl::in_place); REQUIRE(o1); REQUIRE(o1 == 0); REQUIRE(o2); REQUIRE(o2 == 0); tl::optional o3(tl::in_place, 42); REQUIRE(o3 == 42); tl::optional> o4(tl::in_place, 0, 1); REQUIRE(o4); REQUIRE(std::get<0>(*o4) == 0); REQUIRE(std::get<1>(*o4) == 1); tl::optional> o5(tl::in_place, {0, 1}); REQUIRE(o5); REQUIRE((*o5)[0] == 0); REQUIRE((*o5)[1] == 1); tl::optional o6(tl::in_place, {0, 1}, 2, 3); REQUIRE(o6->v[0] == 0); REQUIRE(o6->v[1] == 1); REQUIRE(std::get<0>(o6->t) == 2); REQUIRE(std::get<1>(o6->t) == 3); } TartanLlama-optional-3a1209d/tests/issues.cpp000066400000000000000000000017171437610075600212620ustar00rootroot00000000000000#include #include #include struct foo{ int& v() { return i; } int i = 0; }; int& x(int& i) { i = 42; return i;} TEST_CASE("issue 14") { tl::optional f = foo{}; auto v = f.map(&foo::v).map(x); static_assert(std::is_same>::value, "Must return a reference"); REQUIRE(f->i == 42); REQUIRE(*v == 42); REQUIRE((&f->i) == (&*v)); } struct fail_on_copy_self { int value; fail_on_copy_self(int v) : value(v) {} fail_on_copy_self(const fail_on_copy_self& other) : value(other.value) { REQUIRE(&other != this); } }; TEST_CASE("issue 15") { tl::optional o = fail_on_copy_self(42); o = o; REQUIRE(o->value == 42); } TEST_CASE("issue 33") { int i = 0; int j = 0; tl::optional a = i; a.emplace(j); *a = 42; REQUIRE(j == 42); REQUIRE(*a == 42); REQUIRE(a.has_value()); } TartanLlama-optional-3a1209d/tests/make_optional.cpp000066400000000000000000000024601437610075600225650ustar00rootroot00000000000000#include #include #include #include struct takes_init_and_variadic { std::vector v; std::tuple t; template takes_init_and_variadic(std::initializer_list l, Args &&... args) : v(l), t(std::forward(args)...) {} }; TEST_CASE("Make optional", "[make_optional]") { auto o1 = tl::make_optional(42); auto o2 = tl::optional(42); constexpr bool is_same = std::is_same>::value; REQUIRE(is_same); REQUIRE(o1 == o2); auto o3 = tl::make_optional>(0, 1, 2, 3); REQUIRE(std::get<0>(*o3) == 0); REQUIRE(std::get<1>(*o3) == 1); REQUIRE(std::get<2>(*o3) == 2); REQUIRE(std::get<3>(*o3) == 3); auto o4 = tl::make_optional>({0, 1, 2, 3}); REQUIRE(o4.value()[0] == 0); REQUIRE(o4.value()[1] == 1); REQUIRE(o4.value()[2] == 2); REQUIRE(o4.value()[3] == 3); auto o5 = tl::make_optional({0, 1}, 2, 3); REQUIRE(o5->v[0] == 0); REQUIRE(o5->v[1] == 1); REQUIRE(std::get<0>(o5->t) == 2); REQUIRE(std::get<1>(o5->t) == 3); auto i = 42; auto o6 = tl::make_optional(i); REQUIRE((std::is_same>::value)); REQUIRE(o6); REQUIRE(*o6 == 42); } TartanLlama-optional-3a1209d/tests/noexcept.cpp000066400000000000000000000056331437610075600215750ustar00rootroot00000000000000#include #include TEST_CASE("Noexcept", "[noexcept]") { tl::optional o1{4}; tl::optional o2{42}; SECTION("comparison with nullopt") { REQUIRE(noexcept(o1 == tl::nullopt)); REQUIRE(noexcept(tl::nullopt == o1)); REQUIRE(noexcept(o1 != tl::nullopt)); REQUIRE(noexcept(tl::nullopt != o1)); REQUIRE(noexcept(o1 < tl::nullopt)); REQUIRE(noexcept(tl::nullopt < o1)); REQUIRE(noexcept(o1 <= tl::nullopt)); REQUIRE(noexcept(tl::nullopt <= o1)); REQUIRE(noexcept(o1 > tl::nullopt)); REQUIRE(noexcept(tl::nullopt > o1)); REQUIRE(noexcept(o1 >= tl::nullopt)); REQUIRE(noexcept(tl::nullopt >= o1)); } SECTION("swap") { //TODO see why this fails #if !defined(_MSC_VER) || _MSC_VER > 1900 REQUIRE(noexcept(swap(o1, o2)) == noexcept(o1.swap(o2))); struct nothrow_swappable { nothrow_swappable &swap(const nothrow_swappable &) noexcept { return *this; } }; struct throw_swappable { throw_swappable() = default; throw_swappable(const throw_swappable &) {} throw_swappable(throw_swappable &&) {} throw_swappable &swap(const throw_swappable &) { return *this; } }; tl::optional ont; tl::optional ot; REQUIRE(noexcept(ont.swap(ont))); REQUIRE(!noexcept(ot.swap(ot))); #endif } SECTION("constructors") { //TODO see why this fails #if !defined(_MSC_VER) || _MSC_VER > 1900 REQUIRE(noexcept(tl::optional{})); REQUIRE(noexcept(tl::optional{tl::nullopt})); struct nothrow_move { nothrow_move(nothrow_move &&) noexcept = default; }; struct throw_move { throw_move(throw_move &&){}; }; using nothrow_opt = tl::optional; using throw_opt = tl::optional; REQUIRE(std::is_nothrow_move_constructible::value); REQUIRE(!std::is_nothrow_move_constructible::value); #endif } SECTION("assignment") { REQUIRE(noexcept(o1 = tl::nullopt)); struct nothrow_move_assign { nothrow_move_assign() = default; nothrow_move_assign(nothrow_move_assign &&) noexcept = default; nothrow_move_assign &operator=(const nothrow_move_assign &) = default; }; struct throw_move_assign { throw_move_assign() = default; throw_move_assign(throw_move_assign &&){}; throw_move_assign &operator=(const throw_move_assign &) { return *this; } }; using nothrow_opt = tl::optional; using throw_opt = tl::optional; REQUIRE( noexcept(std::declval() = std::declval())); REQUIRE(!noexcept(std::declval() = std::declval())); } SECTION("observers") { REQUIRE(noexcept(static_cast(o1))); REQUIRE(noexcept(o1.has_value())); } SECTION("modifiers") { REQUIRE(noexcept(o1.reset())); } } TartanLlama-optional-3a1209d/tests/nullopt.cpp000066400000000000000000000005711437610075600214410ustar00rootroot00000000000000#include #include TEST_CASE("Nullopt", "[nullopt]") { tl::optional o1 = tl::nullopt; tl::optional o2{tl::nullopt}; tl::optional o3(tl::nullopt); tl::optional o4 = {tl::nullopt}; REQUIRE(!o1); REQUIRE(!o2); REQUIRE(!o3); REQUIRE(!o4); REQUIRE(!std::is_default_constructible::value); } TartanLlama-optional-3a1209d/tests/observers.cpp000066400000000000000000000017601437610075600217570ustar00rootroot00000000000000#include #include struct move_detector { move_detector() = default; move_detector(move_detector &&rhs) { rhs.been_moved = true; } bool been_moved = false; }; TEST_CASE("Observers", "[observers]") { tl::optional o1 = 42; tl::optional o2; const tl::optional o3 = 42; REQUIRE(*o1 == 42); REQUIRE(*o1 == o1.value()); REQUIRE(o2.value_or(42) == 42); REQUIRE(o3.value() == 42); auto success = std::is_same::value; REQUIRE(success); success = std::is_same::value; REQUIRE(success); success = std::is_same::value; REQUIRE(success); #ifndef TL_OPTIONAL_NO_CONSTRR success = std::is_same::value; REQUIRE(success); #endif tl::optional o4{tl::in_place}; move_detector o5 = std::move(o4).value(); REQUIRE(o4->been_moved); REQUIRE(!o5.been_moved); } TartanLlama-optional-3a1209d/tests/relops.cpp000066400000000000000000000074131437610075600212520ustar00rootroot00000000000000#include #include TEST_CASE("Relational ops", "[relops]") { tl::optional o1{4}; tl::optional o2{42}; tl::optional o3{}; SECTION("self simple") { REQUIRE(!(o1 == o2)); REQUIRE(o1 == o1); REQUIRE(o1 != o2); REQUIRE(!(o1 != o1)); REQUIRE(o1 < o2); REQUIRE(!(o1 < o1)); REQUIRE(!(o1 > o2)); REQUIRE(!(o1 > o1)); REQUIRE(o1 <= o2); REQUIRE(o1 <= o1); REQUIRE(!(o1 >= o2)); REQUIRE(o1 >= o1); } SECTION("nullopt simple") { REQUIRE(!(o1 == tl::nullopt)); REQUIRE(!(tl::nullopt == o1)); REQUIRE(o1 != tl::nullopt); REQUIRE(tl::nullopt != o1); REQUIRE(!(o1 < tl::nullopt)); REQUIRE(tl::nullopt < o1); REQUIRE(o1 > tl::nullopt); REQUIRE(!(tl::nullopt > o1)); REQUIRE(!(o1 <= tl::nullopt)); REQUIRE(tl::nullopt <= o1); REQUIRE(o1 >= tl::nullopt); REQUIRE(!(tl::nullopt >= o1)); REQUIRE(o3 == tl::nullopt); REQUIRE(tl::nullopt == o3); REQUIRE(!(o3 != tl::nullopt)); REQUIRE(!(tl::nullopt != o3)); REQUIRE(!(o3 < tl::nullopt)); REQUIRE(!(tl::nullopt < o3)); REQUIRE(!(o3 > tl::nullopt)); REQUIRE(!(tl::nullopt > o3)); REQUIRE(o3 <= tl::nullopt); REQUIRE(tl::nullopt <= o3); REQUIRE(o3 >= tl::nullopt); REQUIRE(tl::nullopt >= o3); } SECTION("with T simple") { REQUIRE(!(o1 == 1)); REQUIRE(!(1 == o1)); REQUIRE(o1 != 1); REQUIRE(1 != o1); REQUIRE(!(o1 < 1)); REQUIRE(1 < o1); REQUIRE(o1 > 1); REQUIRE(!(1 > o1)); REQUIRE(!(o1 <= 1)); REQUIRE(1 <= o1); REQUIRE(o1 >= 1); REQUIRE(!(1 >= o1)); REQUIRE(o1 == 4); REQUIRE(4 == o1); REQUIRE(!(o1 != 4)); REQUIRE(!(4 != o1)); REQUIRE(!(o1 < 4)); REQUIRE(!(4 < o1)); REQUIRE(!(o1 > 4)); REQUIRE(!(4 > o1)); REQUIRE(o1 <= 4); REQUIRE(4 <= o1); REQUIRE(o1 >= 4); REQUIRE(4 >= o1); } tl::optional o4{"hello"}; tl::optional o5{"xyz"}; SECTION("self complex") { REQUIRE(!(o4 == o5)); REQUIRE(o4 == o4); REQUIRE(o4 != o5); REQUIRE(!(o4 != o4)); REQUIRE(o4 < o5); REQUIRE(!(o4 < o4)); REQUIRE(!(o4 > o5)); REQUIRE(!(o4 > o4)); REQUIRE(o4 <= o5); REQUIRE(o4 <= o4); REQUIRE(!(o4 >= o5)); REQUIRE(o4 >= o4); } SECTION("nullopt complex") { REQUIRE(!(o4 == tl::nullopt)); REQUIRE(!(tl::nullopt == o4)); REQUIRE(o4 != tl::nullopt); REQUIRE(tl::nullopt != o4); REQUIRE(!(o4 < tl::nullopt)); REQUIRE(tl::nullopt < o4); REQUIRE(o4 > tl::nullopt); REQUIRE(!(tl::nullopt > o4)); REQUIRE(!(o4 <= tl::nullopt)); REQUIRE(tl::nullopt <= o4); REQUIRE(o4 >= tl::nullopt); REQUIRE(!(tl::nullopt >= o4)); REQUIRE(o3 == tl::nullopt); REQUIRE(tl::nullopt == o3); REQUIRE(!(o3 != tl::nullopt)); REQUIRE(!(tl::nullopt != o3)); REQUIRE(!(o3 < tl::nullopt)); REQUIRE(!(tl::nullopt < o3)); REQUIRE(!(o3 > tl::nullopt)); REQUIRE(!(tl::nullopt > o3)); REQUIRE(o3 <= tl::nullopt); REQUIRE(tl::nullopt <= o3); REQUIRE(o3 >= tl::nullopt); REQUIRE(tl::nullopt >= o3); } SECTION("with T complex") { REQUIRE(!(o4 == "a")); REQUIRE(!("a" == o4)); REQUIRE(o4 != "a"); REQUIRE("a" != o4); REQUIRE(!(o4 < "a")); REQUIRE("a" < o4); REQUIRE(o4 > "a"); REQUIRE(!("a" > o4)); REQUIRE(!(o4 <= "a")); REQUIRE("a" <= o4); REQUIRE(o4 >= "a"); REQUIRE(!("a" >= o4)); REQUIRE(o4 == "hello"); REQUIRE("hello" == o4); REQUIRE(!(o4 != "hello")); REQUIRE(!("hello" != o4)); REQUIRE(!(o4 < "hello")); REQUIRE(!("hello" < o4)); REQUIRE(!(o4 > "hello")); REQUIRE(!("hello" > o4)); REQUIRE(o4 <= "hello"); REQUIRE("hello" <= o4); REQUIRE(o4 >= "hello"); REQUIRE("hello" >= o4); } } TartanLlama-optional-3a1209d/tests/swap.cpp000066400000000000000000000012051437610075600207110ustar00rootroot00000000000000#include #include TEST_CASE("Swap value", "[swap.value]") { tl::optional o1 = 42; tl::optional o2 = 12; o1.swap(o2); CHECK(o1.value() == 12); CHECK(o2.value() == 42); } TEST_CASE("Swap value with null intialized", "[swap.value_nullopt]") { tl::optional o1 = 42; tl::optional o2 = tl::nullopt; o1.swap(o2); CHECK(!o1.has_value()); CHECK(o2.value() == 42); } TEST_CASE("Swap null intialized with value", "[swap.nullopt_value]") { tl::optional o1 = tl::nullopt; tl::optional o2 = 42; o1.swap(o2); CHECK(o1.value() == 42); CHECK(!o2.has_value()); }