├── .gitattributes ├── LICENSE ├── README.md ├── cxx_function.hpp └── test ├── align.cpp ├── badcopy.cpp ├── callmove.cpp ├── constructible.cpp ├── container.cpp ├── convnull.cpp ├── ctorthrow.cpp ├── heap.cpp ├── nocopy.cpp ├── overlambda.cpp ├── ptm.cpp ├── recover.cpp ├── scoped_allocator.cpp ├── swap.cpp ├── target_access.cpp ├── usernew.cpp └── volatile.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | README.md merge=ours 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David M Krauss (potatoswatter) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `cxx_function` library 2 | ====================== 3 | 4 | A prototype for new `std::function` features, compatible with C++11. 5 | 6 | *by David Krauss (potatoswatter)* 7 | 8 | 9 | Highlights 10 | ========== 11 | 12 | - Conforming to the `std::function` specification in C++11 and C++14 13 | - "Plus" `noexcept` guarantees for `move` and `swap` 14 | 15 | - Multi-signature, overloaded function objects: 16 | 17 | function< ret1( arg1 ), void(), void() const > 18 | 19 | - Including reference qualification 20 | - Deprecation warning when a call operator casts away `const` 21 | 22 | - Non-copyable target objects: 23 | 24 | unique_function< void() > f = [uptr = std::make_unique< resource_t >()] {…}; 25 | 26 | - Including in-place construction 27 | 28 | - Full support of allocators: 29 | 30 | function_container< my_pool, void() > = other_pooled_function; 31 | 32 | - Custom `construct()` and `destroy()` 33 | - `std::scoped_allocator_adaptor` reaches inside target objects 34 | - Allocator propagation by assignment, swap, and copying (needs testing) 35 | - Fancy pointers (not yet tested) 36 | - Queries `allocator_traits` interface exclusively; does not access allocators directly 37 | 38 | - Written in C++11; does not require migrating to a new language edition 39 | 40 | - Efficiency 41 | - Economizes space better than libc++ (Clang) 42 | - Economizes branch instructions better than libstdc++ (GCC) 43 | - Performs well [compared](https://github.com/jamboree/CxxFunctionBenchmark) to similar third-party libraries. 44 | - Avoids indirect branches for trivial target object copy construction, move construction, and destruction 45 | (common case for lambdas) 46 | 47 | 48 | How to use 49 | ========== 50 | 51 | For `std::function` documentation, see [cppreference.com](http://en.cppreference.com/w/cpp/utility/functional/function) 52 | or a [recent C++ draft standard](http://open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4527.pdf). Only new features are 53 | described here. 54 | 55 | This library is tested against Clang 3.6 (with libc++ and libstdc++) and GCC 5.1 (with libstdc++) on OS X. 56 | There is a separate [branch](tree/msvc_port) tested against VS2015. Please [report](#bugs) bugs. 57 | 58 | ## Overloaded functions 59 | 60 | Each signature supplied to a template will be used to declare one `operator()` overload. 61 | Ordinary overload resolution is used to select between them. Each overload forwards its arguments along to the target. 62 | There is no function template or perfect forwarding involved in dispatching, so you can pass braced initializer lists. 63 | 64 | Duplicate signatures are currently ignored. Different permutations of an overload set produce different types, but the distinction 65 | between them is not supported. Hopefully there ultimately may be a 1:1 mapping between overload sets and `function` types. 66 | 67 | C++11 specifies that `std::function::operator()` is `const`-qualified, but implementations have always called the 68 | target object without `const` qualification. This is supported but deprecated by this library. Any signature without 69 | any function qualifiers (that is, supported by C++11) is shadowed by a `const`-qualified version. Calling this function 70 | will produce a deprecation warning. You can replace it by declaring the `const`-qualified signature in the list. 71 | 72 | There are several general solutions to a deprecation warning: 73 | 74 | 1. Pass the `function` by value or forwarding so it is not observed to be `const`. 75 | 76 | This has always been the canonical usage of `std::function` (and all Callable objects). This fix can be applied per call 77 | site, usually without affecting any external interface. 78 | 79 | 2. Add a `const` qualifier to the signature. This explicitly solves the problem through greater exposition and tighter 80 | constraints. It requires that the target object be callable as `const`. 81 | 82 | This is usually a good idea in any case, since in practice most functions are not stateful. Ideally, 83 | `function< void() const >` should represent a function that *does the same thing on each call*. If you have 84 | `function< void() > const` instead, that means that calling may change some state, but you don't have permission to do 85 | so. (This is the crux of the issue.) 86 | 87 | If the target needs to change, but only in a way that doesn't affect its observable behavior, consider using `mutable` instead. 88 | Note that lambdas allow `mutable`, but the keyword is somewhat abused: the members of the lambda are not `mutable`. 89 | It only makes the call operator non-`const`. You will need to explicitly declare a class with a `mutable` member. 90 | 91 | 3. Consistently remove `const` from the reference which gets called. Non-`const` references are the best way to share 92 | mutable access to values. More `const` is not necessarily better. Again, this reflects greater exposition and tighter 93 | constraints. 94 | 95 | If you *must* use a `const_cast`, do so within the target call handler, not on the `function` which gets called. The 96 | validity of `const_cast` depends on whether or not the affected object was created as `const`, and target objects never are. 97 | 98 | If you declare a signature with no qualifiers and one with e.g. `const &` qualifiers, the automatic `const` overload will 99 | cause an overload resolution conflict. Declare a pair of `&` and `&&` qualifiers instead. (Any such design would be very 100 | suspicious, though.) 101 | 102 | A target object is checked against all the signatures when it is assigned to a wrapper. Like permutations of the same 103 | overload set, you can assign one `function` to another specialization which is different but appropriately callable. 104 | The result will be two `function` objects, one wrapped inside the other. This is Bad Design and it should probably be 105 | deprecated. 106 | 107 | 108 | ## Non-copyable functions 109 | 110 | The `unique_function` template works just like `function`, but it can handle any target object and it cannot be copied. 111 | To construct the target object in place (to avoid moving it), pass a dispatch tag like this (in C++11): 112 | 113 | unique_function< void() > fn( in_place_t< target_type >{}, constructor_arg1, arg2 ); 114 | 115 | or this (since C++14): 116 | 117 | unique_function< void() > fn( in_place< target_type >, constructor_arg1, arg2 ); 118 | 119 | This also works with all the other templates. Likewise, there are new functions for assignent after construction: 120 | 121 | fn.emplace_assign< target_type >( constructor_arg1, arg2 ); 122 | fn.allocate_assign< target_type >( allocator, constructor_arg1, arg2 ); 123 | 124 | You can assign any of the other templates to a `unique_function`, but not vice versa. There is no risk of double wrapping, as 125 | long as the signature lists match exactly. Whereas other implementations tend to throw an error deep from deep inside their 126 | templates for a non-copyable target, this library checks copyability for the non-`unique` types before anything else happens. 127 | 128 | Note that copyability is different from an rvalue-qualified (`&&`) signature. For example: 129 | 130 | - `function< void() && >` ideally represents a function that can be copied, and called once per copy. 131 | - `unique_function< void() >` represents a function with abilities that can't be replicated in a copy (hence "unique"), 132 | but can still be called many times. 133 | - `unique_function< void() && >` represents a one-shot function that can only ever be called once. 134 | 135 | To call an object with an rvalue-qualified signature, use `std::move( my_func ) ( args )`. This usage also works on ordinary, 136 | unqualified signatures and it is backward-compatible with C++11 `std::function`. 137 | 138 | ### Movable and non-movable targets 139 | 140 | A target which is not movable must be initialized using in-place initialization. 141 | 142 | A target with a move constructor that is not declared `noexcept` (or `throw()`, which is deprecated) will be allocated on the 143 | heap; it cannot be stored within the wrapper. Once a target is placed on the heap, it will never be moved at all, except to 144 | an allocator representing a different memory pool. (See below.) It's better to remember the `noexcept`. Also, use `= default` 145 | when possible, as it automatically computes and sets `noexcept` for you. 146 | 147 | 148 | ## Enhanced safety 149 | 150 | A function object returning `const &` or `&&` should avoid returning a reference to a temporary. This library forbids target 151 | objects whose return type would force `function` to return a dangling reference. The standard library currently allows them. 152 | 153 | function< int const &() > fn = []{ return 5; }; // Binding 5 to int const& would produce a dangling return value. 154 | 155 | This protection is not perfect. It may be defeated by implicit conversion between class types. This slips through the cracks: 156 | 157 | function< std::pair< long, long > const &() > fn = []{ return std::make_pair( 1, 2 ); }; 158 | 159 | 160 | ## Allocators 161 | 162 | This library implements a new allocator model, conforming (almost, see "Bugs" below) to the C++11 specification. (How can it be both 163 | new and old? Because the normative `std::function` allocator model has been underspecified, there are several equally-conforming 164 | models.) As for `function` alone, this library behaviorally matches libc++ and libstdc++, but additionally supporting the allocator 165 | methods `construct` and `destroy`. 166 | 167 | `function` and `unique_function` use an allocator to handle target objects that cannot fit into the wrapper storage (equal to 168 | the size of three pointers). They behave as clients of the allocator, but not as containers. The behavior of `std::shared_ptr` 169 | via `std::allocate_shared` is closely analogous: the allocator is used when the object is created and destroyed, but it is 170 | invisible in the meantime. The only difference is that `function` permits additional operations like copying. 171 | 172 | `function_container` and `unique_function_container` behave more like allocator-aware containers. They encapsulate a permanent 173 | allocator instance, of a type given by the initial template argument. The target object of these wrappers always uses either the 174 | given allocator, or only the storage within the wrapper. When a pre-existing target is assigned from a corresponding wrapper 175 | specialization, it's adopted by following these steps: 176 | 177 | 1. If the allocator specifies `propagate_on_container_move_assignment` or `propagate_on_container_copy_assignment` 178 | (whichever is applicable) as `std::true_type`, and the source is also a container, then the allocator is assigned. 179 | 180 | 2. If the target is stored within the wrapper, allocators aren't an issue. It's copied or moved directly, using `memcpy` if 181 | possible. 182 | 183 | 3. Otherwise, the type of the new target's allocator is compared to the type of the container allocator. Both are rebound to 184 | `Allocator< char >` and the resulting types are compared using `typeid`. 185 | 186 | 4. **If the types don't match, an exception is thrown!** Do not let this happen: only assign to `function_container` when 187 | you know the memory scheme originating the assigned value. The type of the exception is `allocator_mismatch_error`. 188 | 189 | 5. Otherwise, the allocators are compared using the `==` operator. Again, both instances are rebound to a `value_type` of 190 | `char`. If they compare equal, then the assignment proceeds as if the container were just a non-container `function`. 191 | 192 | If they are unequal, or if it's a copy-assignment, the target is reallocated by the container's allocator. The 193 | container's allocator is then updated to reflect any change. (The new value is still equal to the old value, though.) 194 | 195 | 6. In any case, if the operation is a move, the source wrapper is set to `nullptr`. This guarantees deallocation from the 196 | source memory pool when the destination pool is different. 197 | 198 | Note that initializing a `function_container` from a `function` will not adopt the allocator. It will use a default-initialized 199 | allocator. Ideally, the compiler will complain that the allocator cannot be default-initialized. These constructor overloads 200 | should probably be deprecated, because it looks like the allocator is getting dynamically copy/move-initialized. 201 | 202 | Since the allocator of `function_container` is fixed, it does not implement the `assign` or `allocate_assign` functions. 203 | 204 | 205 | ### Propagation 206 | 207 | Container allocators are "sticky:" the allocator designated at initialization is used throughout the container's lifetime. 208 | 209 | Non-container `function` allocators are "stealthy:" the allocator is only used to manage the target object, and any copies 210 | of it. It cannot be observed by `get_allocator`. (It really does exist though, and an accessor could easily be implemented.) 211 | 212 | There exist allocator properties `propagate_on_container_move_assignment` and `propagate_on_container_copy_assignment`. 213 | These affect the semantics of assignment between containers, but do not enable a container to observe the allocator of a 214 | non-container. They reduce stickiness, but not stealthiness. 215 | 216 | POCMA and POCCA have a similar effect on `function_container` as on any other container such as `std::vector`. In particular, 217 | POCMA is sufficient for container move assignment to be `noexcept`. However, `is_always_equal` is also sufficient for that, 218 | and using these traits is a serious anti-pattern: Allocators are supposed to be orthogonal to containers. `is_always_equal` 219 | furthermore eliminates the requirement that the target be move-constructible, which POCMA does not do. It is the better 220 | alternative. (However, `is_always_equal` support is only getting added to libc++ and libstdc++ as I write this.) 221 | 222 | 223 | ### Usage 224 | 225 | Memory management gets harder as it gets stricter. Here are some ground rules for this library: 226 | 227 | 1. Use best practices for container allocators. Use `scoped_allocator_adaptor`. 228 | 229 | 2. Do not assign something to a `function_container` unless it was created using a compatible `function_container`, 230 | or you're *sure* it was created using `allocator_arg_t` with a `function`. 231 | 232 | 3. Use `function_container` only for persistent storage; don't pass it around everywhere. It's not suitable as a value type. 233 | Use `function` for that. Do not try to hack the allocator model using `propagate_on_container_*_assignment`. 234 | 235 | 4. If you're afraid about copies running against resource constraints, use `unique_*`. Note that when you give a `function` 236 | to someone, they may (in theory) make unlimited copies of it. On the other hand, resource exhaustion is something that 237 | allocators manage, so you should be prepared for this case and not "afraid" of it :) . 238 | 239 | 5. The memory resource backing the allocators must persist as long as any functions may be alive and using the storage. 240 | Always assert that memory resources (pools) are empty before freeing them, lest references go dangling. Do not pass 241 | custom-allocated functions to a library that will retain them indefinitely, unless you're willing to take a risk by either 242 | (a) destroying the pool at the last possible moment before program termination or (b) not destroying the pool at all. 243 | 244 | 245 | Bugs 246 | ==== 247 | 248 | Please report issues [on GitHub](https://github.com/potswa/cxx_function/issues). Here are some that are already known: 249 | 250 | - `function` and `unique_function` constructors taking an allocator but no target should be deprecated, and should accept 251 | any allocator type. Instead, they require a `std::allocator` specialization and give no warning. 252 | 253 | Note that this constructor is not implemented at all by libstdc++ (GCC), and it doesn't do anything in libc++ (Clang), 254 | so unless you already have a more exotic library, you should not be using it. The most likely result of this bug is simply 255 | that usages needing migration to `function_container` get flagged with overload resolution failures. 256 | 257 | - The allocator and the pointer it generates (which may be a "fancy pointer," or a type other than `T*`) must fit into the 258 | wrapper storage space, equal to three native pointers. There are subtle issues here requiring further research, but 259 | `std::allocate_shared` already practically solves this problem so I will probably end up doing the same thing. 260 | 261 | - Double wrapping allows a function to be spread across multiple memory pools. There are many ways to accidentally forget 262 | to reallocate some owned resources, but the library could do a better job of catching mistakes. 263 | 264 | The same goes for all cases of double wrapping, really. None of them are semantically incorrect or involve privilege 265 | violations (such as accidentally allocating with `std::allocator`), but it's an easy mistake which wastes resources. 266 | 267 | 268 | I'm not adding these bugs to the database myself. Feel free to do so. It helps to mention a motivating case. All are fixable. 269 | -------------------------------------------------------------------------------- /cxx_function.hpp: -------------------------------------------------------------------------------- 1 | // cxx_function.hpp: major evolution for std::function 2 | // Copyright 2015 by David Krauss. 3 | // This source is released under the MIT license, http://opensource.org/licenses/MIT 4 | 5 | #ifndef INCLUDED_CXX_FUNCTION_HPP 6 | #define INCLUDED_CXX_FUNCTION_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include // for std::bad_function_call and std::mem_fn 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace cxx_function { 18 | 19 | #if __cplusplus > 201610 20 | template< typename t > 21 | using in_place_t = std::in_place_type_t< t >; 22 | 23 | template< typename t > 24 | constexpr in_place_t< t > & in_place = std::in_place_type< t >; 25 | #else 26 | template< typename > 27 | struct in_place_t {}; 28 | 29 | # if __cplusplus >= 201402 30 | template< typename t > 31 | constexpr in_place_t< t > in_place = {}; 32 | # endif 33 | #endif 34 | 35 | namespace impl { 36 | 37 | #define UGLY( NAME ) CXX_FUNCTION_ ## NAME 38 | 39 | #if __cplusplus < 201402L && ! _MSC_VER 40 | # define deprecated(MSG) __attribute__((deprecated (MSG))) 41 | #else 42 | # define deprecated(MSG) [[deprecated (MSG)]] 43 | #endif 44 | 45 | #if __GNUC__ && ! __clang__ && __GNUC__ < 5 46 | # define is_trivially_move_constructible has_trivial_copy_constructor 47 | # define is_trivially_copy_constructible has_trivial_copy_constructor 48 | 49 | # define OLD_GCC_FIX(...) __VA_ARGS__ 50 | # define OLD_GCC_SKIP(...) 51 | #else 52 | # define OLD_GCC_FIX(...) 53 | # define OLD_GCC_SKIP(...) __VA_ARGS__ 54 | #endif 55 | 56 | #if _MSC_VER 57 | # define MSVC_FIX(...) __VA_ARGS__ 58 | # define MSVC_SKIP(...) 59 | #else 60 | # define MSVC_FIX(...) 61 | # define MSVC_SKIP(...) __VA_ARGS__ 62 | #endif 63 | 64 | #define UNPACK(...) __VA_ARGS__ 65 | #define IGNORE(...) 66 | 67 | #define DISPATCH_CQ( MACRO, UNSAFE, TYPE_QUALS, FN_QUALS ) \ 68 | MACRO( TYPE_QUALS, FN_QUALS, UNSAFE ) MACRO( const TYPE_QUALS, const FN_QUALS, IGNORE ) 69 | #define DISPATCH_CV( MACRO, UNSAFE, TYPE_QUALS, FN_QUALS ) \ 70 | DISPATCH_CQ( MACRO, UNSAFE, TYPE_QUALS, FN_QUALS ) DISPATCH_CQ( MACRO, IGNORE, volatile TYPE_QUALS, volatile FN_QUALS ) 71 | 72 | // Apply a given macro over all reference qualifications. 73 | #define DISPATCH_CVREFQ( MACRO, TYPE_QUALS, FN_QUALS ) \ 74 | DISPATCH_CV( MACRO, IGNORE, & TYPE_QUALS, & FN_QUALS ) DISPATCH_CV( MACRO, IGNORE, && TYPE_QUALS, && FN_QUALS ) 75 | 76 | #define DISPATCH_CVOBJQ( MACRO, UNSAFE, TYPE_QUALS, FN_QUALS ) \ 77 | DISPATCH_CV( MACRO, UNSAFE, & TYPE_QUALS, FN_QUALS ) DISPATCH_CVREFQ( MACRO, TYPE_QUALS, FN_QUALS ) 78 | 79 | // Apply a given macro over all function qualifications. 80 | #if __cpp_noexcept_function_type 81 | # define DISPATCH_ALL( MACRO ) DISPATCH_CVOBJQ( MACRO, UNPACK, , ) DISPATCH_CVOBJQ( MACRO, IGNORE, , noexcept ) 82 | #else 83 | # define DISPATCH_ALL( MACRO ) DISPATCH_CVOBJQ( MACRO, UNPACK, , ) 84 | #endif 85 | 86 | #if __cpp_lib_experimental_logical_traits 87 | using std::experimental::conjunction; 88 | using std::experimental::negation; 89 | #else 90 | template< typename ... cond > 91 | struct conjunction 92 | : std::true_type {}; 93 | template< typename next, typename ... cond > 94 | struct conjunction< next, cond ... > 95 | : conjunction< typename next::type, cond ... >::type {}; // Not conforming: does not propagate critical type-value. 96 | template< typename ... cond > 97 | struct conjunction< std::true_type, cond ... > 98 | : conjunction< cond ... >::type {}; 99 | template< typename ... cond > 100 | struct conjunction< std::false_type, cond ... > 101 | : std::false_type {}; 102 | 103 | template< typename cond > 104 | struct negation 105 | : std::integral_constant< bool, ! cond::value > {}; 106 | #endif 107 | 108 | template< typename T > 109 | T launder_cast( void * p ) 110 | { return 111 | #if __cpp_lib_launder || __clang_major__ >= 6 && __cplusplus >= 201703L 112 | std::launder 113 | #endif 114 | ( static_cast< T >( p ) ); } 115 | template< typename T > 116 | T launder_cast( void const * p ) 117 | { return 118 | #if __cpp_lib_launder || __clang_major__ >= 6 && __cplusplus >= 201703L 119 | std::launder 120 | #endif 121 | ( static_cast< T >( p ) ); } 122 | 123 | // General-purpose dispatch tag. 124 | template< typename ... > struct tag {}; 125 | 126 | // "Abstract" base class for the island inside the wrapper class, e.g. std::function. 127 | // This must appear first in the wrapper class layout. 128 | struct erasure_base { 129 | struct erasure_utility const * table; 130 | 131 | template< typename derived, typename copyable, typename ... sig > 132 | erasure_base( tag< derived, copyable, sig ... > ); // Parameters are valueless tags for template deduction. 133 | }; 134 | 135 | /* Implement a vtable using metaprogramming. Why? 136 | 1. Implement without polymorphic template instantiations (would need 2N of them). 137 | 2. Eliminate overhead and ABI issues associated with RTTI and weak linkage. 138 | 3. Allow static data entries as well as functions. 139 | 140 | The table is stored as an aggregate, with a homegrown tuple. 141 | Entries that would be trivial or useless may be set to nullptr. 142 | Call dispatch entry types are normalized to non-member, unqualified form. 143 | */ 144 | struct erasure_utility { 145 | void (* destructor)( erasure_base & ); 146 | void (* move_constructor_destructor)( erasure_base &&, void * dest, void const * source_alloc ); 147 | void (* copy_constructor)( erasure_base const &, void * dest, void const * alloc ); 148 | 149 | void const * (*target_access)( erasure_base const & ); 150 | std::type_info const & target_type; 151 | std::type_info const * allocator_type; 152 | }; 153 | template< typename ... free > 154 | struct erasure_dispatch {}; 155 | template< typename free, typename ... rem > 156 | struct erasure_dispatch< free, rem ... > { 157 | free * call; 158 | erasure_dispatch< rem ... > r; 159 | }; 160 | template< typename ... free > 161 | struct erasure_table { 162 | erasure_utility utility; // Initial member of standard-layout struct. 163 | erasure_dispatch< free ... > dispatch; 164 | }; 165 | template< int x, typename free, typename ... rem > 166 | typename std::enable_if< x == 0, free * >::type // Duplicate-type entries occur when overloads differ in qualification. 167 | get( erasure_dispatch< free, rem ... > const & v ) // When this overload is enabled, it is more specialized. 168 | { return v.call; } 169 | template< int x, typename free, typename ... list > 170 | free * get( erasure_dispatch< list ... > const & v ) 171 | { return get< x - 1, free >( v.r ); } 172 | 173 | // Convert a member function signature to its free invocation counterpart. 174 | template< typename sig > 175 | struct member_to_free; 176 | 177 | #define TYPE_CONVERT_CASE( TYPE_QUALS, FN_QUALS, UNSAFE ) \ 178 | template< typename ret, typename ... arg > \ 179 | struct member_to_free< ret( arg ... ) FN_QUALS > \ 180 | { typedef ret type( erasure_base &, arg && ... ); }; 181 | DISPATCH_ALL( TYPE_CONVERT_CASE ) 182 | #undef TYPE_CONVERT_CASE 183 | // Allow early, eager conversion without incurring double conversion. 184 | template< typename ret, typename ... arg > 185 | struct member_to_free< ret( erasure_base &, arg ... ) > 186 | { typedef ret type( erasure_base &, arg && ... ); }; 187 | 188 | // Apply given cv-qualifiers and reference category to a new type. 189 | template< typename source, typename target > 190 | struct transfer_sig_qualifiers; 191 | #define TRANSFER_QUALS_CASE( TYPE_QUALS, FN_QUALS, REFLESS ) \ 192 | template< typename ret, typename ... arg, typename target > \ 193 | struct transfer_sig_qualifiers< ret( arg ... ) FN_QUALS, target > \ 194 | { typedef target TYPE_QUALS type; }; 195 | DISPATCH_ALL( TRANSFER_QUALS_CASE ) 196 | #undef TRANSFER_QUALS_CASE 197 | 198 | // Default, generic "virtual" functions to manage the wrapper payload lifetime. 199 | template< typename derived > 200 | struct erasure_special { 201 | static void destroy( erasure_base & self ) noexcept 202 | { static_cast< derived & >( self ). ~ derived(); } 203 | static void move( erasure_base && self, void * dest, void const * ) { 204 | new (dest) derived( std::move( static_cast< derived & >( self ) ) ); 205 | destroy( self ); 206 | } 207 | static void copy( erasure_base const & self, void * dest, void const * ) 208 | { new (dest) derived( static_cast< derived const & >( self ) ); } 209 | }; 210 | 211 | // These accessors generate "vtable" entries, but avoid instantiating functions that do not exist or would be trivial. 212 | template< typename erasure > 213 | struct erasure_trivially_destructible 214 | : std::is_trivially_destructible< erasure > {}; 215 | 216 | template< typename derived > 217 | constexpr typename std::enable_if< ! erasure_trivially_destructible< derived >::value >::type 218 | ( * erasure_destroy() ) ( erasure_base & ) 219 | { return & derived::destroy; } 220 | template< typename derived > 221 | constexpr typename std::enable_if< erasure_trivially_destructible< derived >::value >::type 222 | ( * erasure_destroy() ) ( erasure_base & ) 223 | { return nullptr; } 224 | 225 | template< typename erasure > 226 | struct erasure_trivially_movable : std::integral_constant< bool, 227 | std::is_trivially_move_constructible< erasure >::value 228 | && std::is_trivially_destructible< erasure >::value > {}; 229 | 230 | template< typename derived > 231 | constexpr typename std::enable_if< ! erasure_trivially_movable< derived >::value >::type 232 | ( * erasure_move() ) ( erasure_base &&, void *, void const * ) 233 | { return & derived::move; } 234 | template< typename derived > 235 | constexpr typename std::enable_if< erasure_trivially_movable< derived >::value >::type 236 | ( * erasure_move() ) ( erasure_base &&, void *, void const * ) 237 | { return nullptr; } 238 | 239 | template< typename erasure > 240 | struct erasure_nontrivially_copyable : std::integral_constant< bool, 241 | std::is_copy_constructible< erasure >::value 242 | && ! std::is_trivially_copy_constructible< erasure >::value > {}; 243 | 244 | template< typename derived, typename enable > 245 | constexpr typename std::enable_if< std::enable_if< enable::value, erasure_nontrivially_copyable< derived > >::type::value >::type 246 | ( * erasure_copy( enable * ) ) ( erasure_base const &, void *, void const * ) 247 | { return & derived::copy; } 248 | template< typename derived > 249 | constexpr void ( * erasure_copy( ... ) ) ( erasure_base const &, void *, void const * ) 250 | { return nullptr; } 251 | 252 | 253 | // Collect the above into global tables. 254 | // typename copyable enables non-trivial copyability. Trivially copyable and noncopyable tables both set it to false. 255 | // For trivial, pointer-like target types, sig is actually the sequence of free function types. 256 | #define VTABLE_INITIALIZER = { \ 257 | erasure_destroy< erasure >(), \ 258 | erasure_move< erasure >(), \ 259 | erasure_copy< erasure >( static_cast< copyable * >( nullptr ) ), \ 260 | & erasure::target_access, \ 261 | typeid (typename erasure::target_type), \ 262 | erasure::common_allocator_type_info, \ 263 | & erasure::template call< sig > ..., \ 264 | erasure_dispatch<>{} \ 265 | } 266 | #ifdef __GNUC__ 267 | # pragma GCC diagnostic push 268 | # pragma GCC diagnostic ignored "-Wmissing-braces" // Need brace elision here. 269 | #endif 270 | template< typename erasure, typename copyable, typename ... sig > 271 | struct make_erasure_table { 272 | static MSVC_SKIP (constexpr) erasure_table< typename member_to_free< sig >::type ... > const value MSVC_SKIP( VTABLE_INITIALIZER ); 273 | }; 274 | #ifdef __GNUC__ 275 | # pragma GCC diagnostic pop 276 | #endif 277 | template< typename erasure, typename copyable, typename ... sig > 278 | MSVC_SKIP (constexpr) erasure_table< typename member_to_free< sig >::type ... > const 279 | make_erasure_table< erasure, copyable, sig ... >::value MSVC_FIX( VTABLE_INITIALIZER ); 280 | 281 | template< typename derived, typename copyable, typename ... sig > 282 | erasure_base::erasure_base( tag< derived, copyable, sig ... > ) 283 | : table( & make_erasure_table< derived, copyable, sig ... >::value.utility ) {} 284 | 285 | 286 | // Implement the uninitialized state. 287 | struct null_erasure 288 | : erasure_base // "vtable" interface class 289 | , erasure_special< null_erasure > { // generic implementations of "virtual" functions 290 | typedef void target_type; 291 | static constexpr std::type_info const * common_allocator_type_info = nullptr; 292 | 293 | // The const qualifier is bogus. Rather than type-erase an identical non-const version, let the wrapper do a const_cast. 294 | static void const * target_access( erasure_base const & ) { return nullptr; } // target() still returns nullptr. 295 | 296 | template< typename ... sig > 297 | null_erasure( tag< sig ... > ) noexcept 298 | : erasure_base( tag< null_erasure, std::false_type, sig ... >{} ) {} // Initialize own "vtable pointer" at runtime. 299 | 300 | template< typename, typename ret, typename ... arg > 301 | static ret call( erasure_base &, arg ... ) 302 | { throw std::bad_function_call{}; } 303 | }; 304 | 305 | // Implement erasures of function pointers and std::reference_wrapper, which avoid any allocation. 306 | // For now, small user-defined target types piggyback here. The only concession is accepting a "copyable" flag. 307 | template< typename in_target_type > 308 | struct local_erasure 309 | : erasure_base 310 | , erasure_special< local_erasure< in_target_type > > { 311 | typedef in_target_type target_type; 312 | target_type target; 313 | static constexpr std::type_info const * common_allocator_type_info = nullptr; 314 | 315 | static void const * target_access( erasure_base const & self ) 316 | { return & static_cast< local_erasure const & >( self ).target; } 317 | 318 | template< typename copyable, typename ... free, typename ... arg > 319 | local_erasure( tag< copyable, free ... >, arg && ... a ) 320 | : erasure_base( tag< local_erasure, copyable, free ... >{} ) 321 | , target( std::forward< arg >( a ) ... ) {} 322 | 323 | template< typename sig, typename ret, typename ... arg > 324 | static ret call( erasure_base & self, arg ... a ) 325 | // Directly call the name "target," not a reference, to support devirtualization. 326 | { return static_cast< typename transfer_sig_qualifiers< sig, local_erasure >::type >( self ).target( std::forward< arg >( a ) ... ); } 327 | }; 328 | 329 | // Implement erasures of pointer-to-members, which need std::mem_fn instead of a direct call. 330 | template< typename in_target_type > 331 | struct ptm_erasure 332 | : erasure_base 333 | , erasure_special< ptm_erasure< in_target_type > > { 334 | typedef in_target_type target_type; 335 | target_type target; // Do not use mem_fn here... 336 | static constexpr std::type_info const * common_allocator_type_info = nullptr; 337 | 338 | static void const * target_access( erasure_base const & self ) 339 | { return & static_cast< ptm_erasure const & >( self ).target; } // ... because the user can get read/write access to the target object. 340 | 341 | template< typename ... free > 342 | ptm_erasure( tag< free ... >, target_type a ) 343 | : erasure_base( tag< ptm_erasure, std::false_type, free ... >{} ) 344 | , target( a ) {} 345 | 346 | template< typename, typename ret, typename ... arg > 347 | static ret call( erasure_base & self, arg ... a ) 348 | { return std::mem_fn( static_cast< ptm_erasure const & >( self ).target )( std::forward< arg >( a ) ... ); } 349 | }; 350 | 351 | // Implement erasures of objects that cannot be stored inside the wrapper. 352 | /* This does still store the allocator and pointer in the wrapper. A more general case should be added. 353 | However, there is a conundrum in rebinding an allocator to an instance of itself. 354 | Also, it's not clear that a native pointer will always be stable, as opposed to a fancy pointer. 355 | Fancy pointers exceeding the wrapper storage, with varying underlying referent storage, are another conundrum. */ 356 | // Use Allocator as a common reference point, for the typeid operator and the instance in function_container. 357 | // (The instance in the erasure object is always a bona fide Allocator, though.) 358 | template< typename allocator > 359 | using common_allocator_rebind = typename std::allocator_traits< allocator >::template rebind_alloc< char >; 360 | 361 | template< typename t, typename = void > 362 | struct not_always_equal_by_expression : std::true_type {}; 363 | template< typename t > 364 | using id_t = t; // MSVC can't parse or SFINAE on a nested-name-specifier with decltype. 365 | template< typename t > 366 | struct not_always_equal_by_expression< t, typename std::enable_if< id_t< decltype (std::declval< t >() == std::declval< t >()) >::value >::type > 367 | : std::false_type {}; 368 | template< typename t, typename = void > 369 | struct not_always_equal_by_trait : std::true_type {}; 370 | template< typename t > 371 | struct not_always_equal_by_trait< t, typename std::enable_if< std::allocator_traits< t >::is_always_equal::value >::type > 372 | : std::false_type {}; 373 | template< typename t > 374 | struct is_always_equal : negation< conjunction< not_always_equal_by_trait< t >, not_always_equal_by_expression< t > > > {}; 375 | template< typename t > 376 | struct is_always_equal< std::allocator< t > > : std::true_type {}; 377 | 378 | template< typename allocator_in, typename in_target_type > 379 | struct allocator_erasure 380 | : erasure_base 381 | , allocator_in { // empty base class optimization (EBCO) 382 | typedef std::allocator_traits< allocator_in > allocator_traits; 383 | typedef common_allocator_rebind< allocator_in > common_allocator; 384 | static MSVC_SKIP (constexpr) std::type_info const * const common_allocator_type_info 385 | MSVC_SKIP ( = & typeid (allocator_erasure::common_allocator) ); 386 | 387 | typedef in_target_type target_type; 388 | typename allocator_traits::pointer target; 389 | 390 | allocator_in & alloc() { return static_cast< allocator_in & >( * this ); } 391 | allocator_in const & alloc() const { return static_cast< allocator_in const & >( * this ); } 392 | target_type * target_address() { return std::addressof( * target ); } 393 | static void const * target_access( erasure_base const & self ) 394 | { return std::addressof( * static_cast< allocator_erasure const & >( self ).target ); } 395 | 396 | template< typename ... arg > 397 | void construct_safely( arg && ... a ) try { 398 | allocator_traits::construct( alloc(), target_address(), std::forward< arg >( a ) ... ); 399 | } catch (...) { 400 | allocator_traits::deallocate( alloc(), target, 1 ); // Does not throw according to [allocator.requirements] §17.6.3.5 and DR2384. 401 | throw; 402 | } // The wrapper allocator instance cannot be updated following a failed initialization because the erasure allocator is already gone. 403 | 404 | allocator_erasure( allocator_erasure && ) = default; // Called by move( true_type{}, ... ) when allocator or pointer is nontrivially movable. 405 | allocator_erasure( allocator_erasure const & ) = delete; 406 | 407 | template< typename copyable, typename ... sig, typename ... arg > 408 | allocator_erasure( tag< copyable, sig ... >, std::allocator_arg_t, allocator_in const & in_alloc, arg && ... a ) 409 | : erasure_base( tag< allocator_erasure, copyable, sig ... >{} ) 410 | , allocator_in( in_alloc ) 411 | , target( allocator_traits::allocate( alloc(), 1 ) ) 412 | { construct_safely( std::forward< arg >( a ) ... ); } 413 | private: 414 | // Copy or move. Moving only occurs to a different pool. 415 | template< typename source > 416 | allocator_erasure( std::allocator_arg_t, allocator_in const & dest_allocator, source && o ) 417 | : erasure_base( o ) 418 | , allocator_in( dest_allocator ) 419 | , target( allocator_traits::allocate( alloc(), 1 ) ) { 420 | construct_safely( std::forward< typename std::conditional< std::is_lvalue_reference< source >::value, 421 | target_type const &, target_type && >::type >( * o.target ) ); 422 | } 423 | public: 424 | void move( std::true_type, void * dest, void const * ) noexcept { // Call ordinary move constructor. 425 | new (dest) allocator_erasure( std::move( * this ) ); // Move the pointer, not the object. Don't call the allocator at all. 426 | this-> ~ allocator_erasure(); 427 | } 428 | void move( std::false_type, void * dest, void const * dest_allocator_v ) { 429 | auto * dest_allocator_p = static_cast< common_allocator const * >( dest_allocator_v ); // The wrapper verified the safety of this using typeid. 430 | if ( ! dest_allocator_p || * dest_allocator_p == alloc() ) { 431 | move( std::true_type{}, dest, dest_allocator_v ); // same pool 432 | } else { // different pool 433 | new (dest) allocator_erasure( std::allocator_arg, static_cast< allocator_in >( * dest_allocator_p ), // Reallocate. 434 | std::move_if_noexcept( * this ) ); // Protect user against their own throwing move constructors. 435 | destroy( * this ); 436 | } 437 | } 438 | // [*_]allocator_v points to the wrapper allocator instance, if any. 439 | static void move( erasure_base && self_base, void * dest, void const * dest_allocator_v ) { 440 | auto & self = static_cast< allocator_erasure & >( self_base ); 441 | // is_always_equal is usually false here, because it correlates with triviality which short-circuits this function. 442 | std::move( self ).move( MSVC_FIX (impl::) is_always_equal< allocator_in >{}, dest, dest_allocator_v ); 443 | } 444 | static void copy( erasure_base const & self_base, void * dest, void const * dest_allocator_v ) { 445 | auto & self = static_cast< allocator_erasure const & >( self_base ); 446 | // Structure the control flow differently to avoid instantiating the copy constructor. 447 | new (dest) allocator_erasure( std::allocator_arg, 448 | dest_allocator_v? 449 | static_cast< allocator_in const & >( * static_cast< common_allocator const * >( dest_allocator_v ) ) 450 | : self.alloc(), 451 | self ); 452 | } 453 | static void destroy( erasure_base & self_base ) noexcept { 454 | auto & self = static_cast< allocator_erasure & >( self_base ); 455 | allocator_traits::destroy( self.alloc(), self.target_address() ); 456 | allocator_traits::deallocate( self.alloc(), self.target, 1 ); 457 | self. ~ allocator_erasure(); 458 | } 459 | 460 | template< typename sig, typename ret, typename ... arg > 461 | static ret call( erasure_base & self, arg ... a ) { 462 | return std::forward< typename transfer_sig_qualifiers< sig, target_type >::type > 463 | ( * static_cast< allocator_erasure const & >( self ).target )( std::forward< arg >( a ) ... ); 464 | } 465 | }; 466 | template< typename allocator_in, typename in_target_type > 467 | MSVC_SKIP (constexpr) std::type_info const * const allocator_erasure< allocator_in, in_target_type >::common_allocator_type_info 468 | MSVC_FIX ( = & typeid (allocator_erasure::common_allocator) ); 469 | 470 | template< typename allocator, typename target_type > 471 | struct erasure_trivially_destructible< allocator_erasure< allocator, target_type > > 472 | : std::false_type {}; 473 | 474 | template< typename allocator, typename target_type > 475 | struct erasure_trivially_movable< allocator_erasure< allocator, target_type > > : std::integral_constant< bool, 476 | std::is_trivially_move_constructible< allocator_erasure< allocator, target_type > >::value 477 | && std::is_trivially_destructible< allocator_erasure< allocator, target_type > >::value 478 | && is_always_equal< allocator >::value > {}; 479 | 480 | template< typename allocator, typename target_type > 481 | struct erasure_nontrivially_copyable< allocator_erasure< allocator, target_type > > 482 | : std::is_copy_constructible< target_type > {}; 483 | 484 | 485 | // Forbid certain return value conversions that require temporaries. 486 | template< typename from, typename to > // Primary case: from is object type. A prvalue is returned. Only allow user-defined conversions. 487 | struct is_bad_return // Converting a prvalue to a reference binds a temporary unless the prvalue is a class and the reference is not a base. 488 | : negation< conjunction< std::is_class< from >, negation< std::is_base_of< to, from > > > > {}; 489 | 490 | template< typename from, typename to > // Treat xvalues like lvalues. 491 | struct is_bad_return< from &&, to > : is_bad_return< from &, to > {}; 492 | 493 | template< typename from, typename to > // From is reference type. Allow user-defined and derived-to-base conversions. 494 | struct is_bad_return< from &, to > // Forbid converting an lvalue to a reference unless it's a class or the types match. 495 | : conjunction< negation< std::is_same< typename std::remove_cv< from >::type, to > >, negation< std::is_class< from > > > {}; 496 | 497 | template< typename from, typename to > 498 | struct is_safely_convertible 499 | : conjunction< 500 | negation< conjunction< // Prevent binding a return value to a temporary. 501 | std::is_reference< to >, 502 | is_bad_return< from, typename std::decay< to >::type > 503 | > >, 504 | std::is_convertible< from, to > 505 | >::type {}; 506 | 507 | #if __cpp_lib_is_invocable || __cplusplus >= 201703L && __clang_major__ >= 6 508 | # define INVOKE_RESULT( T, ARG ) invoke_result< T, ARG > 509 | #else 510 | # define INVOKE_RESULT( T, ARG ) result_of< T( ARG ) > 511 | #endif 512 | 513 | template< typename t, typename sig, typename = void > 514 | struct is_callable : std::false_type {}; 515 | 516 | #define IS_CALLABLE_CASE( TYPE_QUALS, FN_QUALS, UNSAFE ) \ 517 | template< typename t, typename ret, typename ... arg > \ 518 | struct is_callable< t, ret( arg ... ) FN_QUALS, \ 519 | typename std::enable_if< is_safely_convertible< \ 520 | typename std::INVOKE_RESULT( t TYPE_QUALS, arg ... )::type \ 521 | , ret >::value >::type > \ 522 | : std::true_type {}; 523 | DISPATCH_CVOBJQ( IS_CALLABLE_CASE, IGNORE, , ) 524 | #undef IS_CALLABLE_CASE 525 | #undef INVOKE_RESULT 526 | 527 | template< typename sig > 528 | struct is_callable< std::nullptr_t, sig > 529 | : std::true_type {}; 530 | 531 | #if __cpp_noexcept_function_type 532 | 533 | # if __cpp_lib_is_invocable || __clang_major__ >= 6 534 | # define IS_NOTHROW_INVOKABLE( T, ARG, RET ) is_nothrow_invocable_r< RET, T, ARG > 535 | # elif __GNUC__ && ! __clang__ && __GNUC__ < 7 536 | # define IS_NOTHROW_INVOKABLE( T, ARG, RET ) is_nothrow_invocable< T( ARG ), RET > 537 | # else 538 | # define IS_NOTHROW_INVOKABLE( T, ARG, RET ) is_nothrow_callable< T( ARG ), RET > 539 | # endif 540 | 541 | # define NOEXCEPT_CASE( TYPE_QUALS, FN_QUALS, UNSAFE ) \ 542 | template< typename t, typename ret, typename ... arg > \ 543 | struct is_callable< t, ret( arg ... ) FN_QUALS noexcept, \ 544 | typename std::enable_if< std::IS_NOTHROW_INVOKABLE( t TYPE_QUALS, arg ..., ret )::value >::type > \ 545 | : is_callable< t, ret( arg ... ) FN_QUALS > {}; 546 | DISPATCH_CVOBJQ( NOEXCEPT_CASE, IGNORE, , ) 547 | # undef NOEXCEPT_CASE 548 | # undef IS_NOTHROW_INVOKABLE 549 | 550 | # define NOEXCEPT_NULLPTR_CASE( TYPE_QUALS, FN_QUALS, UNSAFE ) \ 551 | template< typename ret, typename ... arg > \ 552 | struct is_callable< std::nullptr_t, ret( arg ... ) FN_QUALS > : std::false_type {}; 553 | DISPATCH_CVOBJQ( NOEXCEPT_NULLPTR_CASE, IGNORE, , noexcept ) 554 | # undef NOEXCEPT_NULLPTR_CASE 555 | #endif 556 | 557 | template< typename ... sig > 558 | struct is_all_callable { 559 | template< typename t > 560 | using temp = conjunction< is_callable< t, sig > ... >; 561 | 562 | typedef std::false_type copies; 563 | }; 564 | 565 | template< typename self, typename ... sig > 566 | struct is_copyable_all_callable { 567 | template< typename t, typename = void > 568 | struct temp : conjunction< std::is_copy_constructible< t >, typename is_all_callable< sig ... >::template temp< t > >::type {}; 569 | 570 | template< typename v > // Presume that self is a copyable wrapper, since that is what uses this metafunction. 571 | struct temp< self, v > : std::true_type {}; // Avoid inspecting incomplete self. 572 | 573 | typedef std::true_type copies; 574 | }; 575 | 576 | // Map privileged types to noexcept specifications. 577 | template< typename source > 578 | struct is_noexcept_erasable : std::false_type {}; 579 | template<> 580 | struct is_noexcept_erasable< std::nullptr_t > : std::true_type {}; 581 | template< typename t > 582 | struct is_noexcept_erasable< t * > : std::true_type {}; 583 | template< typename t, typename c > 584 | struct is_noexcept_erasable< t c::* > : std::true_type {}; 585 | template< typename t > 586 | struct is_noexcept_erasable< std::reference_wrapper< t > > : std::true_type {}; 587 | 588 | // Termination of the recursive template generated by WRAPPER_CASE. 589 | template< typename derived, std::size_t n, typename ... sig > 590 | struct wrapper_dispatch { 591 | static_assert ( sizeof ... (sig) == 0, "An unsupported function signature was detected." ); 592 | void operator () ( wrapper_dispatch ) = delete; // Feed the "using operator ();" or "using call;" declaration in next derived class. 593 | }; 594 | 595 | template< typename > 596 | struct const_unsafe_case; // internal tag for function signatures introduced for backward compatibility of const-qualified access 597 | 598 | // Generate the wrapper dispatcher by brute-force specialization over qualified function types. 599 | 600 | // This macro generates a recursive template handling one type qualifier sequence, e.g. "volatile &" or "const." 601 | // The final product converts a sequence of qualified signatures into an overload set, potentially with special cases for signatures of no qualification. 602 | #define WRAPPER_CASE( \ 603 | TYPE_QUALS, FN_QUALS, /* The type qualifiers for this case. */ \ 604 | UNSAFE /* UNPACK if there are no qualifiers, IGNORE otherwise. Supports deprecated const-qualified access. */ \ 605 | ) \ 606 | template< typename derived, std::size_t table_index, typename ret, typename ... arg, typename ... sig > \ 607 | struct wrapper_dispatch< derived, table_index, ret( arg ... ) FN_QUALS, sig ... > \ 608 | : wrapper_dispatch< derived, table_index+1, sig ... \ 609 | UNSAFE (, const_unsafe_case< ret( arg ... ) >) > { \ 610 | using wrapper_dispatch< derived, table_index+1, sig ... \ 611 | UNSAFE (, const_unsafe_case< ret( arg ... ) >) >::operator (); \ 612 | ret operator () ( arg ... a ) FN_QUALS \ 613 | { return static_cast< derived & >( const_cast< wrapper_dispatch & >( * this ) ).template call< table_index, ret >( std::forward< arg >( a ) ... ); } \ 614 | }; 615 | DISPATCH_ALL( WRAPPER_CASE ) 616 | #undef WRAPPER_CASE 617 | 618 | // Additionally implement the legacy casting away of const, but with a warning. 619 | template< typename derived, std::size_t n, typename ret, typename ... arg, typename ... more > 620 | struct wrapper_dispatch< derived, n, const_unsafe_case< ret( arg ... ) >, more ... > 621 | : wrapper_dispatch< derived, n, more ... > { 622 | using wrapper_dispatch< derived, n, more ... >::operator (); 623 | deprecated( "It is unsafe to call a std::function of non-const signature through a const access path." ) 624 | ret operator () ( arg ... a ) const 625 | { return static_cast< derived & >( const_cast< wrapper_dispatch & >( * this ) ) ( std::forward< arg >( a ) ... ); } 626 | }; 627 | 628 | struct allocator_mismatch_error : std::exception // This should be implemented in a .cpp file, but stay header-only for now. 629 | { virtual char const * what() const noexcept override { return "An object could not be transferred into an incompatible memory allocation scheme."; } }; 630 | 631 | template< typename erasure_base, typename allocator > 632 | typename std::enable_if< ! std::is_void< allocator >::value, 633 | bool >::type nontrivial_target( erasure_base const * e, allocator const * ) { 634 | std::type_info const * type = e->table->allocator_type; 635 | if ( type == nullptr ) return false; // It's a raw pointer, PTMF, reference_wrapper, or nullptr. 636 | if ( * type != typeid (allocator) ) throw allocator_mismatch_error{}; // Target belongs to a different allocation scheme. 637 | return true; 638 | } 639 | inline bool nontrivial_target( void const *, void const * ) { return true; } // Give false positives when not checking the allocator. 640 | 641 | template< typename ... free > 642 | class wrapper_base { 643 | template< typename, typename ... > 644 | friend class wrapper; 645 | typedef std::aligned_storage< sizeof (void *[3]) >::type effective_storage_type; 646 | protected: 647 | std::aligned_storage< sizeof (void *[4]), alignof(effective_storage_type) >::type storage; 648 | void * storage_address() { return & storage; } 649 | 650 | // init and destroy enter or recover from invalid states. 651 | // They get on the right side of [basic.life]/7.4, but mind the exceptions. 652 | 653 | // Default, move, and copy construction. 654 | // Adopt by move. 655 | template< typename allocator = void > // Allocator is already rebound to . 656 | void init_dirty( wrapper_base & s, allocator const * dest_alloc = nullptr ) { 657 | decltype (erasure_utility::move_constructor_destructor) nontrivial; 658 | if ( ! nontrivial_target( & s.erasure(), dest_alloc ) // No-op without an allocator. Save an access in pointer-like target case otherwise. 659 | || ! ( nontrivial = s.erasure().table->move_constructor_destructor ) ) { 660 | std::memcpy( & this->erasure(), & s.erasure(), sizeof (storage) ); 661 | } else nontrivial( std::move( s.erasure() ), & this->erasure(), dest_alloc ); 662 | } 663 | template< typename allocator = void > // Allocator is already rebound to . 664 | void init( wrapper_base && s, allocator const * dest_alloc = nullptr ) { 665 | init_dirty( s, dest_alloc ); 666 | s.emplace_trivial( in_place_t< std::nullptr_t >{} ); 667 | } 668 | 669 | // Adopt by copy. 670 | template< typename allocator = void > 671 | void init( wrapper_base const & s, allocator const * dest_alloc = nullptr ) { 672 | decltype (erasure_utility::copy_constructor) nontrivial; 673 | if ( ! nontrivial_target( & s.erasure(), dest_alloc ) // No-op without an allocator. Save an access in pointer-like target case otherwise. 674 | || ! ( nontrivial = s.erasure().table->copy_constructor ) ) { 675 | std::memcpy( & this->erasure(), & s.erasure(), sizeof (storage) ); 676 | } else nontrivial( s.erasure(), & this->erasure(), dest_alloc ); 677 | } 678 | 679 | template< typename source > // Target value is nullptr or a default-constructed pointer, which compares equal to nullptr. 680 | void emplace_trivial( in_place_t< source >, source = {} ) noexcept 681 | { new (storage_address()) null_erasure( tag< free ... >{} ); } 682 | 683 | // Pointers, PTMs, and reference wrappers bypass allocators completely. 684 | template< typename t > 685 | void emplace_trivial( in_place_t< t * >, t * p ) noexcept { 686 | if ( p ) new (storage_address()) local_erasure< t * >( tag< std::false_type, free ... >{}, p ); 687 | else emplace_trivial( in_place_t< std::nullptr_t >{} ); 688 | } 689 | template< typename t, typename c > 690 | void emplace_trivial( in_place_t< t c::* >, t c::* ptm ) noexcept { 691 | if ( ptm ) new (storage_address()) ptm_erasure< t c::* >( tag< free ... >{}, ptm ); 692 | else emplace_trivial( in_place_t< std::nullptr_t >{} ); 693 | } 694 | template< typename t > 695 | void emplace_trivial( in_place_t< std::reference_wrapper< t > >, std::reference_wrapper< t > r ) noexcept { 696 | new (storage_address()) local_erasure< std::reference_wrapper< t > >( tag< std::false_type, free ... >{}, r ); 697 | } 698 | 699 | void destroy() noexcept { 700 | auto nontrivial = erasure().table->destructor; 701 | if ( nontrivial ) nontrivial( erasure() ); 702 | } 703 | 704 | // Implement erasure type verification for always-local targets without touching RTTI. 705 | bool verify_type_impl( void * ) const noexcept 706 | { return erasure().table == & make_erasure_table< null_erasure, std::false_type, free ... >::value.utility; } 707 | 708 | template< typename t > 709 | bool verify_type_impl( std::reference_wrapper< t > * ) const noexcept { 710 | return erasure().table 711 | == & make_erasure_table< local_erasure< std::reference_wrapper< t > >, std::false_type, free ... >::value.utility; 712 | } 713 | template< typename t > 714 | bool verify_type_impl( t ** ) const noexcept 715 | { return erasure().table == & make_erasure_table< local_erasure< t * >, std::false_type, free ... >::value.utility; } 716 | 717 | template< typename t, typename c > 718 | bool verify_type_impl( t c::** ) const noexcept 719 | { return erasure().table == & make_erasure_table< ptm_erasure< t c::* >, std::false_type, free ... >::value.utility; } 720 | 721 | // User-defined class types are never guaranteed to be local. There could exist some allocator for which uses_allocator is true. 722 | // RTTI could be replaced here by a small variable template linked from the table. Since we need it anyway, just use RTTI. 723 | template< typename want > 724 | bool verify_type_impl( want * ) const noexcept 725 | { return target_type() == typeid (want); } 726 | public: 727 | erasure_base & erasure() 728 | { return * launder_cast< erasure_base * >( & storage ); } 729 | erasure_base const & erasure() const 730 | { return * launder_cast< erasure_base const * >( & storage ); } 731 | 732 | void operator = ( wrapper_base && s ) noexcept { // Only suitable for generating implicit members in derived classes: wrong return type. 733 | destroy(); 734 | init( std::move( s ) ); 735 | } 736 | void operator = ( wrapper_base const & s ) { 737 | wrapper_base temp; 738 | temp.init( s ); 739 | destroy(); 740 | init( std::move( temp ) ); 741 | } 742 | template< typename source > 743 | typename std::enable_if< is_noexcept_erasable< source >::value >::type 744 | operator = ( source const & s ) noexcept { 745 | destroy(); 746 | emplace_trivial( in_place_t< source >{}, s ); 747 | } 748 | 749 | template< std::size_t table_index, typename ret, typename ... arg > 750 | ret call( arg && ... a ) { 751 | return get< table_index, ret( erasure_base &, arg && ... ) > 752 | ( launder_cast< erasure_table< free ... > const * >( erasure().table )->dispatch ) 753 | ( erasure(), std::forward< arg >( a ) ... ); 754 | } 755 | 756 | std::type_info const & target_type() const noexcept 757 | { return erasure().table->target_type; } 758 | 759 | template< typename want > 760 | bool verify_type() const noexcept { 761 | static_assert ( std::is_same< want, typename std::decay< want >::type >::value, "function target can only be of cv-unqualified object type." ); 762 | return verify_type_impl( (want *) nullptr ); 763 | } 764 | template< typename want > 765 | bool verify_type() const volatile noexcept 766 | { return const_cast< wrapper_base const * >( this )->verify_type< want >(); } 767 | 768 | void const * complete_object_address() const noexcept 769 | { return erasure().table->target_access( erasure() ); } 770 | void const volatile * complete_object_address() const volatile noexcept 771 | { return const_cast< wrapper_base const * >( this )->complete_object_address(); } 772 | 773 | template< typename want > 774 | want const * target() const noexcept { 775 | if ( ! verify_type< want >() ) return nullptr; 776 | return static_cast< want const * >( erasure().table->target_access( erasure() ) ); 777 | } 778 | template< typename want > 779 | want * target() noexcept 780 | { return const_cast< want * >( static_cast< wrapper_base const & >( * this ).target< want >() ); } 781 | 782 | explicit operator bool () const volatile noexcept 783 | { return ! verify_type< void >(); } 784 | 785 | void swap( wrapper_base & o ) noexcept { // Essentially same as std::swap, but skip some trivial destructors. 786 | // Would be nice to use swap_ranges on trivially-copyable target storage. Double-check that launder can handle it. 787 | wrapper_base temp; // This class is trivially constructed and destroyed. 788 | temp.init_dirty( * this ); 789 | init_dirty( o ); 790 | o.init_dirty( temp ); 791 | } 792 | }; 793 | 794 | template< typename t > 795 | typename std::remove_cv< t >::type value( t & v ) { return v; } 796 | template< typename t > 797 | t value( t const && v ) { return std::move( v ); } 798 | template< typename t > 799 | t && value( t && v ) { return std::move( v ); } 800 | 801 | template< typename target, typename ... free > 802 | typename std::enable_if< conjunction< 803 | std::is_same< target, typename target::UGLY(wrapper_type) > 804 | , std::is_base_of< wrapper_base< free ... >, target > >::value, 805 | bool >::type is_empty_wrapper( target *, wrapper_base< free ... > const * arg ) 806 | { return ! * arg; } 807 | inline bool is_empty_wrapper( ... ) { return false; } 808 | 809 | template< bool is_compatibly_wrapped, typename target, typename allocator > 810 | struct rebind_allocator_for_source 811 | { typedef typename std::allocator_traits< allocator >::template rebind_alloc< target > type; }; 812 | template< typename target, typename allocator > 813 | struct rebind_allocator_for_source< true, target, allocator > 814 | { typedef common_allocator_rebind< allocator > type; }; 815 | 816 | template< typename derived, bool > 817 | struct nullptr_construction { 818 | nullptr_construction() noexcept 819 | { static_cast< derived * >( this )->emplace_trivial( in_place_t< std::nullptr_t >{} ); } 820 | nullptr_construction( tag< struct trivialize > ) {} 821 | }; 822 | template< typename derived > 823 | struct nullptr_construction< derived, false > { 824 | nullptr_construction() noexcept = delete; 825 | nullptr_construction( tag< struct trivialize > ) {} 826 | }; 827 | 828 | template< typename target_policy, typename ... sig > 829 | class wrapper 830 | : public wrapper_base< typename member_to_free< sig >::type ... > 831 | , private nullptr_construction< wrapper< target_policy, sig ... >, target_policy::template temp< std::nullptr_t >::value > 832 | , public wrapper_dispatch< wrapper< target_policy, sig ... >, 0, sig ... > { 833 | using wrapper::wrapper_base::storage; 834 | using wrapper::wrapper_base::storage_address; 835 | using wrapper::wrapper_base::init; 836 | using wrapper::wrapper_base::emplace_trivial; 837 | 838 | protected: // Queries on potential targets. Shared with container_wrapper. 839 | template< typename target > 840 | using is_targetable = typename target_policy::template temp< target >; 841 | 842 | template< typename source > 843 | struct is_small { 844 | static const bool value = sizeof (local_erasure< source >) <= sizeof (storage) 845 | && alignof (decltype (storage)) % alignof (source) == 0 846 | && std::is_nothrow_move_constructible< source >::value; 847 | }; 848 | 849 | template< typename source, typename = source, typename = typename wrapper::wrapper_base > 850 | struct is_compatibly_wrapped : std::false_type {}; 851 | template< typename source > 852 | struct is_compatibly_wrapped< source, typename source::UGLY(wrapper_type), typename source::wrapper_base > 853 | : std::true_type {}; 854 | 855 | template< typename source, typename allocator > 856 | bool nontrivial_target( source const & s, allocator const * alloc ) 857 | { return impl::nontrivial_target( & s.erasure(), alloc ); } // Use friend relationship with compatible wrappers. 858 | private: 859 | template< typename target, typename allocator, typename ... arg > 860 | void allocate( allocator * alloc, arg && ... a ) { 861 | if ( is_empty_wrapper( (target *) nullptr, & a ... ) ) { 862 | return emplace_trivial( in_place_t< std::nullptr_t >{} ); 863 | } 864 | typedef allocator_erasure< allocator, target > erasure; 865 | // TODO: Add a new erasure template to put the fancy pointer on the heap. 866 | static_assert ( sizeof (erasure) <= sizeof storage, "Stateful allocator or fancy pointer is too big for polymorphic function wrapper." ); 867 | new (storage_address()) erasure( tag< typename target_policy::copies, sig ... >{}, std::allocator_arg, * alloc, std::forward< arg >( a ) ... ); 868 | } 869 | 870 | // When value-wise initialization isn't adoption, delegate to in-place construction or allocation. 871 | template< typename source > 872 | typename std::enable_if< ! is_compatibly_wrapped< typename std::decay< source >::type >::value >::type 873 | init( source && s ) 874 | { emplace< typename std::decay< source >::type >( std::forward< source >( s ) ); } 875 | template< typename source, typename allocator > 876 | typename std::enable_if< ! is_compatibly_wrapped< typename std::decay< source >::type >::value >::type 877 | init( source && s, allocator * a ) 878 | { allocate< typename std::decay< source >::type >( a, std::forward< source >( s ) ); } 879 | 880 | template< typename source, typename ... arg > 881 | typename std::enable_if< is_small< source >::value && ! is_noexcept_erasable< source >::value >::type 882 | emplace( arg && ... a ) 883 | { new (storage_address()) local_erasure< source >( tag< typename target_policy::copies, sig ... >{}, std::forward< arg >( a ) ... ); } 884 | 885 | template< typename source, typename ... arg > 886 | typename std::enable_if< is_noexcept_erasable< source >::value >::type 887 | emplace( arg && ... a ) 888 | { emplace_trivial( in_place_t< source >{}, std::forward< arg >( a ) ... ); } 889 | template< typename target, typename ... arg > 890 | typename std::enable_if< ! is_small< target >::value >::type 891 | emplace( arg && ... a ) { 892 | std::allocator< target > alloc; 893 | allocate< target >( & alloc, std::forward< arg >( a ) ... ); 894 | } 895 | 896 | public: 897 | typedef wrapper UGLY(wrapper_type); 898 | 899 | wrapper() noexcept = default; 900 | 901 | friend typename wrapper::nullptr_construction; 902 | #define NON_NULLPTR_CONSTRUCT : wrapper::nullptr_construction( tag< trivialize >{} ) 903 | 904 | wrapper( wrapper && s ) noexcept NON_NULLPTR_CONSTRUCT 905 | { init( std::move( s ) ); } 906 | wrapper( wrapper const & s ) NON_NULLPTR_CONSTRUCT 907 | { init( s ); } 908 | 909 | MSVC_SKIP ( // MSVC cannot parse deprecated constructor templates, so remove this entirely. 910 | template< typename allocator > 911 | deprecated( "This constructor ignores its allocator argument. Specify allocation per-target or use function_container instead." ) 912 | wrapper( std::allocator_arg_t, allocator const & ) NON_NULLPTR_CONSTRUCT 913 | { init( in_place_t< std::nullptr_t >{}, nullptr ); } 914 | ) 915 | template< typename source, 916 | typename std::enable_if< conjunction< 917 | negation< std::is_base_of< wrapper, typename std::decay< source >::type > > 918 | , is_targetable< typename std::decay< source >::type > 919 | >::value >::type * = nullptr > 920 | wrapper( source && s ) 921 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value 922 | || is_compatibly_wrapped< source >::value ) NON_NULLPTR_CONSTRUCT 923 | { init( std::forward< source >( s ) ); } 924 | 925 | template< typename allocator, typename source, 926 | typename = typename std::enable_if< 927 | is_targetable< typename std::decay< source >::type >::value 928 | >::type > 929 | wrapper( std::allocator_arg_t, allocator && alloc, source && s ) 930 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value ) NON_NULLPTR_CONSTRUCT { 931 | // Adapt the allocator to the source. If it's already an rvalue of the right type, use it in-place. 932 | // It might make more sense to use modifiable lvalues in-place, but this is closer to the standard. 933 | typename rebind_allocator_for_source< is_compatibly_wrapped< typename std::decay< source >::type >::value, 934 | typename std::decay< source >::type, typename std::decay< allocator >::type 935 | >::type && target_alloc = value( std::forward< allocator >( alloc ) ); 936 | init( std::forward< source >( s ), & target_alloc ); 937 | } 938 | 939 | template< typename target, typename ... arg, 940 | typename = typename std::enable_if< is_targetable< target >::value >::type > 941 | wrapper( in_place_t< target >, arg && ... a ) 942 | noexcept( is_noexcept_erasable< target >::value ) 943 | { emplace< target >( std::forward< arg >( a ) ... ); } 944 | 945 | template< typename allocator, typename target, typename ... arg, 946 | typename = typename std::enable_if< 947 | is_targetable< target >::value 948 | >::type > 949 | wrapper( std::allocator_arg_t, allocator && alloc, in_place_t< target >, arg && ... a ) 950 | noexcept( is_noexcept_erasable< target >::value ) { 951 | typename std::allocator_traits< typename std::decay< allocator >::type >::template rebind_alloc< target > 952 | && target_alloc = value( std::forward< allocator >( alloc ) ); 953 | allocate< target >( & target_alloc, std::forward< arg >( a ) ... ); 954 | } 955 | #undef NON_NULLPTR_CONSTRUCT 956 | 957 | wrapper & operator = ( wrapper && ) = default; 958 | wrapper & operator = ( wrapper const & ) = default; 959 | template< typename source > 960 | typename std::enable_if< 961 | is_noexcept_erasable< typename std::decay< source >::type >::value 962 | || is_compatibly_wrapped< typename std::decay< source >::type >::value 963 | >::type operator = ( source && s ) 964 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value || is_compatibly_wrapped< source >::value ) 965 | { wrapper::wrapper_base::operator = ( std::forward< source >( s ) ); } 966 | 967 | template< typename source > 968 | typename std::enable_if< 969 | ! is_noexcept_erasable< typename std::decay< source >::type >::value 970 | && ! is_compatibly_wrapped< typename std::decay< source >::type >::value 971 | >::type operator = ( source && s ) 972 | { wrapper::wrapper_base::operator = ( wrapper( std::forward< source >( s ) ) ); } 973 | 974 | template< typename source, typename allocator > 975 | void assign( source && s, allocator const & alloc ) 976 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value ) 977 | { * this = wrapper( std::allocator_arg, alloc, std::forward< source >( s ) ); } 978 | 979 | // Quirk: If emplacing constructor throws, but move constructor does not, and the target is small, it may be moved for the sake of strong exception safety. 980 | template< typename source, typename ... arg > 981 | void emplace_assign( arg && ... a ) // TODO separate non-container case avoiding saved_allocator. 982 | noexcept( is_noexcept_erasable< source >::value ) 983 | { * this = wrapper( in_place_t< source >{}, std::forward< arg >( a ) ... ); } 984 | 985 | template< typename source, typename allocator, typename ... arg > 986 | void allocate_assign( allocator const & alloc, arg && ... a ) 987 | noexcept( is_noexcept_erasable< source >::value ) 988 | { * this = wrapper( std::allocator_arg, alloc, in_place_t< source >{}, std::forward< arg >( a ) ... ); } 989 | 990 | ~ wrapper() noexcept 991 | { this->destroy(); } 992 | }; 993 | 994 | template< typename saved_allocator, typename used_allocator > 995 | void assign_allocator( std::true_type, saved_allocator * d, used_allocator * s ) 996 | { * d = std::move( * s ); } 997 | inline void assign_allocator( std::false_type, void *, void const * ) {} 998 | 999 | template< typename allocator > 1000 | void allocator_swap( std::true_type, allocator * lhs, allocator * rhs ) 1001 | { std::iter_swap( lhs, rhs ); } 1002 | template< typename allocator > 1003 | void allocator_swap( std::false_type, allocator * lhs, allocator * rhs ) 1004 | { assert ( * lhs == * rhs && "Cannot swap containers with non-swapping, unequal allocators." ); } 1005 | 1006 | template< typename source > // If the propagation trait is true_type, and the source is a compatible container, then get its allocator. 1007 | typename source::UGLY(common_allocator) && select_allocator( std::true_type, void *, source const * s ) 1008 | { return std::move( s->saved_allocator() ); } 1009 | template< typename allocator > 1010 | allocator && select_allocator( std::false_type, allocator * a, void const * ) 1011 | { return std::move( * a ); } 1012 | 1013 | /* One or two Allocator objects are stored in a container_wrapper. The allocator base class below is rebound to by common_allocator_rebind. 1014 | If the target is not pointer-like (including nullptr, PTMF, reference_wrapper), the erasure contains another allocator (re-)bound to target_type. 1015 | The internal rebind is always updated after target construction or destruction, even if it's not Assignable. Here's the process: 1016 | 1. Rebind the given allocator to the new target type. This may be the saved allocator, a propagating one, or a default-constructed one. 1017 | 2. Construct the target with the correctly bound allocator object, so the wrapper and the erasure use that and not a copy. 1018 | 3. Construct, assign, or reconstruct the saved (-bound) allocator from the used-in-place (target-bound) one. 1019 | When reconstruction occurs, it always preserves value up to equality. */ 1020 | template< typename target_policy, typename allocator_in, typename ... sig > 1021 | class container_wrapper 1022 | : public wrapper< target_policy, sig ... > // The wrapper constructor may determine the allocator value, so this comes first. 1023 | , public allocator_in { // This is already rebound to , unlike allocator_type. 1024 | typedef std::allocator_traits< allocator_in > allocator_traits; 1025 | typedef typename container_wrapper::wrapper wrapper; 1026 | 1027 | template< typename source, typename = void > 1028 | struct rebind_allocator_for_source 1029 | { typedef typename allocator_traits::template rebind_alloc< typename std::decay< source >::type > type; }; 1030 | template< typename source > 1031 | struct rebind_allocator_for_source< source, 1032 | typename std::enable_if< wrapper::template is_compatibly_wrapped< typename std::decay< source >::type >::value >::type > 1033 | { typedef allocator_in type; }; 1034 | 1035 | template< typename target_allocator = allocator_in, typename propagate = std::false_type, typename source > 1036 | void assign( std::false_type, source && s ) { 1037 | target_allocator && alloc = select_allocator( propagate{}, & saved_allocator(), & s ); // May be a temporary if not propagating. 1038 | wrapper temp( std::allocator_arg, std::move( alloc ), std::forward< source >( s ) ); // Use referent of alloc in-place. 1039 | assign_allocator( std::integral_constant< bool, propagate::value // Adopt updated allocator value if propagating, 1040 | || ( ! std::is_same< allocator_in, target_allocator >::value // or if a temporary was used… 1041 | && std::is_move_assignable< allocator_in >::value ) >{}, // … and it supports assignment. (Duck typing alert!) 1042 | & saved_allocator(), & alloc ); // Allocator assignment might throw; nothing forbids it. Avoid corruption in that case. 1043 | wrapper::operator = ( std::move( temp ) ); // Does not throw. 1044 | } 1045 | template< typename = allocator_in, typename propagate = std::false_type, typename source > 1046 | void assign( std::true_type, source && s ) noexcept { // Source is a compatible container_wrapper rvalue or it is pointer-like. 1047 | assign_allocator( propagate{}, & saved_allocator(), & s ); // Terminate upon throwing allocator move assignment. 1048 | wrapper::operator = ( std::forward< source >( s ) ); // Nothing aside from propagation can modify the allocator. 1049 | } 1050 | 1051 | protected: 1052 | template< typename target > 1053 | using is_targetable = typename target_policy::template temp< target >; 1054 | template< typename source > 1055 | using is_compatibly_wrapped = typename wrapper::template is_compatibly_wrapped< source >; 1056 | 1057 | template< typename source, typename = allocator_in > 1058 | struct has_compatible_allocator : std::false_type {}; // Only valid given is_compatibly_wrapped. 1059 | template< typename source > 1060 | struct has_compatible_allocator< source, typename source::UGLY(common_allocator) > : std::true_type {}; 1061 | 1062 | typedef std::integral_constant< bool, 1063 | allocator_traits::propagate_on_container_move_assignment::value || MSVC_FIX (impl::) is_always_equal< allocator_in >::value > noexcept_move_assign; 1064 | public: 1065 | allocator_in & saved_allocator() { return * this; } // Pseudo private, not const, and return type is not allocator_type. 1066 | allocator_in const & saved_allocator() const { return * this; } 1067 | 1068 | typedef allocator_in UGLY(common_allocator); 1069 | 1070 | container_wrapper() = default; 1071 | explicit container_wrapper( allocator_in const & alloc ) noexcept 1072 | : allocator_in{ alloc } {} 1073 | deprecated( "std::allocator_arg is now unnecessary in function_container classes. Just pass the allocator instead." ) 1074 | container_wrapper( std::allocator_arg_t, allocator_in const & in_alloc ) noexcept 1075 | : UGLY(common_allocator){ in_alloc } {} 1076 | 1077 | container_wrapper( container_wrapper && s ) noexcept 1078 | : container_wrapper( static_cast< wrapper && >( s ), std::move( s.saved_allocator() ) ) {} 1079 | container_wrapper( container_wrapper const & s ) 1080 | : container_wrapper( static_cast< wrapper const & >( s ), allocator_traits::select_on_container_copy_construction( s.saved_allocator() ) ) {} 1081 | 1082 | template< typename source, 1083 | typename std::enable_if< is_targetable< typename std::decay< source >::type >::value >::type * = nullptr > 1084 | container_wrapper( source && s, typename rebind_allocator_for_source< source >::type alloc = {} ) 1085 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value 1086 | || ( is_compatibly_wrapped< source >::value && MSVC_FIX (impl::) is_always_equal< allocator_in >::value ) ) 1087 | : wrapper( std::allocator_arg, /* not invalidating */ std::move( alloc ), std::forward< source >( s ) ) 1088 | , allocator_in{ std::move( alloc ) } {} 1089 | 1090 | template< typename source, typename ... arg, 1091 | typename std::enable_if< is_targetable< typename std::decay< source >::type >::value >::type * = nullptr > 1092 | container_wrapper( std::allocator_arg_t, typename rebind_allocator_for_source< source >::type alloc, in_place_t< source > t, arg && ... a ) 1093 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value ) 1094 | : wrapper( std::allocator_arg, std::move( alloc ), t, std::forward< arg >( a ) ... ) 1095 | , allocator_in{ std::move( alloc ) } {} 1096 | 1097 | template< typename source, typename ... arg, 1098 | typename std::enable_if< is_targetable< typename std::decay< source >::type >::value >::type * = nullptr > 1099 | container_wrapper( in_place_t< source > t, arg && ... a ) 1100 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value ) 1101 | : container_wrapper( std::allocator_arg, {}, t, std::forward< arg >( a ) ... ) {} 1102 | 1103 | container_wrapper & operator = ( container_wrapper && ) = delete; // Always assign [unique_]function_container instead, for conversion to unique. 1104 | container_wrapper & operator = ( container_wrapper const & ) = delete; 1105 | 1106 | template< typename source > 1107 | typename std::enable_if< conjunction< 1108 | is_compatibly_wrapped< source > // Incidentally check that source is not a reference. 1109 | , has_compatible_allocator< source > 1110 | >::value >::type 1111 | operator = ( source && s ) 1112 | noexcept( noexcept_move_assign::value ) { 1113 | typedef typename allocator_traits::propagate_on_container_move_assignment pocma; 1114 | return noexcept_move_assign::value || saved_allocator() == s.saved_allocator() 1115 | ? assign< allocator_in, pocma >( std::true_type{}, std::move( s ) ) 1116 | : assign< allocator_in, pocma >( noexcept_move_assign{}, std::move( s ) ); // If noexcept_move_assign, this is a dead branch and a non-instantiation. 1117 | } 1118 | template< typename source > 1119 | typename std::enable_if< conjunction< 1120 | is_compatibly_wrapped< source > 1121 | , has_compatible_allocator< source > 1122 | >::value >::type 1123 | operator = ( source const & s ) 1124 | { return assign< allocator_in, typename allocator_traits::propagate_on_container_copy_assignment >( std::false_type{}, s ); } 1125 | 1126 | template< typename source > 1127 | typename std::enable_if< conjunction< 1128 | is_compatibly_wrapped< source > // Filter out lvalue reference sources. 1129 | , negation< has_compatible_allocator< source > > // No compatible allocator typically indicates a non-container wrapper source. 1130 | >::value >::type 1131 | operator = ( source && s ) { // Throws allocator_mismatch_error. 1132 | return this->nontrivial_target( s, & saved_allocator() ) // Verify dynamic allocator compatibility even if is_always_equal. 1133 | && ! MSVC_FIX (impl::) is_always_equal< allocator_in >::value // POCMA doesn't apply here. 1134 | ? assign( typename MSVC_FIX (impl::) is_always_equal< allocator_in >::type{}, std::move( s ) ) // If is_always_equal, this is a dead branch and a non-instantiation. 1135 | : assign( std::true_type{}, std::move( s ) ); 1136 | } 1137 | template< typename source > 1138 | typename std::enable_if< conjunction< 1139 | is_compatibly_wrapped< source > 1140 | , negation< has_compatible_allocator< source > > 1141 | >::value >::type 1142 | operator = ( source const & s ) { 1143 | return this->nontrivial_target( s, & saved_allocator() )? 1144 | assign( std::false_type{}, s ) 1145 | : assign( std::true_type{}, s ); // POCCA doesn't apply here. 1146 | } 1147 | 1148 | template< typename source > 1149 | typename std::enable_if< ! is_compatibly_wrapped< typename std::decay< source >::type >::value >::type 1150 | operator = ( source && s ) 1151 | noexcept( is_noexcept_erasable< typename std::decay< source >::type >::value ) { 1152 | assign< typename allocator_traits::template rebind_alloc< typename std::decay< source >::type > > 1153 | ( typename is_noexcept_erasable< typename std::decay< source >::type >::type{}, std::forward< source >( s ) ); 1154 | } 1155 | 1156 | template< typename target, typename ... arg > 1157 | void emplace_assign( arg && ... a ) 1158 | { wrapper::template allocate_assign< target >( saved_allocator(), std::forward< arg >( a ) ... ); } 1159 | 1160 | void swap( container_wrapper & o ) noexcept { 1161 | allocator_swap( typename allocator_traits::propagate_on_container_swap{}, & saved_allocator(), & o.saved_allocator() ); 1162 | wrapper::swap( o ); 1163 | } 1164 | }; 1165 | 1166 | 1167 | #undef DISPATCH_CQ 1168 | #undef DISPATCH_CV 1169 | #undef DISPATCH_CVREFQ 1170 | #undef DISPATCH_CVOBJQ 1171 | #undef DISPATCH_ALL 1172 | #undef DISPATCH_TABLE 1173 | } 1174 | 1175 | // The actual classes all just pull their interfaces out of private inheritance. 1176 | template< typename ... sig > 1177 | class function 1178 | : impl::wrapper< impl::is_copyable_all_callable< function< sig ... >, sig ... >, sig ... > { 1179 | template< typename, typename ... > 1180 | friend class impl::wrapper; 1181 | typedef typename function::wrapper wrapper; 1182 | public: 1183 | typedef function UGLY(wrapper_type); 1184 | MSVC_FIX( typedef typename wrapper::wrapper_base wrapper_base; ) // Help MSVC with name lookup through inheritance and friendship. 1185 | using wrapper::wrapper; 1186 | 1187 | function() noexcept = default; // Investigate why these are needed. Compiler bug? 1188 | function( function && ) noexcept = default; 1189 | function( function const & ) = default; 1190 | 1191 | function & operator = ( function && o ) noexcept { 1192 | if ( & o != this ) wrapper::operator = ( static_cast< wrapper && >( o ) ); 1193 | return * this; 1194 | } 1195 | function & operator = ( function const & o ) { 1196 | if ( & o != this ) wrapper::operator = ( static_cast< wrapper const & >( o ) ); 1197 | return * this; 1198 | } 1199 | function & operator = ( function & s ) 1200 | { return * this = static_cast< function const & >( s ); } 1201 | 1202 | template< typename source > 1203 | typename std::enable_if< std::is_constructible< wrapper, source >::value, 1204 | function & >::type operator = ( source && s ) 1205 | noexcept(noexcept( std::declval< wrapper & >() = std::declval< source >() )) 1206 | { wrapper::operator = ( std::forward< source >( s ) ); return * this; } 1207 | 1208 | using wrapper::operator (); 1209 | void swap( function & o ) { return wrapper::swap( o ); } 1210 | using wrapper::target; 1211 | using wrapper::target_type; 1212 | using wrapper::verify_type; 1213 | using wrapper::operator bool; 1214 | using wrapper::complete_object_address; 1215 | 1216 | using wrapper::assign; 1217 | using wrapper::emplace_assign; 1218 | using wrapper::allocate_assign; 1219 | // No allocator_type or get_allocator. 1220 | }; 1221 | 1222 | template< typename ... sig > 1223 | class unique_function 1224 | : impl::wrapper< impl::is_all_callable< sig ... >, sig ... > { 1225 | template< typename, typename ... > 1226 | friend class impl::wrapper; 1227 | typedef typename unique_function::wrapper wrapper; 1228 | public: 1229 | typedef unique_function UGLY(wrapper_type); 1230 | MSVC_FIX( typedef typename wrapper::wrapper_base wrapper_base; ) 1231 | using wrapper::wrapper; 1232 | 1233 | unique_function() noexcept = default; 1234 | unique_function( unique_function && ) noexcept = default; 1235 | unique_function( unique_function const & ) = delete; 1236 | 1237 | unique_function & operator = ( unique_function && o ) noexcept { 1238 | if ( & o != this ) wrapper::operator = ( static_cast< wrapper && >( o ) ); 1239 | return * this; 1240 | } 1241 | unique_function & operator = ( unique_function const & ) = delete; 1242 | 1243 | template< typename source > 1244 | typename std::enable_if< std::is_constructible< wrapper, source >::value, 1245 | unique_function & >::type operator = ( source && s ) 1246 | noexcept(noexcept( std::declval< wrapper & >() = std::declval< source >() )) 1247 | { wrapper::operator = ( std::forward< source >( s ) ); return * this; } 1248 | 1249 | using wrapper::operator (); 1250 | void swap( unique_function & o ) { return wrapper::swap( o ); } 1251 | using wrapper::target; 1252 | using wrapper::target_type; 1253 | using wrapper::verify_type; 1254 | using wrapper::operator bool; 1255 | using wrapper::complete_object_address; 1256 | 1257 | using wrapper::assign; 1258 | using wrapper::emplace_assign; 1259 | using wrapper::allocate_assign; 1260 | // No allocator_type or get_allocator. 1261 | }; 1262 | 1263 | template< typename allocator_in, typename ... sig > 1264 | class function_container 1265 | : impl::container_wrapper< impl::is_copyable_all_callable< function_container< allocator_in, sig ... >, sig ... >, 1266 | impl::common_allocator_rebind< allocator_in >, sig ... > { 1267 | template< typename, typename ... > 1268 | friend class impl::wrapper; 1269 | template< typename, typename, typename ... > 1270 | friend class impl::container_wrapper; 1271 | 1272 | typedef typename function_container::container_wrapper wrapper; 1273 | public: 1274 | typedef function_container UGLY(wrapper_type); 1275 | MSVC_FIX( typedef typename wrapper::wrapper_base wrapper_base; ) 1276 | using MSVC_FIX (UGLY(common_allocator) =) typename function_container::wrapper::UGLY(common_allocator); 1277 | 1278 | function_container() = default; // Investigate why these are needed. Compiler bug? 1279 | function_container( function_container && ) noexcept = default; 1280 | function_container( function_container const & ) = default; 1281 | 1282 | explicit function_container( allocator_in const & alloc ) noexcept 1283 | : wrapper( alloc ) {} 1284 | 1285 | template< typename source, typename std::enable_if< 1286 | wrapper::template is_targetable< typename std::decay< source >::type >::value 1287 | >::type * = nullptr > 1288 | function_container( source && s ) 1289 | noexcept( std::is_nothrow_constructible< wrapper, source && >::value ) 1290 | : wrapper{ std::forward< source >( s ) } {} 1291 | 1292 | template< typename source, typename std::enable_if< 1293 | wrapper::template is_targetable< typename std::decay< source >::type >::value 1294 | >::type * = nullptr > 1295 | function_container( source && s, allocator_in const & alloc ) 1296 | noexcept( std::is_nothrow_constructible< wrapper, source &&, allocator_in const & >::value ) 1297 | : wrapper( std::forward< source >( s ), alloc ) {} 1298 | 1299 | function_container & operator = ( function_container && s ) 1300 | noexcept ( wrapper::noexcept_move_assign::value ) { 1301 | if ( & s != this ) wrapper::operator = ( std::move( s ) ); 1302 | return * this; 1303 | } 1304 | function_container & operator = ( function_container const & s ) { 1305 | if ( & s != this ) wrapper::operator = ( s ); 1306 | return * this; 1307 | } 1308 | function_container & operator = ( function_container & s ) 1309 | { return * this = static_cast< function_container const & >( s ); } 1310 | 1311 | template< typename source > 1312 | typename std::enable_if< wrapper::template is_targetable< typename std::decay< source >::type >::value, 1313 | function_container & >::type operator = ( source && s ) 1314 | noexcept(noexcept( std::declval< wrapper & >() = std::declval< source >() )) 1315 | { wrapper::operator = ( std::forward< source >( s ) ); return * this; } 1316 | 1317 | using wrapper::emplace_assign; 1318 | 1319 | using wrapper::operator (); 1320 | void swap( function_container & o ) { return wrapper::swap( o ); } 1321 | using wrapper::target; 1322 | using wrapper::target_type; 1323 | using wrapper::verify_type; 1324 | using wrapper::operator bool; 1325 | using wrapper::complete_object_address; 1326 | 1327 | typedef allocator_in allocator_type; 1328 | allocator_type get_allocator() const { return * this; } // wrapper inherits from common allocator. 1329 | }; 1330 | 1331 | template< typename allocator_in, typename ... sig > 1332 | class unique_function_container 1333 | : impl::container_wrapper< impl::is_all_callable< sig ... >, impl::common_allocator_rebind< allocator_in >, sig ... > { 1334 | template< typename, typename ... > 1335 | friend class impl::wrapper; 1336 | template< typename, typename, typename ... > 1337 | friend class impl::container_wrapper; 1338 | 1339 | typedef typename unique_function_container::container_wrapper wrapper; 1340 | public: 1341 | typedef unique_function_container UGLY(wrapper_type); 1342 | MSVC_FIX( typedef typename wrapper::wrapper_base wrapper_base; ) 1343 | using MSVC_FIX (UGLY(common_allocator) =) typename unique_function_container::wrapper::UGLY(common_allocator); 1344 | 1345 | unique_function_container() = default; 1346 | unique_function_container( unique_function_container && ) noexcept = default; 1347 | unique_function_container( unique_function_container const & ) = delete; 1348 | 1349 | template< typename source, typename std::enable_if< 1350 | wrapper::template is_targetable< typename std::decay< source >::type >::value 1351 | >::type * = nullptr > 1352 | unique_function_container( source && s ) 1353 | noexcept( std::is_nothrow_constructible< wrapper, source && >::value ) 1354 | : wrapper{ std::forward< source >( s ) } {} 1355 | 1356 | template< typename source, typename std::enable_if< 1357 | wrapper::template is_targetable< typename std::decay< source >::type >::value 1358 | >::type * = nullptr > 1359 | unique_function_container( source && s, allocator_in const & alloc ) 1360 | noexcept( std::is_nothrow_constructible< wrapper, source &&, allocator_in const & >::value ) 1361 | : wrapper( std::forward< source >( s ), alloc ) {} 1362 | 1363 | unique_function_container & operator = ( unique_function_container && s ) 1364 | noexcept ( wrapper::noexcept_move_assign::value ) { 1365 | if ( & s != this ) wrapper::operator = ( std::move( s ) ); 1366 | return * this; 1367 | } 1368 | unique_function_container & operator = ( unique_function_container const & ) = delete; 1369 | 1370 | template< typename source > 1371 | typename std::enable_if< wrapper::template is_targetable< typename std::decay< source >::type >::value, 1372 | unique_function_container & >::type operator = ( source && s ) 1373 | noexcept(noexcept( std::declval< wrapper & >() = std::declval< source >() )) 1374 | { wrapper::operator = ( std::forward< source >( s ) ); return * this; } 1375 | 1376 | using wrapper::operator (); 1377 | void swap( unique_function_container & o ) { return wrapper::swap( o ); } 1378 | using wrapper::target; 1379 | using wrapper::target_type; 1380 | using wrapper::verify_type; 1381 | using wrapper::operator bool; 1382 | using wrapper::complete_object_address; 1383 | 1384 | typedef allocator_in allocator_type; 1385 | allocator_type get_allocator() const { return * this; } // container_wrapper inherits from common allocator. 1386 | using wrapper::emplace_assign; 1387 | // No assign or allocate_assign. 1388 | }; 1389 | 1390 | #define DEFINE_WRAPPER_OPS( NAME, ... ) \ 1391 | template< __VA_ARGS__ > \ 1392 | bool operator == ( UNPACK NAME const & a, std::nullptr_t ) \ 1393 | { return !a; } \ 1394 | template< __VA_ARGS__ > \ 1395 | bool operator != ( UNPACK NAME const & a, std::nullptr_t ) \ 1396 | { return (bool) a; } \ 1397 | template< __VA_ARGS__ > \ 1398 | bool operator == ( std::nullptr_t, UNPACK NAME const & a ) \ 1399 | { return !a; } \ 1400 | template< __VA_ARGS__ > \ 1401 | bool operator != ( std::nullptr_t, UNPACK NAME const & a ) \ 1402 | { return (bool) a; } \ 1403 | template< __VA_ARGS__ > \ 1404 | void swap( UNPACK NAME & lhs, UNPACK NAME & rhs ) \ 1405 | noexcept(noexcept( lhs.swap( rhs ) )) \ 1406 | { lhs.swap( rhs ); } 1407 | 1408 | DEFINE_WRAPPER_OPS( ( function< sig ... > ), typename ... sig ) 1409 | DEFINE_WRAPPER_OPS( ( unique_function< sig ... > ), typename ... sig ) 1410 | DEFINE_WRAPPER_OPS( ( function_container< cont, sig ... > ), typename cont, typename ... sig ) 1411 | DEFINE_WRAPPER_OPS( ( unique_function_container< cont, sig ... > ), typename cont, typename ... sig ) 1412 | #undef DEFINE_WRAPPER_OPS 1413 | 1414 | #if __cplusplus >= 201402 // Return type deduction really simplifies these. 1415 | // See proposal, "std::recover: undoing type erasure" 1416 | // This section really should be a separate library. It is agnostic to everything above. 1417 | 1418 | namespace impl { 1419 | template< typename erasure > 1420 | void const volatile * recover_address( erasure & e, std::false_type ) 1421 | { return e.complete_object_address(); } 1422 | 1423 | template< typename erasure > 1424 | void const volatile * recover_address( erasure & e, std::true_type ) 1425 | { return e.referent_address(); } // Not used in the cxx_function library because function wrapper targets are never references, always values. 1426 | } 1427 | 1428 | struct bad_type_recovery : std::exception 1429 | { virtual char const * what() const noexcept override { return "An object was not found with its expected type."; } }; 1430 | 1431 | template< typename want, typename erasure_ref > 1432 | constexpr auto && recover( erasure_ref && e ) { 1433 | typedef std::remove_reference_t< erasure_ref > erasure; 1434 | typedef std::conditional_t< std::is_const< erasure >::value, want const, want > prop_const; 1435 | typedef std::conditional_t< std::is_volatile< erasure >::value, prop_const volatile, prop_const > prop_cv; 1436 | typedef std::conditional_t< std::is_lvalue_reference< erasure_ref >::value, prop_cv &, prop_cv && > prop_cvref; 1437 | if ( e.template verify_type< want >() ) { 1438 | return static_cast< prop_cvref >( * static_cast< std::decay_t< want > * >( const_cast< void * >( impl::recover_address( e, std::is_reference< want >{} ) ) ) ); 1439 | } else throw bad_type_recovery{}; 1440 | } 1441 | #endif 1442 | 1443 | #undef UGLY 1444 | #undef is_trivially_move_constructible 1445 | #undef is_trivially_copy_constructible 1446 | #undef deprecated 1447 | #undef OLD_GCC_FIX 1448 | #undef OLD_GCC_SKIP 1449 | #undef MSVC_FIX 1450 | #undef MSVC_SKIP 1451 | #undef UNPACK 1452 | #undef IGNORE 1453 | } 1454 | 1455 | #endif 1456 | -------------------------------------------------------------------------------- /test/align.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | 4 | bool did_move = false; 5 | 6 | struct alignas(32) x { 7 | void operator () () {} 8 | x() = default; 9 | x( x const & ) = default; 10 | x( x && ) noexcept { did_move = true; } 11 | }; 12 | 13 | int main() { 14 | cxx_function::function< void() > q = { cxx_function::in_place_t< x >{} }, r = std::move( q ); 15 | static_assert ( alignof (x) > alignof (decltype (q)), "test defeated" ); 16 | static_assert ( alignof (decltype(q)) == alignof (std::aligned_storage< sizeof(void *[2]) >::type), "Unexpected alignment." ); 17 | assert ( ! did_move ); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /test/badcopy.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | 3 | template< typename t > 4 | struct bad { 5 | bad() = default; 6 | bad( bad && ) = default; 7 | bad( bad const & ) 8 | { t::nonsense(); } 9 | 10 | void operator () () {} 11 | }; 12 | 13 | int main() { 14 | cxx_function::unique_function< void() > q = bad< void >{}; 15 | } 16 | -------------------------------------------------------------------------------- /test/callmove.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | 4 | using namespace cxx_function; 5 | 6 | int c[ 2 ] = {}; 7 | 8 | struct mc { 9 | int x; 10 | mc operator() ( mc ) { return {1}; } 11 | mc( int i ) : x{ i } {} 12 | mc( mc && o ) noexcept : x(o.x) { ++ c[ x ]; } 13 | mc( mc const & o ) = default; 14 | }; 15 | 16 | int main() { 17 | mc m {0}; 18 | function< mc( mc ) > q = m; // Standard mandates a move here because constructor passes by value. 19 | assert ( c[0] == 0 ); // Better not to. 20 | q( m ); 21 | assert ( c[0] == 1 ); 22 | q = m; // Copy-and-swap adds an unnecessary move. 23 | assert ( c[0] == 2 ); 24 | int reliable_copy_elision = 25 | #ifdef _MSC_VER 26 | 0; 27 | #else 28 | 1; 29 | #endif 30 | assert ( c[1] == 1 - reliable_copy_elision ); // Copy elision of return prvalues. 31 | } 32 | -------------------------------------------------------------------------------- /test/constructible.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | #include 4 | 5 | using namespace cxx_function; 6 | 7 | struct f { 8 | int operator () ( int ) & { return 0; } 9 | }; 10 | 11 | struct d : f { 12 | operator int & () const { static int q; return q; } 13 | }; 14 | 15 | #if __cpp_noexcept_function_type 16 | struct ne { 17 | int operator () ( int ) noexcept { return 0; } 18 | }; 19 | #endif 20 | 21 | static_assert ( std::is_default_constructible< function< int( int ) > >::value, "" ); 22 | static_assert ( std::is_default_constructible< unique_function< int( int ) > >::value, "" ); 23 | 24 | static_assert ( std::is_convertible< f, function< int( int ) > >::value, "" ); 25 | static_assert ( std::is_convertible< f, function< int( std::integral_constant< long, 100 > ) > >::value, "" ); 26 | static_assert ( ! std::is_convertible< f, function< int( int ) && > >::value, "" ); 27 | static_assert ( ! std::is_convertible< f, function< int( int ) &, int( int ) && > >::value, "" ); 28 | 29 | #if __cpp_noexcept_function_type 30 | static_assert ( ! std::is_default_constructible< function< int( int ) noexcept > >::value, "" ); 31 | static_assert ( ! std::is_default_constructible< unique_function< int( int ) noexcept > >::value, "" ); 32 | 33 | static_assert ( ! std::is_convertible< f, function< int( int ) noexcept > >::value, "" ); 34 | static_assert ( std::is_convertible< ne, function< int( int ) noexcept > >::value, "" ); 35 | #endif 36 | 37 | static_assert ( std::is_convertible< function< d() >, function< f() > >::value, "" ); 38 | static_assert ( std::is_convertible< function< d &() >, function< f const &() > >::value, "" ); 39 | static_assert ( ! std::is_convertible< function< d() >, function< f const &() > >::value, "" ); 40 | static_assert ( std::is_convertible< function< d() >, function< int const &() > >::value, "" ); 41 | static_assert ( std::is_convertible< function< d &() >, function< int const &() > >::value, "" ); 42 | 43 | static_assert ( std::is_convertible< function< int() >, function< long() > >::value, "" ); 44 | static_assert ( std::is_convertible< function< int &() >, function< int const &() > >::value, "" ); 45 | static_assert ( ! std::is_convertible< function< int &() >, function< long const &() > >::value, "" ); 46 | static_assert ( std::is_convertible< function< int * const &() >, function< void *() > >::value, "" ); 47 | static_assert ( ! std::is_convertible< function< int * const &() >, function< void * const &() > >::value, "" ); 48 | 49 | static_assert ( std::is_nothrow_constructible< function< int( int ) >, function< int( int ) > >::value, "" ); 50 | static_assert ( std::is_nothrow_constructible< function< int( int ) >, function< int( int ) > && >::value, "" ); 51 | static_assert ( std::is_nothrow_constructible< function< int( int ) >, function< int( int ) & > >::value, "" ); 52 | static_assert ( std::is_nothrow_constructible< function< int( int ) >, function< int( int ) const & > >::value, "" ); 53 | 54 | static_assert ( std::is_nothrow_constructible< unique_function< int( int ) >, unique_function< int( int ) > >::value, "" ); 55 | static_assert ( std::is_nothrow_constructible< unique_function< int( int ) >, unique_function< int( int ) > && >::value, "" ); 56 | static_assert ( std::is_nothrow_constructible< unique_function< int( int ) >, unique_function< int( int ) & > >::value, "" ); 57 | static_assert ( std::is_nothrow_constructible< unique_function< int( int ) >, unique_function< int( int ) const & > >::value, "" ); 58 | 59 | static_assert ( std::is_nothrow_constructible< unique_function< int( int ) >, function< int( int ) const & > >::value, "" ); 60 | static_assert ( ! std::is_constructible< function< int( int ) >, unique_function< int( int ) > >::value, "" ); 61 | 62 | static_assert ( std::is_nothrow_assignable< unique_function< int( int ) >, function< int( int ) > >::value, "" ); 63 | static_assert ( std::is_nothrow_assignable< unique_function< int( int ) >, function< int( int ) const & > >::value, "" ); 64 | static_assert ( std::is_nothrow_assignable< unique_function< int( int ) >, unique_function< int( int ) const & > >::value, "" ); 65 | static_assert ( ! std::is_assignable< function< int( int ) >, unique_function< int( int ) > >::value, "" ); 66 | static_assert ( ! std::is_assignable< function< int( int ) >, unique_function< int( int ) const & > >::value, "" ); 67 | 68 | static_assert ( std::is_nothrow_assignable< function< int( int ) >, function_container< std::allocator< char >, int( int ) > >::value, "" ); 69 | static_assert ( std::is_nothrow_assignable< function< int( int ) >, function_container< std::allocator< char >, int( int ) const & > >::value, "" ); 70 | static_assert ( ! std::is_assignable< function< int( int ) >, unique_function_container< std::allocator< char >, int( int ) > >::value, "" ); 71 | static_assert ( ! std::is_assignable< function< int( int ) >, unique_function_container< std::allocator< char >, int( int ) const & > >::value, "" ); 72 | 73 | static_assert ( ! std::is_constructible< function< int( int ) >, function< int( int ) >, std::allocator >::value, "" ); 74 | 75 | #if ! _MSC_VER || _MSC_VER >= 1910 76 | static_assert ( std::is_nothrow_constructible< function< int( int ) >, std::allocator_arg_t, std::allocator, 77 | in_place_t< std::nullptr_t >, std::nullptr_t >::value, "" ); 78 | static_assert ( ! std::is_nothrow_constructible< function< int( int ) >, std::allocator_arg_t, std::allocator, 79 | in_place_t< function< int( int ) > >, f >::value, "" ); 80 | #endif 81 | 82 | typedef unique_function< int( int ) & > uft; 83 | static_assert ( std::is_assignable< uft, uft >::value, "" ); 84 | static_assert ( std::is_assignable< uft, uft && >::value, "" ); 85 | //static_assert ( ! std::is_assignable< uft, uft & >::value, "" ); -- Removed assignability SFINAE. 86 | static_assert ( ! std::is_assignable< uft, uft const & >::value, "" ); 87 | //static_assert ( ! std::is_assignable< uft, uft const && >::value, "" ); 88 | 89 | typedef function< int( int ) & > ft; 90 | static_assert ( std::is_assignable< ft, ft >::value, "" ); 91 | static_assert ( std::is_assignable< ft, ft && >::value, "" ); 92 | static_assert ( std::is_assignable< ft, ft & >::value, "" ); 93 | static_assert ( std::is_assignable< ft, ft const & >::value, "" ); 94 | static_assert ( std::is_assignable< ft, ft const && >::value, "" ); 95 | 96 | static_assert ( ! std::is_constructible< function< int( int ) & >, std::allocator_arg_t, std::allocator< uft >, uft >::value, "" ); 97 | static_assert ( ! std::is_constructible< function< int( int ) & >, std::allocator_arg_t, std::allocator< uft >, uft & >::value, "" ); 98 | 99 | static_assert ( ! std::is_constructible< function< int( int ) && >, f >::value, "" ); 100 | 101 | uft a, q( std::allocator_arg, std::allocator< uft >{}, std::move( a ) ); 102 | 103 | int main () { 104 | struct s { 105 | volatile int i; 106 | ~ s() { i = 42; } 107 | int operator () ( int v ) { 108 | int ret = i; 109 | i = v; 110 | return ret; 111 | } 112 | }; 113 | 114 | function< int( int ) > x = s{ 3 }; 115 | #if __GNUC__ && defined(__has_warning) 116 | # if __has_warning("-Wself-move") 117 | # pragma GCC diagnostic ignored "-Wself-move" 118 | # endif 119 | #endif 120 | x = std::move( x ); 121 | assert ( x( 5 ) == 3 ); 122 | } 123 | -------------------------------------------------------------------------------- /test/container.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cxx_function.hpp" 4 | #include 5 | 6 | std::map< int, std::ptrdiff_t > pool_total; 7 | std::ptrdiff_t global_total = 0; 8 | 9 | 10 | struct accounting { 11 | int id; 12 | static int next_id; 13 | 14 | accounting() 15 | : id( next_id ++ ) {} 16 | 17 | std::ptrdiff_t total() const { return pool_total[ id ]; } 18 | }; 19 | int accounting::next_id = 0; 20 | 21 | bool operator == ( accounting const & lhs, accounting const & rhs ) 22 | { return lhs.id == rhs.id; } 23 | bool operator != ( accounting const & lhs, accounting const & rhs ) 24 | { return lhs.id != rhs.id; } 25 | 26 | 27 | template< typename t > 28 | struct accountant 29 | : accounting { 30 | typedef t value_type; 31 | 32 | t * allocate( std::size_t n ) { 33 | n *= sizeof (t); 34 | pool_total[ id ] += n; 35 | global_total += n; 36 | return static_cast< t * >( ::operator new( n ) ); 37 | } 38 | void deallocate( t * p, std::size_t n ) { 39 | n *= sizeof (t); 40 | pool_total[ id ] -= n; 41 | global_total -= n; 42 | assert ( pool_total[ id ] >= 0 ); 43 | assert ( global_total >= 0 ); 44 | ::operator delete( p ); 45 | } 46 | 47 | accountant select_on_container_copy_construction() const 48 | { return {}; } 49 | 50 | accountant() = default; 51 | 52 | template< typename u > 53 | accountant( accountant< u > const & o ) 54 | : accounting( o ) {} 55 | }; 56 | 57 | template< std::size_t n > 58 | struct immovable { 59 | char weight[ n ]; 60 | immovable() = default; 61 | immovable( immovable const & ) = default; 62 | immovable( immovable && ) {} 63 | }; 64 | 65 | int main() { 66 | cxx_function::function_container< accountant, accounting() > q; 67 | immovable< 5 > c5; 68 | auto five = [c5] { (void) c5; return accounting{}; }; 69 | static_assert ( sizeof five == 5, "" ); 70 | q = five; 71 | assert ( q.get_allocator().total() == 5 ); 72 | auto r = q; 73 | assert ( q.get_allocator().total() == 5 ); 74 | assert ( global_total == 10 ); 75 | q = [q] { return q.get_allocator(); }; // Copy, then overwrite q. 76 | assert ( q.get_allocator().total() == sizeof (q) ); 77 | assert ( global_total == 10 + sizeof (q) ); 78 | cxx_function::function< accounting() > f; 79 | f = q; // Use q's allocator for a new wrapper targeting a container with a five. 80 | assert ( q.get_allocator().total() == 2 * sizeof (q) ); 81 | assert ( global_total == 15 + 2 * sizeof (q) ); 82 | 83 | f.assign( five, q.get_allocator() ); 84 | assert ( q.get_allocator().total() == 5 + sizeof (q) ); 85 | 86 | q = f; 87 | r = std::move( q ); 88 | cxx_function::unique_function_container< accountant, accounting() > s = r; 89 | s = std::move( r ); 90 | #if __GNUC__ && defined(__has_warning) 91 | # if __has_warning("-Wself-move") 92 | # pragma GCC diagnostic ignored "-Wself-move" 93 | # endif 94 | #endif 95 | s = std::move( s ); 96 | assert ( s != nullptr ); 97 | } 98 | -------------------------------------------------------------------------------- /test/convnull.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | using namespace cxx_function; 4 | 5 | struct c { 6 | void operator() ( int ) {} 7 | operator function< void() > () const { return {}; } 8 | }; 9 | 10 | struct d : private function< void() > { 11 | void operator() ( int ) {} 12 | }; 13 | 14 | struct d2 : public function< void() > { 15 | void operator() ( int ) {} 16 | }; 17 | 18 | int main() { 19 | function< void( int ) > a; 20 | function< void( long ) > b = a; 21 | assert ( ! b ); 22 | b = c{}; 23 | assert ( b ); 24 | b = d{}; 25 | assert ( b ); 26 | b = d2{}; 27 | assert ( b ); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /test/ctorthrow.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | 3 | struct ctor_error : std::exception {}; 4 | 5 | struct throwy { 6 | mutable int when; 7 | 8 | explicit throwy( int in_when ) 9 | : when( in_when ) 10 | { if ( when == 1 ) throw ctor_error{}; } 11 | 12 | throwy( throwy && o ) 13 | : when( o.when ) { 14 | o.when = 0; 15 | if ( when == 2 ) throw ctor_error{}; 16 | } 17 | throwy( throwy const & o ) 18 | : when( o.when ) { 19 | o.when = 0; 20 | if ( when == 3 ) throw ctor_error{}; 21 | } 22 | ~ throwy() { 23 | if ( when != 0 ) abort(); 24 | } 25 | 26 | void operator () () {} 27 | }; 28 | 29 | template< typename t > 30 | struct alloc { 31 | typedef t value_type; 32 | int * count; 33 | 34 | explicit alloc( int * in_count ) 35 | : count( in_count ) {} 36 | template< typename u > 37 | alloc( alloc< u > const & o ) 38 | : count( o.count ) {} 39 | 40 | t * allocate( std::size_t n ) { 41 | ++ * count; 42 | return static_cast< t * >( ::operator new( n * sizeof (t) ) ); 43 | } 44 | void deallocate( t * p, std::size_t ) { 45 | -- * count; 46 | ::operator delete( p ); 47 | } 48 | }; 49 | 50 | template< typename t, typename u > 51 | bool operator == ( alloc< t > const & lhs, alloc< u > const & rhs ) 52 | { return lhs.count == rhs.count; } 53 | template< typename t, typename u > 54 | bool operator != ( alloc< t > const & lhs, alloc< u > const & rhs ) 55 | { return lhs.count != rhs.count; } 56 | 57 | namespace std { 58 | template<> 59 | struct uses_allocator< throwy, alloc< throwy > > : true_type {}; 60 | } 61 | 62 | struct fine { 63 | void operator () () {} 64 | }; 65 | 66 | using namespace cxx_function; 67 | 68 | int main() { 69 | function< void() > f = fine(); 70 | 71 | try { 72 | f.emplace_assign< throwy >( 1 ); 73 | abort(); 74 | } catch ( ctor_error & ) {} 75 | 76 | assert ( f.target< fine >() ); 77 | 78 | int count_f = 0, count_g = 0; 79 | 80 | f.allocate_assign< throwy >( alloc< throwy >{ & count_f }, 2 ); 81 | 82 | try { 83 | function< void() > g( std::allocator_arg, alloc< throwy >{ & count_g }, std::move( f ) ); 84 | abort(); 85 | } catch ( ctor_error & ) {} 86 | assert ( count_f == 1 ); 87 | assert ( count_g == 0 ); 88 | 89 | f(); 90 | f.allocate_assign< throwy >( alloc< throwy >{ & count_f }, 3 ); 91 | 92 | try { 93 | function< void() > g = f; 94 | abort(); 95 | } catch ( ctor_error & ) {} 96 | assert ( count_f == 1 ); 97 | 98 | f.target< throwy >()->when = 3; 99 | try { 100 | function< void() > g; 101 | g = f; 102 | abort(); 103 | } catch ( ctor_error & ) {} 104 | assert ( count_f == 1 ); 105 | 106 | f(); 107 | auto g = f; 108 | assert ( count_f == 2 ); 109 | } 110 | -------------------------------------------------------------------------------- /test/heap.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | 4 | using namespace cxx_function; 5 | 6 | int main() { 7 | #if ! _MSC_VER || _MSC_VER >= 1910 8 | int d[ 100 ] = { 1, 2, 3 }; 9 | function< int *( int ) > f = [d] ( int i ) mutable { return d + i; }; 10 | assert ( f( 0 ) != d ); 11 | 12 | assert ( * f( 2 ) == 3 ); 13 | auto g = f; 14 | * f( 2 ) = 5; 15 | assert ( * g( 2 ) == 3 ); 16 | assert ( * f( 2 ) == 5 ); 17 | auto p = f( 2 ); 18 | g = std::move( f ); 19 | assert ( g( 2 ) == p ); 20 | try { 21 | f( 0 ); 22 | abort(); 23 | } catch ( std::bad_function_call & ) {} 24 | #endif 25 | } 26 | -------------------------------------------------------------------------------- /test/nocopy.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | 3 | using namespace cxx_function; 4 | 5 | struct nc { 6 | nc( nc && ) = default; 7 | nc( nc const & ) = delete; 8 | 9 | void operator () () {} 10 | }; 11 | 12 | static_assert ( std::is_constructible< unique_function< void() >, nc >::value, "" ); 13 | static_assert ( ! std::is_constructible< function< void() >, nc >::value, "" ); 14 | static_assert ( std::is_constructible< unique_function< void() >, function< void() > >::value, "" ); 15 | static_assert ( std::is_constructible< unique_function< void() >, function< void() > & >::value, "" ); 16 | static_assert ( ! std::is_copy_constructible< unique_function< void() > >::value, "" ); 17 | static_assert ( ! std::is_constructible< function< void() >, unique_function< void() > >::value, "" ); 18 | 19 | int main() {} 20 | 21 | -------------------------------------------------------------------------------- /test/overlambda.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | using namespace cxx_function; 3 | 4 | 5 | void f(function) {} 6 | void f(function) {} 7 | 8 | int main() { 9 | f([]{}); 10 | f([](int){}); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/ptm.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | 4 | using namespace cxx_function; 5 | 6 | struct vf { 7 | virtual int fn() { return m; } 8 | int m = 5; 9 | }; 10 | 11 | 12 | int main() { 13 | function< int( vf ) const volatile > f = & vf::m, g = & vf::fn; 14 | vf o; 15 | 16 | assert ( f( o ) == 5 ); 17 | o.m = 42; 18 | assert ( g( o ) == 42 ); 19 | 20 | int (vf::* ptmf)() = & vf::fn; 21 | g = ptmf; 22 | assert ( g( o ) == 42 ); 23 | ptmf = nullptr; 24 | g = ptmf; 25 | assert ( ! g ); 26 | 27 | int vf::* ptm = & vf::m; 28 | f = ptm; 29 | o.m = 99; 30 | assert ( f( o ) == 99 ); 31 | ptm = nullptr; 32 | f = ptm; 33 | assert ( ! f ); 34 | } 35 | -------------------------------------------------------------------------------- /test/recover.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | 3 | using namespace cxx_function; 4 | 5 | struct fs { 6 | void operator () () const {} 7 | }; 8 | 9 | int main() { 10 | function< void() > f = fs{}; 11 | 12 | #if __cplusplus >= 201402 13 | fs & tl = recover< fs >( f ); 14 | fs && tr = recover< fs >( std::move( f ) ); 15 | try { 16 | recover< std::nullptr_t >( function< void() >{} ); 17 | abort(); 18 | } catch ( bad_type_recovery & ) {} 19 | (void) tl, (void) tr; 20 | 21 | f = + []{}; 22 | assert ( f.complete_object_address() == & recover< void(*)() >( f ) ); 23 | #endif 24 | } 25 | -------------------------------------------------------------------------------- /test/scoped_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::map< int, std::size_t > pool; 10 | 11 | template< typename t > 12 | struct pool_alloc : std::allocator< t > { 13 | int id; 14 | 15 | template< typename u > 16 | pool_alloc( pool_alloc< u > const & o ) 17 | : id( o.id ) {} 18 | 19 | pool_alloc( int in_id 20 | #if _MSC_VER && _MSC_VER < 1910 21 | = 0 22 | #endif 23 | ) 24 | : id( in_id ) {} 25 | 26 | t * allocate( std::size_t n ) { 27 | n *= sizeof (t); 28 | pool[ id ] += n; 29 | return static_cast< t * >( ::operator new( n ) ); 30 | } 31 | 32 | void deallocate( t * p, std::size_t n ) { 33 | n *= sizeof (t); 34 | pool[ id ] -= n; 35 | return ::operator delete( p ); 36 | } 37 | 38 | template< typename o > struct rebind { typedef pool_alloc< o > other; }; 39 | 40 | typedef std::false_type is_always_equal; 41 | typedef std::false_type propagate_on_container_move_assignment; 42 | }; 43 | 44 | template< typename t, typename u > 45 | bool operator == ( pool_alloc< t > lhs, pool_alloc< u > rhs ) 46 | { return lhs.id == rhs.id; } 47 | template< typename t, typename u > 48 | bool operator != ( pool_alloc< t > lhs, pool_alloc< u > rhs ) 49 | { return lhs.id != rhs.id; } 50 | 51 | typedef std::basic_string< char, std::char_traits< char >, pool_alloc< char > > pool_string; 52 | 53 | struct stateful_op { 54 | pool_string state; 55 | 56 | stateful_op( stateful_op const & o, pool_alloc< stateful_op > a ) 57 | : state( o.state, a ) {} 58 | 59 | explicit stateful_op( pool_string s ) 60 | : state( std::move( s ) ) {} 61 | 62 | void operator () () const 63 | { /* std::cout << "op says " << state << " from pool id " << state.get_allocator().id << '\n'; */ } 64 | }; 65 | 66 | struct listful_op { 67 | typedef std::scoped_allocator_adaptor< pool_alloc< pool_string > > state_alloc; 68 | std::forward_list< pool_string, state_alloc > state; 69 | 70 | listful_op( listful_op const & o, state_alloc a ) 71 | : state( o.state, a ) {} 72 | 73 | explicit listful_op( pool_string s, state_alloc a ) 74 | : state( { std::move( s ) }, a ) {} 75 | 76 | void operator () () const 77 | { /* std::cout << "op says " << state << " from pool id " << state.get_allocator().id << '\n'; */ } 78 | }; 79 | static_assert ( sizeof (std::forward_list< int >) < sizeof(void *[3]), "list is bigger than anticipated." ); 80 | static_assert ( sizeof (listful_op) <= sizeof (void *[3]), "Small-size container test defeated." ); 81 | 82 | namespace std { 83 | template< typename a > struct uses_allocator< stateful_op, a > : std::true_type {}; 84 | template< typename a > struct uses_allocator< listful_op, a > : std::true_type {}; 85 | } 86 | 87 | using namespace cxx_function; 88 | 89 | int main() { 90 | stateful_op op( { "hello from a very long string", pool_alloc< char >{ 0 } } ); 91 | 92 | typedef function_container< std::scoped_allocator_adaptor< pool_alloc >, void() > fct; 93 | fct fc1( pool_alloc< char >{ 1 } ); 94 | fc1 = op; 95 | function< void() > fv = fc1; 96 | fct fc2( pool_alloc< char >{ 2 } ); 97 | fc2 = fv; 98 | 99 | op(); 100 | fc1(); 101 | fc2(); 102 | fv(); 103 | 104 | assert ( pool[ 0 ] == op.state.capacity() + 1 ); 105 | assert ( pool[ 1 ] == op.state.capacity() * 2 + 2 + sizeof (stateful_op) * 2 ); 106 | assert ( pool[ 2 ] == op.state.capacity() + 1 + sizeof (stateful_op) ); 107 | { 108 | std::map< int, function< void() >, std::less< int >, 109 | typename fct::allocator_type::template rebind< std::pair< int const, function< void() > > >::other > m( pool_alloc< char >{ 3 } ); 110 | 111 | #if __clang__ || __GNUC__ >= 5 112 | m[ 42 ].assign( fv, m.get_allocator() ); 113 | #else 114 | m.insert( std::make_pair( 42, fv ) ); 115 | #endif 116 | std::size_t pre = pool[ 3 ]; 117 | pool_string{ pool_alloc< char >{ 3 } }.swap( m[ 42 ].target< stateful_op >()->state ); 118 | assert ( pre - pool[ 3 ] == op.state.capacity() + 1 ); 119 | } 120 | assert ( fc2.target< stateful_op >() ); 121 | fc2 = listful_op( fc2.target< stateful_op >()->state, pool_alloc< char >{ 2 } ); 122 | fc1 = fc2; 123 | #if ! _MSC_VER || _MSC_VER >= 1910 124 | fv = nullptr; 125 | #else 126 | fv.operator = < std::nullptr_t >( nullptr ); 127 | #endif 128 | 129 | assert ( pool[ 1 ] == pool[ 2 ] ); 130 | } 131 | -------------------------------------------------------------------------------- /test/swap.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | 4 | using namespace cxx_function; 5 | 6 | struct vf { 7 | int operator () () { return m; } 8 | const int m; 9 | }; 10 | 11 | 12 | int main() { 13 | function< int() > f = vf{ 3 }, g = vf{ 10 }; 14 | assert ( f.target< vf >()->m == 3 ); 15 | f.swap( g ); 16 | assert ( f.target< vf >()->m == 10 ); 17 | 18 | assert ( f() == 10 ); 19 | assert ( g() == 3 ); 20 | } 21 | -------------------------------------------------------------------------------- /test/target_access.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | 4 | using namespace cxx_function; 5 | 6 | int * alias; 7 | 8 | struct vf { 9 | virtual void operator() ( int ) {} 10 | int m = 5; 11 | 12 | vf() = default; 13 | vf( vf const & ) noexcept { alias = & m; } 14 | }; 15 | 16 | 17 | int main() { 18 | function< void( int ) > f = vf(); 19 | * alias = 3; 20 | assert ( f.target_type() == typeid (vf) ); 21 | assert ( f.target< vf >()->m == 3 ); 22 | assert ( function< void() >().target_type() == typeid (void) ); 23 | assert ( function< void() >().target< void >() == nullptr ); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /test/usernew.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | #include 3 | #include 4 | 5 | bool intercepted = false; 6 | 7 | template< typename t > 8 | void * operator new( std::size_t n, t * p ) { 9 | intercepted = true; 10 | return operator new( n, (void*) p ); 11 | } 12 | 13 | int main() { 14 | std::array< int, 100 > arr; 15 | 16 | cxx_function::function< void() > q = [arr] () mutable { ++ arr[ 0 ]; }; 17 | assert ( ! intercepted ); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /test/volatile.cpp: -------------------------------------------------------------------------------- 1 | #include "cxx_function.hpp" 2 | 3 | #include 4 | 5 | using namespace cxx_function; 6 | 7 | int q = 0; 8 | 9 | struct fs { 10 | void operator () () { q = 1; } 11 | void operator () () volatile { ++ q; } 12 | }; 13 | 14 | int main() { 15 | function< void(), void() volatile > f = fs{}; 16 | auto volatile g = f; 17 | 18 | f(); 19 | assert ( q == 1 ); 20 | g(); 21 | assert ( q == 2 ); 22 | 23 | #if __cplusplus >= 201402 24 | recover< fs >( g ) (); 25 | assert ( q == 3 ); 26 | #endif 27 | } 28 | --------------------------------------------------------------------------------