├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── fixed_size_function.hpp ├── fixed_size_function.sln ├── fixed_size_function.vcxproj ├── fixed_size_function.vcxproj.filters └── tests.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # VC++ files 2 | Debug/ 3 | Release/ 4 | *.suo 5 | *.vcproj 6 | *.vcxproj 7 | *.vcxproj.user 8 | *.vcxproj.filters 9 | UpgradeLog*.XML 10 | _UpgradeReport_Files/ 11 | ipch/ 12 | *.sdf 13 | *.opensdf 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.4) 2 | project(fixed_size_function) 3 | 4 | set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -std=c++0x") 5 | set(SOURCE_FILES tests.cpp) 6 | add_executable(fixed_size_function ${SOURCE_FILES}) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Pavel Medvedev 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fixed size function 2 | =================== 3 | 4 | Fixed size function wrapper like [std::function](http://en.cppreference.com/w/cpp/utility/functional/function) to avoid dynamic memory allocation. 5 | 6 | Usage: 7 | 8 | ```c++ 9 | #include "fixed_size_function.hpp" 10 | 11 | int f(int a) 12 | { 13 | std::cout << __FUNCTION__ << "\n"; 14 | return a; 15 | } 16 | 17 | int g(int a, int b) 18 | { 19 | std::cout << __FUNCTION__ << "\n"; 20 | return a; 21 | } 22 | 23 | struct X 24 | { 25 | int operator()(int a) const 26 | { 27 | std::cout << __FUNCTION__ << "\n"; 28 | return a; 29 | } 30 | 31 | int mem_fun(int a) 32 | { 33 | std::cout << __FUNCTION__ << "\n"; 34 | return a; 35 | } 36 | }; 37 | 38 | int main() 39 | { 40 | fixed_size_function fun; 41 | 42 | // Functor 43 | X x; 44 | fun = x; 45 | fun(1); 46 | 47 | // Free function 48 | fun = f; 49 | fun(2); 50 | 51 | // Bind function 52 | fun = std::bind(g, std::placeholders::_1, 0); 53 | fun(3); 54 | 55 | // Bind member function 56 | fun = std::bind(&X::mem_fun, x, std::placeholders::_1); 57 | fun(4); 58 | 59 | // Lambda 60 | fun = [](int a) -> int 61 | { 62 | std::cout << __FUNCTION__ << "\n"; 63 | return a; 64 | }; 65 | fun(5); 66 | 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /fixed_size_function.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014-2016 Pavel Medvedev. All rights reserved. 3 | // 4 | // Distributed under the MIT software license, see the accompanying 5 | // file LICENSE 6 | 7 | #ifndef FIXED_SIZE_FUNCTION_HPP_INCLUDED 8 | #define FIXED_SIZE_FUNCTION_HPP_INCLUDED 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | enum class construct_type { 16 | none, 17 | copy, 18 | move, 19 | copy_and_move, 20 | }; 21 | 22 | namespace details { 23 | 24 | // V-table implementation 25 | template 26 | struct fixed_function_vtable_base 27 | { 28 | Ret (*call)(void*, Args&& ...) = nullptr; 29 | void (*destroy)(void*) = nullptr; 30 | }; 31 | 32 | template 33 | struct fixed_function_vtable; 34 | 35 | template 36 | struct fixed_function_vtable 37 | : fixed_function_vtable_base 38 | { 39 | }; 40 | 41 | template 42 | struct fixed_function_vtable 43 | : fixed_function_vtable_base 44 | { 45 | void (*copy)(const void*, void*) = nullptr; 46 | }; 47 | 48 | template 49 | struct fixed_function_vtable 50 | : fixed_function_vtable_base 51 | { 52 | void (*move)(void*, void*) = nullptr; 53 | }; 54 | 55 | template 56 | struct fixed_function_vtable 57 | : fixed_function_vtable_base 58 | { 59 | void (*copy)(const void*, void*) = nullptr; 60 | void (*move)(void*, void*) = nullptr; 61 | }; 62 | 63 | } // namespace details 64 | 65 | template 66 | class fixed_size_function; 67 | 68 | template 69 | class fixed_size_function 70 | { 71 | public: 72 | // Compile-time information 73 | 74 | using is_copyable = std::integral_constant; 76 | using is_movable = std::integral_constant; 78 | 79 | using result_type = Ret; 80 | 81 | static const std::size_t arity = sizeof...(Args); 82 | 83 | template 84 | struct argument 85 | { 86 | static_assert(N < arity, "invalid argument index"); 87 | using type = typename std::tuple_element>::type; 88 | }; 89 | 90 | public: 91 | template fixed_size_function(fixed_size_function const&) = delete; 92 | template fixed_size_function(fixed_size_function&) = delete; 93 | template fixed_size_function(fixed_size_function&&) = delete; 94 | template fixed_size_function& operator=(fixed_size_function const&) = delete; 95 | template fixed_size_function& operator=(fixed_size_function&) = delete; 96 | template fixed_size_function& operator=(fixed_size_function&&) = delete; 97 | template void assign(fixed_size_function const&) = delete; 98 | template void assign(fixed_size_function&) = delete; 99 | template void assign(fixed_size_function&&) = delete; 100 | 101 | fixed_size_function() {} 102 | 103 | ~fixed_size_function() 104 | { 105 | reset(); 106 | } 107 | 108 | fixed_size_function(std::nullptr_t) {} 109 | 110 | fixed_size_function& operator=(std::nullptr_t) 111 | { 112 | reset(); 113 | return *this; 114 | } 115 | 116 | fixed_size_function(fixed_size_function const& src) 117 | { 118 | copy(src); 119 | } 120 | 121 | fixed_size_function& operator=(fixed_size_function const& src) 122 | { 123 | assign(src); 124 | return *this; 125 | } 126 | 127 | fixed_size_function(fixed_size_function& src) 128 | { 129 | copy(src); 130 | } 131 | 132 | fixed_size_function& operator=(fixed_size_function& src) 133 | { 134 | assign(src); 135 | return *this; 136 | } 137 | 138 | fixed_size_function(fixed_size_function&& src) 139 | { 140 | move(std::move(src), is_movable()); 141 | } 142 | 143 | fixed_size_function& operator=(fixed_size_function&& src) 144 | { 145 | assign(std::move(src)); 146 | return *this; 147 | } 148 | 149 | template 150 | fixed_size_function(Functor&& f) 151 | { 152 | create(std::forward(f)); 153 | } 154 | 155 | template 156 | fixed_size_function& operator=(Functor&& f) 157 | { 158 | assign(std::forward(f)); 159 | return *this; 160 | } 161 | 162 | void assign(fixed_size_function const& src) 163 | { 164 | reset(); 165 | copy(src); 166 | } 167 | 168 | void assign(fixed_size_function& src) 169 | { 170 | reset(); 171 | copy(src); 172 | } 173 | 174 | void assign(fixed_size_function&& src) 175 | { 176 | reset(); 177 | move(std::move(src), is_movable()); 178 | } 179 | 180 | template 181 | void assign(Functor&& f) 182 | { 183 | reset(); 184 | create(std::forward(f)); 185 | } 186 | 187 | void reset() 188 | { 189 | auto destroy = vtable_.destroy; 190 | if (destroy) 191 | { 192 | vtable_ = vtable(); 193 | destroy(&storage_); 194 | } 195 | } 196 | 197 | explicit operator bool() const { return vtable_.call != nullptr; } 198 | 199 | Ret operator()(Args... args) 200 | { 201 | return vtable_.call ? vtable_.call(&storage_, std::forward(args)...) : throw std::bad_function_call(); 202 | } 203 | 204 | void swap(fixed_size_function& other) 205 | { 206 | fixed_size_function tmp = std::move(other); 207 | other = std::move(*this); 208 | *this = std::move(tmp); 209 | } 210 | 211 | friend void swap(fixed_size_function& lhs, fixed_size_function& rhs) 212 | { 213 | lhs.swap(rhs); 214 | } 215 | 216 | friend bool operator==(std::nullptr_t, fixed_size_function const& f) 217 | { 218 | return !f; 219 | } 220 | 221 | friend bool operator==(fixed_size_function const& f, std::nullptr_t) 222 | { 223 | return !f; 224 | } 225 | 226 | friend bool operator!=(std::nullptr_t, fixed_size_function const& f) 227 | { 228 | return f; 229 | } 230 | 231 | friend bool operator!=(fixed_size_function const& f, std::nullptr_t) 232 | { 233 | return f; 234 | } 235 | 236 | private: 237 | template 238 | void create(Functor&& f) 239 | { 240 | using functor_type = typename std::decay::type; 241 | static_assert(sizeof(functor_type) <= StorageSize, "Functor must be smaller than storage buffer"); 242 | 243 | new (&storage_) functor_type(std::forward(f)); 244 | 245 | vtable_.call = &call_impl; 246 | vtable_.destroy = &destroy_impl; 247 | init_copy(is_copyable()); 248 | init_move(is_movable()); 249 | } 250 | 251 | void copy(fixed_size_function const& src) 252 | { 253 | if (src.vtable_.copy) 254 | { 255 | src.vtable_.copy(&src.storage_, &storage_); 256 | vtable_ = src.vtable_; 257 | } 258 | } 259 | 260 | void move(fixed_size_function&& src, std::true_type movable) 261 | { 262 | if (src.vtable_.move) 263 | { 264 | src.vtable_.move(&src.storage_, &storage_); 265 | vtable_ = src.vtable_; 266 | src.reset(); 267 | } 268 | } 269 | 270 | void move(fixed_size_function const& src, std::false_type movable) 271 | { 272 | copy(src); 273 | } 274 | 275 | private: 276 | template 277 | static Ret call_impl(void* functor, Args&& ... args) 278 | { 279 | return (*static_cast(functor))(std::forward(args)...); 280 | } 281 | 282 | template 283 | static void destroy_impl(void* functor) 284 | { 285 | static_cast(functor)->~Functor(); 286 | } 287 | 288 | template 289 | static void copy_impl(void const* functor, void* dest) 290 | { 291 | new (dest) Functor(*static_cast(functor)); 292 | } 293 | 294 | template 295 | static void move_impl(void* functor, void* dest) 296 | { 297 | new (dest) Functor(std::move(*static_cast(functor))); 298 | } 299 | 300 | template 301 | void init_copy(std::true_type /*copyable*/) { vtable_.copy = ©_impl; } 302 | 303 | template 304 | void init_copy(std::false_type /*copyable*/) {} 305 | 306 | template 307 | void init_move(std::true_type /*movable*/) { vtable_.move = &move_impl; } 308 | 309 | template 310 | void init_move(std::false_type /*movable*/) {} 311 | 312 | private: 313 | using vtable = details::fixed_function_vtable; 314 | static const size_t StorageSize = MaxSize - sizeof(vtable); 315 | using storage = typename std::aligned_storage::type; 316 | 317 | vtable vtable_; 318 | storage storage_; 319 | }; 320 | 321 | #endif // FIXED_SIZE_FUNCTION_HPP_INCLUDED 322 | -------------------------------------------------------------------------------- /fixed_size_function.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Express 2013 for Windows Desktop 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fixed_size_function", "fixed_size_function.vcxproj", "{EC69A9E5-FB0C-4907-BC12-73B82F099D75}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EC69A9E5-FB0C-4907-BC12-73B82F099D75}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {EC69A9E5-FB0C-4907-BC12-73B82F099D75}.Debug|Win32.Build.0 = Debug|Win32 16 | {EC69A9E5-FB0C-4907-BC12-73B82F099D75}.Release|Win32.ActiveCfg = Release|Win32 17 | {EC69A9E5-FB0C-4907-BC12-73B82F099D75}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /fixed_size_function.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {EC69A9E5-FB0C-4907-BC12-73B82F099D75} 15 | Win32Proj 16 | zz 17 | 18 | 19 | 20 | Application 21 | true 22 | Unicode 23 | v120 24 | 25 | 26 | Application 27 | false 28 | true 29 | Unicode 30 | v120 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | 48 | 49 | 50 | 51 | 52 | Level3 53 | Disabled 54 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 55 | 56 | 57 | Console 58 | true 59 | 60 | 61 | 62 | 63 | Level3 64 | 65 | 66 | MaxSpeed 67 | true 68 | true 69 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 70 | 71 | 72 | Console 73 | true 74 | true 75 | true 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /fixed_size_function.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fixed_size_function.hpp" 4 | 5 | int f(int a) 6 | { 7 | std::cout << a << ' ' << __FUNCTION__ << '\n'; 8 | return a; 9 | } 10 | 11 | int g(int a, int b) 12 | { 13 | std::cout << a << ' ' << __FUNCTION__ << '\n'; 14 | return a; 15 | } 16 | 17 | struct X 18 | { 19 | X() 20 | { 21 | //std::cout << "X::X()\n"; 22 | } 23 | 24 | X(X const&) 25 | { 26 | //std::cout << "X::X(X const&)\n"; 27 | } 28 | 29 | X& operator=(X const&) 30 | { 31 | //std::cout << "X::operator=(X const&)\n"; 32 | return *this; 33 | } 34 | 35 | X(X&&) 36 | { 37 | //std::cout << "X::X(X&&)\n"; 38 | } 39 | 40 | X& operator=(X&&) 41 | { 42 | //std::cout << "X::operator=(X&&)\n"; 43 | return *this; 44 | } 45 | 46 | ~X() 47 | { 48 | //std::cout << "X::~X()\n"; 49 | } 50 | 51 | int operator()(int a) const 52 | { 53 | std::cout << a << ' ' << __FUNCTION__ << '\n'; 54 | return a; 55 | } 56 | 57 | int mem_fun(int a) 58 | { 59 | std::cout << a << ' ' << __FUNCTION__ << '\n'; 60 | return a; 61 | } 62 | }; 63 | 64 | #define ensure(condition) if (!(condition)) { std::cerr << __FILE__ << ':' << __LINE__ << ' ' << #condition << " failed\n"; } 65 | 66 | int main() 67 | { 68 | using function = fixed_size_function; 69 | 70 | X x; 71 | 72 | // Default ctor 73 | function fun0; 74 | ensure(!fun0); 75 | 76 | // Template ctors 77 | function fun(x), fun2(f); 78 | ensure(fun(1) == 1); 79 | ensure(fun2(2) == 2); 80 | 81 | // Copy ctor 82 | function fun3(fun); 83 | ensure(fun3(3) == 3); 84 | 85 | // Move 86 | fun2 = std::move(fun); 87 | ensure(fun2(4) == 4); 88 | ensure(!fun); 89 | 90 | // Assign function 91 | fun = fun2; 92 | ensure(fun); 93 | ensure(fun2); 94 | 95 | // Swap 96 | fun.reset(); 97 | fun2.swap(fun); 98 | ensure(fun && !fun2); 99 | 100 | // Assign free function 101 | fun = f; 102 | ensure(fun(5) == 5); 103 | 104 | // Bind free function 105 | fun = std::bind(g, std::placeholders::_1, 0); 106 | ensure(fun(6) == 6); 107 | 108 | // Bind member function 109 | fun = std::bind(&X::mem_fun, x, std::placeholders::_1); 110 | ensure(fun(7) == 7); 111 | 112 | // Lambda 113 | fun = [](int a) -> int 114 | { 115 | std::cout << a << ' ' << __FUNCTION__ << '\n'; 116 | return a; 117 | }; 118 | ensure(fun(8) == 8); 119 | 120 | // Reset 121 | fun = nullptr; 122 | fun3.reset(); 123 | ensure(!fun && !fun3); 124 | 125 | // std::bad_function_call exception on empty function call 126 | bool bad_call_catched = false; 127 | if (fun == nullptr) 128 | { 129 | try 130 | { 131 | fun(0); 132 | } 133 | catch (std::bad_function_call const&) 134 | { 135 | bad_call_catched = true; 136 | } 137 | } 138 | ensure(bad_call_catched); 139 | } 140 | --------------------------------------------------------------------------------