KiCad PCB EDA Suite
Loading...
Searching...
No Matches
inplace_function.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifndef INPLACE_FUNCTION_H
21#define INPLACE_FUNCTION_H
22
23#include <cstddef>
24#include <functional>
25#include <new>
26#include <type_traits>
27#include <utility>
28
41template <typename Sig, std::size_t Capacity = 4 * sizeof( void* )>
43
44template <typename R, typename... Args, std::size_t Capacity>
45class INPLACE_FUNCTION<R( Args... ), Capacity>
46{
47public:
48 INPLACE_FUNCTION() = default;
49
50 template <typename F,
51 typename = std::enable_if_t<
52 !std::is_same_v<std::decay_t<F>, INPLACE_FUNCTION>
53 && std::is_invocable_r_v<R, const std::decay_t<F>&, Args...>>>
54 INPLACE_FUNCTION( F&& aCallable )
55 {
56 assign( std::forward<F>( aCallable ) );
57 }
58
59 INPLACE_FUNCTION( const INPLACE_FUNCTION& aOther ) { copyFrom( aOther ); }
60 INPLACE_FUNCTION( INPLACE_FUNCTION&& aOther ) noexcept { moveFrom( aOther ); }
61
63 {
64 if( this != &aOther )
65 {
66 // Build the copy before tearing down the old target so a throwing copy
67 // leaves *this unchanged; the move back in is noexcept (see assign()).
68 INPLACE_FUNCTION tmp( aOther );
69 reset();
70 moveFrom( tmp );
71 }
72
73 return *this;
74 }
75
77 {
78 if( this != &aOther )
79 {
80 reset();
81 moveFrom( aOther );
82 }
83
84 return *this;
85 }
86
87 template <typename F,
88 typename = std::enable_if_t<
89 !std::is_same_v<std::decay_t<F>, INPLACE_FUNCTION>
90 && std::is_invocable_r_v<R, const std::decay_t<F>&, Args...>>>
92 {
93 INPLACE_FUNCTION tmp( std::forward<F>( aCallable ) );
94 reset();
95 moveFrom( tmp );
96 return *this;
97 }
98
100
101 explicit operator bool() const { return m_invoke != nullptr; }
102
103 R operator()( Args... aArgs ) const
104 {
105 if( !m_invoke )
106 throw std::bad_function_call();
107
108 return m_invoke( &m_storage, std::forward<Args>( aArgs )... );
109 }
110
111private:
112 enum class OP { DESTROY, MOVE, COPY };
113
114 using INVOKE_FN = R ( * )( const void*, Args&&... );
115 using MANAGE_FN = void ( * )( OP, void*, const void* );
116
117 template <typename F>
118 void assign( F&& aCallable )
119 {
120 using DECAYED = std::decay_t<F>;
121 static_assert( sizeof( DECAYED ) <= Capacity,
122 "callable too large for INPLACE_FUNCTION; raise its Capacity argument" );
123 static_assert( alignof( DECAYED ) <= alignof( std::max_align_t ), "callable over-aligned" );
124 static_assert( std::is_nothrow_move_constructible_v<DECAYED>,
125 "callable must be nothrow move constructible for INPLACE_FUNCTION" );
126
127 ::new( &m_storage ) DECAYED( std::forward<F>( aCallable ) );
128
129 m_invoke = []( const void* aStorage, Args&&... aArgs ) -> R
130 {
131 return ( *static_cast<const DECAYED*>( aStorage ) )( std::forward<Args>( aArgs )... );
132 };
133
134 m_manage = []( OP aOp, void* aDst, const void* aSrc )
135 {
136 switch( aOp )
137 {
138 case OP::DESTROY:
139 static_cast<DECAYED*>( aDst )->~DECAYED();
140 break;
141 case OP::MOVE:
142 ::new( aDst ) DECAYED( std::move( *const_cast<DECAYED*>( static_cast<const DECAYED*>( aSrc ) ) ) );
143 break;
144 case OP::COPY:
145 ::new( aDst ) DECAYED( *static_cast<const DECAYED*>( aSrc ) );
146 break;
147 }
148 };
149 }
150
151 void reset()
152 {
153 if( m_manage )
154 {
155 m_manage( OP::DESTROY, &m_storage, nullptr );
156 m_manage = nullptr;
157 m_invoke = nullptr;
158 }
159 }
160
162 {
163 if( aOther.m_manage )
164 {
165 aOther.m_manage( OP::MOVE, &m_storage, &aOther.m_storage );
166 m_invoke = aOther.m_invoke;
167 m_manage = aOther.m_manage;
168 aOther.reset();
169 }
170 }
171
172 // The COPY path keeps INPLACE_FUNCTION (and anything embedding it, e.g. VALUE) copyable;
173 // the stored callable must therefore be copy-constructible, as std::function's target is.
174 void copyFrom( const INPLACE_FUNCTION& aOther )
175 {
176 if( aOther.m_manage )
177 {
178 aOther.m_manage( OP::COPY, &m_storage, &aOther.m_storage );
179 m_invoke = aOther.m_invoke;
180 m_manage = aOther.m_manage;
181 }
182 }
183
184 alignas( std::max_align_t ) unsigned char m_storage[Capacity];
187};
188
189#endif // INPLACE_FUNCTION_H
INPLACE_FUNCTION & operator=(const INPLACE_FUNCTION &aOther)
INPLACE_FUNCTION(INPLACE_FUNCTION &&aOther) noexcept
INPLACE_FUNCTION(const INPLACE_FUNCTION &aOther)
R(*)(const void *, Args &&...) INVOKE_FN
void(*)(OP, void *, const void *) MANAGE_FN
void copyFrom(const INPLACE_FUNCTION &aOther)
INPLACE_FUNCTION & operator=(F &&aCallable)
void moveFrom(INPLACE_FUNCTION &aOther)
INPLACE_FUNCTION & operator=(INPLACE_FUNCTION &&aOther) noexcept
A std::function-style callable wrapper that stores the target in a fixed inline buffer and never allo...
#define F(x, y, z)
Definition md5_hash.cpp:15