├── .clang-format ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── Findfunction2.cmake ├── LICENSE.txt ├── Readme.md ├── appveyor.yml ├── cmake ├── CMakeLists.txt ├── compiler │ ├── clang.cmake │ ├── gcc.cmake │ └── msvc.cmake ├── config.cmake.in └── configure_compiler.cmake ├── conanfile.py ├── include └── function2 │ └── function2.hpp ├── test ├── CMakeLists.txt ├── assign-and-constructible-test.cpp ├── build-test.cpp ├── dist-package │ ├── CMakeLists.txt │ └── main.cpp ├── dist-subproject │ ├── CMakeLists.txt │ └── main.cpp ├── empty-function-call-test.cpp ├── function2-test.hpp ├── functionality-test.cpp ├── multi-signature-test.cpp ├── noexcept-test.cpp ├── overload-test.cpp ├── playground.cpp ├── regressions.cpp ├── self-containing-test.cpp ├── standard-compliant-test.cpp ├── type-test.cpp └── view-test.cpp └── tools └── travis-ci.sh /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | PointerAlignment: Left 4 | IndentCaseLabels: true 5 | AllowShortFunctionsOnASingleLine: false 6 | AllowShortCaseLabelsOnASingleLine: false 7 | AlwaysBreakTemplateDeclarations: true 8 | BinPackArguments: true 9 | FixNamespaceComments: true 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | indent_size = 2 4 | tab_width = 2 5 | indent_style = space 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | max_line_length = 80 9 | 10 | [*.{cpp,hpp}] 11 | charset = latin1 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | #sources 2 | *.c text 3 | *.cc text 4 | *.cxx text 5 | *.cpp text 6 | *.c++ text 7 | *.hpp text 8 | *.h text 9 | *.h++ text 10 | *.hh text 11 | 12 | # Compiled Object files 13 | *.slo binary 14 | *.lo binary 15 | *.o binary 16 | *.obj binary 17 | 18 | # Precompiled Headers 19 | *.gch binary 20 | *.pch binary 21 | 22 | # Compiled Dynamic libraries 23 | *.so binary 24 | *.dylib binary 25 | *.dll binary 26 | 27 | # Compiled Static libraries 28 | *.lai binary 29 | *.la binary 30 | *.a binary 31 | *.lib binary 32 | 33 | # Executables 34 | *.exe binary 35 | *.out binary 36 | *.app binary 37 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Naios 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thank you for contributing to function2! 2 | ========================================= 3 | 4 | ## Notifications 5 | 6 | Usually I try to respond to issues and pull requests as fast as possible. In case you are waiting longer for my response, you may notify me through mail: `denis.blank outlook com`. 7 | 8 | ## Templates 9 | 10 | Please use the issue/PR templates which are inserted automatically. 11 | 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | @Naios 2 | 3 | 4 | 5 | 6 | 7 | ----- 8 | 9 | ### Commit Hash 10 | 11 | {Please write here} 12 | 13 | ### Expected Behavior 14 | 15 | {Please write here} 16 | 17 | ### Actual Behavior 18 | 19 | {Please write here} 20 | 21 | ### Steps to Reproduce 22 | 23 | {Please write here} 24 | 25 | ### Your Environment 26 | 27 | - OS: {Please write here - Windows/Linux dist/OSX} 28 | - Compiler and version: {Please write here - MSVC/Clang/GCC/Other} 29 | - Standard library (if non default): {Please write here} 30 | 31 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | @Naios 2 | 3 | 4 | 5 | ----- 6 | 7 | ### What was a problem? 8 | 9 | {Please write here} 10 | 11 | ### How this PR fixes the problem? 12 | 13 | {Please write here} 14 | 15 | ### Check lists (check `x` in `[ ]` of list items) 16 | 17 | - [ ] Additional Unit Tests were added that test the feature or regression 18 | - [ ] Coding style (Clang format was applied) 19 | 20 | ### Additional Comments (if any) 21 | 22 | {Please write here} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # User-specific files 31 | *.suo 32 | *.user 33 | *.userosscache 34 | *.sln.docstates 35 | 36 | # Build results 37 | [Dd]ebug/ 38 | [Dd]ebugPublic/ 39 | [Rr]elease/ 40 | [Rr]eleases/ 41 | x64/ 42 | x86/ 43 | build/ 44 | bld/ 45 | [Bb]in/ 46 | [Oo]bj/ 47 | 48 | # Visual Studo 2015 cache/options directory 49 | .vs/ 50 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/googletest"] 2 | path = test/googletest 3 | url = https://github.com/google/googletest.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | dist: trusty 3 | language: cpp 4 | cache: apt 5 | 6 | git: 7 | depth: 1 8 | 9 | matrix: 10 | include: 11 | - os: linux 12 | compiler: gcc 13 | addons: 14 | apt: 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | packages: 18 | - g++-5 19 | - valgrind 20 | - ninja-build 21 | env: 22 | - COMPILER=g++-5 23 | - NO_EXCEPTIONS=OFF 24 | 25 | - os: linux 26 | compiler: gcc 27 | addons: 28 | apt: 29 | sources: 30 | - ubuntu-toolchain-r-test 31 | packages: 32 | - g++-6 33 | - valgrind 34 | - ninja-build 35 | env: 36 | - COMPILER=g++-6 37 | - NO_EXCEPTIONS=OFF 38 | 39 | - os: linux 40 | compiler: gcc 41 | addons: 42 | apt: 43 | sources: 44 | - ubuntu-toolchain-r-test 45 | packages: 46 | - g++-6 47 | - valgrind 48 | - ninja-build 49 | env: 50 | - COMPILER=g++-6 51 | - NO_EXCEPTIONS=ON 52 | 53 | - os: linux 54 | compiler: clang 55 | addons: 56 | apt: 57 | sources: 58 | - ubuntu-toolchain-r-test 59 | - llvm-toolchain-trusty-5.0 60 | packages: 61 | - g++-6 62 | - clang-5.0 63 | - ninja-build 64 | env: 65 | - COMPILER=clang++-5.0 66 | - NO_EXCEPTIONS=OFF 67 | 68 | - os: linux 69 | compiler: clang 70 | addons: 71 | apt: 72 | sources: 73 | - ubuntu-toolchain-r-test 74 | - llvm-toolchain-trusty-5.0 75 | packages: 76 | - g++-6 77 | - clang-5.0 78 | - ninja-build 79 | env: 80 | - COMPILER=clang++-5.0 81 | - NO_EXCEPTIONS=ON 82 | 83 | 84 | install: 85 | - export CXX=$COMPILER 86 | - $CXX --version 87 | - chmod +x tools/travis-ci.sh 88 | 89 | script: 90 | - ./tools/travis-ci.sh 91 | 92 | notifications: 93 | email: false 94 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | project(function2 VERSION 4.2.0 LANGUAGES CXX) 3 | 4 | if (NOT FU2_IS_FIND_INCLUDED) 5 | string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} 6 | FU2_IS_TOP_LEVEL_PROJECT) 7 | endif() 8 | 9 | if (FU2_IS_TOP_LEVEL_PROJECT) 10 | add_library(function2 INTERFACE) 11 | else() 12 | add_library(function2 INTERFACE IMPORTED GLOBAL) 13 | endif() 14 | 15 | add_library(function2::function2 ALIAS function2) 16 | 17 | target_include_directories(function2 18 | INTERFACE 19 | $ 20 | $) 21 | 22 | target_compile_features(function2 23 | INTERFACE 24 | cxx_alias_templates 25 | cxx_auto_type 26 | cxx_constexpr 27 | cxx_decltype 28 | cxx_decltype_auto 29 | cxx_final 30 | cxx_lambdas 31 | cxx_lambda_init_captures 32 | cxx_generic_lambdas 33 | cxx_variadic_templates 34 | cxx_defaulted_functions 35 | cxx_nullptr 36 | cxx_trailing_return_types 37 | cxx_return_type_deduction) 38 | 39 | if (FU2_IS_TOP_LEVEL_PROJECT) 40 | include(ExternalProject) 41 | include(GNUInstallDirs) 42 | include(CMakePackageConfigHelpers) 43 | 44 | # Create an install target: 45 | # Headers and license files 46 | install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/function2" 47 | DESTINATION "include") 48 | install(FILES "LICENSE.txt" DESTINATION .) 49 | install(FILES "Readme.md" DESTINATION .) 50 | 51 | # Config.cmake 52 | write_basic_package_version_file( 53 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 54 | VERSION ${PROJECT_VERSION} 55 | COMPATIBILITY SameMajorVersion) 56 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 57 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 58 | 59 | # ConfigVersion.cmake 60 | configure_package_config_file("cmake/config.cmake.in" 61 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 62 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 63 | # PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR 64 | ) 65 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 66 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 67 | 68 | # Targets.cmake 69 | export(TARGETS ${PROJECT_NAME} 70 | NAMESPACE ${PROJECT_NAME}:: 71 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") 72 | install(TARGETS ${PROJECT_NAME} 73 | EXPORT "${PROJECT_NAME}Targets" 74 | INCLUDES DESTINATION "include") 75 | install(EXPORT "${PROJECT_NAME}Targets" 76 | NAMESPACE ${PROJECT_NAME}:: 77 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 78 | 79 | # Setup CPack for bundling 80 | set(CPACK_GENERATOR "ZIP") 81 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 82 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 83 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 84 | 85 | # Since the header only library is platform independent 86 | # we name the packages after the native line feed 87 | if(WIN32) 88 | set(CPACK_SYSTEM_NAME "crlf") 89 | else() 90 | set(CPACK_SYSTEM_NAME "lf") 91 | endif() 92 | 93 | include(CPack) 94 | 95 | if (MSVC) 96 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 97 | string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 98 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 99 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") 100 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-") 101 | endif() 102 | 103 | include(cmake/CMakeLists.txt) 104 | 105 | include(CTest) 106 | 107 | option(FU2_WITH_NO_EXCEPTIONS 108 | "Test without exceptions" 109 | OFF) 110 | option(FU2_WITH_NO_DEATH_TESTS 111 | "Test without death tests" 112 | OFF) 113 | option(FU2_WITH_CPP_LATEST 114 | "Enable the highest C++ standard available for testing polyfills" 115 | OFF) 116 | option(FU2_WITH_LIMITED_EMPTY_PROPAGATION 117 | "Test limiting empty propagation to only function pointers, member pointers, std::function, and specializations of fu2::function_base" 118 | OFF) 119 | 120 | if (BUILD_TESTING) 121 | if (FU2_WITH_NO_EXCEPTIONS) 122 | message(STATUS "Testing with exceptions disabled") 123 | add_definitions(-DTESTS_NO_EXCEPTIONS) 124 | endif() 125 | 126 | if (FU2_WITH_NO_DEATH_TESTS) 127 | message(STATUS "Testing without death tests") 128 | add_definitions(-DTESTS_NO_DEATH_TESTS) 129 | endif() 130 | 131 | if (FU2_WITH_LIMITED_EMPTY_PROPAGATION) 132 | message(STATUS "Testing with limited empty propagation") 133 | add_definitions(-DFU2_WITH_LIMITED_EMPTY_PROPAGATION) 134 | endif() 135 | 136 | add_subdirectory(test) 137 | endif() 138 | endif () 139 | -------------------------------------------------------------------------------- /Findfunction2.cmake: -------------------------------------------------------------------------------- 1 | # Makes it possible to find function2 through find_package(function2 REQUIRED) 2 | # when this source directory was added to the CMake module path. 3 | # For instance it could be done through adding this to the CMakeLists.txt 4 | # file in the parent directory: 5 | # ```cmake 6 | # list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/function2") 7 | # ``` 8 | 9 | set(FU2_IS_FIND_INCLUDED ON) 10 | include("${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt") 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # fu2::function an improved drop-in replacement to std::function 3 | 4 | ![](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) 5 | 6 | Provides improved implementations of `std::function`: 7 | 8 | - **copyable** `fu2::function` 9 | - **move-only** `fu2::unique_function` (capable of holding move only types) 10 | - **non-owning** `fu2::function_view` (capable of referencing callables in a non owning way) 11 | 12 | that provide many benefits and improvements over `std::function`: 13 | 14 | - [x] **const**, **volatile**, **reference** and **noexcept** correct (qualifiers are part of the `operator()` signature) 15 | - [x] **convertible** to and from `std::function` as well as other callable types 16 | - [x] **adaptable** through `fu2::function_base` (internal capacity, copyable and exception guarantees) 17 | - [x] **overloadable** with an arbitrary count of signatures (`fu2::function`) 18 | - [x] **full allocator support** in contrast to `std::function`, which doesn't provide support anymore 19 | - [x] **covered** by many unit tests and continuous integration services (*GCC*, *Clang* and *MSVC*) 20 | - [x] **header only**, just copy and include `function.hpp` in your project 21 | - [x] **permissively licensed** under the **boost** license 22 | 23 | 24 | ## Table of Contents 25 | 26 | * **[Documentation](#documentation)** 27 | * **[How to use](#how-to-use)** 28 | * **[Constructing a function](#constructing-a-function)** 29 | * **[Non copyable unique functions](#non-copyable-unique-functions)** 30 | * **[Convertibility of functions](#convertibility-of-functions)** 31 | * **[Adapt function2](#adapt-function2)** 32 | * **[Performance and optimization](#performance-and-optimization)** 33 | * **[Small functor optimization](#small-functor-optimization)** 34 | * **[Compiler optimization](#compiler-optimization)** 35 | * **[std::function vs fu2::function](#stdfunction-vs-fu2function)** 36 | * **[Coverage and runtime checks](#coverage-and-runtime-checks)** 37 | * **[Compatibility](#compatibility)** 38 | * **[License](#licence)** 39 | * **[Similar implementations](#similar-implementations)** 40 | 41 | ## Documentation 42 | 43 | ### How to use 44 | 45 | **function2** is implemented in one header (`function.hpp`), no compilation is required. 46 | Just copy the `function.hpp` header in your project and include it to start. 47 | It's recommended to import the library as git submodule using CMake: 48 | 49 | ```sh 50 | # Shell: 51 | git submodule add https://github.com/Naios/function2.git 52 | ``` 53 | 54 | ```cmake 55 | # CMake file: 56 | add_subdirectory(function2) 57 | # function2 provides an interface target which makes it's 58 | # headers available to all projects using function2 59 | target_link_libraries(my_project function2) 60 | ``` 61 | 62 | Use `fu2::function` as a wrapper for copyable function wrappers and `fu2::unique_function` for move only types. 63 | The standard implementation `std::function` and `fu2::function` are convertible to each other, see [the chapter convertibility of functions](#convertibility-of-functions) for details. 64 | 65 | A function wrapper is declared as following: 66 | ```c++ 67 | fu2::function 68 | // Return type ~^ ^ ^ ^ 69 | // Parameters ~~~~~|~~~~~| ^ 70 | // Qualifier ~~~~~~~~~~~~~~~~~~~| 71 | ``` 72 | 73 | * **Return type**: The return type of the function to wrap. 74 | * **Arguments**: The argument types of the function to wrap. 75 | Any argument types are allowed. 76 | * **Qualifiers**: There are several qualifiers allowed: 77 | - **no qualifier** provides `ReturnType operator() (Args...)` 78 | - Can be assigned from const and no const objects (*mutable lambdas* for example). 79 | - **const** provides `ReturnType operator() (Args...) const` 80 | - Requires that the assigned functor is const callable (won't work with *mutable lambdas*), 81 | - **volatile** provides `ReturnType operator() (Args...) volatile` 82 | - Can only be assigned from volatile qualified functors. 83 | - **const volatile** provides `ReturnType operator() (Args...) const volatile` 84 | - Same as const and volatile together. 85 | - **r-value (one-shot) functions** `ReturnType operator() (Args...) &&` 86 | - 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. 87 | - **noexcept functions** `ReturnType operator() (Args...) noexcept` 88 | - 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)). 89 | * **Multiple overloads**: The library is capable of providing multiple overloads: 90 | ```cpp 91 | fu2::function const&), 92 | int(std::set const&) const> fn = [] (auto const& container) { 93 | return container.size()); 94 | }; 95 | ``` 96 | 97 | ### Constructing a function 98 | 99 | `fu2::function` and `fu2::unique_function` (non copyable) are easy to use: 100 | 101 | ```c++ 102 | fu2::function fun = [] { 103 | // ... 104 | }; 105 | 106 | // fun provides void operator()() const now 107 | fun(); 108 | ``` 109 | 110 | ### Non copyable unique functions 111 | 112 | `fu2::unique_function` also works with non copyable functors/ lambdas. 113 | 114 | ```c++ 115 | fu2::unique_function fun = [ptr = std::make_unique(true)] { 116 | return *ptr; 117 | }; 118 | 119 | // unique functions are move only 120 | fu2::unique_function otherfun = std::move(fun): 121 | 122 | otherfun(); 123 | ``` 124 | 125 | 126 | ### Non owning functions 127 | 128 | 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. 129 | 130 | ```c++ 131 | auto callable = [ptr = std::make_unique(true)] { 132 | return *ptr; 133 | }; 134 | 135 | fu2::function_view view(callable); 136 | ``` 137 | 138 | ### Convertibility of functions 139 | 140 | `fu2::function`, `fu2::unique_function` and `std::function` are convertible to each other when: 141 | 142 | - The return type and parameter type match. 143 | - The functions are both volatile or not. 144 | - The functions are const correct: 145 | - `noconst = const` 146 | - `const = const` 147 | - `noconst = noconst` 148 | - The functions are copyable correct when: 149 | - `unique = unique` 150 | - `unique = copyable` 151 | - `copyable = copyable` 152 | - The functions are reference correct when: 153 | - `lvalue = lvalue` 154 | - `lvalue = rvalue` 155 | - `rvalue = rvalue` 156 | - The functions are `noexcept` correct when: 157 | - `callable = callable` 158 | - `callable = noexcept callable ` 159 | - `noexcept callable = noexcept callable` 160 | 161 | | Convertibility from \ to | fu2::function | fu2::unique_function | std::function | 162 | | ------------------------ | ------------- | -------------------- | ------------- | 163 | | fu2::function | Yes | Yes | Yes | 164 | | fu2::unique_function | No | Yes | No | 165 | | std::function | Yes | Yes | Yes | 166 | 167 | ```c++ 168 | fu2::function fun = []{}; 169 | // OK 170 | std::function std_fun = fun; 171 | // OK 172 | fu2::unique_function un_fun = fun; 173 | 174 | // Error (non copyable -> copyable) 175 | fun = un_fun; 176 | // Error (non copyable -> copyable) 177 | fun = un_fun; 178 | 179 | ``` 180 | 181 | ### Adapt function2 182 | 183 | function2 is adaptable through `fu2::function_base` which allows you to set: 184 | 185 | - **IsOwning**: defines whether the function owns its contained object 186 | - **Copyable:** defines if the function is copyable or not. 187 | - **Capacity:** defines the internal capacity used for [sfo optimization](#small-functor-optimization): 188 | ```cpp 189 | struct my_capacity { 190 | static constexpr std::size_t capacity = sizeof(my_type); 191 | static constexpr std::size_t alignment = alignof(my_type); 192 | }; 193 | ``` 194 | - **IsThrowing** defines if empty function calls throw an `fu2::bad_function_call` exception, otherwise `std::abort` is called. 195 | - **HasStrongExceptGuarantee** defines whether the strong exception guarantees shall be met. 196 | - **Signatures:** defines the signatures of the function. 197 | 198 | The following code defines an owning function with a variadic signature which is copyable and sfo optimization is disabled: 199 | 200 | ```c++ 201 | template 202 | using my_function = fu2::function_base; 203 | ``` 204 | 205 | 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. 206 | 207 | ```c++ 208 | template 209 | using my_consumer = fu2::function_base, 210 | true, false, void(Arg)&&>; 211 | 212 | // Example 213 | my_consumer consumer = [](int, float) { } 214 | std::move(consumer)(44, 1.7363f); 215 | ``` 216 | 217 | ## Performance and optimization 218 | 219 | ### Small functor optimization 220 | 221 | 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. 222 | 223 | Smart heap allocation moves the inplace allocated functor automatically to the heap to speed up moving between objects. 224 | 225 | It's possible to disable small functor optimization through setting the internal capacity to 0. 226 | 227 | 228 | ## Coverage and runtime checks 229 | 230 | Function2 is checked with unit tests and valgrind (for memory leaks), where the unit tests provide coverage for all possible template parameter assignments. 231 | 232 | ## Compatibility 233 | 234 | Tested with: 235 | 236 | - Visual Studio 2017+ Update 3 237 | - Clang 3.8+ 238 | - GCC 5.4+ 239 | 240 | Every compiler with modern C++14 support should work. 241 | *function2* only depends on the standard library. 242 | 243 | ## License 244 | *function2* is licensed under the very permissive Boost 1.0 License. 245 | 246 | ## Similar implementations 247 | 248 | There are similar implementations of a function wrapper: 249 | 250 | - [pmed/fixed_size_function](https://github.com/pmed/fixed_size_function) 251 | - stdex::function - A multi-signature function implementation. 252 | - multifunction - Example from [Boost.TypeErasure](http://www.boost.org/doc/html/boost_typeerasure/examples.html#boost_typeerasure.examples.multifunction), another multi-signature function. 253 | - std::function - [Standard](http://en.cppreference.com/w/cpp/utility/functional/function). 254 | - boost::function - The one from [Boost](http://www.boost.org/doc/libs/1_55_0/doc/html/function.html). 255 | - func::function - From this [blog](http://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction/). 256 | - 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/). 257 | - ssvu::FastFunc - Another Don Clugston's FastDelegate, as shown [here](https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/QgvHF7YMi3o). 258 | - [cxx_function::function](https://github.com/potswa/cxx_function) - By David Krauss 259 | 260 | Also check out the amazing [**CxxFunctionBenchmark**](https://github.com/jamboree/CxxFunctionBenchmark) which compares several implementations. 261 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: 2 | - Visual Studio 2017 3 | - Visual Studio 2019 4 | 5 | environment: 6 | matrix: 7 | - FU2_WITH_NO_EXCEPTIONS: OFF 8 | FU2_WITH_CPP_LATEST: OFF 9 | - FU2_WITH_NO_EXCEPTIONS: ON 10 | FU2_WITH_CPP_LATEST: OFF 11 | - FU2_WITH_NO_EXCEPTIONS: OFF 12 | FU2_WITH_CPP_LATEST: ON 13 | - FU2_WITH_NO_EXCEPTIONS: ON 14 | FU2_WITH_CPP_LATEST: ON 15 | 16 | platform: 17 | - x64 18 | 19 | clone_script: 20 | - cmd: git clone -q --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER% 21 | - cmd: cd %APPVEYOR_BUILD_FOLDER% 22 | - cmd: git checkout -qf %APPVEYOR_REPO_COMMIT% 23 | - cmd: git submodule update --init --recursive 24 | 25 | before_build: 26 | - cmd: > 27 | cmake -H. -Bbuild -A%PLATFORM% 28 | -DFU2_WITH_NO_EXCEPTIONS=%FU2_WITH_NO_EXCEPTIONS% 29 | -DFU2_WITH_CPP_LATEST=%FU2_WITH_CPP_LATEST% 30 | 31 | build_script: 32 | - cmd: > 33 | cmake --build build --config Debug --target ALL_BUILD 34 | -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 35 | /verbosity:minimal /maxcpucount:2 /nologo 36 | - cmd: > 37 | cmake --build build --config Release --target ALL_BUILD 38 | -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 39 | /verbosity:minimal /maxcpucount:2 /nologo 40 | 41 | test_script: 42 | - cmd: cd build 43 | - cmd: ctest -C Debug -V . 44 | - cmd: ctest -C Release -V . 45 | -------------------------------------------------------------------------------- /cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(cmake/configure_compiler.cmake) 2 | -------------------------------------------------------------------------------- /cmake/compiler/clang.cmake: -------------------------------------------------------------------------------- 1 | # Enable full warnings 2 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wdocumentation -pedantic -Wextra") 3 | 4 | if (FU2_WITH_NO_EXCEPTIONS) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") 6 | message(STATUS "Clang: Disabled exceptions") 7 | endif() 8 | 9 | -------------------------------------------------------------------------------- /cmake/compiler/gcc.cmake: -------------------------------------------------------------------------------- 1 | # Enable full warnings 2 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra") 3 | 4 | if (FU2_WITH_NO_EXCEPTIONS) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") 6 | message(STATUS "GCC: Disabled exceptions") 7 | endif() 8 | -------------------------------------------------------------------------------- /cmake/compiler/msvc.cmake: -------------------------------------------------------------------------------- 1 | if (${MSVC_VERSION} LESS 1800) 2 | message(FATAL_ERROR "You are using an unsupported version of Visual Studio " 3 | "which doesn't support all required C++11 features. " 4 | "(Visual Studio 2013 (version >= 1800) is required!)") 5 | endif() 6 | 7 | if(CMAKE_SIZEOF_VOID_P MATCHES 8) 8 | set(PLATFORM 64) 9 | else() 10 | set(PLATFORM 32) 11 | endif() 12 | 13 | if (PLATFORM EQUAL 64) 14 | add_definitions("-D_WIN64") 15 | endif() 16 | 17 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP") 18 | 19 | if (FU2_WITH_NO_EXCEPTIONS) 20 | add_definitions(-D_HAS_EXCEPTIONS=0) 21 | string(REGEX REPLACE "/GX" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 22 | string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 23 | message(STATUS "MSVC: Disabled exceptions") 24 | endif() 25 | 26 | if (FU2_WITH_CPP_LATEST) 27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest") 28 | message(STATUS "MSVC: Using latest available C++ standard") 29 | endif() 30 | -------------------------------------------------------------------------------- /cmake/config.cmake.in: -------------------------------------------------------------------------------- 1 | set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) 2 | 3 | @PACKAGE_INIT@ 4 | 5 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 6 | 7 | check_required_components(@PROJECT_NAME@) 8 | -------------------------------------------------------------------------------- /cmake/configure_compiler.cmake: -------------------------------------------------------------------------------- 1 | # Select the compiler specific cmake file 2 | set(MSVC_ID "MSVC") 3 | if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") 4 | include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang.cmake) 5 | elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") 6 | include(${CMAKE_SOURCE_DIR}/cmake/compiler/gcc.cmake) 7 | elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL ${MSVC_ID}) 8 | include(${CMAKE_SOURCE_DIR}/cmake/compiler/msvc.cmake) 9 | else() 10 | message(FATAL_ERROR "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}") 11 | endif() 12 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from conans import ConanFile, tools 5 | 6 | def get_version(): 7 | git = tools.Git() 8 | try: 9 | return git.run("describe --tags --abbrev=0") 10 | except: 11 | return None 12 | 13 | class Function2Conan(ConanFile): 14 | name = "function2" 15 | version = get_version() 16 | license = "boost" 17 | url = "https://github.com/Naios/function2" 18 | author = "Denis Blank (denis.blank@outlook.com)" 19 | description = "Improved and configurable drop-in replacement to std::function" 20 | homepage = "http://naios.github.io/function2" 21 | no_copy_source = True 22 | scm = { 23 | "type": "git", 24 | "url": "auto", 25 | "revision": "auto" 26 | } 27 | 28 | def package(self): 29 | self.copy("LICENSE.txt", "licenses") 30 | self.copy("include/function2/function2.hpp") 31 | 32 | def package_id(self): 33 | self.info.header_only() 34 | -------------------------------------------------------------------------------- /include/function2/function2.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2015-2020 Denis Blank 3 | // Distributed under the Boost Software License, Version 1.0 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #ifndef FU2_INCLUDED_FUNCTION2_HPP_ 8 | #define FU2_INCLUDED_FUNCTION2_HPP_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // Defines: 19 | // - FU2_HAS_DISABLED_EXCEPTIONS 20 | #if defined(FU2_WITH_DISABLED_EXCEPTIONS) || \ 21 | defined(FU2_MACRO_DISABLE_EXCEPTIONS) 22 | #define FU2_HAS_DISABLED_EXCEPTIONS 23 | #else // FU2_WITH_DISABLED_EXCEPTIONS 24 | #if defined(_MSC_VER) 25 | #if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0) 26 | #define FU2_HAS_DISABLED_EXCEPTIONS 27 | #endif 28 | #elif defined(__clang__) 29 | #if !(__EXCEPTIONS && __has_feature(cxx_exceptions)) 30 | #define FU2_HAS_DISABLED_EXCEPTIONS 31 | #endif 32 | #elif defined(__GNUC__) 33 | #if !__EXCEPTIONS 34 | #define FU2_HAS_DISABLED_EXCEPTIONS 35 | #endif 36 | #endif 37 | #endif // FU2_WITH_DISABLED_EXCEPTIONS 38 | // - FU2_HAS_LIMITED_EMPTY_PROPAGATION 39 | #if defined(FU2_WITH_LIMITED_EMPTY_PROPAGATION) 40 | #define FU2_HAS_LIMITED_EMPTY_PROPAGATION 41 | #endif // FU2_WITH_NO_EMPTY_PROPAGATION 42 | // - FU2_HAS_NO_FUNCTIONAL_HEADER 43 | #if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) && \ 44 | !defined(FU2_NO_FUNCTIONAL_HEADER) && \ 45 | (!defined(FU2_HAS_DISABLED_EXCEPTIONS) || \ 46 | defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION)) 47 | #include 48 | #else 49 | #define FU2_HAS_NO_FUNCTIONAL_HEADER 50 | #endif 51 | // - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 52 | #if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE) 53 | #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 54 | #else // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE 55 | #if defined(_MSC_VER) 56 | #if defined(_HAS_CXX17) && _HAS_CXX17 57 | #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 58 | #endif 59 | #elif defined(__cpp_noexcept_function_type) 60 | #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 61 | #elif defined(__cplusplus) && (__cplusplus >= 201703L) 62 | #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 63 | #endif 64 | #endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE 65 | 66 | // - FU2_HAS_NO_EMPTY_PROPAGATION 67 | #if defined(FU2_WITH_NO_EMPTY_PROPAGATION) 68 | #define FU2_HAS_NO_EMPTY_PROPAGATION 69 | #endif // FU2_WITH_NO_EMPTY_PROPAGATION 70 | 71 | #if !defined(FU2_HAS_DISABLED_EXCEPTIONS) 72 | #include 73 | #endif 74 | 75 | #if defined(__cpp_constexpr) && (__cpp_constexpr >= 201304) 76 | #define FU2_DETAIL_CXX14_CONSTEXPR constexpr 77 | #elif defined(__clang__) && defined(__has_feature) 78 | #if __has_feature(__cxx_generic_lambdas__) && \ 79 | __has_feature(__cxx_relaxed_constexpr__) 80 | #define FU2_DETAIL_CXX14_CONSTEXPR constexpr 81 | #endif 82 | #elif defined(_MSC_VER) && (_MSC_VER >= 1915) && (_MSVC_LANG >= 201402) 83 | #define FU2_DETAIL_CXX14_CONSTEXPR constexpr 84 | #endif 85 | #ifndef FU2_DETAIL_CXX14_CONSTEXPR 86 | #define FU2_DETAIL_CXX14_CONSTEXPR 87 | #endif 88 | 89 | /// Hint for the compiler that this point should be unreachable 90 | #if defined(_MSC_VER) 91 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 92 | #define FU2_DETAIL_UNREACHABLE_INTRINSIC() __assume(false) 93 | #elif defined(__GNUC__) 94 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 95 | #define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable() 96 | #elif defined(__has_builtin) 97 | #if __has_builtin(__builtin_unreachable) 98 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 99 | #define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable() 100 | #endif 101 | #endif 102 | #ifndef FU2_DETAIL_UNREACHABLE_INTRINSIC 103 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 104 | #define FU2_DETAIL_UNREACHABLE_INTRINSIC() abort() 105 | #endif 106 | 107 | /// Causes the application to exit abnormally 108 | #if defined(_MSC_VER) 109 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 110 | #define FU2_DETAIL_TRAP() __debugbreak() 111 | #elif defined(__GNUC__) 112 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 113 | #define FU2_DETAIL_TRAP() __builtin_trap() 114 | #elif defined(__has_builtin) 115 | #if __has_builtin(__builtin_trap) 116 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 117 | #define FU2_DETAIL_TRAP() __builtin_trap() 118 | #endif 119 | #endif 120 | #ifndef FU2_DETAIL_TRAP 121 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 122 | #define FU2_DETAIL_TRAP() *(volatile int*)0x11 = 0 123 | #endif 124 | 125 | #ifndef NDEBUG 126 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 127 | #define FU2_DETAIL_UNREACHABLE() ::fu2::detail::unreachable_debug() 128 | #else 129 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 130 | #define FU2_DETAIL_UNREACHABLE() FU2_DETAIL_UNREACHABLE_INTRINSIC() 131 | #endif 132 | 133 | namespace fu2 { 134 | inline namespace abi_400 { 135 | namespace detail { 136 | template 137 | class function; 138 | 139 | template 140 | struct identity {}; 141 | 142 | // Equivalent to C++17's std::void_t which targets a bug in GCC, 143 | // that prevents correct SFINAE behavior. 144 | // See http://stackoverflow.com/questions/35753920 for details. 145 | template 146 | struct deduce_to_void : std::common_type {}; 147 | 148 | template 149 | using void_t = typename deduce_to_void::type; 150 | 151 | template 152 | using unrefcv_t = std::remove_cv_t>; 153 | 154 | template 155 | struct lazy_and; 156 | 157 | template 158 | struct lazy_and : B1 {}; 159 | 160 | template 161 | struct lazy_and : std::conditional::type {}; 162 | 163 | // template 164 | // struct lazy_and 165 | // : std::conditional, B1>::type {}; 166 | 167 | // Copy enabler helper class 168 | template 169 | struct copyable {}; 170 | template <> 171 | struct copyable { 172 | copyable() = default; 173 | ~copyable() = default; 174 | copyable(copyable const&) = delete; 175 | copyable(copyable&&) = default; 176 | copyable& operator=(copyable const&) = delete; 177 | copyable& operator=(copyable&&) = default; 178 | }; 179 | 180 | /// Configuration trait to configure the function_base class. 181 | template 182 | struct config { 183 | // Is true if the function is owning. 184 | static constexpr auto const is_owning = Owning; 185 | 186 | // Is true if the function is copyable. 187 | static constexpr auto const is_copyable = Copyable; 188 | 189 | // The internal capacity of the function 190 | // used in small functor optimization. 191 | // The object shall expose the real capacity through Capacity::capacity 192 | // and the intended alignment through Capacity::alignment. 193 | using capacity = Capacity; 194 | }; 195 | 196 | /// A config which isn't compatible to other configs 197 | template 198 | struct property { 199 | // Is true when the function throws an exception on empty invocation. 200 | static constexpr auto const is_throwing = Throws; 201 | 202 | // Is true when the function throws an exception on empty invocation. 203 | static constexpr auto const is_strong_exception_guaranteed = 204 | HasStrongExceptGuarantee; 205 | }; 206 | 207 | #ifndef NDEBUG 208 | [[noreturn]] inline void unreachable_debug() { 209 | FU2_DETAIL_TRAP(); 210 | std::abort(); 211 | } 212 | #endif 213 | 214 | /// Provides utilities for invocing callable objects 215 | namespace invocation { 216 | /// Invokes the given callable object with the given arguments 217 | template 218 | constexpr auto invoke(Callable&& callable, Args&&... args) noexcept( 219 | noexcept(std::forward(callable)(std::forward(args)...))) 220 | -> decltype(std::forward(callable)(std::forward(args)...)) { 221 | 222 | return std::forward(callable)(std::forward(args)...); 223 | } 224 | /// Invokes the given member function pointer by reference 225 | template 226 | constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( 227 | noexcept((std::forward(self).*member)(std::forward(args)...))) 228 | -> decltype((std::forward(self).* 229 | member)(std::forward(args)...)) { 230 | return (std::forward(self).*member)(std::forward(args)...); 231 | } 232 | /// Invokes the given member function pointer by pointer 233 | template 234 | constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( 235 | noexcept((std::forward(self)->*member)(std::forward(args)...))) 236 | -> decltype((std::forward(self)->*member)( 237 | std::forward(args)...)) { 238 | return (std::forward(self)->*member)(std::forward(args)...); 239 | } 240 | /// Invokes the given pointer to a scalar member by reference 241 | template 242 | constexpr auto 243 | invoke(Type T::*member, 244 | Self&& self) noexcept(noexcept(std::forward(self).*member)) 245 | -> decltype(std::forward(self).*member) { 246 | return (std::forward(self).*member); 247 | } 248 | /// Invokes the given pointer to a scalar member by pointer 249 | template 250 | constexpr auto 251 | invoke(Type T::*member, 252 | Self&& self) noexcept(noexcept(std::forward(self)->*member)) 253 | -> decltype(std::forward(self)->*member) { 254 | return std::forward(self)->*member; 255 | } 256 | 257 | /// Deduces to a true type if the callable object can be invoked with 258 | /// the given arguments. 259 | /// We don't use invoke here because MSVC can't evaluate the nested expression 260 | /// SFINAE here. 261 | template 262 | struct can_invoke : std::false_type {}; 263 | template 264 | struct can_invoke, 265 | decltype((void)std::declval()(std::declval()...))> 266 | : std::true_type {}; 267 | template 268 | struct can_invoke, 269 | decltype((void)((std::declval().*std::declval())( 270 | std::declval()...)))> : std::true_type {}; 271 | template 272 | struct can_invoke, 273 | decltype(( 274 | void)((std::declval().*std::declval())( 275 | std::declval()...)))> : std::true_type {}; 276 | template 277 | struct can_invoke, 278 | decltype(( 279 | void)((std::declval()->*std::declval())( 280 | std::declval()...)))> : std::true_type {}; 281 | template 282 | struct can_invoke, 283 | decltype((void)(std::declval().*std::declval()))> 284 | : std::true_type {}; 285 | template 286 | struct can_invoke, 287 | decltype((void)(std::declval().* 288 | std::declval()))> : std::true_type { 289 | }; 290 | template 291 | struct can_invoke, 292 | decltype(( 293 | void)(std::declval()->*std::declval()))> 294 | : std::true_type {}; 295 | 296 | template 297 | struct is_noexcept_correct : std::true_type {}; 298 | template 299 | struct is_noexcept_correct> 300 | : std::integral_constant(), std::declval()...))> { 303 | }; 304 | } // end namespace invocation 305 | 306 | namespace overloading { 307 | template 308 | struct overload_impl; 309 | template 310 | struct overload_impl : Current, 311 | overload_impl { 312 | explicit overload_impl(Current current, Next next, Rest... rest) 313 | : Current(std::move(current)), 314 | overload_impl(std::move(next), std::move(rest)...) { 315 | } 316 | 317 | using Current::operator(); 318 | using overload_impl::operator(); 319 | }; 320 | template 321 | struct overload_impl : Current { 322 | explicit overload_impl(Current current) : Current(std::move(current)) { 323 | } 324 | 325 | using Current::operator(); 326 | }; 327 | 328 | template 329 | constexpr auto overload(T&&... callables) { 330 | return overload_impl...>{std::forward(callables)...}; 331 | } 332 | } // namespace overloading 333 | 334 | /// Declares the namespace which provides the functionality to work with a 335 | /// type-erased object. 336 | namespace type_erasure { 337 | /// Specialization to work with addresses of callable objects 338 | template 339 | struct address_taker { 340 | template 341 | static auto take(O&& obj) { 342 | return std::addressof(obj); 343 | } 344 | static T& restore(void* ptr) { 345 | return *static_cast(ptr); 346 | } 347 | static T const& restore(void const* ptr) { 348 | return *static_cast(ptr); 349 | } 350 | static T volatile& restore(void volatile* ptr) { 351 | return *static_cast(ptr); 352 | } 353 | static T const volatile& restore(void const volatile* ptr) { 354 | return *static_cast(ptr); 355 | } 356 | }; 357 | /// Specialization to work with addresses of raw function pointers 358 | template 359 | struct address_taker::value>> { 360 | template 361 | static void* take(O&& obj) { 362 | return reinterpret_cast(obj); 363 | } 364 | template 365 | static T restore(O ptr) { 366 | return reinterpret_cast(const_cast(ptr)); 367 | } 368 | }; 369 | 370 | template 371 | struct box_factory; 372 | /// Store the allocator inside the box 373 | template 374 | struct box : private Allocator { 375 | friend box_factory; 376 | 377 | T value_; 378 | 379 | explicit box(T value, Allocator allocator_) 380 | : Allocator(std::move(allocator_)), value_(std::move(value)) { 381 | } 382 | 383 | box(box&&) = default; 384 | box(box const&) = default; 385 | box& operator=(box&&) = default; 386 | box& operator=(box const&) = default; 387 | ~box() = default; 388 | }; 389 | template 390 | struct box : private Allocator { 391 | friend box_factory; 392 | 393 | T value_; 394 | 395 | explicit box(T value, Allocator allocator_) 396 | : Allocator(std::move(allocator_)), value_(std::move(value)) { 397 | } 398 | 399 | box(box&&) = default; 400 | box(box const&) = delete; 401 | box& operator=(box&&) = default; 402 | box& operator=(box const&) = delete; 403 | ~box() = default; 404 | }; 405 | 406 | template 407 | struct box_factory> { 408 | using real_allocator = 409 | typename std::allocator_traits>:: 410 | template rebind_alloc>; 411 | 412 | /// Allocates space through the boxed allocator 413 | static box* 414 | box_allocate(box const* me) { 415 | real_allocator allocator_(*static_cast(me)); 416 | 417 | return static_cast*>( 418 | std::allocator_traits::allocate(allocator_, 1U)); 419 | } 420 | 421 | /// Destroys the box through the given allocator 422 | static void box_deallocate(box* me) { 423 | real_allocator allocator_(*static_cast(me)); 424 | 425 | me->~box(); 426 | std::allocator_traits::deallocate(allocator_, me, 1U); 427 | } 428 | }; 429 | 430 | /// Creates a box containing the given value and allocator 431 | template 432 | auto make_box(std::integral_constant, T&& value, 433 | Allocator&& allocator_) { 434 | return box, std::decay_t>( 435 | std::forward(value), std::forward(allocator_)); 436 | } 437 | 438 | template 439 | struct is_box : std::false_type {}; 440 | template 441 | struct is_box> : std::true_type {}; 442 | 443 | /// Provides access to the pointer to a heal allocated erased object 444 | /// as well to the inplace storage. 445 | union data_accessor { 446 | data_accessor() = default; 447 | explicit constexpr data_accessor(std::nullptr_t) noexcept : ptr_(nullptr) { 448 | } 449 | explicit constexpr data_accessor(void* ptr) noexcept : ptr_(ptr) { 450 | } 451 | explicit constexpr data_accessor(void const* ptr) noexcept 452 | : data_accessor(const_cast(ptr)) { 453 | } 454 | 455 | constexpr void assign_ptr(void* ptr) noexcept { 456 | ptr_ = ptr; 457 | } 458 | constexpr void assign_ptr(void const* ptr) noexcept { 459 | ptr_ = const_cast(ptr); 460 | } 461 | 462 | /// The pointer we use if the object is on the heap 463 | void* ptr_; 464 | /// The first field of the inplace storage 465 | std::size_t inplace_storage_; 466 | }; 467 | 468 | /// See opcode::op_fetch_empty 469 | static FU2_DETAIL_CXX14_CONSTEXPR void write_empty(data_accessor* accessor, 470 | bool empty) noexcept { 471 | accessor->inplace_storage_ = std::size_t(empty); 472 | } 473 | 474 | template 475 | using transfer_const_t = 476 | std::conditional_t>::value, 477 | std::add_const_t, To>; 478 | template 479 | using transfer_volatile_t = 480 | std::conditional_t>::value, 481 | std::add_volatile_t, To>; 482 | 483 | /// The retriever when the object is allocated inplace 484 | template 485 | FU2_DETAIL_CXX14_CONSTEXPR auto retrieve(std::true_type /*is_inplace*/, 486 | Accessor from, 487 | std::size_t from_capacity) { 488 | using type = transfer_const_t>*; 489 | 490 | /// Process the command by using the data inside the internal capacity 491 | auto storage = &(from->inplace_storage_); 492 | auto inplace = const_cast(static_cast(storage)); 493 | return type(std::align(alignof(T), sizeof(T), inplace, from_capacity)); 494 | } 495 | 496 | /// The retriever which is used when the object is allocated 497 | /// through the allocator 498 | template 499 | constexpr auto retrieve(std::false_type /*is_inplace*/, Accessor from, 500 | std::size_t /*from_capacity*/) { 501 | 502 | return from->ptr_; 503 | } 504 | 505 | namespace invocation_table { 506 | #if !defined(FU2_HAS_DISABLED_EXCEPTIONS) 507 | #if defined(FU2_HAS_NO_FUNCTIONAL_HEADER) 508 | struct bad_function_call : std::exception { 509 | bad_function_call() noexcept { 510 | } 511 | 512 | char const* what() const noexcept override { 513 | return "bad function call"; 514 | } 515 | }; 516 | #else 517 | using std::bad_function_call; 518 | #endif 519 | #endif 520 | 521 | #ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 522 | #define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) \ 523 | F(, , noexcept, , &) \ 524 | F(const, , noexcept, , &) \ 525 | F(, volatile, noexcept, , &) \ 526 | F(const, volatile, noexcept, , &) \ 527 | F(, , noexcept, &, &) \ 528 | F(const, , noexcept, &, &) \ 529 | F(, volatile, noexcept, &, &) \ 530 | F(const, volatile, noexcept, &, &) \ 531 | F(, , noexcept, &&, &&) \ 532 | F(const, , noexcept, &&, &&) \ 533 | F(, volatile, noexcept, &&, &&) \ 534 | F(const, volatile, noexcept, &&, &&) 535 | #define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) \ 536 | F(, , noexcept) \ 537 | F(const, , noexcept) \ 538 | F(, volatile, noexcept) \ 539 | F(const, volatile, noexcept) 540 | #else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 541 | #define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) 542 | #define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) 543 | #endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE 544 | 545 | #define FU2_DETAIL_EXPAND_QUALIFIERS(F) \ 546 | F(, , , , &) \ 547 | F(const, , , , &) \ 548 | F(, volatile, , , &) \ 549 | F(const, volatile, , , &) \ 550 | F(, , , &, &) \ 551 | F(const, , , &, &) \ 552 | F(, volatile, , &, &) \ 553 | F(const, volatile, , &, &) \ 554 | F(, , , &&, &&) \ 555 | F(const, , , &&, &&) \ 556 | F(, volatile, , &&, &&) \ 557 | F(const, volatile, , &&, &&) \ 558 | FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) 559 | #define FU2_DETAIL_EXPAND_CV(F) \ 560 | F(, , ) \ 561 | F(const, , ) \ 562 | F(, volatile, ) \ 563 | F(const, volatile, ) \ 564 | FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) 565 | 566 | /// If the function is qualified as noexcept, the call will never throw 567 | template 568 | [[noreturn]] void throw_or_abortnoexcept( 569 | std::integral_constant /*is_throwing*/) noexcept { 570 | std::abort(); 571 | } 572 | /// Calls std::abort on empty function calls 573 | [[noreturn]] inline void 574 | throw_or_abort(std::false_type /*is_throwing*/) noexcept { 575 | std::abort(); 576 | } 577 | /// Throws bad_function_call on empty funciton calls 578 | [[noreturn]] inline void throw_or_abort(std::true_type /*is_throwing*/) { 579 | #ifdef FU2_HAS_DISABLED_EXCEPTIONS 580 | throw_or_abort(std::false_type{}); 581 | #else 582 | throw bad_function_call{}; 583 | #endif 584 | } 585 | 586 | template 587 | struct function_trait; 588 | 589 | using is_noexcept_ = std::false_type; 590 | using is_noexcept_noexcept = std::true_type; 591 | 592 | #define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \ 593 | template \ 594 | struct function_trait { \ 595 | using pointer_type = Ret (*)(data_accessor CONST VOLATILE*, \ 596 | std::size_t capacity, Args...); \ 597 | template \ 598 | struct internal_invoker { \ 599 | static Ret invoke(data_accessor CONST VOLATILE* data, \ 600 | std::size_t capacity, Args... args) NOEXCEPT { \ 601 | auto obj = retrieve(std::integral_constant{}, \ 602 | data, capacity); \ 603 | auto box = static_cast(obj); \ 604 | return invocation::invoke( \ 605 | static_castvalue_)> CONST VOLATILE \ 606 | REF>(box->value_), \ 607 | std::forward(args)...); \ 608 | } \ 609 | }; \ 610 | \ 611 | template \ 612 | struct view_invoker { \ 613 | static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t, \ 614 | Args... args) NOEXCEPT { \ 615 | \ 616 | auto ptr = static_cast(data->ptr_); \ 617 | return invocation::invoke(address_taker::restore(ptr), \ 618 | std::forward(args)...); \ 619 | } \ 620 | }; \ 621 | \ 622 | template \ 623 | using callable = T CONST VOLATILE REF; \ 624 | \ 625 | using arguments = identity; \ 626 | \ 627 | using is_noexcept = is_noexcept_##NOEXCEPT; \ 628 | \ 629 | template \ 630 | struct empty_invoker { \ 631 | static Ret invoke(data_accessor CONST VOLATILE* /*data*/, \ 632 | std::size_t /*capacity*/, Args... /*args*/) NOEXCEPT { \ 633 | throw_or_abort##NOEXCEPT(std::integral_constant{}); \ 634 | } \ 635 | }; \ 636 | }; 637 | 638 | FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) 639 | #undef FU2_DEFINE_FUNCTION_TRAIT 640 | 641 | /// Deduces to the function pointer to the given signature 642 | template 643 | using function_pointer_of = typename function_trait::pointer_type; 644 | 645 | template 646 | struct invoke_table; 647 | 648 | /// We optimize the vtable_t in case there is a single function overload 649 | template 650 | struct invoke_table { 651 | using type = function_pointer_of; 652 | 653 | /// Return the function pointer itself 654 | template 655 | static constexpr auto fetch(type pointer) noexcept { 656 | static_assert(Index == 0U, "The index should be 0 here!"); 657 | return pointer; 658 | } 659 | 660 | /// Returns the thunk of an single overloaded callable 661 | template 662 | static constexpr type get_invocation_table_of() noexcept { 663 | return &function_trait::template internal_invoker::invoke; 665 | } 666 | /// Returns the thunk of an single overloaded callable 667 | template 668 | static constexpr type get_invocation_view_table_of() noexcept { 669 | return &function_trait::template view_invoker::invoke; 670 | } 671 | /// Returns the thunk of an empty single overloaded callable 672 | template 673 | static constexpr type get_empty_invocation_table() noexcept { 674 | return &function_trait::template empty_invoker::invoke; 675 | } 676 | }; 677 | /// We generate a table in case of multiple function overloads 678 | template 679 | struct invoke_table { 680 | using type = 681 | std::tuple, function_pointer_of, 682 | function_pointer_of...> const*; 683 | 684 | /// Return the function pointer at the particular index 685 | template 686 | static constexpr auto fetch(type table) noexcept { 687 | return std::get(*table); 688 | } 689 | 690 | /// The invocation vtable for a present object 691 | template 692 | struct invocation_vtable : public std::tuple, 693 | function_pointer_of, 694 | function_pointer_of...> { 695 | constexpr invocation_vtable() noexcept 696 | : std::tuple, function_pointer_of, 697 | function_pointer_of...>(std::make_tuple( 698 | &function_trait::template internal_invoker< 699 | T, IsInplace>::invoke, 700 | &function_trait::template internal_invoker< 701 | T, IsInplace>::invoke, 702 | &function_trait::template internal_invoker< 703 | T, IsInplace>::invoke...)) { 704 | } 705 | }; 706 | 707 | /// Returns the thunk of an multi overloaded callable 708 | template 709 | static type get_invocation_table_of() noexcept { 710 | static invocation_vtable const table; 711 | return &table; 712 | } 713 | 714 | /// The invocation vtable for a present object 715 | template 716 | struct invocation_view_vtable 717 | : public std::tuple, 718 | function_pointer_of, 719 | function_pointer_of...> { 720 | constexpr invocation_view_vtable() noexcept 721 | : std::tuple, function_pointer_of, 722 | function_pointer_of...>(std::make_tuple( 723 | &function_trait::template view_invoker::invoke, 724 | &function_trait::template view_invoker::invoke, 725 | &function_trait::template view_invoker::invoke...)) { 726 | } 727 | }; 728 | 729 | /// Returns the thunk of an multi overloaded callable 730 | template 731 | static type get_invocation_view_table_of() noexcept { 732 | static invocation_view_vtable const table; 733 | return &table; 734 | } 735 | 736 | /// The invocation table for an empty wrapper 737 | template 738 | struct empty_vtable : public std::tuple, 739 | function_pointer_of, 740 | function_pointer_of...> { 741 | constexpr empty_vtable() noexcept 742 | : std::tuple, function_pointer_of, 743 | function_pointer_of...>( 744 | std::make_tuple(&function_trait::template empty_invoker< 745 | IsThrowing>::invoke, 746 | &function_trait::template empty_invoker< 747 | IsThrowing>::invoke, 748 | &function_trait::template empty_invoker< 749 | IsThrowing>::invoke...)) { 750 | } 751 | }; 752 | 753 | /// Returns the thunk of an multi single overloaded callable 754 | template 755 | static type get_empty_invocation_table() noexcept { 756 | static empty_vtable const table; 757 | return &table; 758 | } 759 | }; 760 | 761 | template 762 | class operator_impl; 763 | 764 | #define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \ 765 | template \ 767 | class operator_impl \ 770 | : operator_impl { \ 771 | \ 772 | template \ 773 | friend class operator_impl; \ 774 | \ 775 | protected: \ 776 | operator_impl() = default; \ 777 | ~operator_impl() = default; \ 778 | operator_impl(operator_impl const&) = default; \ 779 | operator_impl(operator_impl&&) = default; \ 780 | operator_impl& operator=(operator_impl const&) = default; \ 781 | operator_impl& operator=(operator_impl&&) = default; \ 782 | \ 783 | using operator_impl::operator(); \ 784 | \ 785 | Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \ 786 | auto parent = static_cast(this); \ 787 | using erasure_t = std::decay_terasure_)>; \ 788 | \ 789 | /* `std::decay_terasure_)>` is a workaround for a */ \ 790 | /* compiler regression of MSVC 16.3.1, see #29 for details. */ \ 791 | return std::decay_terasure_)>::template invoke( \ 792 | static_cast(parent->erasure_), \ 793 | std::forward(args)...); \ 794 | } \ 795 | }; \ 796 | template \ 798 | class operator_impl, \ 799 | Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \ 800 | : copyable { \ 801 | \ 802 | template \ 803 | friend class operator_impl; \ 804 | \ 805 | protected: \ 806 | operator_impl() = default; \ 807 | ~operator_impl() = default; \ 808 | operator_impl(operator_impl const&) = default; \ 809 | operator_impl(operator_impl&&) = default; \ 810 | operator_impl& operator=(operator_impl const&) = default; \ 811 | operator_impl& operator=(operator_impl&&) = default; \ 812 | \ 813 | Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \ 814 | auto parent = \ 815 | static_cast CONST VOLATILE*>(this); \ 816 | using erasure_t = std::decay_terasure_)>; \ 817 | \ 818 | /* `std::decay_terasure_)>` is a workaround for a */ \ 819 | /* compiler regression of MSVC 16.3.1, see #29 for details. */ \ 820 | return std::decay_terasure_)>::template invoke( \ 821 | static_cast(parent->erasure_), \ 822 | std::forward(args)...); \ 823 | } \ 824 | }; 825 | 826 | FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) 827 | #undef FU2_DEFINE_FUNCTION_TRAIT 828 | } // namespace invocation_table 829 | 830 | namespace tables { 831 | /// Identifies the action which is dispatched on the erased object 832 | enum class opcode { 833 | op_move, ///< Move the object and set the vtable 834 | op_copy, ///< Copy the object and set the vtable 835 | op_destroy, ///< Destroy the object and reset the vtable 836 | op_weak_destroy, ///< Destroy the object without resetting the vtable 837 | op_fetch_empty, ///< Stores true or false into the to storage 838 | ///< to indicate emptiness 839 | }; 840 | 841 | /// Abstraction for a vtable together with a command table 842 | /// TODO Add optimization for a single formal argument 843 | /// TODO Add optimization to merge both tables if the function is size 844 | /// optimized 845 | template 846 | class vtable; 847 | template 849 | class vtable> { 850 | using command_function_t = void (*)(vtable* /*this*/, opcode /*op*/, 851 | data_accessor* /*from*/, 852 | std::size_t /*from_capacity*/, 853 | data_accessor* /*to*/, 854 | std::size_t /*to_capacity*/); 855 | 856 | using invoke_table_t = invocation_table::invoke_table; 857 | 858 | command_function_t cmd_; 859 | typename invoke_table_t::type vtable_; 860 | 861 | template 862 | struct trait { 863 | static_assert(is_box::value, 864 | "The trait must be specialized with a box!"); 865 | 866 | /// The command table 867 | template 868 | static void process_cmd(vtable* to_table, opcode op, data_accessor* from, 869 | std::size_t from_capacity, data_accessor* to, 870 | std::size_t to_capacity) { 871 | 872 | switch (op) { 873 | case opcode::op_move: { 874 | /// Retrieve the pointer to the object 875 | auto box = static_cast(retrieve( 876 | std::integral_constant{}, from, from_capacity)); 877 | assert(box && "The object must not be over aligned or null!"); 878 | 879 | if (!IsInplace) { 880 | // Just swap both pointers if we allocated on the heap 881 | to->ptr_ = from->ptr_; 882 | 883 | #ifndef NDEBUG 884 | // We don't need to null the pointer since we know that 885 | // we don't own the data anymore through the vtable 886 | // which is set to empty. 887 | from->ptr_ = nullptr; 888 | #endif 889 | 890 | to_table->template set_allocated(); 891 | 892 | } 893 | // The object is allocated inplace 894 | else { 895 | construct(std::true_type{}, std::move(*box), to_table, to, 896 | to_capacity); 897 | box->~T(); 898 | } 899 | return; 900 | } 901 | case opcode::op_copy: { 902 | auto box = static_cast(retrieve( 903 | std::integral_constant{}, from, from_capacity)); 904 | assert(box && "The object must not be over aligned or null!"); 905 | 906 | assert(std::is_copy_constructible::value && 907 | "The box is required to be copyable here!"); 908 | 909 | // Try to allocate the object inplace 910 | construct(std::is_copy_constructible{}, *box, to_table, to, 911 | to_capacity); 912 | return; 913 | } 914 | case opcode::op_destroy: 915 | case opcode::op_weak_destroy: { 916 | 917 | assert(!to && !to_capacity && "Arg overflow!"); 918 | auto box = static_cast(retrieve( 919 | std::integral_constant{}, from, from_capacity)); 920 | 921 | if (IsInplace) { 922 | box->~T(); 923 | } else { 924 | box_factory::box_deallocate(box); 925 | } 926 | 927 | if (op == opcode::op_destroy) { 928 | to_table->set_empty(); 929 | } 930 | return; 931 | } 932 | case opcode::op_fetch_empty: { 933 | write_empty(to, false); 934 | return; 935 | } 936 | } 937 | 938 | FU2_DETAIL_UNREACHABLE(); 939 | } 940 | 941 | template 942 | static void 943 | construct(std::true_type /*apply*/, Box&& box, vtable* to_table, 944 | data_accessor* to, 945 | std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) { 946 | // Try to allocate the object inplace 947 | void* storage = retrieve(std::true_type{}, to, to_capacity); 948 | if (storage) { 949 | to_table->template set_inplace(); 950 | } else { 951 | // Allocate the object through the allocator 952 | to->ptr_ = storage = 953 | box_factory>::box_allocate(std::addressof(box)); 954 | to_table->template set_allocated(); 955 | } 956 | new (storage) T(std::forward(box)); 957 | } 958 | 959 | template 960 | static void 961 | construct(std::false_type /*apply*/, Box&& /*box*/, vtable* /*to_table*/, 962 | data_accessor* /*to*/, 963 | std::size_t /*to_capacity*/) noexcept(HasStrongExceptGuarantee) { 964 | } 965 | }; 966 | 967 | /// The command table 968 | static void empty_cmd(vtable* to_table, opcode op, data_accessor* /*from*/, 969 | std::size_t /*from_capacity*/, data_accessor* to, 970 | std::size_t /*to_capacity*/) { 971 | 972 | switch (op) { 973 | case opcode::op_move: 974 | case opcode::op_copy: { 975 | to_table->set_empty(); 976 | break; 977 | } 978 | case opcode::op_destroy: 979 | case opcode::op_weak_destroy: { 980 | // Do nothing 981 | break; 982 | } 983 | case opcode::op_fetch_empty: { 984 | write_empty(to, true); 985 | break; 986 | } 987 | default: { 988 | FU2_DETAIL_UNREACHABLE(); 989 | } 990 | } 991 | } 992 | 993 | public: 994 | vtable() noexcept = default; 995 | 996 | /// Initialize an object at the given position 997 | template 998 | static void init(vtable& table, T&& object, data_accessor* to, 999 | std::size_t to_capacity) { 1000 | 1001 | trait>::construct(std::true_type{}, std::forward(object), 1002 | &table, to, to_capacity); 1003 | } 1004 | 1005 | /// Moves the object at the given position 1006 | void move(vtable& to_table, data_accessor* from, std::size_t from_capacity, 1007 | data_accessor* to, 1008 | std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) { 1009 | cmd_(&to_table, opcode::op_move, from, from_capacity, to, to_capacity); 1010 | set_empty(); 1011 | } 1012 | 1013 | /// Destroys the object at the given position 1014 | void copy(vtable& to_table, data_accessor const* from, 1015 | std::size_t from_capacity, data_accessor* to, 1016 | std::size_t to_capacity) const { 1017 | cmd_(&to_table, opcode::op_copy, const_cast(from), 1018 | from_capacity, to, to_capacity); 1019 | } 1020 | 1021 | /// Destroys the object at the given position 1022 | void destroy(data_accessor* from, 1023 | std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) { 1024 | cmd_(this, opcode::op_destroy, from, from_capacity, nullptr, 0U); 1025 | } 1026 | 1027 | /// Destroys the object at the given position without invalidating the 1028 | /// vtable 1029 | void 1030 | weak_destroy(data_accessor* from, 1031 | std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) { 1032 | cmd_(this, opcode::op_weak_destroy, from, from_capacity, nullptr, 0U); 1033 | } 1034 | 1035 | /// Returns true when the vtable doesn't hold any erased object 1036 | bool empty() const noexcept { 1037 | data_accessor data; 1038 | cmd_(nullptr, opcode::op_fetch_empty, nullptr, 0U, &data, 0U); 1039 | return bool(data.inplace_storage_); 1040 | } 1041 | 1042 | /// Invoke the function at the given index 1043 | template 1044 | constexpr decltype(auto) invoke(Args&&... args) const { 1045 | auto thunk = invoke_table_t::template fetch(vtable_); 1046 | return thunk(std::forward(args)...); 1047 | } 1048 | /// Invoke the function at the given index 1049 | template 1050 | constexpr decltype(auto) invoke(Args&&... args) const volatile { 1051 | auto thunk = invoke_table_t::template fetch(vtable_); 1052 | return thunk(std::forward(args)...); 1053 | } 1054 | 1055 | template 1056 | void set_inplace() noexcept { 1057 | using type = std::decay_t; 1058 | vtable_ = invoke_table_t::template get_invocation_table_of(); 1059 | cmd_ = &trait::template process_cmd; 1060 | } 1061 | 1062 | template 1063 | void set_allocated() noexcept { 1064 | using type = std::decay_t; 1065 | vtable_ = invoke_table_t::template get_invocation_table_of(); 1066 | cmd_ = &trait::template process_cmd; 1067 | } 1068 | 1069 | void set_empty() noexcept { 1070 | vtable_ = invoke_table_t::template get_empty_invocation_table(); 1071 | cmd_ = &empty_cmd; 1072 | } 1073 | }; 1074 | } // namespace tables 1075 | 1076 | /// A union which makes the pointer to the heap object share the 1077 | /// same space with the internal capacity. 1078 | /// The storage type is distinguished by multiple versions of the 1079 | /// control and vtable. 1080 | template 1081 | struct internal_capacity { 1082 | /// We extend the union through a technique similar to the tail object hack 1083 | typedef union { 1084 | /// Tag to access the structure in a type-safe way 1085 | data_accessor accessor_; 1086 | /// The internal capacity we use to allocate in-place 1087 | std::aligned_storage_t capacity_; 1088 | } type; 1089 | }; 1090 | template 1091 | struct internal_capacity< 1092 | Capacity, std::enable_if_t<(Capacity::capacity < sizeof(void*))>> { 1093 | typedef struct { 1094 | /// Tag to access the structure in a type-safe way 1095 | data_accessor accessor_; 1096 | } type; 1097 | }; 1098 | 1099 | template 1100 | class internal_capacity_holder { 1101 | // Tag to access the structure in a type-safe way 1102 | typename internal_capacity::type storage_; 1103 | 1104 | public: 1105 | constexpr internal_capacity_holder() = default; 1106 | 1107 | FU2_DETAIL_CXX14_CONSTEXPR data_accessor* opaque_ptr() noexcept { 1108 | return &storage_.accessor_; 1109 | } 1110 | constexpr data_accessor const* opaque_ptr() const noexcept { 1111 | return &storage_.accessor_; 1112 | } 1113 | FU2_DETAIL_CXX14_CONSTEXPR data_accessor volatile* 1114 | opaque_ptr() volatile noexcept { 1115 | return &storage_.accessor_; 1116 | } 1117 | constexpr data_accessor const volatile* opaque_ptr() const volatile noexcept { 1118 | return &storage_.accessor_; 1119 | } 1120 | 1121 | static constexpr std::size_t capacity() noexcept { 1122 | return sizeof(storage_); 1123 | } 1124 | }; 1125 | 1126 | /// An owning erasure 1127 | template 1128 | class erasure : internal_capacity_holder { 1129 | template 1130 | friend class erasure; 1131 | template 1132 | friend class operator_impl; 1133 | 1134 | using vtable_t = tables::vtable; 1135 | 1136 | vtable_t vtable_; 1137 | 1138 | public: 1139 | /// Returns the capacity of this erasure 1140 | static constexpr std::size_t capacity() noexcept { 1141 | return internal_capacity_holder::capacity(); 1142 | } 1143 | 1144 | FU2_DETAIL_CXX14_CONSTEXPR erasure() noexcept { 1145 | vtable_.set_empty(); 1146 | } 1147 | 1148 | FU2_DETAIL_CXX14_CONSTEXPR erasure(std::nullptr_t) noexcept { 1149 | vtable_.set_empty(); 1150 | } 1151 | 1152 | FU2_DETAIL_CXX14_CONSTEXPR 1153 | erasure(erasure&& right) noexcept(Property::is_strong_exception_guaranteed) { 1154 | right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), 1155 | this->opaque_ptr(), capacity()); 1156 | } 1157 | 1158 | FU2_DETAIL_CXX14_CONSTEXPR erasure(erasure const& right) { 1159 | right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), 1160 | this->opaque_ptr(), capacity()); 1161 | } 1162 | 1163 | template 1164 | FU2_DETAIL_CXX14_CONSTEXPR 1165 | erasure(erasure right) noexcept( 1166 | Property::is_strong_exception_guaranteed) { 1167 | right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), 1168 | this->opaque_ptr(), capacity()); 1169 | } 1170 | 1171 | template >> 1172 | FU2_DETAIL_CXX14_CONSTEXPR erasure(std::false_type /*use_bool_op*/, 1173 | T&& callable, 1174 | Allocator&& allocator_ = Allocator{}) { 1175 | vtable_t::init(vtable_, 1176 | type_erasure::make_box( 1177 | std::integral_constant{}, 1178 | std::forward(callable), 1179 | std::forward(allocator_)), 1180 | this->opaque_ptr(), capacity()); 1181 | } 1182 | template >> 1183 | FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type /*use_bool_op*/, 1184 | T&& callable, 1185 | Allocator&& allocator_ = Allocator{}) { 1186 | if (!!callable) { 1187 | vtable_t::init(vtable_, 1188 | type_erasure::make_box( 1189 | std::integral_constant{}, 1190 | std::forward(callable), 1191 | std::forward(allocator_)), 1192 | this->opaque_ptr(), capacity()); 1193 | } else { 1194 | vtable_.set_empty(); 1195 | } 1196 | } 1197 | 1198 | ~erasure() { 1199 | vtable_.weak_destroy(this->opaque_ptr(), capacity()); 1200 | } 1201 | 1202 | FU2_DETAIL_CXX14_CONSTEXPR erasure& 1203 | operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) { 1204 | vtable_.destroy(this->opaque_ptr(), capacity()); 1205 | return *this; 1206 | } 1207 | 1208 | FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure&& right) noexcept( 1209 | Property::is_strong_exception_guaranteed) { 1210 | vtable_.weak_destroy(this->opaque_ptr(), capacity()); 1211 | right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), 1212 | this->opaque_ptr(), capacity()); 1213 | return *this; 1214 | } 1215 | 1216 | FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure const& right) { 1217 | vtable_.weak_destroy(this->opaque_ptr(), capacity()); 1218 | right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), 1219 | this->opaque_ptr(), capacity()); 1220 | return *this; 1221 | } 1222 | 1223 | template 1224 | FU2_DETAIL_CXX14_CONSTEXPR erasure& 1225 | operator=(erasure right) noexcept( 1226 | Property::is_strong_exception_guaranteed) { 1227 | vtable_.weak_destroy(this->opaque_ptr(), capacity()); 1228 | right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), 1229 | this->opaque_ptr(), capacity()); 1230 | return *this; 1231 | } 1232 | 1233 | template >> 1234 | void assign(std::false_type /*use_bool_op*/, T&& callable, 1235 | Allocator&& allocator_ = {}) { 1236 | vtable_.weak_destroy(this->opaque_ptr(), capacity()); 1237 | vtable_t::init(vtable_, 1238 | type_erasure::make_box( 1239 | std::integral_constant{}, 1240 | std::forward(callable), 1241 | std::forward(allocator_)), 1242 | this->opaque_ptr(), capacity()); 1243 | } 1244 | 1245 | template >> 1246 | void assign(std::true_type /*use_bool_op*/, T&& callable, 1247 | Allocator&& allocator_ = {}) { 1248 | if (!!callable) { 1249 | assign(std::false_type{}, std::forward(callable), 1250 | std::forward(allocator_)); 1251 | } else { 1252 | operator=(nullptr); 1253 | } 1254 | } 1255 | 1256 | /// Returns true when the erasure doesn't hold any erased object 1257 | constexpr bool empty() const noexcept { 1258 | return vtable_.empty(); 1259 | } 1260 | 1261 | /// Invoke the function of the erasure at the given index 1262 | /// 1263 | /// We define this out of class to be able to forward the qualified 1264 | /// erasure correctly. 1265 | template 1266 | static constexpr decltype(auto) invoke(Erasure&& erasure, Args&&... args) { 1267 | auto const capacity = erasure.capacity(); 1268 | return erasure.vtable_.template invoke( 1269 | std::forward(erasure).opaque_ptr(), capacity, 1270 | std::forward(args)...); 1271 | } 1272 | }; 1273 | 1274 | // A non owning erasure 1275 | template 1277 | class erasure> { 1279 | template 1280 | friend class erasure; 1281 | template 1282 | friend class operator_impl; 1283 | 1284 | using property_t = property; 1285 | 1286 | using invoke_table_t = invocation_table::invoke_table; 1287 | typename invoke_table_t::type invoke_table_; 1288 | 1289 | /// The internal pointer to the non owned object 1290 | data_accessor view_; 1291 | 1292 | public: 1293 | // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) 1294 | constexpr erasure() noexcept 1295 | : invoke_table_( 1296 | invoke_table_t::template get_empty_invocation_table()), 1297 | view_(nullptr) { 1298 | } 1299 | 1300 | // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) 1301 | constexpr erasure(std::nullptr_t) noexcept 1302 | : invoke_table_( 1303 | invoke_table_t::template get_empty_invocation_table()), 1304 | view_(nullptr) { 1305 | } 1306 | 1307 | // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) 1308 | constexpr erasure(erasure&& right) noexcept 1309 | : invoke_table_(right.invoke_table_), view_(right.view_) { 1310 | } 1311 | 1312 | constexpr erasure(erasure const& /*right*/) = default; 1313 | 1314 | template 1315 | // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) 1316 | constexpr erasure(erasure right) noexcept 1317 | : invoke_table_(right.invoke_table_), view_(right.view_) { 1318 | } 1319 | 1320 | template 1321 | // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) 1322 | constexpr erasure(std::false_type /*use_bool_op*/, T&& object) 1323 | : invoke_table_(invoke_table_t::template get_invocation_view_table_of< 1324 | std::decay_t>()), 1325 | view_(address_taker>::take(std::forward(object))) { 1326 | } 1327 | template 1328 | // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) 1329 | FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type use_bool_op, T&& object) { 1330 | this->assign(use_bool_op, std::forward(object)); 1331 | } 1332 | 1333 | ~erasure() = default; 1334 | 1335 | constexpr erasure& 1336 | operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) { 1337 | invoke_table_ = 1338 | invoke_table_t::template get_empty_invocation_table(); 1339 | view_.ptr_ = nullptr; 1340 | return *this; 1341 | } 1342 | 1343 | constexpr erasure& operator=(erasure&& right) noexcept { 1344 | invoke_table_ = right.invoke_table_; 1345 | view_ = right.view_; 1346 | right = nullptr; 1347 | return *this; 1348 | } 1349 | 1350 | constexpr erasure& operator=(erasure const& /*right*/) = default; 1351 | 1352 | template 1353 | constexpr erasure& 1354 | operator=(erasure right) noexcept { 1355 | invoke_table_ = right.invoke_table_; 1356 | view_ = right.view_; 1357 | return *this; 1358 | } 1359 | 1360 | template 1361 | constexpr void assign(std::false_type /*use_bool_op*/, T&& callable) { 1362 | invoke_table_ = invoke_table_t::template get_invocation_view_table_of< 1363 | std::decay_t>(); 1364 | view_.assign_ptr( 1365 | address_taker>::take(std::forward(callable))); 1366 | } 1367 | template 1368 | constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) { 1369 | if (!!callable) { 1370 | assign(std::false_type{}, std::forward(callable)); 1371 | } else { 1372 | operator=(nullptr); 1373 | } 1374 | } 1375 | 1376 | /// Returns true when the erasure doesn't hold any erased object 1377 | constexpr bool empty() const noexcept { 1378 | return view_.ptr_ == nullptr; 1379 | } 1380 | 1381 | template 1382 | static constexpr decltype(auto) invoke(Erasure&& erasure, T&&... args) { 1383 | auto thunk = invoke_table_t::template fetch(erasure.invoke_table_); 1384 | return thunk(&(erasure.view_), 0UL, std::forward(args)...); 1385 | } 1386 | }; 1387 | } // namespace type_erasure 1388 | 1389 | /// Deduces to a true_type if the type T provides the given signature and the 1390 | /// signature is noexcept correct callable. 1391 | template > 1394 | struct accepts_one 1395 | : detail::lazy_and< // both are std::integral_constant 1396 | invocation::can_invoke, 1397 | typename Trait::arguments>, 1398 | invocation::is_noexcept_correct, 1400 | typename Trait::arguments>> {}; 1401 | 1402 | /// Deduces to a true_type if the type T provides all signatures 1403 | template 1404 | struct accepts_all : std::false_type {}; 1405 | template 1406 | struct accepts_all< 1407 | T, identity, 1408 | void_t::value>...>> 1409 | : std::true_type {}; 1410 | 1411 | #if defined(FU2_HAS_NO_EMPTY_PROPAGATION) 1412 | template 1413 | struct use_bool_op : std::false_type {}; 1414 | #elif defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION) 1415 | /// Implementation for use_bool_op based on the behaviour of std::function, 1416 | /// propagating empty state for pointers, `std::function` and 1417 | /// `fu2::detail::function` types only. 1418 | template 1419 | struct use_bool_op : std::false_type {}; 1420 | 1421 | #if !defined(FU2_HAS_NO_FUNCTIONAL_HEADER) 1422 | template 1423 | struct use_bool_op> : std::true_type {}; 1424 | #endif 1425 | 1426 | template 1427 | struct use_bool_op> : std::true_type {}; 1428 | 1429 | template 1430 | struct use_bool_op : std::true_type {}; 1431 | 1432 | template 1433 | struct use_bool_op : std::true_type {}; 1434 | #else 1435 | template 1436 | struct has_bool_op : std::false_type {}; 1437 | template 1438 | struct has_bool_op()))>> 1439 | : std::true_type { 1440 | #ifndef NDEBUG 1441 | static_assert(!std::is_pointer::value, 1442 | "Missing deduction for function pointer!"); 1443 | #endif 1444 | }; 1445 | 1446 | /// Deduces to a true_type if the type T is implementing operator bool() 1447 | /// or if the type is convertible to bool directly, this also implements an 1448 | /// optimizations for function references `void(&)()` which are can never 1449 | /// be null and for such a conversion to bool would never return false. 1450 | template 1451 | struct use_bool_op : has_bool_op {}; 1452 | 1453 | #define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, NOEXCEPT) \ 1454 | template \ 1455 | struct use_bool_op \ 1456 | : std::true_type {}; 1457 | 1458 | FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT) 1459 | #undef FU2_DEFINE_USE_OP_TRAIT 1460 | 1461 | template 1462 | struct use_bool_op : std::false_type {}; 1463 | 1464 | #if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE) 1465 | template 1466 | struct use_bool_op : std::false_type {}; 1467 | #endif 1468 | #endif // FU2_HAS_NO_EMPTY_PROPAGATION 1469 | 1470 | template 1471 | struct assert_wrong_copy_assign { 1472 | static_assert(!Config::is_owning || !Config::is_copyable || 1473 | std::is_copy_constructible>::value, 1474 | "Can't wrap a non copyable object into a unique function!"); 1475 | 1476 | using type = void; 1477 | }; 1478 | 1479 | template 1480 | struct assert_no_strong_except_guarantee { 1481 | static_assert( 1482 | !IsStrongExceptGuaranteed || 1483 | (std::is_nothrow_move_constructible::value && 1484 | std::is_nothrow_destructible::value), 1485 | "Can't wrap a object an object that has no strong exception guarantees " 1486 | "if this is required by the wrapper!"); 1487 | 1488 | using type = void; 1489 | }; 1490 | 1491 | /// SFINAES out if the given callable is not copyable correct to the left one. 1492 | template 1493 | using enable_if_copyable_correct_t = 1494 | std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>; 1495 | 1496 | template 1497 | using is_owning_correct = 1498 | std::integral_constant; 1500 | 1501 | /// SFINAES out if the given function2 is not owning correct to this one 1502 | template 1503 | using enable_if_owning_correct_t = 1504 | std::enable_if_t::value>; 1505 | 1506 | template 1508 | class function> 1509 | : type_erasure::invocation_table::operator_impl< 1510 | 0U, 1511 | function>, 1513 | Args...> { 1514 | 1515 | template 1516 | friend class function; 1517 | 1518 | template 1519 | friend class type_erasure::invocation_table::operator_impl; 1520 | 1521 | using property_t = property; 1522 | using erasure_t = 1523 | type_erasure::erasure; 1524 | 1525 | template 1526 | using enable_if_can_accept_all_t = 1527 | std::enable_if_t, identity>::value>; 1528 | 1529 | template 1530 | struct is_convertible_to_this : std::false_type {}; 1531 | template 1532 | struct is_convertible_to_this< 1533 | function, 1534 | void_t, 1535 | enable_if_owning_correct_t>> 1536 | : std::true_type {}; 1537 | 1538 | template 1539 | using enable_if_not_convertible_to_this = 1540 | std::enable_if_t>::value>; 1541 | 1542 | template 1543 | using enable_if_owning_t = 1544 | std::enable_if_t::value && Config::is_owning>; 1545 | 1546 | template 1547 | using assert_wrong_copy_assign_t = 1548 | typename assert_wrong_copy_assign>::type; 1549 | 1550 | template 1551 | using assert_no_strong_except_guarantee_t = 1552 | typename assert_no_strong_except_guarantee>::type; 1554 | 1555 | erasure_t erasure_; 1556 | 1557 | public: 1558 | /// Default constructor which empty constructs the function 1559 | function() = default; 1560 | ~function() = default; 1561 | 1562 | explicit FU2_DETAIL_CXX14_CONSTEXPR 1563 | function(function const& /*right*/) = default; 1564 | explicit FU2_DETAIL_CXX14_CONSTEXPR function(function&& /*right*/) = default; 1565 | 1566 | /// Copy construction from another copyable function 1567 | template * = nullptr, 1569 | enable_if_copyable_correct_t* = nullptr, 1570 | enable_if_owning_correct_t* = nullptr> 1571 | FU2_DETAIL_CXX14_CONSTEXPR 1572 | function(function const& right) 1573 | : erasure_(right.erasure_) { 1574 | } 1575 | 1576 | /// Move construction from another function 1577 | template * = nullptr, 1579 | enable_if_owning_correct_t* = nullptr> 1580 | FU2_DETAIL_CXX14_CONSTEXPR function(function&& right) 1581 | : erasure_(std::move(right.erasure_)) { 1582 | } 1583 | 1584 | /// Construction from a callable object which overloads the `()` operator 1585 | template * = nullptr, 1587 | enable_if_can_accept_all_t* = nullptr, 1588 | assert_wrong_copy_assign_t* = nullptr, 1589 | assert_no_strong_except_guarantee_t* = nullptr> 1590 | FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable) 1591 | : erasure_(use_bool_op>{}, std::forward(callable)) { 1592 | } 1593 | template * = nullptr, 1595 | enable_if_can_accept_all_t* = nullptr, 1596 | enable_if_owning_t* = nullptr, 1597 | assert_wrong_copy_assign_t* = nullptr, 1598 | assert_no_strong_except_guarantee_t* = nullptr> 1599 | FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable, Allocator&& allocator_) 1600 | : erasure_(use_bool_op>{}, std::forward(callable), 1601 | std::forward(allocator_)) { 1602 | } 1603 | 1604 | /// Empty constructs the function 1605 | FU2_DETAIL_CXX14_CONSTEXPR function(std::nullptr_t np) : erasure_(np) { 1606 | } 1607 | 1608 | function& operator=(function const& /*right*/) = default; 1609 | function& operator=(function&& /*right*/) = default; 1610 | 1611 | /// Copy assigning from another copyable function 1612 | template * = nullptr, 1614 | enable_if_copyable_correct_t* = nullptr, 1615 | enable_if_owning_correct_t* = nullptr> 1616 | function& operator=(function const& right) { 1617 | erasure_ = right.erasure_; 1618 | return *this; 1619 | } 1620 | 1621 | /// Move assigning from another function 1622 | template * = nullptr, 1624 | enable_if_owning_correct_t* = nullptr> 1625 | function& operator=(function&& right) { 1626 | erasure_ = std::move(right.erasure_); 1627 | return *this; 1628 | } 1629 | 1630 | /// Move assigning from a callable object 1631 | template * = nullptr, 1633 | enable_if_can_accept_all_t* = nullptr, 1634 | assert_wrong_copy_assign_t* = nullptr, 1635 | assert_no_strong_except_guarantee_t* = nullptr> 1636 | function& operator=(T&& callable) { 1637 | erasure_.assign(use_bool_op>{}, std::forward(callable)); 1638 | return *this; 1639 | } 1640 | 1641 | /// Clears the function 1642 | function& operator=(std::nullptr_t np) { 1643 | erasure_ = np; 1644 | return *this; 1645 | } 1646 | 1647 | /// Returns true when the function is empty 1648 | bool empty() const noexcept { 1649 | return erasure_.empty(); 1650 | } 1651 | 1652 | /// Returns true when the function isn't empty 1653 | explicit operator bool() const noexcept { 1654 | return !empty(); 1655 | } 1656 | 1657 | /// Assigns a new target with an optional allocator 1658 | template >, 1659 | enable_if_not_convertible_to_this* = nullptr, 1660 | enable_if_can_accept_all_t* = nullptr, 1661 | assert_wrong_copy_assign_t* = nullptr, 1662 | assert_no_strong_except_guarantee_t* = nullptr> 1663 | void assign(T&& callable, Allocator&& allocator_ = Allocator{}) { 1664 | erasure_.assign(use_bool_op>{}, std::forward(callable), 1665 | std::forward(allocator_)); 1666 | } 1667 | 1668 | /// Swaps this function with the given function 1669 | void swap(function& other) noexcept(HasStrongExceptGuarantee) { 1670 | if (&other == this) { 1671 | return; 1672 | } 1673 | 1674 | function cache = std::move(other); 1675 | other = std::move(*this); 1676 | *this = std::move(cache); 1677 | } 1678 | 1679 | /// Swaps the left function with the right one 1680 | friend void swap(function& left, 1681 | function& right) noexcept(HasStrongExceptGuarantee) { 1682 | left.swap(right); 1683 | } 1684 | 1685 | /// Calls the wrapped callable object 1686 | using type_erasure::invocation_table::operator_impl< 1687 | 0U, function, Args...>::operator(); 1688 | }; 1689 | 1690 | template 1691 | bool operator==(function const& f, std::nullptr_t) { 1692 | return !bool(f); 1693 | } 1694 | 1695 | template 1696 | bool operator!=(function const& f, std::nullptr_t) { 1697 | return bool(f); 1698 | } 1699 | 1700 | template 1701 | bool operator==(std::nullptr_t, function const& f) { 1702 | return !bool(f); 1703 | } 1704 | 1705 | template 1706 | bool operator!=(std::nullptr_t, function const& f) { 1707 | return bool(f); 1708 | } 1709 | 1710 | // Default intended object size of the function 1711 | using object_size = std::integral_constant; 1712 | } // namespace detail 1713 | } // namespace abi_400 1714 | 1715 | /// Can be passed to function_base as template argument which causes 1716 | /// the internal small buffer to be sized according to the given size, 1717 | /// and aligned with the given alignment. 1718 | template 1720 | struct capacity_fixed { 1721 | static constexpr std::size_t capacity = Capacity; 1722 | static constexpr std::size_t alignment = Alignment; 1723 | }; 1724 | 1725 | /// Default capacity for small functor optimization 1726 | struct capacity_default 1727 | : capacity_fixed {}; 1728 | 1729 | /// Can be passed to function_base as template argument which causes 1730 | /// the internal small buffer to be removed from the callable wrapper. 1731 | /// The owning function_base will then allocate memory for every object 1732 | /// it applies a type erasure on. 1733 | struct capacity_none : capacity_fixed<0UL> {}; 1734 | 1735 | /// Can be passed to function_base as template argument which causes 1736 | /// the internal small buffer to be sized such that it can hold 1737 | /// the given object without allocating memory for an applied type erasure. 1738 | template 1739 | struct capacity_can_hold { 1740 | static constexpr std::size_t capacity = sizeof(T); 1741 | static constexpr std::size_t alignment = alignof(T); 1742 | }; 1743 | 1744 | /// An adaptable function wrapper base for arbitrary functional types. 1745 | /// 1746 | /// \tparam IsOwning Is true when the type erasure shall be owning the object. 1747 | /// 1748 | /// \tparam IsCopyable Defines whether the function is copyable or not 1749 | /// 1750 | /// \tparam Capacity Defines the internal capacity of the function 1751 | /// for small functor optimization. 1752 | /// The size of the whole function object will be the capacity 1753 | /// plus the size of two pointers. If the capacity is zero, 1754 | /// the size will increase through one additional pointer 1755 | /// so the whole object has the size of 3 * sizeof(void*). 1756 | /// The type which is passed to the Capacity template parameter 1757 | /// shall provide a capacity and alignment member which 1758 | /// looks like the following example: 1759 | /// ```cpp 1760 | /// struct my_capacity { 1761 | /// static constexpr std::size_t capacity = sizeof(my_type); 1762 | /// static constexpr std::size_t alignment = alignof(my_type); 1763 | /// }; 1764 | /// ``` 1765 | /// 1766 | /// \tparam IsThrowing Defines whether the function throws an exception on 1767 | /// empty function call, `std::abort` is called otherwise. 1768 | /// 1769 | /// \tparam HasStrongExceptGuarantee Defines whether all objects satisfy the 1770 | /// strong exception guarantees, 1771 | /// which means the function type will satisfy 1772 | /// the strong exception guarantees too. 1773 | /// 1774 | /// \tparam Signatures Defines the signature of the callable wrapper 1775 | /// 1776 | template 1778 | using function_base = detail::function< 1779 | detail::config, 1780 | detail::property>; 1781 | 1782 | /// An owning copyable function wrapper for arbitrary callable types. 1783 | template 1784 | using function = function_base; 1786 | 1787 | /// An owning non copyable function wrapper for arbitrary callable types. 1788 | template 1789 | using unique_function = function_base; 1791 | 1792 | /// A non owning copyable function wrapper for arbitrary callable types. 1793 | template 1794 | using function_view = function_base; 1796 | 1797 | #if !defined(FU2_HAS_DISABLED_EXCEPTIONS) 1798 | /// Exception type that is thrown when invoking empty function objects 1799 | /// and exception support isn't disabled. 1800 | /// 1801 | /// Exception support is enabled if 1802 | /// the template parameter 'Throwing' is set to true (default). 1803 | /// 1804 | /// This type will default to std::bad_function_call if the 1805 | /// functional header is used, otherwise the library provides its own type. 1806 | /// 1807 | /// You may disable the inclusion of the functional header 1808 | /// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`. 1809 | /// 1810 | using detail::type_erasure::invocation_table::bad_function_call; 1811 | #endif 1812 | 1813 | /// Returns a callable object, which unifies all callable objects 1814 | /// that were passed to this function. 1815 | /// 1816 | /// ```cpp 1817 | /// auto overloaded = fu2::overload([](std::true_type) { return true; }, 1818 | /// [](std::false_type) { return false; }); 1819 | /// ``` 1820 | /// 1821 | /// \param callables A pack of callable objects with arbitrary signatures. 1822 | /// 1823 | /// \returns A callable object which exposes the 1824 | /// 1825 | template 1826 | constexpr auto overload(T&&... callables) { 1827 | return detail::overloading::overload(std::forward(callables)...); 1828 | } 1829 | } // namespace fu2 1830 | 1831 | namespace std{ 1832 | template 1833 | struct uses_allocator< 1834 | ::fu2::detail::function, 1835 | Alloc 1836 | > : std::true_type {}; 1837 | } // namespace std 1838 | 1839 | #undef FU2_DETAIL_EXPAND_QUALIFIERS 1840 | #undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT 1841 | #undef FU2_DETAIL_EXPAND_CV 1842 | #undef FU2_DETAIL_EXPAND_CV_NOEXCEPT 1843 | #undef FU2_DETAIL_UNREACHABLE_INTRINSIC 1844 | #undef FU2_DETAIL_TRAP 1845 | #undef FU2_DETAIL_CXX14_CONSTEXPR 1846 | 1847 | #endif // FU2_INCLUDED_FUNCTION2_HPP_ 1848 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(gtest STATIC 2 | "${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc" 3 | "${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest_main.cc") 4 | 5 | target_include_directories(gtest 6 | PRIVATE 7 | "${CMAKE_CURRENT_LIST_DIR}/googletest/googletest" 8 | PUBLIC 9 | "${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/include") 10 | 11 | target_compile_definitions(gtest 12 | PUBLIC 13 | -DGTEST_HAS_PTHREAD=0 14 | -DGTEST_LANG_CXX11=1) 15 | 16 | target_compile_features(gtest 17 | PUBLIC 18 | cxx_alias_templates 19 | cxx_auto_type 20 | cxx_decltype 21 | cxx_final 22 | cxx_lambdas 23 | cxx_nullptr 24 | cxx_override) 25 | 26 | add_executable(function2_tests 27 | ${CMAKE_CURRENT_LIST_DIR}/../include/function2/function2.hpp 28 | ${CMAKE_CURRENT_LIST_DIR}/assign-and-constructible-test.cpp 29 | ${CMAKE_CURRENT_LIST_DIR}/build-test.cpp 30 | ${CMAKE_CURRENT_LIST_DIR}/empty-function-call-test.cpp 31 | ${CMAKE_CURRENT_LIST_DIR}/function2-test.hpp 32 | ${CMAKE_CURRENT_LIST_DIR}/functionality-test.cpp 33 | ${CMAKE_CURRENT_LIST_DIR}/noexcept-test.cpp 34 | ${CMAKE_CURRENT_LIST_DIR}/self-containing-test.cpp 35 | ${CMAKE_CURRENT_LIST_DIR}/standard-compliant-test.cpp 36 | ${CMAKE_CURRENT_LIST_DIR}/type-test.cpp 37 | ${CMAKE_CURRENT_LIST_DIR}/multi-signature-test.cpp 38 | ${CMAKE_CURRENT_LIST_DIR}/regressions.cpp 39 | ${CMAKE_CURRENT_LIST_DIR}/view-test.cpp 40 | ${CMAKE_CURRENT_LIST_DIR}/overload-test.cpp) 41 | 42 | target_link_libraries(function2_tests 43 | PRIVATE 44 | function2 45 | gtest) 46 | 47 | add_test(NAME function2-unit-tests COMMAND function2_tests) 48 | 49 | add_executable(function2_playground 50 | ${CMAKE_CURRENT_LIST_DIR}/../include/function2/function2.hpp 51 | ${CMAKE_CURRENT_LIST_DIR}/playground.cpp) 52 | 53 | target_link_libraries(function2_playground 54 | PRIVATE 55 | function2) 56 | -------------------------------------------------------------------------------- /test/assign-and-constructible-test.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2015-2020 Denis Blank 3 | // Distributed under the Boost Software License, Version 1.0 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "function2-test.hpp" 8 | 9 | namespace { 10 | /// Coroutine which increases it's return value by every call 11 | class UniqueIncreasingCoroutine { 12 | std::unique_ptr state = make_unique(0); 13 | 14 | public: 15 | UniqueIncreasingCoroutine() { 16 | } 17 | 18 | std::size_t operator()() { 19 | return (*state)++; 20 | } 21 | }; 22 | 23 | /// Coroutine which increases it's return value by every call 24 | class CopyableIncreasingCoroutine { 25 | std::size_t state = 0UL; 26 | 27 | public: 28 | CopyableIncreasingCoroutine() { 29 | } 30 | 31 | std::size_t operator()() { 32 | return state++; 33 | } 34 | }; 35 | 36 | /// Functor which returns it's shared count 37 | class SharedCountFunctor { 38 | std::shared_ptr state = std::make_shared(0); 39 | 40 | public: 41 | std::size_t operator()() const { 42 | return state.use_count(); 43 | } 44 | }; 45 | } // namespace 46 | 47 | ALL_LEFT_RIGHT_TYPED_TEST_CASE(AllMoveAssignConstructTests) 48 | 49 | TYPED_TEST(AllMoveAssignConstructTests, AreMoveConstructible) { 50 | typename TestFixture::template right_t right = returnTrue; 51 | typename TestFixture::template left_t left(std::move(right)); 52 | EXPECT_TRUE(left()); 53 | } 54 | 55 | TYPED_TEST(AllMoveAssignConstructTests, AreMoveAssignable) { 56 | typename TestFixture::template left_t left; 57 | typename TestFixture::template right_t right = returnTrue; 58 | left = std::move(right); 59 | EXPECT_TRUE(left()); 60 | } 61 | 62 | TYPED_TEST(AllMoveAssignConstructTests, TransferStatesOnConstruct) { 63 | typename TestFixture::template left_t left; 64 | typename TestFixture::template right_t right = 65 | CopyableIncreasingCoroutine(); 66 | EXPECT_EQ(right(), 0UL); 67 | EXPECT_EQ(right(), 1UL); 68 | left = std::move(right); 69 | EXPECT_EQ(left(), 2UL); 70 | EXPECT_EQ(left(), 3UL); 71 | EXPECT_EQ(left(), 4UL); 72 | } 73 | 74 | TYPED_TEST(AllMoveAssignConstructTests, TransferStatesOnAssign) { 75 | typename TestFixture::template right_t right = 76 | CopyableIncreasingCoroutine(); 77 | EXPECT_EQ(right(), 0UL); 78 | EXPECT_EQ(right(), 1UL); 79 | typename TestFixture::template left_t left(std::move(right)); 80 | EXPECT_EQ(left(), 2UL); 81 | EXPECT_EQ(left(), 3UL); 82 | EXPECT_EQ(left(), 4UL); 83 | } 84 | 85 | TYPED_TEST(AllMoveAssignConstructTests, AreEmptyAfterMoveConstruct) { 86 | typename TestFixture::template right_t right = returnTrue; 87 | EXPECT_TRUE(right); 88 | EXPECT_TRUE(right()); 89 | typename TestFixture::template left_t left(std::move(right)); 90 | EXPECT_FALSE(right); 91 | EXPECT_TRUE(left); 92 | EXPECT_TRUE(left()); 93 | } 94 | 95 | TYPED_TEST(AllMoveAssignConstructTests, AreEmptyAfterMoveAssign) { 96 | typename TestFixture::template left_t left; 97 | typename TestFixture::template right_t right; 98 | EXPECT_FALSE(left); 99 | EXPECT_FALSE(right); 100 | right = returnTrue; 101 | EXPECT_TRUE(right); 102 | EXPECT_TRUE(right()); 103 | left = std::move(right); 104 | EXPECT_FALSE(right); 105 | EXPECT_TRUE(left); 106 | EXPECT_TRUE(left()); 107 | } 108 | 109 | UNIQUE_LEFT_RIGHT_TYPED_TEST_CASE(UniqueMoveAssignConstructTests) 110 | 111 | TYPED_TEST(UniqueMoveAssignConstructTests, TransferStateOnMoveConstruct) { 112 | { 113 | typename TestFixture::template right_t right = 114 | UniqueIncreasingCoroutine(); 115 | typename TestFixture::template left_t left(std::move(right)); 116 | EXPECT_EQ(left(), 0UL); 117 | } 118 | 119 | { 120 | typename TestFixture::template right_t right = 121 | UniqueIncreasingCoroutine(); 122 | EXPECT_EQ(right(), 0UL); 123 | EXPECT_EQ(right(), 1UL); 124 | EXPECT_EQ(right(), 2UL); 125 | typename TestFixture::template left_t left(std::move(right)); 126 | EXPECT_EQ(left(), 3UL); 127 | EXPECT_EQ(left(), 4UL); 128 | } 129 | } 130 | 131 | TYPED_TEST(UniqueMoveAssignConstructTests, TransferStateOnMoveAssign) { 132 | { 133 | typename TestFixture::template left_t left; 134 | typename TestFixture::template right_t right = 135 | UniqueIncreasingCoroutine(); 136 | left = std::move(right); 137 | EXPECT_EQ(left(), 0UL); 138 | } 139 | 140 | { 141 | typename TestFixture::template left_t left; 142 | typename TestFixture::template right_t right = 143 | UniqueIncreasingCoroutine(); 144 | EXPECT_EQ(right(), 0UL); 145 | EXPECT_EQ(right(), 1UL); 146 | EXPECT_EQ(right(), 2UL); 147 | left = std::move(right); 148 | EXPECT_EQ(left(), 3UL); 149 | EXPECT_EQ(left(), 4UL); 150 | } 151 | } 152 | 153 | COPYABLE_LEFT_RIGHT_TYPED_TEST_CASE(CopyableCopyAssignConstructTests) 154 | 155 | TYPED_TEST(CopyableCopyAssignConstructTests, AreCopyConstructible) { 156 | typename TestFixture::template right_t right = returnTrue; 157 | typename TestFixture::template left_t left(right); 158 | EXPECT_TRUE(left()); 159 | EXPECT_TRUE(left); 160 | EXPECT_TRUE(right()); 161 | EXPECT_TRUE(right); 162 | } 163 | 164 | TYPED_TEST(CopyableCopyAssignConstructTests, AreCopyAssignable) { 165 | typename TestFixture::template left_t left; 166 | typename TestFixture::template right_t right = returnTrue; 167 | EXPECT_FALSE(left); 168 | left = right; 169 | EXPECT_TRUE(left()); 170 | EXPECT_TRUE(left); 171 | EXPECT_TRUE(right()); 172 | EXPECT_TRUE(right); 173 | } 174 | 175 | TYPED_TEST(CopyableCopyAssignConstructTests, CopyStateOnCopyConstruct) { 176 | { 177 | typename TestFixture::template right_t right = 178 | CopyableIncreasingCoroutine(); 179 | typename TestFixture::template left_t left(right); 180 | EXPECT_EQ(left(), 0UL); 181 | EXPECT_EQ(right(), 0UL); 182 | } 183 | 184 | { 185 | typename TestFixture::template right_t right = 186 | CopyableIncreasingCoroutine(); 187 | EXPECT_EQ(right(), 0UL); 188 | EXPECT_EQ(right(), 1UL); 189 | EXPECT_EQ(right(), 2UL); 190 | typename TestFixture::template left_t left(right); 191 | EXPECT_EQ(left(), 3UL); 192 | EXPECT_EQ(right(), 3UL); 193 | EXPECT_EQ(left(), 4UL); 194 | EXPECT_EQ(right(), 4UL); 195 | } 196 | } 197 | 198 | TYPED_TEST(CopyableCopyAssignConstructTests, CopyStateOnCopyAssign) { 199 | { 200 | typename TestFixture::template left_t left; 201 | typename TestFixture::template right_t right = 202 | CopyableIncreasingCoroutine(); 203 | left = right; 204 | EXPECT_EQ(left(), 0UL); 205 | EXPECT_EQ(right(), 0UL); 206 | } 207 | 208 | { 209 | typename TestFixture::template left_t left; 210 | typename TestFixture::template right_t right = 211 | CopyableIncreasingCoroutine(); 212 | EXPECT_EQ(right(), 0UL); 213 | EXPECT_EQ(right(), 1UL); 214 | EXPECT_EQ(right(), 2UL); 215 | left = right; 216 | EXPECT_EQ(left(), 3UL); 217 | EXPECT_EQ(right(), 3UL); 218 | EXPECT_EQ(left(), 4UL); 219 | EXPECT_EQ(right(), 4UL); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /test/build-test.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2015-2020 Denis Blank 3 | // Distributed under the Boost Software License, Version 1.0 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "function2-test.hpp" 8 | 9 | bool testSelfContaining(); 10 | 11 | int scrn() { 12 | 13 | fu2::unique_function fun = [](int /*a*/, int /*b*/) { 14 | // ... 15 | }; 16 | 17 | (void)fun; 18 | return 0; 19 | } 20 | 21 | // Issue #6 22 | TEST(build_tests, the_header_is_self_containing) { 23 | EXPECT_TRUE(testSelfContaining()); 24 | } 25 | -------------------------------------------------------------------------------- /test/dist-package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(function2containtest VERSION 2.2.0 LANGUAGES CXX) 3 | 4 | find_package(function2 2) 5 | 6 | add_executable(function2-dist-package main.cpp) 7 | 8 | target_link_libraries(function2-dist-package function2::function2) 9 | -------------------------------------------------------------------------------- /test/dist-package/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int main() { 5 | fu2::function myfn = [] { 6 | // 7 | }; 8 | myfn(); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /test/dist-subproject/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(function2containtest VERSION 2.2.0 LANGUAGES CXX) 3 | 4 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../..) 5 | 6 | add_executable(function2-dist-subproject main.cpp) 7 | 8 | target_link_libraries(function2-dist-subproject function2::function2) 9 | -------------------------------------------------------------------------------- /test/dist-subproject/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int main() { 5 | fu2::function myfn = [] { 6 | // 7 | }; 8 | myfn(); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /test/empty-function-call-test.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2015-2020 Denis Blank 3 | // Distributed under the Boost Software License, Version 1.0 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "function2-test.hpp" 8 | 9 | struct some_tag {}; 10 | 11 | ALL_LEFT_TYPED_TEST_CASE(AllEmptyFunctionCallTests) 12 | 13 | TYPED_TEST(AllEmptyFunctionCallTests, CallSucceedsIfNonEmpty) { 14 | typename TestFixture::template left_t left = returnTrue; 15 | EXPECT_TRUE(left()); 16 | } 17 | 18 | TYPED_TEST(AllEmptyFunctionCallTests, CallSucceedsIfNonEmptyPtr) { 19 | using ptr_t = bool (*)(); 20 | 21 | ptr_t ptr(returnTrue); 22 | 23 | typename TestFixture::template left_t left = ptr; 24 | EXPECT_TRUE(left()); 25 | } 26 | 27 | TYPED_TEST(AllEmptyFunctionCallTests, CallSucceedsIfNonEmptyRef) { 28 | using ref_t = bool (&)(); 29 | 30 | ref_t ref(returnTrue); 31 | 32 | typename TestFixture::template left_t left = ref; 33 | EXPECT_TRUE(left()); 34 | } 35 | 36 | #if !defined(FU2_HAS_DISABLED_EXCEPTIONS) 37 | #ifndef FU2_HAS_NO_FUNCTIONAL_HEADER 38 | static_assert(std::is_same::value, 40 | "Wrong fu2::bad_function_call exposed!"); 41 | #endif 42 | 43 | TYPED_TEST(AllEmptyFunctionCallTests, CallThrowsIfEmpty) { 44 | typename TestFixture::template left_t left; 45 | EXPECT_THROW(left(), fu2::bad_function_call); 46 | } 47 | #endif // FU2_HAS_DISABLED_EXCEPTIONS 48 | 49 | #if !defined(FU2_HAS_NO_EMPTY_PROPAGATION) 50 | COPYABLE_LEFT_TYPED_TEST_CASE(AllEmptyFunctionViewCallTests) 51 | 52 | TYPED_TEST(AllEmptyFunctionViewCallTests, CallPropagatesEmpty) { 53 | typename TestFixture::template left_t emptyf{}; 54 | ASSERT_TRUE(emptyf.empty()); 55 | 56 | typename TestFixture::template left_t emptyf2 = 57 | std::move(emptyf); 58 | ASSERT_TRUE(emptyf2.empty()); 59 | 60 | typename TestFixture::template left_view_t emptyf3 = 61 | std::move(emptyf2); 62 | ASSERT_TRUE(emptyf3.empty()); 63 | } 64 | 65 | TYPED_TEST(AllEmptyFunctionCallTests, CallPropagatesEmptyFnPtr) { 66 | using fn_t = void (*)(some_tag); 67 | fn_t const fn = nullptr; 68 | typename TestFixture::template left_t emptyf(fn); 69 | ASSERT_TRUE(emptyf.empty()); 70 | } 71 | 72 | struct my_callable { 73 | void operator()(some_tag const&) { 74 | } 75 | 76 | explicit operator bool() { 77 | return true; 78 | } 79 | }; 80 | 81 | struct my_callable_empty { 82 | void operator()(some_tag const&) { 83 | } 84 | 85 | explicit operator bool() { 86 | return false; 87 | } 88 | }; 89 | 90 | TYPED_TEST(AllEmptyFunctionCallTests, CallPropagatesEmptyCustom) { 91 | { 92 | typename TestFixture::template left_t fn(my_callable{}); 93 | ASSERT_FALSE(fn.empty()); 94 | } 95 | 96 | { 97 | typename TestFixture::template left_t fn( 98 | my_callable_empty{}); 99 | #if defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION) || \ 100 | defined(FU2_HAS_NO_EMPTY_PROPAGATION) 101 | ASSERT_FALSE(fn.empty()); 102 | #else 103 | ASSERT_TRUE(fn.empty()); 104 | #endif 105 | } 106 | } 107 | #endif // FU2_HAS_NO_EMPTY_PROPAGATION 108 | -------------------------------------------------------------------------------- /test/function2-test.hpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2015-2020 Denis Blank 3 | // Distributed under the Boost Software License, Version 1.0 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #ifndef FU2_INCLUDED_FUNCTION2_TEST_HPP__ 8 | #define FU2_INCLUDED_FUNCTION2_TEST_HPP__ 9 | 10 | #include "function2/function2.hpp" 11 | #include "gtest/gtest.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /// A function which always returns true 19 | constexpr bool returnTrue() noexcept { 20 | return true; 21 | } 22 | /// A function which always returns false 23 | constexpr bool returnFalse() noexcept { 24 | return false; 25 | } 26 | 27 | template 29 | using short_def = 30 | fu2::function_base, 31 | Throwing, false, Fn, Additional...>; 32 | 33 | /// NonCopyable functions with several SFO capacities 34 | template 36 | using unique_no_sfo = short_def; 37 | template 39 | using unique_256_sfo = 40 | short_def; 41 | template 43 | using unique_512_sfo = 44 | short_def; 45 | /// Copyable functions with several SFO capacities 46 | template 48 | using copyable_no_sfo = short_def; 49 | template 51 | using copyable_256_sfo = 52 | short_def; 53 | template 55 | using copyable_512_sfo = 56 | short_def; 57 | /// std::function 58 | template 59 | using std_function = std::function; 60 | 61 | /// Adds given types to the type list 62 | template 63 | struct MergeTypes : std::common_type> {}; 64 | 65 | template 66 | struct MergeTypes, Rest...> 67 | : MergeTypes {}; 68 | 69 | template 70 | struct TupleToTypes; 71 | 72 | template 73 | struct TupleToTypes> 74 | : std::common_type> {}; 75 | 76 | /// Provides the left type which is used in this test case 77 | template