diff options
Diffstat (limited to 'third_party/function2')
-rw-r--r-- | third_party/function2/.clang-format | 9 | ||||
-rw-r--r-- | third_party/function2/.editorconfig | 11 | ||||
-rw-r--r-- | third_party/function2/.travis.yml | 93 | ||||
-rw-r--r-- | third_party/function2/CMakeLists.txt | 138 | ||||
-rw-r--r-- | third_party/function2/Findfunction2.cmake | 10 | ||||
-rw-r--r-- | third_party/function2/LICENSE.txt | 23 | ||||
-rw-r--r-- | third_party/function2/Readme.md | 260 | ||||
-rw-r--r-- | third_party/function2/appveyor.yml | 44 | ||||
-rw-r--r-- | third_party/function2/conanfile.py | 33 | ||||
-rw-r--r-- | third_party/function2/include/function2/function2.hpp | 1829 | ||||
-rw-r--r-- | third_party/function2/moz.yaml | 37 |
11 files changed, 2487 insertions, 0 deletions
diff --git a/third_party/function2/.clang-format b/third_party/function2/.clang-format new file mode 100644 index 0000000000..822564901a --- /dev/null +++ b/third_party/function2/.clang-format @@ -0,0 +1,9 @@ +BasedOnStyle: LLVM + +PointerAlignment: Left +IndentCaseLabels: true +AllowShortFunctionsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +FixNamespaceComments: true diff --git a/third_party/function2/.editorconfig b/third_party/function2/.editorconfig new file mode 100644 index 0000000000..a148dfe072 --- /dev/null +++ b/third_party/function2/.editorconfig @@ -0,0 +1,11 @@ +[*] +charset = utf-8 +indent_size = 2 +tab_width = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 80 + +[*.{cpp,hpp}] +charset = latin1 diff --git a/third_party/function2/.travis.yml b/third_party/function2/.travis.yml new file mode 100644 index 0000000000..f3917101cb --- /dev/null +++ b/third_party/function2/.travis.yml @@ -0,0 +1,93 @@ +sudo: true +dist: trusty +language: cpp +cache: apt + +git: + depth: 1 + +matrix: + include: + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + - valgrind + - ninja-build + env: + - COMPILER=g++-5 + - NO_EXCEPTIONS=OFF + + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + - valgrind + - ninja-build + env: + - COMPILER=g++-6 + - NO_EXCEPTIONS=OFF + + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + - valgrind + - ninja-build + env: + - COMPILER=g++-6 + - NO_EXCEPTIONS=ON + + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - g++-6 + - clang-5.0 + - ninja-build + env: + - COMPILER=clang++-5.0 + - NO_EXCEPTIONS=OFF + + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - g++-6 + - clang-5.0 + - ninja-build + env: + - COMPILER=clang++-5.0 + - NO_EXCEPTIONS=ON + + +install: + - export CXX=$COMPILER + - $CXX --version + - chmod +x tools/travis-ci.sh + +script: + - ./tools/travis-ci.sh + +notifications: + email: false diff --git a/third_party/function2/CMakeLists.txt b/third_party/function2/CMakeLists.txt new file mode 100644 index 0000000000..e0eb44e87d --- /dev/null +++ b/third_party/function2/CMakeLists.txt @@ -0,0 +1,138 @@ +cmake_minimum_required(VERSION 3.11) +project(function2 VERSION 4.2.0 LANGUAGES CXX) + +if (NOT FU2_IS_FIND_INCLUDED) + string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} + FU2_IS_TOP_LEVEL_PROJECT) +endif() + +if (FU2_IS_TOP_LEVEL_PROJECT) + add_library(function2 INTERFACE) +else() + add_library(function2 INTERFACE IMPORTED GLOBAL) +endif() + +add_library(function2::function2 ALIAS function2) + +target_include_directories(function2 + INTERFACE + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include> + $<INSTALL_INTERFACE:include>) + +target_compile_features(function2 + INTERFACE + cxx_alias_templates + cxx_auto_type + cxx_constexpr + cxx_decltype + cxx_decltype_auto + cxx_final + cxx_lambdas + cxx_lambda_init_captures + cxx_generic_lambdas + cxx_variadic_templates + cxx_defaulted_functions + cxx_nullptr + cxx_trailing_return_types + cxx_return_type_deduction) + +if (FU2_IS_TOP_LEVEL_PROJECT) + include(ExternalProject) + include(GNUInstallDirs) + include(CMakePackageConfigHelpers) + + # Create an install target: + # Headers and license files + install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/function2" + DESTINATION "include") + install(FILES "LICENSE.txt" DESTINATION .) + install(FILES "Readme.md" DESTINATION .) + + # Config.cmake + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + # ConfigVersion.cmake + configure_package_config_file("cmake/config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + # PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR + ) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + # Targets.cmake + export(TARGETS ${PROJECT_NAME} + NAMESPACE ${PROJECT_NAME}:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") + install(TARGETS ${PROJECT_NAME} + EXPORT "${PROJECT_NAME}Targets" + INCLUDES DESTINATION "include") + install(EXPORT "${PROJECT_NAME}Targets" + NAMESPACE ${PROJECT_NAME}:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + # Setup CPack for bundling + set(CPACK_GENERATOR "ZIP") + set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) + set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) + set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) + + # Since the header only library is platform independent + # we name the packages after the native line feed + if(WIN32) + set(CPACK_SYSTEM_NAME "crlf") + else() + set(CPACK_SYSTEM_NAME "lf") + endif() + + include(CPack) + + if (MSVC) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-") + endif() + + include(cmake/CMakeLists.txt) + + include(CTest) + + option(FU2_WITH_NO_EXCEPTIONS + "Test without exceptions" + OFF) + option(FU2_WITH_NO_DEATH_TESTS + "Test without death tests" + OFF) + option(FU2_WITH_CPP_LATEST + "Enable the highest C++ standard available for testing polyfills" + OFF) + option(FU2_WITH_LIMITED_EMPTY_PROPAGATION + "Test limiting empty propagation to only function pointers, member pointers, std::function, and specializations of fu2::function_base" + OFF) + + if (BUILD_TESTING) + if (FU2_WITH_NO_EXCEPTIONS) + message(STATUS "Testing with exceptions disabled") + add_definitions(-DTESTS_NO_EXCEPTIONS) + endif() + + if (FU2_WITH_NO_DEATH_TESTS) + message(STATUS "Testing without death tests") + add_definitions(-DTESTS_NO_DEATH_TESTS) + endif() + + if (FU2_WITH_LIMITED_EMPTY_PROPAGATION) + message(STATUS "Testing with limited empty propagation") + add_definitions(-DFU2_WITH_LIMITED_EMPTY_PROPAGATION) + endif() + + add_subdirectory(test) + endif() +endif () diff --git a/third_party/function2/Findfunction2.cmake b/third_party/function2/Findfunction2.cmake new file mode 100644 index 0000000000..ca1fc3e41f --- /dev/null +++ b/third_party/function2/Findfunction2.cmake @@ -0,0 +1,10 @@ +# Makes it possible to find function2 through find_package(function2 REQUIRED) +# when this source directory was added to the CMake module path. +# For instance it could be done through adding this to the CMakeLists.txt +# file in the parent directory: +# ```cmake +# list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/function2") +# ``` + +set(FU2_IS_FIND_INCLUDED ON) +include("${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt") diff --git a/third_party/function2/LICENSE.txt b/third_party/function2/LICENSE.txt new file mode 100644 index 0000000000..36b7cd93cd --- /dev/null +++ b/third_party/function2/LICENSE.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/function2/Readme.md b/third_party/function2/Readme.md new file mode 100644 index 0000000000..7556e83e8e --- /dev/null +++ b/third_party/function2/Readme.md @@ -0,0 +1,260 @@ + +# fu2::function an improved drop-in replacement to std::function + +![](https://img.shields.io/badge/Version-4.0.0-0091EA.svg) ![](https://img.shields.io/badge/License-Boost-blue.svg) [![Build Status](https://travis-ci.org/Naios/function2.svg?branch=master)](https://travis-ci.org/Naios/function2) [![Build status](https://ci.appveyor.com/api/projects/status/1tl0vqpg8ndccats/branch/master?svg=true)](https://ci.appveyor.com/project/Naios/function2/branch/master) + +Provides improved implementations of `std::function`: + +- **copyable** `fu2::function` +- **move-only** `fu2::unique_function` (capable of holding move only types) +- **non-owning** `fu2::function_view` (capable of referencing callables in a non owning way) + +that provide many benefits and improvements over `std::function`: + +- [x] **const**, **volatile**, **reference** and **noexcept** correct (qualifiers are part of the `operator()` signature) +- [x] **convertible** to and from `std::function` as well as other callable types +- [x] **adaptable** through `fu2::function_base` (internal capacity, copyable and exception guarantees) +- [x] **overloadable** with an arbitrary count of signatures (`fu2::function<bool(int), bool(float)>`) +- [x] **full allocator support** in contrast to `std::function`, which doesn't provide support anymore +- [x] **covered** by many unit tests and continuous integration services (*GCC*, *Clang* and *MSVC*) +- [x] **header only**, just copy and include `function.hpp` in your project +- [x] **permissively licensed** under the **boost** license + + +## Table of Contents + +* **[Documentation](#documentation)** + * **[How to use](#how-to-use)** + * **[Constructing a function](#constructing-a-function)** + * **[Non copyable unique functions](#non-copyable-unique-functions)** + * **[Convertibility of functions](#convertibility-of-functions)** + * **[Adapt function2](#adapt-function2)** +* **[Performance and optimization](#performance-and-optimization)** + * **[Small functor optimization](#small-functor-optimization)** + * **[Compiler optimization](#compiler-optimization)** + * **[std::function vs fu2::function](#stdfunction-vs-fu2function)** +* **[Coverage and runtime checks](#coverage-and-runtime-checks)** +* **[Compatibility](#compatibility)** +* **[License](#licence)** +* **[Similar implementations](#similar-implementations)** + +## Documentation + +### How to use + +**function2** is implemented in one header (`function.hpp`), no compilation is required. +Just copy the `function.hpp` header in your project and include it to start. +It's recommended to import the library as git submodule using CMake: + +```sh +# Shell: +git submodule add https://github.com/Naios/function2.git +``` + +```cmake +# CMake file: +add_subdirectory(function2) +# function2 provides an interface target which makes it's +# headers available to all projects using function2 +target_link_libraries(my_project function2) +``` + +Use `fu2::function` as a wrapper for copyable function wrappers and `fu2::unique_function` for move only types. +The standard implementation `std::function` and `fu2::function` are convertible to each other, see [the chapter convertibility of functions](#convertibility-of-functions) for details. + +A function wrapper is declared as following: +```c++ +fu2::function<void(int, float) const> +// Return type ~^ ^ ^ ^ +// Parameters ~~~~~|~~~~~| ^ +// Qualifier ~~~~~~~~~~~~~~~~~~~| +``` + +* **Return type**: The return type of the function to wrap. +* **Arguments**: The argument types of the function to wrap. + Any argument types are allowed. +* **Qualifiers**: There are several qualifiers allowed: + - **no qualifier** provides `ReturnType operator() (Args...)` + - Can be assigned from const and no const objects (*mutable lambdas* for example). + - **const** provides `ReturnType operator() (Args...) const` + - Requires that the assigned functor is const callable (won't work with *mutable lambdas*), + - **volatile** provides `ReturnType operator() (Args...) volatile` + - Can only be assigned from volatile qualified functors. + - **const volatile** provides `ReturnType operator() (Args...) const volatile` + - Same as const and volatile together. + - **r-value (one-shot) functions** `ReturnType operator() (Args...) &&` + - one-shot functions which are invalidated after the first call (can be mixed with `const`, `volatile` and `noexcept`). Can only wrap callable objects which call operator is also qualified as `&&` (r-value callable). Normal (*C*) functions are considered to be r-value callable by default. + - **noexcept functions** `ReturnType operator() (Args...) noexcept` + - such functions are guaranteed not to throw an exception (can be mixed with `const`, `volatile` and `&&`). Can only wrap functions or callable objects which call operator is also qualified as `noexcept`. Requires enabled C++17 compilation to work (support is detected automatically). Empty function calls to such a wrapped function will lead to a call to `std::abort` regardless the wrapper is configured to support exceptions or not (see [adapt function2](#adapt-function2)). +* **Multiple overloads**: The library is capable of providing multiple overloads: + ```cpp + fu2::function<int(std::vector<int> const&), + int(std::set<int> const&) const> fn = [] (auto const& container) { + return container.size()); + }; + ``` + +### Constructing a function + +`fu2::function` and `fu2::unique_function` (non copyable) are easy to use: + +```c++ +fu2::function<void() const> fun = [] { + // ... +}; + +// fun provides void operator()() const now +fun(); +``` + +### Non copyable unique functions + +`fu2::unique_function` also works with non copyable functors/ lambdas. + +```c++ +fu2::unique_function<bool() const> fun = [ptr = std::make_unique<bool>(true)] { + return *ptr; +}; + +// unique functions are move only +fu2::unique_function<bool() const> otherfun = std::move(fun): + +otherfun(); +``` + + +### Non owning functions + +A `fu2::function_view` can be used to create a non owning view on a persistent object. Note that the view is only valid as long as the object lives. + +```c++ +auto callable = [ptr = std::make_unique<bool>(true)] { + return *ptr; +}; + +fu2::function_view<bool() const> view(callable); +``` + +### Convertibility of functions + +`fu2::function`, `fu2::unique_function` and `std::function` are convertible to each other when: + +- The return type and parameter type match. +- The functions are both volatile or not. +- The functions are const correct: + - `noconst = const` + - `const = const` + - `noconst = noconst` +- The functions are copyable correct when: + - `unique = unique` + - `unique = copyable` + - `copyable = copyable` +- The functions are reference correct when: + - `lvalue = lvalue` + - `lvalue = rvalue` + - `rvalue = rvalue` +- The functions are `noexcept` correct when: + - `callable = callable` + - `callable = noexcept callable ` + - `noexcept callable = noexcept callable` + +| Convertibility from \ to | fu2::function | fu2::unique_function | std::function | +| ------------------------ | ------------- | -------------------- | ------------- | +| fu2::function | Yes | Yes | Yes | +| fu2::unique_function | No | Yes | No | +| std::function | Yes | Yes | Yes | + +```c++ +fu2::function<void()> fun = []{}; +// OK +std::function<void()> std_fun = fun; +// OK +fu2::unique_function<void()> un_fun = fun; + +// Error (non copyable -> copyable) +fun = un_fun; +// Error (non copyable -> copyable) +fun = un_fun; + +``` + +### Adapt function2 + +function2 is adaptable through `fu2::function_base` which allows you to set: + +- **IsOwning**: defines whether the function owns its contained object +- **Copyable:** defines if the function is copyable or not. +- **Capacity:** defines the internal capacity used for [sfo optimization](#small-functor-optimization): +```cpp +struct my_capacity { + static constexpr std::size_t capacity = sizeof(my_type); + static constexpr std::size_t alignment = alignof(my_type); +}; +``` +- **IsThrowing** defines if empty function calls throw an `fu2::bad_function_call` exception, otherwise `std::abort` is called. +- **HasStrongExceptGuarantee** defines whether the strong exception guarantees shall be met. +- **Signatures:** defines the signatures of the function. + +The following code defines an owning function with a variadic signature which is copyable and sfo optimization is disabled: + +```c++ +template<typename Signature> +using my_function = fu2::function_base<true, true, fu2::capacity_none, true, false, Signature>; +``` + +The following code defines a non copyable function which just takes 1 argument, and has a huge capacity for internal sfo optimization. Also it must be called as r-value. + +```c++ +template<typename Arg> +using my_consumer = fu2::function_base<true, false, fu2::capacity_fixed<100U>, + true, false, void(Arg)&&>; + +// Example +my_consumer<int, float> consumer = [](int, float) { } +std::move(consumer)(44, 1.7363f); +``` + +## Performance and optimization + +### Small functor optimization + +function2 uses small functor optimization like the most common `std::function` implementations which means it allocates a small internal capacity to evade heap allocation for small functors. + +Smart heap allocation moves the inplace allocated functor automatically to the heap to speed up moving between objects. + +It's possible to disable small functor optimization through setting the internal capacity to 0. + + +## Coverage and runtime checks + +Function2 is checked with unit tests and valgrind (for memory leaks), where the unit tests provide coverage for all possible template parameter assignments. + +## Compatibility + +Tested with: + +- Visual Studio 2017+ Update 3 +- Clang 3.8+ +- GCC 5.4+ + +Every compiler with modern C++14 support should work. +*function2* only depends on the standard library. + +## License +*function2* is licensed under the very permissive Boost 1.0 License. + +## Similar implementations + +There are similar implementations of a function wrapper: + +- [pmed/fixed_size_function](https://github.com/pmed/fixed_size_function) +- stdex::function - A multi-signature function implementation. +- multifunction - Example from [Boost.TypeErasure](http://www.boost.org/doc/html/boost_typeerasure/examples.html#boost_typeerasure.examples.multifunction), another multi-signature function. +- std::function - [Standard](http://en.cppreference.com/w/cpp/utility/functional/function). +- boost::function - The one from [Boost](http://www.boost.org/doc/libs/1_55_0/doc/html/function.html). +- func::function - From this [blog](http://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction/). +- generic::delegate - [Fast delegate in C++11](http://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11), also see [here](https://code.google.com/p/cpppractice/source/browse/trunk/). +- ssvu::FastFunc - Another Don Clugston's FastDelegate, as shown [here](https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/QgvHF7YMi3o). +- [cxx_function::function](https://github.com/potswa/cxx_function) - By David Krauss + +Also check out the amazing [**CxxFunctionBenchmark**](https://github.com/jamboree/CxxFunctionBenchmark) which compares several implementations. diff --git a/third_party/function2/appveyor.yml b/third_party/function2/appveyor.yml new file mode 100644 index 0000000000..acd6dc2173 --- /dev/null +++ b/third_party/function2/appveyor.yml @@ -0,0 +1,44 @@ +image: + - Visual Studio 2017 + - Visual Studio 2019 + +environment: + matrix: + - FU2_WITH_NO_EXCEPTIONS: OFF + FU2_WITH_CPP_LATEST: OFF + - FU2_WITH_NO_EXCEPTIONS: ON + FU2_WITH_CPP_LATEST: OFF + - FU2_WITH_NO_EXCEPTIONS: OFF + FU2_WITH_CPP_LATEST: ON + - FU2_WITH_NO_EXCEPTIONS: ON + FU2_WITH_CPP_LATEST: ON + +platform: + - x64 + +clone_script: + - cmd: git clone -q --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER% + - cmd: cd %APPVEYOR_BUILD_FOLDER% + - cmd: git checkout -qf %APPVEYOR_REPO_COMMIT% + - cmd: git submodule update --init --recursive + +before_build: + - cmd: > + cmake -H. -Bbuild -A%PLATFORM% + -DFU2_WITH_NO_EXCEPTIONS=%FU2_WITH_NO_EXCEPTIONS% + -DFU2_WITH_CPP_LATEST=%FU2_WITH_CPP_LATEST% + +build_script: + - cmd: > + cmake --build build --config Debug --target ALL_BUILD + -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + /verbosity:minimal /maxcpucount:2 /nologo + - cmd: > + cmake --build build --config Release --target ALL_BUILD + -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + /verbosity:minimal /maxcpucount:2 /nologo + +test_script: + - cmd: cd build + - cmd: ctest -C Debug -V . + - cmd: ctest -C Release -V . diff --git a/third_party/function2/conanfile.py b/third_party/function2/conanfile.py new file mode 100644 index 0000000000..06dfdfe166 --- /dev/null +++ b/third_party/function2/conanfile.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from conans import ConanFile, tools + +def get_version(): + git = tools.Git() + try: + return git.run("describe --tags --abbrev=0") + except: + return None + +class Function2Conan(ConanFile): + name = "function2" + version = get_version() + license = "boost" + url = "https://github.com/Naios/function2" + author = "Denis Blank (denis.blank@outlook.com)" + description = "Improved and configurable drop-in replacement to std::function" + homepage = "http://naios.github.io/function2" + no_copy_source = True + scm = { + "type": "git", + "url": "auto", + "revision": "auto" + } + + def package(self): + self.copy("LICENSE.txt", "licenses") + self.copy("include/function2/function2.hpp") + + def package_id(self): + self.info.header_only() diff --git a/third_party/function2/include/function2/function2.hpp b/third_party/function2/include/function2/function2.hpp new file mode 100644 index 0000000000..542c72ba58 --- /dev/null +++ b/third_party/function2/include/function2/function2.hpp @@ -0,0 +1,1829 @@ + +// Copyright 2015-2020 Denis Blank <denis.blank at outlook dot com> +// Distributed under the Boost Software License, Version 1.0 +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FU2_INCLUDED_FUNCTION2_HPP_ +#define FU2_INCLUDED_FUNCTION2_HPP_ + +#include <cassert> +#include <cstddef> +#include <cstdlib> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> + +// Defines: +// - FU2_HAS_DISABLED_EXCEPTIONS +#if defined(FU2_WITH_DISABLED_EXCEPTIONS) || \ + defined(FU2_MACRO_DISABLE_EXCEPTIONS) +#define FU2_HAS_DISABLED_EXCEPTIONS +#else // FU2_WITH_DISABLED_EXCEPTIONS +#if defined(_MSC_VER) +#if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0) +#define FU2_HAS_DISABLED_EXCEPTIONS +#endif +#elif defined(__clang__) +#if !(__EXCEPTIONS && __has_feature(cxx_exceptions)) +#define FU2_HAS_DISABLED_EXCEPTIONS +#endif +#elif defined(__GNUC__) +#if !__EXCEPTIONS +#define FU2_HAS_DISABLED_EXCEPTIONS +#endif +#endif +#endif // FU2_WITH_DISABLED_EXCEPTIONS +// - FU2_HAS_LIMITED_EMPTY_PROPAGATION +#if defined(FU2_WITH_LIMITED_EMPTY_PROPAGATION) +#define FU2_HAS_LIMITED_EMPTY_PROPAGATION +#endif // FU2_WITH_NO_EMPTY_PROPAGATION +// - FU2_HAS_NO_FUNCTIONAL_HEADER +#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) && \ + !defined(FU2_NO_FUNCTIONAL_HEADER) && \ + (!defined(FU2_HAS_DISABLED_EXCEPTIONS) || \ + defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION)) +#include <functional> +#else +#define FU2_HAS_NO_FUNCTIONAL_HEADER +#endif +// - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE +#if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE) +#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE +#else // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE +#if defined(_MSC_VER) +#if defined(_HAS_CXX17) && _HAS_CXX17 +#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE +#endif +#elif defined(__cpp_noexcept_function_type) +#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE +#endif +#endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE + +// - FU2_HAS_NO_EMPTY_PROPAGATION +#if defined(FU2_WITH_NO_EMPTY_PROPAGATION) +#define FU2_HAS_NO_EMPTY_PROPAGATION +#endif // FU2_WITH_NO_EMPTY_PROPAGATION + +#if !defined(FU2_HAS_DISABLED_EXCEPTIONS) +#include <exception> +#endif + +#if defined(__cpp_constexpr) && (__cpp_constexpr >= 201304) +#define FU2_DETAIL_CXX14_CONSTEXPR constexpr +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(__cxx_generic_lambdas__) && \ + __has_feature(__cxx_relaxed_constexpr__) +#define FU2_DETAIL_CXX14_CONSTEXPR constexpr +#endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1915) && (_MSVC_LANG >= 201402) +#define FU2_DETAIL_CXX14_CONSTEXPR constexpr +#endif +#ifndef FU2_DETAIL_CXX14_CONSTEXPR +#define FU2_DETAIL_CXX14_CONSTEXPR +#endif + +/// Hint for the compiler that this point should be unreachable +#if defined(_MSC_VER) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __assume(false) +#elif defined(__GNUC__) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable() +#elif defined(__has_builtin) +#if __has_builtin(__builtin_unreachable) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable() +#endif +#endif +#ifndef FU2_DETAIL_UNREACHABLE_INTRINSIC +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_UNREACHABLE_INTRINSIC() abort() +#endif + +/// Causes the application to exit abnormally +#if defined(_MSC_VER) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_TRAP() __debugbreak() +#elif defined(__GNUC__) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_TRAP() __builtin_trap() +#elif defined(__has_builtin) +#if __has_builtin(__builtin_trap) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_TRAP() __builtin_trap() +#endif +#endif +#ifndef FU2_DETAIL_TRAP +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_TRAP() *(volatile int*)0x11 = 0 +#endif + +#ifndef NDEBUG +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_UNREACHABLE() ::fu2::detail::unreachable_debug() +#else +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_UNREACHABLE() FU2_DETAIL_UNREACHABLE_INTRINSIC() +#endif + +namespace fu2 { +inline namespace abi_400 { +namespace detail { +template <typename Config, typename Property> +class function; + +template <typename...> +struct identity {}; + +// Equivalent to C++17's std::void_t which targets a bug in GCC, +// that prevents correct SFINAE behavior. +// See http://stackoverflow.com/questions/35753920 for details. +template <typename...> +struct deduce_to_void : std::common_type<void> {}; + +template <typename... T> +using void_t = typename deduce_to_void<T...>::type; + +template <typename T> +using unrefcv_t = std::remove_cv_t<std::remove_reference_t<T>>; + +template <typename...> +struct lazy_and; + +template <typename B1> +struct lazy_and<B1> : B1 {}; + +template <typename B1, typename B2> +struct lazy_and<B1, B2> : std::conditional<B1::value, B2, B1>::type {}; + +// template <typename B1, typename B2, typename B3, typename... Bn> +// struct lazy_and<B1, B2, B3, Bn...> +// : std::conditional<B1::value, lazy_and<B2, B3, Bn...>, B1>::type {}; + +// Copy enabler helper class +template <bool /*Copyable*/> +struct copyable {}; +template <> +struct copyable<false> { + copyable() = default; + ~copyable() = default; + copyable(copyable const&) = delete; + copyable(copyable&&) = default; + copyable& operator=(copyable const&) = delete; + copyable& operator=(copyable&&) = default; +}; + +/// Configuration trait to configure the function_base class. +template <bool Owning, bool Copyable, typename Capacity> +struct config { + // Is true if the function is owning. + static constexpr auto const is_owning = Owning; + + // Is true if the function is copyable. + static constexpr auto const is_copyable = Copyable; + + // The internal capacity of the function + // used in small functor optimization. + // The object shall expose the real capacity through Capacity::capacity + // and the intended alignment through Capacity::alignment. + using capacity = Capacity; +}; + +/// A config which isn't compatible to other configs +template <bool Throws, bool HasStrongExceptGuarantee, typename... Args> +struct property { + // Is true when the function throws an exception on empty invocation. + static constexpr auto const is_throwing = Throws; + + // Is true when the function throws an exception on empty invocation. + static constexpr auto const is_strong_exception_guaranteed = + HasStrongExceptGuarantee; +}; + +#ifndef NDEBUG +[[noreturn]] inline void unreachable_debug() { + FU2_DETAIL_TRAP(); + std::abort(); +} +#endif + +/// Provides utilities for invocing callable objects +namespace invocation { +/// Invokes the given callable object with the given arguments +template <typename Callable, typename... Args> +constexpr auto invoke(Callable&& callable, Args&&... args) noexcept( + noexcept(std::forward<Callable>(callable)(std::forward<Args>(args)...))) + -> decltype(std::forward<Callable>(callable)(std::forward<Args>(args)...)) { + + return std::forward<Callable>(callable)(std::forward<Args>(args)...); +} +/// Invokes the given member function pointer by reference +template <typename T, typename Type, typename Self, typename... Args> +constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( + noexcept((std::forward<Self>(self).*member)(std::forward<Args>(args)...))) + -> decltype((std::forward<Self>(self).* + member)(std::forward<Args>(args)...)) { + return (std::forward<Self>(self).*member)(std::forward<Args>(args)...); +} +/// Invokes the given member function pointer by pointer +template <typename T, typename Type, typename Self, typename... Args> +constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( + noexcept((std::forward<Self>(self)->*member)(std::forward<Args>(args)...))) + -> decltype((std::forward<Self>(self)->*member)( + std::forward<Args>(args)...)) { + return (std::forward<Self>(self)->*member)(std::forward<Args>(args)...); +} +/// Invokes the given pointer to a scalar member by reference +template <typename T, typename Type, typename Self> +constexpr auto +invoke(Type T::*member, + Self&& self) noexcept(noexcept(std::forward<Self>(self).*member)) + -> decltype(std::forward<Self>(self).*member) { + return (std::forward<Self>(self).*member); +} +/// Invokes the given pointer to a scalar member by pointer +template <typename T, typename Type, typename Self> +constexpr auto +invoke(Type T::*member, + Self&& self) noexcept(noexcept(std::forward<Self>(self)->*member)) + -> decltype(std::forward<Self>(self)->*member) { + return std::forward<Self>(self)->*member; +} + +/// Deduces to a true type if the callable object can be invoked with +/// the given arguments. +/// We don't use invoke here because MSVC can't evaluate the nested expression +/// SFINAE here. +template <typename T, typename Args, typename = void> +struct can_invoke : std::false_type {}; +template <typename T, typename... Args> +struct can_invoke<T, identity<Args...>, + decltype((void)std::declval<T>()(std::declval<Args>()...))> + : std::true_type {}; +template <typename Pointer, typename T, typename... Args> +struct can_invoke<Pointer, identity<T&, Args...>, + decltype((void)((std::declval<T&>().*std::declval<Pointer>())( + std::declval<Args>()...)))> : std::true_type {}; +template <typename Pointer, typename T, typename... Args> +struct can_invoke<Pointer, identity<T&&, Args...>, + decltype(( + void)((std::declval<T&&>().*std::declval<Pointer>())( + std::declval<Args>()...)))> : std::true_type {}; +template <typename Pointer, typename T, typename... Args> +struct can_invoke<Pointer, identity<T*, Args...>, + decltype(( + void)((std::declval<T*>()->*std::declval<Pointer>())( + std::declval<Args>()...)))> : std::true_type {}; +template <typename Pointer, typename T> +struct can_invoke<Pointer, identity<T&>, + decltype((void)(std::declval<T&>().*std::declval<Pointer>()))> + : std::true_type {}; +template <typename Pointer, typename T> +struct can_invoke<Pointer, identity<T&&>, + decltype((void)(std::declval<T&&>().* + std::declval<Pointer>()))> : std::true_type { +}; +template <typename Pointer, typename T> +struct can_invoke<Pointer, identity<T*>, + decltype(( + void)(std::declval<T*>()->*std::declval<Pointer>()))> + : std::true_type {}; + +template <bool RequiresNoexcept, typename T, typename Args> +struct is_noexcept_correct : std::true_type {}; +template <typename T, typename... Args> +struct is_noexcept_correct<true, T, identity<Args...>> + : std::integral_constant<bool, + noexcept(::fu2::detail::invocation::invoke( + std::declval<T>(), std::declval<Args>()...))> { +}; +} // end namespace invocation + +namespace overloading { +template <typename... Args> +struct overload_impl; +template <typename Current, typename Next, typename... Rest> +struct overload_impl<Current, Next, Rest...> : Current, + overload_impl<Next, Rest...> { + explicit overload_impl(Current current, Next next, Rest... rest) + : Current(std::move(current)), overload_impl<Next, Rest...>( + std::move(next), std::move(rest)...) { + } + + using Current::operator(); + using overload_impl<Next, Rest...>::operator(); +}; +template <typename Current> +struct overload_impl<Current> : Current { + explicit overload_impl(Current current) : Current(std::move(current)) { + } + + using Current::operator(); +}; + +template <typename... T> +constexpr auto overload(T&&... callables) { + return overload_impl<std::decay_t<T>...>{std::forward<T>(callables)...}; +} +} // namespace overloading + +/// Declares the namespace which provides the functionality to work with a +/// type-erased object. +namespace type_erasure { +/// Specialization to work with addresses of callable objects +template <typename T, typename = void> +struct address_taker { + template <typename O> + static void* take(O&& obj) { + return std::addressof(obj); + } + static T& restore(void* ptr) { + return *static_cast<T*>(ptr); + } + static T const& restore(void const* ptr) { + return *static_cast<T const*>(ptr); + } + static T volatile& restore(void volatile* ptr) { + return *static_cast<T volatile*>(ptr); + } + static T const volatile& restore(void const volatile* ptr) { + return *static_cast<T const volatile*>(ptr); + } +}; +/// Specialization to work with addresses of raw function pointers +template <typename T> +struct address_taker<T, std::enable_if_t<std::is_pointer<T>::value>> { + template <typename O> + static void* take(O&& obj) { + return reinterpret_cast<void*>(obj); + } + template <typename O> + static T restore(O ptr) { + return reinterpret_cast<T>(const_cast<void*>(ptr)); + } +}; + +template <typename Box> +struct box_factory; +/// Store the allocator inside the box +template <bool IsCopyable, typename T, typename Allocator> +struct box : private Allocator { + friend box_factory<box>; + + T value_; + + explicit box(T value, Allocator allocator_) + : Allocator(std::move(allocator_)), value_(std::move(value)) { + } + + box(box&&) = default; + box(box const&) = default; + box& operator=(box&&) = default; + box& operator=(box const&) = default; + ~box() = default; +}; +template <typename T, typename Allocator> +struct box<false, T, Allocator> : private Allocator { + friend box_factory<box>; + + T value_; + + explicit box(T value, Allocator allocator_) + : Allocator(std::move(allocator_)), value_(std::move(value)) { + } + + box(box&&) = default; + box(box const&) = delete; + box& operator=(box&&) = default; + box& operator=(box const&) = delete; + ~box() = default; +}; + +template <bool IsCopyable, typename T, typename Allocator> +struct box_factory<box<IsCopyable, T, Allocator>> { + using real_allocator = + typename std::allocator_traits<std::decay_t<Allocator>>:: + template rebind_alloc<box<IsCopyable, T, Allocator>>; + + /// Allocates space through the boxed allocator + static box<IsCopyable, T, Allocator>* + box_allocate(box<IsCopyable, T, Allocator> const* me) { + real_allocator allocator_(*static_cast<Allocator const*>(me)); + + return static_cast<box<IsCopyable, T, Allocator>*>( + std::allocator_traits<real_allocator>::allocate(allocator_, 1U)); + } + + /// Destroys the box through the given allocator + static void box_deallocate(box<IsCopyable, T, Allocator>* me) { + real_allocator allocator_(*static_cast<Allocator const*>(me)); + + me->~box(); + std::allocator_traits<real_allocator>::deallocate(allocator_, me, 1U); + } +}; + +/// Creates a box containing the given value and allocator +template <bool IsCopyable, typename T, typename Allocator> +auto make_box(std::integral_constant<bool, IsCopyable>, T&& value, + Allocator&& allocator_) { + return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>( + std::forward<T>(value), std::forward<Allocator>(allocator_)); +} + +template <typename T> +struct is_box : std::false_type {}; +template <bool IsCopyable, typename T, typename Allocator> +struct is_box<box<IsCopyable, T, Allocator>> : std::true_type {}; + +/// Provides access to the pointer to a heal allocated erased object +/// as well to the inplace storage. +union data_accessor { + data_accessor() = default; + explicit constexpr data_accessor(std::nullptr_t) noexcept : ptr_(nullptr) { + } + explicit constexpr data_accessor(void* ptr) noexcept : ptr_(ptr) { + } + + /// The pointer we use if the object is on the heap + void* ptr_; + /// The first field of the inplace storage + std::size_t inplace_storage_; +}; + +/// See opcode::op_fetch_empty +static FU2_DETAIL_CXX14_CONSTEXPR void write_empty(data_accessor* accessor, + bool empty) noexcept { + accessor->inplace_storage_ = std::size_t(empty); +} + +template <typename From, typename To> +using transfer_const_t = + std::conditional_t<std::is_const<std::remove_pointer_t<From>>::value, + std::add_const_t<To>, To>; +template <typename From, typename To> +using transfer_volatile_t = + std::conditional_t<std::is_volatile<std::remove_pointer_t<From>>::value, + std::add_volatile_t<To>, To>; + +/// The retriever when the object is allocated inplace +template <typename T, typename Accessor> +FU2_DETAIL_CXX14_CONSTEXPR auto retrieve(std::true_type /*is_inplace*/, + Accessor from, + std::size_t from_capacity) { + using type = transfer_const_t<Accessor, transfer_volatile_t<Accessor, void>>*; + + /// Process the command by using the data inside the internal capacity + auto storage = &(from->inplace_storage_); + auto inplace = const_cast<void*>(static_cast<type>(storage)); + return type(std::align(alignof(T), sizeof(T), inplace, from_capacity)); +} + +/// The retriever which is used when the object is allocated +/// through the allocator +template <typename T, typename Accessor> +constexpr auto retrieve(std::false_type /*is_inplace*/, Accessor from, + std::size_t /*from_capacity*/) { + + return from->ptr_; +} + +namespace invocation_table { +#if !defined(FU2_HAS_DISABLED_EXCEPTIONS) +#if defined(FU2_HAS_NO_FUNCTIONAL_HEADER) +struct bad_function_call : std::exception { + bad_function_call() noexcept { + } + + char const* what() const noexcept override { + return "bad function call"; + } +}; +#else +using std::bad_function_call; +#endif +#endif + +#ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE +#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) \ + F(, , noexcept, , &) \ + F(const, , noexcept, , &) \ + F(, volatile, noexcept, , &) \ + F(const, volatile, noexcept, , &) \ + F(, , noexcept, &, &) \ + F(const, , noexcept, &, &) \ + F(, volatile, noexcept, &, &) \ + F(const, volatile, noexcept, &, &) \ + F(, , noexcept, &&, &&) \ + F(const, , noexcept, &&, &&) \ + F(, volatile, noexcept, &&, &&) \ + F(const, volatile, noexcept, &&, &&) +#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) \ + F(, , noexcept) \ + F(const, , noexcept) \ + F(, volatile, noexcept) \ + F(const, volatile, noexcept) +#else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE +#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) +#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) +#endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE + +#define FU2_DETAIL_EXPAND_QUALIFIERS(F) \ + F(, , , , &) \ + F(const, , , , &) \ + F(, volatile, , , &) \ + F(const, volatile, , , &) \ + F(, , , &, &) \ + F(const, , , &, &) \ + F(, volatile, , &, &) \ + F(const, volatile, , &, &) \ + F(, , , &&, &&) \ + F(const, , , &&, &&) \ + F(, volatile, , &&, &&) \ + F(const, volatile, , &&, &&) \ + FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) +#define FU2_DETAIL_EXPAND_CV(F) \ + F(, , ) \ + F(const, , ) \ + F(, volatile, ) \ + F(const, volatile, ) \ + FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) + +/// If the function is qualified as noexcept, the call will never throw +template <bool IsNoexcept> +[[noreturn]] void throw_or_abortnoexcept( + std::integral_constant<bool, IsNoexcept> /*is_throwing*/) noexcept { + std::abort(); +} +/// Calls std::abort on empty function calls +[[noreturn]] inline void +throw_or_abort(std::false_type /*is_throwing*/) noexcept { + std::abort(); +} +/// Throws bad_function_call on empty funciton calls +[[noreturn]] inline void throw_or_abort(std::true_type /*is_throwing*/) { +#ifdef FU2_HAS_DISABLED_EXCEPTIONS + throw_or_abort(std::false_type{}); +#else + throw bad_function_call{}; +#endif +} + +template <typename T> +struct function_trait; + +using is_noexcept_ = std::false_type; +using is_noexcept_noexcept = std::true_type; + +#define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \ + template <typename Ret, typename... Args> \ + struct function_trait<Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> { \ + using pointer_type = Ret (*)(data_accessor CONST VOLATILE*, \ + std::size_t capacity, Args...); \ + template <typename T, bool IsInplace> \ + struct internal_invoker { \ + static Ret invoke(data_accessor CONST VOLATILE* data, \ + std::size_t capacity, Args... args) NOEXCEPT { \ + auto obj = retrieve<T>(std::integral_constant<bool, IsInplace>{}, \ + data, capacity); \ + auto box = static_cast<T CONST VOLATILE*>(obj); \ + return invocation::invoke( \ + static_cast<std::decay_t<decltype(box->value_)> CONST VOLATILE \ + REF>(box->value_), \ + std::forward<Args>(args)...); \ + } \ + }; \ + \ + template <typename T> \ + struct view_invoker { \ + static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t, \ + Args... args) NOEXCEPT { \ + \ + auto ptr = static_cast<void CONST VOLATILE*>(data->ptr_); \ + return invocation::invoke(address_taker<T>::restore(ptr), \ + std::forward<Args>(args)...); \ + } \ + }; \ + \ + template <typename T> \ + using callable = T CONST VOLATILE REF; \ + \ + using arguments = identity<Args...>; \ + \ + using is_noexcept = is_noexcept_##NOEXCEPT; \ + \ + template <bool Throws> \ + struct empty_invoker { \ + static Ret invoke(data_accessor CONST VOLATILE* /*data*/, \ + std::size_t /*capacity*/, Args... /*args*/) NOEXCEPT { \ + throw_or_abort##NOEXCEPT(std::integral_constant<bool, Throws>{}); \ + } \ + }; \ + }; + +FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) +#undef FU2_DEFINE_FUNCTION_TRAIT + +/// Deduces to the function pointer to the given signature +template <typename Signature> +using function_pointer_of = typename function_trait<Signature>::pointer_type; + +template <typename... Args> +struct invoke_table; + +/// We optimize the vtable_t in case there is a single function overload +template <typename First> +struct invoke_table<First> { + using type = function_pointer_of<First>; + + /// Return the function pointer itself + template <std::size_t Index> + static constexpr auto fetch(type pointer) noexcept { + static_assert(Index == 0U, "The index should be 0 here!"); + return pointer; + } + + /// Returns the thunk of an single overloaded callable + template <typename T, bool IsInplace> + static constexpr type get_invocation_table_of() noexcept { + return &function_trait<First>::template internal_invoker<T, + IsInplace>::invoke; + } + /// Returns the thunk of an single overloaded callable + template <typename T> + static constexpr type get_invocation_view_table_of() noexcept { + return &function_trait<First>::template view_invoker<T>::invoke; + } + /// Returns the thunk of an empty single overloaded callable + template <bool IsThrowing> + static constexpr type get_empty_invocation_table() noexcept { + return &function_trait<First>::template empty_invoker<IsThrowing>::invoke; + } +}; +/// We generate a table in case of multiple function overloads +template <typename First, typename Second, typename... Args> +struct invoke_table<First, Second, Args...> { + using type = + std::tuple<function_pointer_of<First>, function_pointer_of<Second>, + function_pointer_of<Args>...> const*; + + /// Return the function pointer at the particular index + template <std::size_t Index> + static constexpr auto fetch(type table) noexcept { + return std::get<Index>(*table); + } + + /// The invocation vtable for a present object + template <typename T, bool IsInplace> + struct invocation_vtable : public std::tuple<function_pointer_of<First>, + function_pointer_of<Second>, + function_pointer_of<Args>...> { + constexpr invocation_vtable() noexcept + : std::tuple<function_pointer_of<First>, function_pointer_of<Second>, + function_pointer_of<Args>...>(std::make_tuple( + &function_trait<First>::template internal_invoker< + T, IsInplace>::invoke, + &function_trait<Second>::template internal_invoker< + T, IsInplace>::invoke, + &function_trait<Args>::template internal_invoker< + T, IsInplace>::invoke...)) { + } + }; + + /// Returns the thunk of an multi overloaded callable + template <typename T, bool IsInplace> + static type get_invocation_table_of() noexcept { + static invocation_vtable<T, IsInplace> const table; + return &table; + } + + /// The invocation vtable for a present object + template <typename T> + struct invocation_view_vtable + : public std::tuple<function_pointer_of<First>, + function_pointer_of<Second>, + function_pointer_of<Args>...> { + constexpr invocation_view_vtable() noexcept + : std::tuple<function_pointer_of<First>, function_pointer_of<Second>, + function_pointer_of<Args>...>(std::make_tuple( + &function_trait<First>::template view_invoker<T>::invoke, + &function_trait<Second>::template view_invoker<T>::invoke, + &function_trait<Args>::template view_invoker<T>::invoke...)) { + } + }; + + /// Returns the thunk of an multi overloaded callable + template <typename T> + static type get_invocation_view_table_of() noexcept { + static invocation_view_vtable<T> const table; + return &table; + } + + /// The invocation table for an empty wrapper + template <bool IsThrowing> + struct empty_vtable : public std::tuple<function_pointer_of<First>, + function_pointer_of<Second>, + function_pointer_of<Args>...> { + constexpr empty_vtable() noexcept + : std::tuple<function_pointer_of<First>, function_pointer_of<Second>, + function_pointer_of<Args>...>( + std::make_tuple(&function_trait<First>::template empty_invoker< + IsThrowing>::invoke, + &function_trait<Second>::template empty_invoker< + IsThrowing>::invoke, + &function_trait<Args>::template empty_invoker< + IsThrowing>::invoke...)) { + } + }; + + /// Returns the thunk of an multi single overloaded callable + template <bool IsThrowing> + static type get_empty_invocation_table() noexcept { + static empty_vtable<IsThrowing> const table; + return &table; + } +}; + +template <std::size_t Index, typename Function, typename... Signatures> +class operator_impl; + +#define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \ + template <std::size_t Index, typename Function, typename Ret, \ + typename... Args, typename Next, typename... Signatures> \ + class operator_impl<Index, Function, \ + Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT, Next, \ + Signatures...> \ + : operator_impl<Index + 1, Function, Next, Signatures...> { \ + \ + template <std::size_t, typename, typename...> \ + friend class operator_impl; \ + \ + protected: \ + operator_impl() = default; \ + ~operator_impl() = default; \ + operator_impl(operator_impl const&) = default; \ + operator_impl(operator_impl&&) = default; \ + operator_impl& operator=(operator_impl const&) = default; \ + operator_impl& operator=(operator_impl&&) = default; \ + \ + using operator_impl<Index + 1, Function, Next, Signatures...>::operator(); \ + \ + Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \ + auto parent = static_cast<Function CONST VOLATILE*>(this); \ + using erasure_t = std::decay_t<decltype(parent->erasure_)>; \ + \ + /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \ + /* compiler regression of MSVC 16.3.1, see #29 for details. */ \ + return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \ + static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \ + std::forward<Args>(args)...); \ + } \ + }; \ + template <std::size_t Index, typename Config, typename Property, \ + typename Ret, typename... Args> \ + class operator_impl<Index, function<Config, Property>, \ + Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \ + : copyable<!Config::is_owning || Config::is_copyable> { \ + \ + template <std::size_t, typename, typename...> \ + friend class operator_impl; \ + \ + protected: \ + operator_impl() = default; \ + ~operator_impl() = default; \ + operator_impl(operator_impl const&) = default; \ + operator_impl(operator_impl&&) = default; \ + operator_impl& operator=(operator_impl const&) = default; \ + operator_impl& operator=(operator_impl&&) = default; \ + \ + Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \ + auto parent = \ + static_cast<function<Config, Property> CONST VOLATILE*>(this); \ + using erasure_t = std::decay_t<decltype(parent->erasure_)>; \ + \ + /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \ + /* compiler regression of MSVC 16.3.1, see #29 for details. */ \ + return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \ + static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \ + std::forward<Args>(args)...); \ + } \ + }; + +FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) +#undef FU2_DEFINE_FUNCTION_TRAIT +} // namespace invocation_table + +namespace tables { +/// Identifies the action which is dispatched on the erased object +enum class opcode { + op_move, ///< Move the object and set the vtable + op_copy, ///< Copy the object and set the vtable + op_destroy, ///< Destroy the object and reset the vtable + op_weak_destroy, ///< Destroy the object without resetting the vtable + op_fetch_empty, ///< Stores true or false into the to storage + ///< to indicate emptiness +}; + +/// Abstraction for a vtable together with a command table +/// TODO Add optimization for a single formal argument +/// TODO Add optimization to merge both tables if the function is size +/// optimized +template <typename Property> +class vtable; +template <bool IsThrowing, bool HasStrongExceptGuarantee, + typename... FormalArgs> +class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> { + using command_function_t = void (*)(vtable* /*this*/, opcode /*op*/, + data_accessor* /*from*/, + std::size_t /*from_capacity*/, + data_accessor* /*to*/, + std::size_t /*to_capacity*/); + + using invoke_table_t = invocation_table::invoke_table<FormalArgs...>; + + command_function_t cmd_; + typename invoke_table_t::type vtable_; + + template <typename T> + struct trait { + static_assert(is_box<T>::value, + "The trait must be specialized with a box!"); + + /// The command table + template <bool IsInplace> + static void process_cmd(vtable* to_table, opcode op, data_accessor* from, + std::size_t from_capacity, data_accessor* to, + std::size_t to_capacity) { + + switch (op) { + case opcode::op_move: { + /// Retrieve the pointer to the object + auto box = static_cast<T*>(retrieve<T>( + std::integral_constant<bool, IsInplace>{}, from, from_capacity)); + assert(box && "The object must not be over aligned or null!"); + + if (!IsInplace) { + // Just swap both pointers if we allocated on the heap + to->ptr_ = from->ptr_; + +#ifndef NDEBUG + // We don't need to null the pointer since we know that + // we don't own the data anymore through the vtable + // which is set to empty. + from->ptr_ = nullptr; +#endif + + to_table->template set_allocated<T>(); + + } + // The object is allocated inplace + else { + construct(std::true_type{}, std::move(*box), to_table, to, + to_capacity); + box->~T(); + } + return; + } + case opcode::op_copy: { + auto box = static_cast<T const*>(retrieve<T>( + std::integral_constant<bool, IsInplace>{}, from, from_capacity)); + assert(box && "The object must not be over aligned or null!"); + + assert(std::is_copy_constructible<T>::value && + "The box is required to be copyable here!"); + + // Try to allocate the object inplace + construct(std::is_copy_constructible<T>{}, *box, to_table, to, + to_capacity); + return; + } + case opcode::op_destroy: + case opcode::op_weak_destroy: { + + assert(!to && !to_capacity && "Arg overflow!"); + auto box = static_cast<T*>(retrieve<T>( + std::integral_constant<bool, IsInplace>{}, from, from_capacity)); + + if (IsInplace) { + box->~T(); + } else { + box_factory<T>::box_deallocate(box); + } + + if (op == opcode::op_destroy) { + to_table->set_empty(); + } + return; + } + case opcode::op_fetch_empty: { + write_empty(to, false); + return; + } + } + + FU2_DETAIL_UNREACHABLE(); + } + + template <typename Box> + static void + construct(std::true_type /*apply*/, Box&& box, vtable* to_table, + data_accessor* to, + std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) { + // Try to allocate the object inplace + void* storage = retrieve<T>(std::true_type{}, to, to_capacity); + if (storage) { + to_table->template set_inplace<T>(); + } else { + // Allocate the object through the allocator + to->ptr_ = storage = + box_factory<std::decay_t<Box>>::box_allocate(std::addressof(box)); + to_table->template set_allocated<T>(); + } + new (storage) T(std::forward<Box>(box)); + } + + template <typename Box> + static void + construct(std::false_type /*apply*/, Box&& /*box*/, vtable* /*to_table*/, + data_accessor* /*to*/, + std::size_t /*to_capacity*/) noexcept(HasStrongExceptGuarantee) { + } + }; + + /// The command table + static void empty_cmd(vtable* to_table, opcode op, data_accessor* /*from*/, + std::size_t /*from_capacity*/, data_accessor* to, + std::size_t /*to_capacity*/) { + + switch (op) { + case opcode::op_move: + case opcode::op_copy: { + to_table->set_empty(); + break; + } + case opcode::op_destroy: + case opcode::op_weak_destroy: { + // Do nothing + break; + } + case opcode::op_fetch_empty: { + write_empty(to, true); + break; + } + default: { + FU2_DETAIL_UNREACHABLE(); + } + } + } + +public: + vtable() noexcept = default; + + /// Initialize an object at the given position + template <typename T> + static void init(vtable& table, T&& object, data_accessor* to, + std::size_t to_capacity) { + + trait<std::decay_t<T>>::construct(std::true_type{}, std::forward<T>(object), + &table, to, to_capacity); + } + + /// Moves the object at the given position + void move(vtable& to_table, data_accessor* from, std::size_t from_capacity, + data_accessor* to, + std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) { + cmd_(&to_table, opcode::op_move, from, from_capacity, to, to_capacity); + set_empty(); + } + + /// Destroys the object at the given position + void copy(vtable& to_table, data_accessor const* from, + std::size_t from_capacity, data_accessor* to, + std::size_t to_capacity) const { + cmd_(&to_table, opcode::op_copy, const_cast<data_accessor*>(from), + from_capacity, to, to_capacity); + } + + /// Destroys the object at the given position + void destroy(data_accessor* from, + std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) { + cmd_(this, opcode::op_destroy, from, from_capacity, nullptr, 0U); + } + + /// Destroys the object at the given position without invalidating the + /// vtable + void + weak_destroy(data_accessor* from, + std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) { + cmd_(this, opcode::op_weak_destroy, from, from_capacity, nullptr, 0U); + } + + /// Returns true when the vtable doesn't hold any erased object + bool empty() const noexcept { + data_accessor data; + cmd_(nullptr, opcode::op_fetch_empty, nullptr, 0U, &data, 0U); + return bool(data.inplace_storage_); + } + + /// Invoke the function at the given index + template <std::size_t Index, typename... Args> + constexpr decltype(auto) invoke(Args&&... args) const { + auto thunk = invoke_table_t::template fetch<Index>(vtable_); + return thunk(std::forward<Args>(args)...); + } + /// Invoke the function at the given index + template <std::size_t Index, typename... Args> + constexpr decltype(auto) invoke(Args&&... args) const volatile { + auto thunk = invoke_table_t::template fetch<Index>(vtable_); + return thunk(std::forward<Args>(args)...); + } + + template <typename T> + void set_inplace() noexcept { + using type = std::decay_t<T>; + vtable_ = invoke_table_t::template get_invocation_table_of<type, true>(); + cmd_ = &trait<type>::template process_cmd<true>; + } + + template <typename T> + void set_allocated() noexcept { + using type = std::decay_t<T>; + vtable_ = invoke_table_t::template get_invocation_table_of<type, false>(); + cmd_ = &trait<type>::template process_cmd<false>; + } + + void set_empty() noexcept { + vtable_ = invoke_table_t::template get_empty_invocation_table<IsThrowing>(); + cmd_ = &empty_cmd; + } +}; +} // namespace tables + +/// A union which makes the pointer to the heap object share the +/// same space with the internal capacity. +/// The storage type is distinguished by multiple versions of the +/// control and vtable. +template <typename Capacity, typename = void> +struct internal_capacity { + /// We extend the union through a technique similar to the tail object hack + typedef union { + /// Tag to access the structure in a type-safe way + data_accessor accessor_; + /// The internal capacity we use to allocate in-place + std::aligned_storage_t<Capacity::capacity, Capacity::alignment> capacity_; + } type; +}; +template <typename Capacity> +struct internal_capacity< + Capacity, std::enable_if_t<(Capacity::capacity < sizeof(void*))>> { + typedef struct { + /// Tag to access the structure in a type-safe way + data_accessor accessor_; + } type; +}; + +template <typename Capacity> +class internal_capacity_holder { + // Tag to access the structure in a type-safe way + typename internal_capacity<Capacity>::type storage_; + +public: + constexpr internal_capacity_holder() = default; + + FU2_DETAIL_CXX14_CONSTEXPR data_accessor* opaque_ptr() noexcept { + return &storage_.accessor_; + } + constexpr data_accessor const* opaque_ptr() const noexcept { + return &storage_.accessor_; + } + FU2_DETAIL_CXX14_CONSTEXPR data_accessor volatile* + opaque_ptr() volatile noexcept { + return &storage_.accessor_; + } + constexpr data_accessor const volatile* opaque_ptr() const volatile noexcept { + return &storage_.accessor_; + } + + static constexpr std::size_t capacity() noexcept { + return sizeof(storage_); + } +}; + +/// An owning erasure +template <bool IsOwning /* = true*/, typename Config, typename Property> +class erasure : internal_capacity_holder<typename Config::capacity> { + template <bool, typename, typename> + friend class erasure; + template <std::size_t, typename, typename...> + friend class operator_impl; + + using vtable_t = tables::vtable<Property>; + + vtable_t vtable_; + +public: + /// Returns the capacity of this erasure + static constexpr std::size_t capacity() noexcept { + return internal_capacity_holder<typename Config::capacity>::capacity(); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure() noexcept { + vtable_.set_empty(); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure(std::nullptr_t) noexcept { + vtable_.set_empty(); + } + + FU2_DETAIL_CXX14_CONSTEXPR + erasure(erasure&& right) noexcept(Property::is_strong_exception_guaranteed) { + right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure(erasure const& right) { + right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + } + + template <typename OtherConfig> + FU2_DETAIL_CXX14_CONSTEXPR + erasure(erasure<true, OtherConfig, Property> right) noexcept( + Property::is_strong_exception_guaranteed) { + right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + } + + template <typename T, typename Allocator = std::allocator<std::decay_t<T>>> + FU2_DETAIL_CXX14_CONSTEXPR erasure(std::false_type /*use_bool_op*/, + T&& callable, + Allocator&& allocator_ = Allocator{}) { + vtable_t::init(vtable_, + type_erasure::make_box( + std::integral_constant<bool, Config::is_copyable>{}, + std::forward<T>(callable), + std::forward<Allocator>(allocator_)), + this->opaque_ptr(), capacity()); + } + template <typename T, typename Allocator = std::allocator<std::decay_t<T>>> + FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type /*use_bool_op*/, + T&& callable, + Allocator&& allocator_ = Allocator{}) { + if (!!callable) { + vtable_t::init(vtable_, + type_erasure::make_box( + std::integral_constant<bool, Config::is_copyable>{}, + std::forward<T>(callable), + std::forward<Allocator>(allocator_)), + this->opaque_ptr(), capacity()); + } else { + vtable_.set_empty(); + } + } + + ~erasure() { + vtable_.weak_destroy(this->opaque_ptr(), capacity()); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure& + operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) { + vtable_.destroy(this->opaque_ptr(), capacity()); + return *this; + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure&& right) noexcept( + Property::is_strong_exception_guaranteed) { + vtable_.weak_destroy(this->opaque_ptr(), capacity()); + right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + return *this; + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure const& right) { + vtable_.weak_destroy(this->opaque_ptr(), capacity()); + right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + return *this; + } + + template <typename OtherConfig> + FU2_DETAIL_CXX14_CONSTEXPR erasure& + operator=(erasure<true, OtherConfig, Property> right) noexcept( + Property::is_strong_exception_guaranteed) { + vtable_.weak_destroy(this->opaque_ptr(), capacity()); + right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + return *this; + } + + template <typename T, typename Allocator = std::allocator<std::decay_t<T>>> + void assign(std::false_type /*use_bool_op*/, T&& callable, + Allocator&& allocator_ = {}) { + vtable_.weak_destroy(this->opaque_ptr(), capacity()); + vtable_t::init(vtable_, + type_erasure::make_box( + std::integral_constant<bool, Config::is_copyable>{}, + std::forward<T>(callable), + std::forward<Allocator>(allocator_)), + this->opaque_ptr(), capacity()); + } + + template <typename T, typename Allocator = std::allocator<std::decay_t<T>>> + void assign(std::true_type /*use_bool_op*/, T&& callable, + Allocator&& allocator_ = {}) { + if (bool(callable)) { + assign(std::false_type{}, std::forward<T>(callable), + std::forward<Allocator>(allocator_)); + } else { + operator=(nullptr); + } + } + + /// Returns true when the erasure doesn't hold any erased object + constexpr bool empty() const noexcept { + return vtable_.empty(); + } + + /// Invoke the function of the erasure at the given index + /// + /// We define this out of class to be able to forward the qualified + /// erasure correctly. + template <std::size_t Index, typename Erasure, typename... Args> + static constexpr decltype(auto) invoke(Erasure&& erasure, Args&&... args) { + auto const capacity = erasure.capacity(); + return erasure.vtable_.template invoke<Index>( + std::forward<Erasure>(erasure).opaque_ptr(), capacity, + std::forward<Args>(args)...); + } +}; + +// A non owning erasure +template </*bool IsOwning = false, */ typename Config, bool IsThrowing, + bool HasStrongExceptGuarantee, typename... Args> +class erasure<false, Config, + property<IsThrowing, HasStrongExceptGuarantee, Args...>> { + template <bool, typename, typename> + friend class erasure; + template <std::size_t, typename, typename...> + friend class operator_impl; + + using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>; + + using invoke_table_t = invocation_table::invoke_table<Args...>; + typename invoke_table_t::type invoke_table_; + + /// The internal pointer to the non owned object + data_accessor view_; + +public: + // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) + constexpr erasure() noexcept + : invoke_table_( + invoke_table_t::template get_empty_invocation_table<IsThrowing>()), + view_(nullptr) { + } + + // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) + constexpr erasure(std::nullptr_t) noexcept + : invoke_table_( + invoke_table_t::template get_empty_invocation_table<IsThrowing>()), + view_(nullptr) { + } + + // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) + constexpr erasure(erasure&& right) noexcept + : invoke_table_(right.invoke_table_), view_(right.view_) { + } + + constexpr erasure(erasure const& /*right*/) = default; + + template <typename OtherConfig> + // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) + constexpr erasure(erasure<false, OtherConfig, property_t> right) noexcept + : invoke_table_(right.invoke_table_), view_(right.view_) { + } + + template <typename T> + // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) + constexpr erasure(std::false_type /*use_bool_op*/, T&& object) + : invoke_table_(invoke_table_t::template get_invocation_view_table_of< + std::decay_t<T>>()), + view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) { + } + template <typename T> + // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) + FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type use_bool_op, T&& object) { + this->assign(use_bool_op, std::forward<T>(object)); + } + + ~erasure() = default; + + constexpr erasure& + operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) { + invoke_table_ = + invoke_table_t::template get_empty_invocation_table<IsThrowing>(); + view_.ptr_ = nullptr; + return *this; + } + + constexpr erasure& operator=(erasure&& right) noexcept { + invoke_table_ = right.invoke_table_; + view_ = right.view_; + right = nullptr; + return *this; + } + + constexpr erasure& operator=(erasure const& /*right*/) = default; + + template <typename OtherConfig> + constexpr erasure& + operator=(erasure<true, OtherConfig, property_t> right) noexcept { + invoke_table_ = right.invoke_table_; + view_ = right.view_; + return *this; + } + + template <typename T> + constexpr void assign(std::false_type /*use_bool_op*/, T&& callable) { + invoke_table_ = invoke_table_t::template get_invocation_view_table_of< + std::decay_t<T>>(); + view_.ptr_ = + address_taker<std::decay_t<T>>::take(std::forward<T>(callable)); + } + template <typename T> + constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) { + if (bool(callable)) { + assign(std::false_type{}, std::forward<T>(callable)); + } else { + operator=(nullptr); + } + } + + /// Returns true when the erasure doesn't hold any erased object + constexpr bool empty() const noexcept { + return view_.ptr_ == nullptr; + } + + template <std::size_t Index, typename Erasure, typename... T> + static constexpr decltype(auto) invoke(Erasure&& erasure, T&&... args) { + auto thunk = invoke_table_t::template fetch<Index>(erasure.invoke_table_); + return thunk(&(erasure.view_), 0UL, std::forward<T>(args)...); + } +}; +} // namespace type_erasure + +/// Deduces to a true_type if the type T provides the given signature and the +/// signature is noexcept correct callable. +template <typename T, typename Signature, + typename Trait = + type_erasure::invocation_table::function_trait<Signature>> +struct accepts_one + : detail::lazy_and< // both are std::integral_constant + invocation::can_invoke<typename Trait::template callable<T>, + typename Trait::arguments>, + invocation::is_noexcept_correct<Trait::is_noexcept::value, + typename Trait::template callable<T>, + typename Trait::arguments>> {}; + +/// Deduces to a true_type if the type T provides all signatures +template <typename T, typename Signatures, typename = void> +struct accepts_all : std::false_type {}; +template <typename T, typename... Signatures> +struct accepts_all< + T, identity<Signatures...>, + void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>> + : std::true_type {}; + +#if defined(FU2_HAS_NO_EMPTY_PROPAGATION) +template <typename T> +struct use_bool_op : std::false_type {}; +#elif defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION) +/// Implementation for use_bool_op based on the behaviour of std::function, +/// propagating empty state for pointers, `std::function` and +/// `fu2::detail::function` types only. +template <typename T> +struct use_bool_op : std::false_type {}; + +#if !defined(FU2_HAS_NO_FUNCTIONAL_HEADER) +template <typename Signature> +struct use_bool_op<std::function<Signature>> : std::true_type {}; +#endif + +template <typename Config, typename Property> +struct use_bool_op<function<Config, Property>> : std::true_type {}; + +template <typename T> +struct use_bool_op<T*> : std::true_type {}; + +template <typename Class, typename T> +struct use_bool_op<T Class::*> : std::true_type {}; +#else +template <typename T, typename = void> +struct has_bool_op : std::false_type {}; +template <typename T> +struct has_bool_op<T, void_t<decltype(bool(std::declval<T>()))>> + : std::true_type { +#ifndef NDEBUG + static_assert(!std::is_pointer<T>::value, + "Missing deduction for function pointer!"); +#endif +}; + +/// Deduces to a true_type if the type T is implementing operator bool() +/// or if the type is convertible to bool directly, this also implements an +/// optimizations for function references `void(&)()` which are can never +/// be null and for such a conversion to bool would never return false. +template <typename T> +struct use_bool_op : has_bool_op<T> {}; + +#define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, NOEXCEPT) \ + template <typename Ret, typename... Args> \ + struct use_bool_op<Ret (*CONST VOLATILE)(Args...) NOEXCEPT> \ + : std::true_type {}; + +FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT) +#undef FU2_DEFINE_USE_OP_TRAIT + +template <typename Ret, typename... Args> +struct use_bool_op<Ret(Args...)> : std::false_type {}; + +#if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE) +template <typename Ret, typename... Args> +struct use_bool_op<Ret(Args...) noexcept> : std::false_type {}; +#endif +#endif // FU2_HAS_NO_EMPTY_PROPAGATION + +template <typename Config, typename T> +struct assert_wrong_copy_assign { + static_assert(!Config::is_owning || !Config::is_copyable || + std::is_copy_constructible<std::decay_t<T>>::value, + "Can't wrap a non copyable object into a unique function!"); + + using type = void; +}; + +template <bool IsStrongExceptGuaranteed, typename T> +struct assert_no_strong_except_guarantee { + static_assert( + !IsStrongExceptGuaranteed || + (std::is_nothrow_move_constructible<T>::value && + std::is_nothrow_destructible<T>::value), + "Can't wrap a object an object that has no strong exception guarantees " + "if this is required by the wrapper!"); + + using type = void; +}; + +/// SFINAES out if the given callable is not copyable correct to the left one. +template <typename LeftConfig, typename RightConfig> +using enable_if_copyable_correct_t = + std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>; + +template <typename LeftConfig, typename RightConfig> +using is_owning_correct = + std::integral_constant<bool, + (LeftConfig::is_owning == RightConfig::is_owning)>; + +/// SFINAES out if the given function2 is not owning correct to this one +template <typename LeftConfig, typename RightConfig> +using enable_if_owning_correct_t = + std::enable_if_t<is_owning_correct<LeftConfig, RightConfig>::value>; + +template <typename Config, bool IsThrowing, bool HasStrongExceptGuarantee, + typename... Args> +class function<Config, property<IsThrowing, HasStrongExceptGuarantee, Args...>> + : type_erasure::invocation_table::operator_impl< + 0U, + function<Config, + property<IsThrowing, HasStrongExceptGuarantee, Args...>>, + Args...> { + + template <typename, typename> + friend class function; + + template <std::size_t, typename, typename...> + friend class type_erasure::invocation_table::operator_impl; + + using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>; + using erasure_t = + type_erasure::erasure<Config::is_owning, Config, property_t>; + + template <typename T> + using enable_if_can_accept_all_t = + std::enable_if_t<accepts_all<std::decay_t<T>, identity<Args...>>::value>; + + template <typename Function, typename = void> + struct is_convertible_to_this : std::false_type {}; + template <typename RightConfig> + struct is_convertible_to_this< + function<RightConfig, property_t>, + void_t<enable_if_copyable_correct_t<Config, RightConfig>, + enable_if_owning_correct_t<Config, RightConfig>>> + : std::true_type {}; + + template <typename T> + using enable_if_not_convertible_to_this = + std::enable_if_t<!is_convertible_to_this<std::decay_t<T>>::value>; + + template <typename T> + using enable_if_owning_t = + std::enable_if_t<std::is_same<T, T>::value && Config::is_owning>; + + template <typename T> + using assert_wrong_copy_assign_t = + typename assert_wrong_copy_assign<Config, std::decay_t<T>>::type; + + template <typename T> + using assert_no_strong_except_guarantee_t = + typename assert_no_strong_except_guarantee<HasStrongExceptGuarantee, + std::decay_t<T>>::type; + + erasure_t erasure_; + +public: + /// Default constructor which empty constructs the function + function() = default; + ~function() = default; + + explicit FU2_DETAIL_CXX14_CONSTEXPR + function(function const& /*right*/) = default; + explicit FU2_DETAIL_CXX14_CONSTEXPR function(function&& /*right*/) = default; + + /// Copy construction from another copyable function + template <typename RightConfig, + std::enable_if_t<RightConfig::is_copyable>* = nullptr, + enable_if_copyable_correct_t<Config, RightConfig>* = nullptr, + enable_if_owning_correct_t<Config, RightConfig>* = nullptr> + FU2_DETAIL_CXX14_CONSTEXPR + function(function<RightConfig, property_t> const& right) + : erasure_(right.erasure_) { + } + + /// Move construction from another function + template <typename RightConfig, + enable_if_copyable_correct_t<Config, RightConfig>* = nullptr, + enable_if_owning_correct_t<Config, RightConfig>* = nullptr> + FU2_DETAIL_CXX14_CONSTEXPR function(function<RightConfig, property_t>&& right) + : erasure_(std::move(right.erasure_)) { + } + + /// Construction from a callable object which overloads the `()` operator + template <typename T, // + enable_if_not_convertible_to_this<T>* = nullptr, + enable_if_can_accept_all_t<T>* = nullptr, + assert_wrong_copy_assign_t<T>* = nullptr, + assert_no_strong_except_guarantee_t<T>* = nullptr> + FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable) + : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable)) { + } + template <typename T, typename Allocator, // + enable_if_not_convertible_to_this<T>* = nullptr, + enable_if_can_accept_all_t<T>* = nullptr, + enable_if_owning_t<T>* = nullptr, + assert_wrong_copy_assign_t<T>* = nullptr, + assert_no_strong_except_guarantee_t<T>* = nullptr> + FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable, Allocator&& allocator_) + : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable), + std::forward<Allocator>(allocator_)) { + } + + /// Empty constructs the function + FU2_DETAIL_CXX14_CONSTEXPR function(std::nullptr_t np) : erasure_(np) { + } + + function& operator=(function const& /*right*/) = default; + function& operator=(function&& /*right*/) = default; + + /// Copy assigning from another copyable function + template <typename RightConfig, + std::enable_if_t<RightConfig::is_copyable>* = nullptr, + enable_if_copyable_correct_t<Config, RightConfig>* = nullptr, + enable_if_owning_correct_t<Config, RightConfig>* = nullptr> + function& operator=(function<RightConfig, property_t> const& right) { + erasure_ = right.erasure_; + return *this; + } + + /// Move assigning from another function + template <typename RightConfig, + enable_if_copyable_correct_t<Config, RightConfig>* = nullptr, + enable_if_owning_correct_t<Config, RightConfig>* = nullptr> + function& operator=(function<RightConfig, property_t>&& right) { + erasure_ = std::move(right.erasure_); + return *this; + } + + /// Move assigning from a callable object + template <typename T, // ... + enable_if_not_convertible_to_this<T>* = nullptr, + enable_if_can_accept_all_t<T>* = nullptr, + assert_wrong_copy_assign_t<T>* = nullptr, + assert_no_strong_except_guarantee_t<T>* = nullptr> + function& operator=(T&& callable) { + erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable)); + return *this; + } + + /// Clears the function + function& operator=(std::nullptr_t np) { + erasure_ = np; + return *this; + } + + /// Returns true when the function is empty + bool empty() const noexcept { + return erasure_.empty(); + } + + /// Returns true when the function isn't empty + explicit operator bool() const noexcept { + return !empty(); + } + + /// Assigns a new target with an optional allocator + template <typename T, typename Allocator = std::allocator<std::decay_t<T>>, + enable_if_not_convertible_to_this<T>* = nullptr, + enable_if_can_accept_all_t<T>* = nullptr, + assert_wrong_copy_assign_t<T>* = nullptr, + assert_no_strong_except_guarantee_t<T>* = nullptr> + void assign(T&& callable, Allocator&& allocator_ = Allocator{}) { + erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable), + std::forward<Allocator>(allocator_)); + } + + /// Swaps this function with the given function + void swap(function& other) noexcept(HasStrongExceptGuarantee) { + if (&other == this) { + return; + } + + function cache = std::move(other); + other = std::move(*this); + *this = std::move(cache); + } + + /// Swaps the left function with the right one + friend void swap(function& left, + function& right) noexcept(HasStrongExceptGuarantee) { + left.swap(right); + } + + /// Calls the wrapped callable object + using type_erasure::invocation_table::operator_impl< + 0U, function<Config, property_t>, Args...>::operator(); +}; + +template <typename Config, typename Property> +bool operator==(function<Config, Property> const& f, std::nullptr_t) { + return !bool(f); +} + +template <typename Config, typename Property> +bool operator!=(function<Config, Property> const& f, std::nullptr_t) { + return bool(f); +} + +template <typename Config, typename Property> +bool operator==(std::nullptr_t, function<Config, Property> const& f) { + return !bool(f); +} + +template <typename Config, typename Property> +bool operator!=(std::nullptr_t, function<Config, Property> const& f) { + return bool(f); +} + +// Default intended object size of the function +using object_size = std::integral_constant<std::size_t, 32U>; +} // namespace detail +} // namespace abi_400 + +/// Can be passed to function_base as template argument which causes +/// the internal small buffer to be sized according to the given size, +/// and aligned with the given alignment. +template <std::size_t Capacity, + std::size_t Alignment = alignof(std::max_align_t)> +struct capacity_fixed { + static constexpr std::size_t capacity = Capacity; + static constexpr std::size_t alignment = Alignment; +}; + +/// Default capacity for small functor optimization +struct capacity_default + : capacity_fixed<detail::object_size::value - (2 * sizeof(void*))> {}; + +/// Can be passed to function_base as template argument which causes +/// the internal small buffer to be removed from the callable wrapper. +/// The owning function_base will then allocate memory for every object +/// it applies a type erasure on. +struct capacity_none : capacity_fixed<0UL> {}; + +/// Can be passed to function_base as template argument which causes +/// the internal small buffer to be sized such that it can hold +/// the given object without allocating memory for an applied type erasure. +template <typename T> +struct capacity_can_hold { + static constexpr std::size_t capacity = sizeof(T); + static constexpr std::size_t alignment = alignof(T); +}; + +/// An adaptable function wrapper base for arbitrary functional types. +/// +/// \tparam IsOwning Is true when the type erasure shall be owning the object. +/// +/// \tparam IsCopyable Defines whether the function is copyable or not +/// +/// \tparam Capacity Defines the internal capacity of the function +/// for small functor optimization. +/// The size of the whole function object will be the capacity +/// plus the size of two pointers. If the capacity is zero, +/// the size will increase through one additional pointer +/// so the whole object has the size of 3 * sizeof(void*). +/// The type which is passed to the Capacity template parameter +/// shall provide a capacity and alignment member which +/// looks like the following example: +/// ```cpp +/// struct my_capacity { +/// static constexpr std::size_t capacity = sizeof(my_type); +/// static constexpr std::size_t alignment = alignof(my_type); +/// }; +/// ``` +/// +/// \tparam IsThrowing Defines whether the function throws an exception on +/// empty function call, `std::abort` is called otherwise. +/// +/// \tparam HasStrongExceptGuarantee Defines whether all objects satisfy the +/// strong exception guarantees, +/// which means the function type will satisfy +/// the strong exception guarantees too. +/// +/// \tparam Signatures Defines the signature of the callable wrapper +/// +template <bool IsOwning, bool IsCopyable, typename Capacity, bool IsThrowing, + bool HasStrongExceptGuarantee, typename... Signatures> +using function_base = detail::function< + detail::config<IsOwning, IsCopyable, Capacity>, + detail::property<IsThrowing, HasStrongExceptGuarantee, Signatures...>>; + +/// An owning copyable function wrapper for arbitrary callable types. +template <typename... Signatures> +using function = function_base<true, true, capacity_default, // + true, false, Signatures...>; + +/// An owning non copyable function wrapper for arbitrary callable types. +template <typename... Signatures> +using unique_function = function_base<true, false, capacity_default, // + true, false, Signatures...>; + +/// A non owning copyable function wrapper for arbitrary callable types. +template <typename... Signatures> +using function_view = function_base<false, true, capacity_default, // + true, false, Signatures...>; + +#if !defined(FU2_HAS_DISABLED_EXCEPTIONS) +/// Exception type that is thrown when invoking empty function objects +/// and exception support isn't disabled. +/// +/// Exception support is enabled if +/// the template parameter 'Throwing' is set to true (default). +/// +/// This type will default to std::bad_function_call if the +/// functional header is used, otherwise the library provides its own type. +/// +/// You may disable the inclusion of the functional header +/// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`. +/// +using detail::type_erasure::invocation_table::bad_function_call; +#endif + +/// Returns a callable object, which unifies all callable objects +/// that were passed to this function. +/// +/// ```cpp +/// auto overloaded = fu2::overload([](std::true_type) { return true; }, +/// [](std::false_type) { return false; }); +/// ``` +/// +/// \param callables A pack of callable objects with arbitrary signatures. +/// +/// \returns A callable object which exposes the +/// +template <typename... T> +constexpr auto overload(T&&... callables) { + return detail::overloading::overload(std::forward<T>(callables)...); +} +} // namespace fu2 + +#undef FU2_DETAIL_EXPAND_QUALIFIERS +#undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT +#undef FU2_DETAIL_EXPAND_CV +#undef FU2_DETAIL_EXPAND_CV_NOEXCEPT +#undef FU2_DETAIL_UNREACHABLE_INTRINSIC +#undef FU2_DETAIL_TRAP +#undef FU2_DETAIL_CXX14_CONSTEXPR + +#endif // FU2_INCLUDED_FUNCTION2_HPP_ diff --git a/third_party/function2/moz.yaml b/third_party/function2/moz.yaml new file mode 100644 index 0000000000..74f825d3de --- /dev/null +++ b/third_party/function2/moz.yaml @@ -0,0 +1,37 @@ +schema: 1 + +bugzilla: + product: Core + component: MFBT + +origin: + name: function2 + description: Improved and configurable drop-in replacement to std::function + + url: https://naios.github.io/function2/ + + release: 2d3a878ef19dd5d2fb188898513610fac0a48621 (2022-08-17T08:11:51Z). + + revision: 2d3a878ef19dd5d2fb188898513610fac0a48621 + + license: BSL-1.0 + license-file: LICENSE.txt + +updatebot: + maintainer-phab: nika + maintainer-bz: nika@thelayzells.com + tasks: + - type: vendoring + enabled: True + frequency: every + +vendoring: + url: https://github.com/Naios/function2 + source-hosting: github + tracking: commit + + exclude: + - .github/ + - cmake/ + - test/ + - tools/ |