-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathufunction.hpp
More file actions
155 lines (140 loc) · 5.66 KB
/
ufunction.hpp
File metadata and controls
155 lines (140 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// itlib-ufunction v1.03
//
// Unique Function
// Non-copyable and noexcept move-constructible replacement for std::function
// Similar to C++23's move_only_function
//
// SPDX-License-Identifier: MIT
// MIT License:
// Copyright(c) 2020-2026 Borislav Stanimirov
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files(the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and / or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions :
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
// VERSION HISTORY
//
// 1.03 (2026-01-15) Move assignment from nullptr_t to the template
// assignment overload to allow the `ufunc = {}` syntax
// 1.02 (2024-09-24) Allow binding to copies of source functions as per C++23
// 1.01 (2022-09-23) Allow ufunction from a free function
// 1.00 (2020-10-15) Initial release
//
//
// DOCUMENTATION
//
// Simply include this file wherever you need.
// It defines the class itlib::ufunction. It can serve as a replacement
// of std::function, but it differs in the following two ways:
//
// 1. itlib::ufunction is not copyable. Thus you can capture non-copyable
// values from the outside world, like std::unique_ptr, or wrap other
// non-copyable function objects.
// 2. itlib::ufunction is noexcept move-constructible, thus vectors of
// ufunction won't copy when expanded and structures with ufunction members
// will not be implicitly no-noexcept move-constructuble
//
// You can use itlib::ufunction in most places where you would use
// std::function as long as you don't copy it
//
// ufunction is essentially equivalent to std::move_only_function from C++23
//
// Example:
//
// std::unique_ptr<foo> fp;
// itlib::ufunction<void()> = [captured = std::move(fp)]() { ... }
//
//
// TESTS
//
// You can find unit tests in the official repo:
// https://github.com/iboB/itlib/blob/master/test/
//
#pragma once
#include <functional>
#include <type_traits>
namespace itlib
{
template <typename F>
class ufunction : private std::function<F>
{
using function = std::function<F>;
public:
ufunction() noexcept = default;
ufunction(std::nullptr_t) noexcept : function(nullptr) {}
ufunction(const ufunction&) = delete;
ufunction operator=(const ufunction&) = delete;
ufunction(ufunction&&) noexcept = default;
ufunction& operator=(ufunction&&) noexcept = default;
template <typename FO>
ufunction(FO f) noexcept : function(copy_wrapper<FO>{std::move(f)}) {}
// this also servers to handle ufunc = nullptr_t
// we use it instead of a separate assignment overload for nullptr_t, to allow us to write
// we can write `ufunc = {}` which is a nice syntax for resetting
// `ufunc = {}` will resolve to operator=(F* fptr) which is a tiny bit slower than assigning
// nullptr_t directly (because of an additional if check) but we consider it negligible
// and worth the syntactic sugar
template <typename FO>
ufunction& operator=(FO f) noexcept
{
function::operator=(
typename std::conditional<
std::is_same<FO, std::nullptr_t>::value,
std::nullptr_t,
copy_wrapper<FO>
>::type(std::move(f))
);
return *this;
}
// function pointer overloads (otherwise clang and gcc complain for const_cast of function pointers)
// noexcept since we're relying on the small function optimization to kick in here
// we can also afford to disregard the copy wrapper here since function pointers are copyable
ufunction(F* fptr) noexcept : function(fptr) {}
ufunction& operator=(F* fptr) noexcept
{
function::operator=(fptr);
return *this;
}
using function::operator bool;
using function::operator();
private:
template <typename FO>
struct copy_wrapper
{
static_assert(!std::is_const<FO>::value, "Cannot bind to a const function");
FO func_object;
copy_wrapper(FO&& f) : func_object(std::move(f)) {}
// we need these copy ops in order for our parent std::function to compile, but they will never be called
copy_wrapper(const copy_wrapper& other) : func_object(const_cast<FO&&>(other.func_object)) { throw 0; } // should never get to here
copy_wrapper& operator=(const copy_wrapper&) { throw 0; } // should never get to here
// not `= default` so we can force noexcept
copy_wrapper(copy_wrapper&& other) noexcept : func_object(std::move(other.func_object)) {}
copy_wrapper& operator=(copy_wrapper&& other) noexcept
{
func_object = std::move(other.func_object);
return *this;
}
template <typename... Args>
auto operator()(Args&&... args) -> decltype(func_object(std::forward<Args>(args)...))
{
return func_object(std::forward<Args>(args)...);
}
};
};
}