├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── doc ├── delegate.md └── mem_fkn.md ├── include └── delegate │ └── delegate.hpp ├── local_build.sh └── test └── delegate_test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: Empty 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: TopLevel 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Allman 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | ColumnLimit: 80 40 | CommentPragmas: '^ IWYU pragma:' 41 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 42 | ConstructorInitializerIndentWidth: 4 43 | ContinuationIndentWidth: 4 44 | Cpp11BracedListStyle: true 45 | DerivePointerAlignment: false 46 | DisableFormat: false 47 | ExperimentalAutoDetectBinPacking: false 48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 49 | IncludeCategories: 50 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 51 | Priority: 2 52 | - Regex: '^(<|"(gtest|isl|json)/)' 53 | Priority: 3 54 | - Regex: '.*' 55 | Priority: 1 56 | IndentCaseLabels: false 57 | IndentWidth: 4 58 | IndentWrappedFunctionNames: false 59 | KeepEmptyLinesAtTheStartOfBlocks: true 60 | MacroBlockBegin: '' 61 | MacroBlockEnd: '' 62 | MaxEmptyLinesToKeep: 1 63 | NamespaceIndentation: None 64 | ObjCBlockIndentWidth: 2 65 | ObjCSpaceAfterProperty: false 66 | ObjCSpaceBeforeProtocolList: true 67 | PenaltyBreakBeforeFirstCallParameter: 19 68 | PenaltyBreakComment: 300 69 | PenaltyBreakFirstLessLess: 120 70 | PenaltyBreakString: 1000 71 | PenaltyExcessCharacter: 1000000 72 | PenaltyReturnTypeOnItsOwnLine: 60 73 | PointerAlignment: Left 74 | ReflowComments: true 75 | SortIncludes: true 76 | SpaceAfterCStyleCast: false 77 | SpaceBeforeAssignmentOperators: true 78 | SpaceBeforeParens: ControlStatements 79 | SpaceInEmptyParentheses: false 80 | SpacesBeforeTrailingComments: 1 81 | SpacesInAngles: false 82 | SpacesInContainerLiterals: true 83 | SpacesInCStyleCastParentheses: false 84 | SpacesInParentheses: false 85 | SpacesInSquareBrackets: false 86 | Standard: Cpp11 87 | TabWidth: 4 88 | UseTab: Never 89 | ... 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Emacs. 35 | *~ 36 | .cproject 37 | .project 38 | .settings 39 | 40 | .idea 41 | cmake-build-* 42 | 43 | build* 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project (delegate) 4 | 5 | add_library(delegate INTERFACE) 6 | 7 | target_sources(delegate INTERFACE ${CMAKE_SOURCE_DIR}/include/delegate/delegate.hpp) 8 | 9 | target_include_directories(delegate INTERFACE include/) 10 | 11 | 12 | # Testing 13 | 14 | set(PICKY_FLAGS -Wall -Wextra -Wstrict-aliasing -pedantic -Werror -Wunreachable-code -Wcast-align -Wcast-qual -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wno-unused -Wno-variadic-macros -Wno-parentheses -fdiagnostics-show-option 15 | ) 16 | 17 | enable_testing() 18 | 19 | # Note: If you have trouble locating gtest, try: 20 | # export GTEST_ROOT= 21 | # In Ubuntu, gtest source is installed under /usr/src/gtest. 22 | # build it and set GTEST_ROOT to the build folder. 23 | find_package(GTest REQUIRED) 24 | 25 | add_test(del_11 delegate_11) 26 | add_test(del_14 delegate_14) 27 | add_test(del_17 delegate_17) 28 | 29 | add_executable(delegate_11 test/delegate_test.cpp) 30 | add_executable(delegate_14 test/delegate_test.cpp) 31 | add_executable(delegate_17 test/delegate_test.cpp) 32 | 33 | target_compile_options(delegate_11 PRIVATE -std=c++11 ${PICKY_FLAGS}) 34 | target_compile_options(delegate_14 PRIVATE -std=c++14 ${PICKY_FLAGS}) 35 | target_compile_options(delegate_17 PRIVATE -std=c++17 ${PICKY_FLAGS}) 36 | 37 | # The following is normally the recommended way, but will give gnu extensions. 38 | #set_property(TARGET delegate_11 PROPERTY CXX_STANDARD 11) 39 | #set_property(TARGET delegate_14 PROPERTY CXX_STANDARD 14) 40 | #set_property(TARGET delegate_17 PROPERTY CXX_STANDARD 17) 41 | 42 | target_link_libraries(delegate_11 PRIVATE delegate GTest::GTest GTest::Main) 43 | target_link_libraries(delegate_14 PRIVATE delegate GTest::GTest GTest::Main) 44 | target_link_libraries(delegate_17 PRIVATE delegate GTest::GTest GTest::Main) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mikael Rosbacke 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: run_test 2 | 3 | 4 | PICKY_FLAGS:=-Wall -Wextra -Wstrict-aliasing -pedantic -Werror -Wunreachable-code -Wcast-align -Wcast-qual -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wno-unused -Wno-variadic-macros -Wno-parentheses -fdiagnostics-show-option 5 | PICKY_FLAGS_G:=-Wstrict-null-sentinel -Wnoexcept -Wlogical-op 6 | 7 | INC_FLAGS:= -Iinclude -I/usr/src/gtest/include 8 | LIB_FLAGS:= -L/usr/src/gtest -lgtest -lgtest_main -pthread 9 | CXX_FLAGS=-Wall -pedantic $(PICKY_FLAGS) 10 | 11 | GCC_FLAGS:=$(PICKY_FLAGS_G) 12 | 13 | 14 | .PHONY: clean 15 | clean: 16 | rm delegate_test_11_g.out delegate_test_14_g.out delegate_test_17_g.out 17 | rm delegate_test_11_c.out delegate_test_14_c.out delegate_test_17_c.out 18 | 19 | delegate_test_11.out: test/delegate_test.cpp include/delegate/delegate.hpp 20 | g++ -std=c++11 $(CXX_FLAGS) $(INC_FLAGS) $(GCC_FLAGS) -o delegate_test_11_g.out -Iinclude test/delegate_test.cpp $(LIB_FLAGS) 21 | g++ -std=c++14 $(CXX_FLAGS) $(INC_FLAGS) $(GCC_FLAGS) -o delegate_test_14_g.out -Iinclude test/delegate_test.cpp $(LIB_FLAGS) 22 | g++ -std=c++17 $(CXX_FLAGS) $(INC_FLAGS) $(GCC_FLAGS) -o delegate_test_17_g.out -Iinclude test/delegate_test.cpp $(LIB_FLAGS) 23 | clang++ -std=c++11 $(CXX_FLAGS) $(INC_FLAGS) -o delegate_test_11_c.out -Iinclude test/delegate_test.cpp $(LIB_FLAGS) 24 | clang++ -std=c++14 $(CXX_FLAGS) $(INC_FLAGS) -o delegate_test_14_c.out -Iinclude test/delegate_test.cpp $(LIB_FLAGS) 25 | clang++ -std=c++17 $(CXX_FLAGS) $(INC_FLAGS) -o delegate_test_17_c.out -Iinclude test/delegate_test.cpp $(LIB_FLAGS) 26 | 27 | run_test: delegate_test_11.out 28 | ./delegate_test_11_g.out && ./delegate_test_14_g.out && ./delegate_test_17_g.out && ./delegate_test_11_c.out && ./delegate_test_14_c.out && ./delegate_test_17_c.out 29 | 30 | 31 | .PHONY: format 32 | format: 33 | clang-format-6.0 -i include/delegate/delegate.hpp 34 | clang-format-6.0 -i test/delegate_test.cpp 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # delegate 2 | 3 | Embedded friendly std::function alternative. Never heap alloc, no exceptions. Is 4 | trivially_copyable. 5 | 6 | ## Overview 7 | 8 | The 'delegate' is an include only embedded friendly alternative to std::function. 9 | The main purpose is to store callable things such as free functions, member functions, 10 | and functors. Once stored, the delegate can be called without knowledge of the 11 | type of stored thing. 12 | 13 | The delegate guarantees no heap allocation. In will never throw exceptions itself. 14 | Intended use is as general callback storage (think function pointer analog). 15 | The price to pay is that the delegate only stores a pointer to referenced functor 16 | objects or objects to call member functions on. 17 | The user needs to handle the lifetime of a referred object. 18 | 19 | In addition, the delegation object has a smaller footprint compared to common std::function 20 | implementations, using only 2 pointers (free function pointer and void pointer). 21 | This is small enough so that a delegate stored inside a std::function will use 22 | small object optimization on both clang and gcc. 23 | 24 | It avoids virtual dispatch internally. That results in small object code footprint and 25 | allows the optimizer to see through part of the call. This also means it fulfills the requirements 26 | for being trivially_copyable, meaning it can safely be memcpy:d between objects. 27 | See e.g. [documentation on trivially copyable](https://en.cppreference.com/w/cpp/types/is_trivially_copyable). 28 | 29 | A video with a presentation about motivation and examples about the used 30 | call mechanism can be [found at reddit](https://www.reddit.com/r/cpp/comments/a6yg5g/mikael_rosbacke_embedded_friendly_stdfunction/). It is given for an earlier version, but the major parts are still valid. The presentation is [here](https://slides.com/mikaelrosbacke/delegates#/). 31 | 32 | Detailed [delegate API documentation is here](doc/delegate.md). 33 | 34 | ## Quick start 35 | - clone the repo. 36 | - Add include path /include 37 | - In program '#include "delegate/delegate.hpp"' 38 | - Use the 'delegate' template class. 39 | 40 | Example use case: 41 | 42 | #include "delegate/delegate.hpp" 43 | 44 | struct Example { 45 | static void staticFkn(int) {}; 46 | void memberFkn(int) {}; 47 | }; 48 | 49 | int main() 50 | { 51 | Example e; 52 | delegate del; 53 | 54 | del.set<&Example::memberFkn>(e); // C++17 55 | del.set(e); // C++11,C++14 56 | del(123); 57 | 58 | del.set(); 59 | del(456); 60 | } 61 | 62 | 63 | ## Design goals driving the design 64 | 65 | * Should behave close to a normal function pointer. 66 | Small, efficient, no heap allocation, no exceptions. 67 | Does not keep track of referred object lifetime. 68 | 69 | * Implement type erasure. The delegate needs to only contain 70 | enough type information to do a call. Additional type information 71 | should be hidden from the one instantiating a delegate object. 72 | 73 | * Support several call mechanisms. Supports: 74 | - Call to a member function. 75 | - Call to a free function. 76 | - Call to a functor (by reference). 77 | - Call to a stateless lambda. 78 | - Extra call operations such as passing on a stored void pointer. 79 | 80 | * It is always safe to call the delegate. In the null state, a call will not 81 | do anything and return a default constructed return value. 82 | 83 | * Behave like a normal pointer type. Can be copied, compared for equality, 84 | called, and compared to nullptr. 85 | 86 | * Observe constness. Stored const objects references can only be called by 87 | by const member functions or via operator() const. 88 | 89 | * Delegate have two main way to set a function to be called: 90 | - set: Member function for setting a new value to an already existing delegate. 91 | - make: Static functions for constructing new delegate objects. 92 | Do note that constructors aren't used for function setup. See discussion later 93 | on why. 94 | 95 | * Be usable with C++11 while offering more functionality for later editions. 96 | - C++11 offer most functionality. 97 | - C++14 add constexpr on 'set' functions. 98 | - C++17 allow for simpler member function setup using template<auto>. 99 | 100 | * Discourage lifetime issues. Will not allow storing a reference to a temporary 101 | (rvalue-reference) to objects. 102 | 103 | * Be constexpr and exception friendly. As much as possible should be declared constexpr and noexcept. 104 | 105 | ## mem_fkn (Prev. MemFkn) 106 | 107 | A helper class to store a call to a member function without supplying an actual object to call on. It is a stand-alone class that can encapsulate a member function. 108 | It has an 'invoke' member which accept an object and the stored member function is 109 | called on that object, similar to std::invoke. 110 | It can be stored, compared and copied around. Could e.g. be set up 111 | in an std::map for lookup and later supplied an object to be called on. 112 | mem_fkn uses a single free function pointer for storage and behaves as a standard value type. 113 | It could be useful on its own to avoid having to deal with member pointer syntax in user code. 114 | 115 | The main intended use is as an argument to delegate. A mem_fkn and an object can be given to the delegate class to tie an object to the member function and later the delegate 116 | can be called in the normal way. mem_fkn offers a way to delay the combination of 117 | the object and the member function name. 118 | mem_fkn require more type information compared to delegate. In addition, to call the signature it requires the type of the object to call on and a bool to signal if the functions stored 119 | is a const method. 120 | 121 | Detailed [API for mem_fkn](doc/mem_fkn.md). 122 | 123 | ## Delegate examples 124 | 125 | #include "delegate/delegate.hpp" 126 | 127 | int testFkn(int x) 128 | { 129 | return x + 1; 130 | } 131 | 132 | int main() 133 | { 134 | // Only need to know enough type information to construct 135 | // the signature used during a call of the delegate. E.g. 136 | 137 | delegate del; 138 | int res = del(23); // No delegate set, do default behavior. 139 | // res is now 0. 140 | 141 | if (!del) // del return false if no fkn is stored. 142 | del.set(); // Set a new function to be called. 143 | 144 | res = del(1); 145 | // res is now 2. 146 | } 147 | 148 | The base case for free functions is covered above. For member functions see below. 149 | 150 | #include "delegate/delegate.hpp" 151 | 152 | struct Test 153 | { 154 | int member(int x) 155 | { return x + 2; } 156 | int cmember(int x) const 157 | { return x + 3; } 158 | }; 159 | 160 | int main() 161 | { 162 | delegate del; 163 | Test t; 164 | const Test ct; 165 | 166 | del.set(t); 167 | int res = del(1); 168 | // res is now 3. 169 | 170 | del.set(ct); 171 | res = del(1); 172 | // res is now 4. 173 | 174 | // For C++17, we can deduce the object type, the following will work. 175 | del.set<&Test::member>(t); 176 | del.set<&Test::cmember>(ct); 177 | // ... 178 | } 179 | 180 | For functors, the delegate expects the user to keep them alive. 181 | 182 | #include "delegate/delegate.hpp" 183 | 184 | struct TestF 185 | { 186 | int operator()(int x) 187 | { return x + 2; } 188 | int operator()(int x) const 189 | { return x + 3; } 190 | }; 191 | 192 | int main() 193 | { 194 | delegate del; 195 | TestF t; 196 | const TestF ct; 197 | 198 | del.set(t); 199 | int res = del(1); 200 | // res is now 3. 201 | 202 | del.set(ct); 203 | res = del(1); 204 | // res is now 4. 205 | } 206 | 207 | The delegate can rely on stateless lambdas to convert to function pointers. In this 208 | case, we use runtime storage of the function pointer. Do note it will be less 209 | easy for the compiler to optimize compared to a fully static setup. 210 | 211 | #include "delegate/delegate.hpp" 212 | 213 | int testFkn(int x) 214 | { 215 | return x + 5; 216 | } 217 | 218 | int main() 219 | { 220 | delegate del; 221 | 222 | del.set(testFkn); 223 | int res = del(1); 224 | // res is now 6. 225 | 226 | del.set(static_cast([](int x) -> int { return x + 6; })); 227 | res = del(1); 228 | // res is now 7. 229 | 230 | // Or, use 'set_fkn' to get implicit conversion. 231 | del.set_fkn([](int x) -> int { return x + 7; }); 232 | res = del(1); 233 | // res is now 8. 234 | } 235 | 236 | For the runtime case, there is no constructor template argument to give, 237 | So delegate supports passing in a compatible free function pointer. This 238 | will accept stateless lambdas also via function pointer conversion. 239 | There is no lifetime issue with stateless lambdas. 240 | 241 | #include "delegate/delegate.hpp" 242 | 243 | int testFkn(int x) 244 | { 245 | return x + 5; 246 | } 247 | 248 | int main() 249 | { 250 | delegate del{testFkn}; 251 | res = del(1); 252 | // res is now 6. 253 | 254 | auto del2 = delegate{ [](int x) -> int { return x + 6; } }; 255 | res = del2(1); 256 | // res is now 7. 257 | } 258 | 259 | Except for the set function, one can use 'make' static functions to build delegate 260 | objects directly. The most common one given below. 261 | 262 | #include "delegate/delegate.hpp" 263 | 264 | // .. types as before. 265 | 266 | int main() 267 | { 268 | using Del = delegate; 269 | Del del; 270 | 271 | Test t; // For members. 272 | TestF functor; // For functor. 273 | 274 | del = Del::make(); 275 | del = Del::make(t); 276 | del = Del::make<&Test::member>(t); // C++17 277 | 278 | del = Del::make(functor); 279 | 280 | del = Del::make(static_cast([](int x) -> int 281 | { return x + 6; })); 282 | del = Del::make_fkn([](int x) -> int { return x + 6; }); 283 | } 284 | 285 | ## Aid for porting legacy code 286 | 287 | The delegate offer some extra set/make functions to aid in porting legacy code. 288 | You can make a delegate call a free function with either a void* first argument, 289 | or an explicit first argument object. This argument is given by the delegate 290 | internal context pointer. e.g: 291 | 292 | struct Test { 293 | int member(int x) 294 | { return x + 2; } 295 | }; 296 | 297 | int fknWithObject(Test& obj, int val) 298 | { return obj.member(val); } 299 | 300 | int fknWithVoid(void* ctx, int val) 301 | { return static_cast(ctx)->member(val); } 302 | 303 | int main() 304 | { 305 | Test obj; 306 | delegate del; 307 | 308 | del.set_free_with_void(static_cast(&obj)); 309 | int res = del(1); 310 | // res is now == 3. 311 | 312 | del.set_free_with_object(obj)); 313 | res = del(2); 314 | // res is now == 4. 315 | } 316 | 317 | This allows you to replace legacy callbacks one step at a time. 318 | You can make a driver use delegates while the user code still uses void* context pointers. 319 | It should allow for a decoupled refactoring of the driver/user code. 320 | These have the same const behavior and set/make variants as the rest of the setup functions. 321 | We use longer names to ease refactoring. 322 | 323 | ## Extending with a custom wrapper function 324 | 325 | Especially legacy code can have weird calling conventions in their callbacks. 326 | The delegate allow you to write external 'make' functions with their own custom 327 | adapter function. The adpater gets access to the stored void* pointer and can then freely 328 | use the void pointer and the parameters from the call. 329 | The wrapper function must follow the calling convention used internally in the delegate. 330 | Below is an example from the test suite where the private stateless lambda act as an adapter function: 331 | 332 | #include "delegate/delegate.hpp" 333 | 334 | delegate make_exchange(int& store) 335 | { 336 | using Del = delegate; 337 | typename Del::Trampoline adapterFkn = [](const typename Del::DataPtr& v, 338 | int val) -> int 339 | { 340 | int* p = static_cast(v.ptr()); 341 | std::swap(*p, val); 342 | return val; 343 | }; 344 | return Del{adapterFkn, static_cast(&store)}; 345 | } 346 | 347 | TEST(delegate, use_extension) 348 | { 349 | int t = 2; 350 | delegate del = make_exchange(t); 351 | EXPECT_EQ(t, 2); 352 | EXPECT_EQ(del(5), 2); 353 | EXPECT_EQ(t, 5); 354 | } 355 | 356 | ## Using a custom namespace for delegate 357 | 358 | Currently the delegate and mem_fkn classes are placed in the global namespace. 359 | The header file does not include anything else so it is possible to 360 | include it into another namespace. The unit test checks this and seems to work. 361 | 362 | ## Performance and code generation 363 | 364 | For the moment there are no proper measurement on timing. What has been done 365 | is to just paste in the delegate code into [compiler explorer](https://godbolt.org/) 366 | and look at the assembly output. The setup used gcc 7.2.1 on ARM compiling for Cortex-M4 367 | and -O2 optimization. 368 | 369 | In general using static template setup compared to a runtime pointer allow the compiler to generate 370 | smaller code. Some specific notes: 371 | 372 | - If the compiler can see the target function body, it can inline it within the wrapper function and in that case, directly jump to the useful code. 373 | - Without arguments, the wrapper functions becomes one assembly instruction to just 374 | forward the branch in all cases. 375 | - With additional arguments: 376 | - Member functions: No resuffling is needed, one extra instruction to load the _this_ pointer. 377 | - Functors: No resuffling is needed, one extra instruction to load the _this_ pointer. 378 | - Free functions: Some argument reshuffling is needed unless the target can be inlined. 379 | - Runtime free functions: Resuffling is needed in the common wrapper function. 380 | - Extra void/object set methods, no reshuffling is needed. Just load the pointer. 381 | 382 | - Testing callables for equal etc is often reduced down to comparing pointers. With the static 383 | setup this can often be a compile time calculation. 384 | 385 | So in most cases you should get decent code generation. In the case of free, non-inlined 386 | functions it is worth considering to just use an ordinary function pointer unless the 387 | extra generality is needed. 388 | 389 | ## A note on skipping constructors 390 | 391 | Not using constructors to set up functions is due to how a template 392 | arguments are used. 393 | The delegate require class template arguments for the class and then 394 | function address argument for the set/make function template. 395 | 396 | Doing this with a constructor means the user needs to supply both 397 | the class and constructor argument at the same time. 398 | The syntax (if it is even possible) is not run of the mill. 399 | 400 | Using a static member function it becomes easier: 401 | 402 | auto del = delegate::make(); 403 | 404 | // Pseudocode for constructor alternative: 405 | auto del = delegate /* some way to get */ (); 406 | 407 | The price to pay for normal static member function is the extra '::make'. 408 | Another reason is that you can 'borrow' type information. 409 | 410 | auto del = delegate{}; 411 | auto del2 = del.make(); 412 | 413 | For normal runtime variables, there is no constructor template type to 414 | pass, so delegate does support passing in a runtime variable function pointer 415 | of the right signature. It also allows implicit conversion from stateless lambdas 416 | in this case. 417 | 418 | ### Alternative make_delegate free function 419 | 420 | In the spirit of smart pointer, one could use 'make_delegate' to construct a 421 | delegate. However, there is one thing to keep in mind. 422 | The address of a function is given by the combination of the signature and 423 | the name. Hence make delegate would need to take both. E.g. 424 | 425 | auto del = make_delegate(); 426 | 427 | Omitting the signature could work, but would be ambiguous as soon as the function is overloaded, severely affecting maintainability. 428 | This will not offer extra functionality over using the 'make' functions 429 | so there is currently no effort in supporting this. 430 | 431 | ## Do not take the address of things in std 432 | 433 | In C and traditional C++, function pointers and taking function addresses are normal operation. Do note it can affect maintainability of your code. 434 | In particular, it is not allowed to take the address of functions 435 | (member/free functions) in std. The standard only allows calls, not taking the address of callables in std. 436 | It allows the implementation to change a free function 437 | into e.g. a functor in a compatible way. 438 | 439 | In general, taking the address will tie down your implementation to the 440 | current presented interface. It will also prevent adding default arguments. 441 | 442 | So, when starting to pass around pointers to callable things, keep this in mind. 443 | It works, it will continue to work but do plan for future maintenance. 444 | 445 | From a technical side, using the template method used here, we do not actually 446 | take the address. It wraps the supplied call into a free function and stores an address 447 | to that. Hence we take an address of a user-defined free function. 448 | Still, when the standard changes a free function into a functor you will still 449 | have the same issue. Only now, the template pattern matching fails instead of 450 | the compiler failing to find the right function. 451 | To get scared, see [Titus Winters talk](https://www.youtube.com/watch?v=BWvSSsKCiAw) 452 | on what can go wrong. 453 | 454 | Delegate does require you to specify signature before accepting a function name 455 | so the worst problems becoming ambiguous when adding new functions should 456 | be mitigated. 457 | 458 | So normal engineering practices are still valid. Decide on what you consider 459 | foreign code, put adapters on it so you have a single point to change when 460 | it changes. Decide what you consider stable/key interfaces in your code and let that 461 | be used more freely. 462 | -------------------------------------------------------------------------------- /doc/delegate.md: -------------------------------------------------------------------------------- 1 | ## delegate 2 | 3 | Defined in header "delegate/delegate.h" 4 | 5 | The class template `delegate` intend to be a reference to callable entities such as free functions, member functions, and functors. It wraps the actual call into a wrapper function. It stores a pointer to it and a reference to the supplied object 6 | to call on. 7 | The delegate class requires enough type information to define how a call is made, that is, 8 | the return type and arguments of the call. Other type information such as the stored function name and type of function (free, member, functor) is invisible to the caller. 9 | 10 | The main intended use case is as an std::function analog where were we guarantee small 11 | memory size (2 pointers), no exceptions, no heap allocation and being trivially_copyable. 12 | A delegate does not store the object to call on, only a reference, so the user must 13 | ensure the lifetime. 14 | 15 | The delegate intends to be similar to a normal function pointer. It should work as a normal pointer (default constructible, compared to nullptr, act as a value type w.r.t. to the stored address and pointer const behavior.) 16 | 17 | There are 2 main overload sets to set up delegates, `set` and `make`. 18 | * `set`: A templated member function which will set the value for the current object. 19 | * `make`: A templated static member function which returns a newly constructed delegate. 20 | 21 | The actual function to call is supplied via the template argument which allows for creating 22 | the stored wrapper function. Giving the function statically will also allow the compiler to optimize better. 23 | The delegate allows for storing normal runtime function pointers. It makes it possible to store calls to stateless lambdas via lambda to function pointer conversion. Do note we still get the extra indirect call via an internal wrapper. A runtime function can be made via 24 | a delegate constructor call. 25 | 26 | --- 27 | 28 | ``` cpp 29 | template 30 | class delegate; 31 | template 32 | class delegate 33 | ``` 34 | 35 | | Type parameters | Description 36 | | --- | --- 37 | | **Signature** | Signature for function call. Contain return type and argument types, similar to std::function. 38 | | **R** | Return type from the called function. 39 | | **Args** | Parameter pack with parameters for the function call. 40 | 41 | ### Member types 42 | 43 | | Member types | Description 44 | | --- | --- 45 | | `Equal` | Binary predicate functor. Forward call to static member function *equal*. Intended to work as a standard STL binary predicate. 46 | | `Less` | Binary predicate functor. Forward call to static member function *less*. Intended to work as a standard STL binary predicate. 47 | 48 | ### Member functions 49 | 50 | | Member function | Description 51 | | --- | --- 52 | | `delegate()` | Default construct a *delegate*. Post-condition: `this->null() == true.` 53 | | `delegate(R (*fkn)(Args...))` | Construct a *delegate*. Will call *fkn* on call. 54 | | `bool null() const` | Return _true_ if the mem_fkn is in a null state. 55 | | `void clear()`| Set *delegate* to null state. Post-condition: `this->null() == true.` 56 | | `explicit operator bool() const`| Return `!null()` 57 | | `R operator()(Args...) const`| Call the stored wrapper fkn. Valid to call when *null()==true* 58 | | `set` | See separate overload set table. 59 | | `delegate& set_fkn(R (*fkn)(Args...)` | Same as *set*. With unique member name it can implicitly convert from stateless lambda to a free function. 60 | | `set_free_with_object` | See separate section. 61 | | `set_free_with_void` | See separate section. 62 | 63 | #### Overload set for *set* member function family 64 | 65 | All *set* functions are declared *noexcept*. For *C++14* and newer, it is *constexpr*. They return a reference to its own object. 66 | 67 | **Set a free function.** 68 | Create a wrapper for *fkn* and store in this object. 69 | `template ` 70 | `delegate& set()` 71 | 72 | **Set a member function with object reference to call on.** 73 | Create a wrapper for *mf* member function and store a reference to *obj* to call on. 74 | `template ` 75 | `delegate& set(T& obj)` 76 | 77 | `template ` 78 | `delegate& set(T const& obj)` 79 | 80 | **C++17** Create a wrapper for *mf* member function and store a reference to *obj* to call on. Deduce *T* from *mf*. Only for C++17 and beyond. 81 | `template ` 82 | `delegate& set(T& obj)` 83 | 84 | `template ` 85 | `delegate& set(T const& obj)` // Require mf to be const declared. 86 | 87 | **Set a reference to a functor.** 88 | Create a wrapper for a functor and store a reference to *obj* to call on. 89 | `template ` 90 | `delegate& set(T& obj)` // Will call non-const operator(). 91 | 92 | `template ` 93 | `delegate& set(T const& obj)` // Will call const operator(). 94 | 95 | **Set a mem_fkn with an object to call on.** 96 | Store the wrapper from `mem_fkn` and a reference to *obj*. 97 | `template ` 98 | `delegate& set(mem_fkn const& mf, T& obj)` 99 | 100 | `template ` 101 | `delegate& set(mem_fkn const& mf, T const& obj)` 102 | 103 | `template ` 104 | `delegate& set(mem_fkn const& mf, T& obj)` 105 | 106 | **Store a runtime variable free function pointer.** 107 | `delegate& set(R (*fkn)(Args...))` 108 | 109 | `delegate& set_fkn(R (*fkn)(Args...))` // Separate function name allow implicit conversion. 110 | 111 | **Set a free function with extra void context arg**. 112 | Call a free function with an extra _void*_ as the first argument. Intended to allow calling back to 113 | legacy _void*_ context pointer-based code. 114 | `template ` 115 | `delegate& set_free_with_void(void*)` 116 | 117 | `template ` 118 | `delegate& set_free_with_void(void const*)` 119 | 120 | **Set a free function with extra object arg**. 121 | Call a free function with an extra object reference as the first argument. Will pass on `obj` when called. 122 | Intended to allow calling back to code based on free functions, typically found in older systems. 123 | `template ` 124 | `delegate& set_free_with_object(T& obj)` 125 | 126 | `template ` 127 | `delegate& set_free_with_object(T& obj)` 128 | 129 | `template ` 130 | `delegate& set_free_with_object(T const& obj)` 131 | 132 | #### Deleted functions, *set* family. 133 | 134 | `template ` 135 | `delegate& set(T&&) =delete;` // No temporaries. 136 | 137 | `template ` 138 | `delegate& set(T&&) =delete;` // No temporaries. 139 | 140 | `template ` // No temporaries. 141 | `delegate& set(T&&) =delete;` 142 | 143 | `template ` // violates const correctness. 144 | `delegate& set(mem_fkn const& f, T const& o) =delete;` 145 | 146 | `template ` // No temporaries. 147 | `delegate& set(mem_fkn const& f, T&& o) =delete;` 148 | 149 | `template ` // No temporaries. 150 | `delegate& set(mem_fkn const& f, T&& o) =delete;` 151 | 152 | `template ` // No temporaries. 153 | `delegate& set(&& obj) =delete; // >= C++17` 154 | 155 | `template ` // violates const correctness. 156 | `delegate& set_free_with_void(void const*) = delete;` 157 | 158 | `template ` // violates const correctness. 159 | `delegate& set_free_with_object(T const&) = delete;` 160 | 161 | `template ` // No temporaries. 162 | `delegate& set_free_with_object(T&&) = delete;` 163 | 164 | `template ` // No temporaries. 165 | `delegate& set_free_with_object(T&&) = delete;` 166 | 167 | ### Static member functions 168 | 169 | **equal**. Return _true_ if both arguments point to the same wrapper function and the context pointer is equal, or both delegates are in the null state. 170 | `bool equal(delegate const&, delegate const&)` 171 | 172 | **less**. Return _true_ if the _lhs_ wrapper function is < the _rhs_ wrapper function. If wrapper functions are equal, return true if < is true on context pointers. The null state is < all stored wrappers. 173 | `bool less(delegate const&, delegate const&)` 174 | 175 | 176 | #### Overload set for static member function *make* 177 | 178 | The entire *make* family of functions are a mirror image of the *set* family. 179 | See the *set* family documentation and do the following transform: 180 | - Make it static member instead of normal member. 181 | - Have it return `delegate` instead of `delegate&`. 182 | - The *make* family is constexpr even for C++11. 183 | 184 | ### Free functions 185 | #### operator overloads, relations 186 | 187 | The basic equality operators `==` and `!=` are defined. They are in terms of static member function `equal`. 188 | They are defined with this pattern: 189 | 190 | `template ` 191 | `constexpr bool` 192 | `operator op(const delegate& lhs, const delegate& rhs) noexcept` 193 | 194 | 195 | #### operator overloads, nullptr 196 | 197 | Defined in terms of static member function `null` and `equal`. 198 | 199 | `template ` 200 | `constexpr bool ` 201 | `operator==(delegate const& lhs, nullptr_t const& rhs) noexcept;` 202 | 203 | `template ` 204 | `constexpr bool ` 205 | `operator==(nullptr_t const&, delegate const& rhs) noexcept;` 206 | 207 | `template ` 208 | `constexpr bool` 209 | `operator!=(delegate const& lhs, nullptr_t const& rhs) noexcept;` 210 | 211 | `template ` 212 | `constexpr bool ` 213 | `operator!=(nullptr_t const&, delegate const& rhs) noexcept;` 214 | -------------------------------------------------------------------------------- /doc/mem_fkn.md: -------------------------------------------------------------------------------- 1 | ## mem_fkn 2 | 3 | Defined in header "delegate/delegate.h" 4 | 5 | Wrap a member function into a wrapper function and stores a pointer to this function. 6 | Keeps enough type information about the member function to later call it in a 7 | type-safe manner. 8 | The member function is supplied as a template value argument at construction and is used to synthesize a wrapper function. A pointer to the wrapper function is stored in the mem_fkn object. 9 | Once stored the `mem_fkn` only contain enough type information to safely call the stored wrapper. 10 | 11 | A `mem_fkn` in null state points to a default wrapper function that does nothing and returns a default constructed return object. Hence it is valid to call a `mem_fkn` which is in the null state. It will not affect the supplied object to the `invoke` call. 12 | 13 | --- 14 | 15 | ``` cpp 16 | template 17 | class mem_fkn; 18 | template 19 | class mem_fkn 20 | ``` 21 | 22 | | Type parameters | Description 23 | | --- | --- 24 | | **T** | Type for the object to call this member function on. 25 | | **cnst** | *false* : Need to call on non const object. *true* : can call on const object. 26 | | **Signature** | Signature for function call. Contain return type and argument types, similar to std::function. 27 | | **R** | Return type from the called function. 28 | | **Args** | Parameter pack with parameters for the function call. 29 | 30 | ### Member types 31 | 32 | ### Member functions 33 | 34 | | Member function | Description 35 | | --- | --- 36 | | `mem_fkn()` | Default construct a *mem_fkn*. Post-condition: `this->null() == true.` 37 | | `bool null() const` | Return _true_ if the mem_fkn is in a null state. 38 | | `void clear()`| Set *mem_fkn* to null state. Post-condition: `this->null() == true.` 39 | | `explicit operator bool() const`| Return `!null()` 40 | 41 | ##### When cnst == false 42 | 43 | | Member function | Description 44 | | --- | --- 45 | | `R invoke(T& o, Args...)`| Call the stored wrapper on object _o_. Return the result from the call. 46 | | `template mem_fkn& set()` | Create a wrapper for *mf* member function and store the pointer. return `*this` 47 | | `template mem_fkn& set_from_const()` | Create a wrapper for *mf* member function and store the pointer. return `*this` 48 | 49 | ##### When cnst == true 50 | 51 | | Member function | Description 52 | | --- | --- 53 | | `R invoke(T const& o, Args...)`| Call the stored wrapper on object _o_. Return the result from the call. 54 | | `template mem_fkn& set()` | Create a wrapper for *mf* member function and store the pointer. return `*this` 55 | 56 | ### Static member functions 57 | 58 | | Static function | Description 59 | | --- | --- 60 | | `bool equal(mem_fkn const&, mem_fkn const&)` | Return _true_ if both arguments point to the same wrapper function or both are in null state. 61 | | `bool less(mem_fkn const&, mem_fkn const&)` | Return _true_ if the _lhs_ wrapper function is < the _rhs_ wrapper function. The null state is < all stored wrappers. 62 | 63 | ##### When cnst == false 64 | 65 | | Static function | Description 66 | | --- | --- 67 | | `template mem_fkn make()` | Create a wrapper for *mf* member function. return a mem_fkn pointing to the wrapper. 68 | | `template mem_fkn make_from_const()` | Create a wrapper for *mf* member function. Return a *mem_fkn* pointing to the wrapper. 69 | 70 | ##### When cnst == true 71 | 72 | | Static function | Description 73 | | --- | --- 74 | | `template mem_fkn make()` |Create a wrapper for *mf* member function. return a *mem_fkn* pointing to the wrapper. 75 | 76 | ### Free functions, operator overloads. 77 | 78 | | Static function | Description 79 | | --- | --- 80 | | `bool operator==()` | Return true when considered *equal*. 81 | | `bool operator!=()` | Return true when considered not *equal*. 82 | | `bool operator<()` | Return true when considered *less(lhs,rhs)*. 83 | | `bool operator<=()` | Return true when considered *less(lhs,rhs)* or *equal*. 84 | | `bool operator>=()` | Return true when considered *less(rhs,lhs)* or *equal*. 85 | | `bool operator>()` | Return true when considered *less(rhs,lhs)*. 86 | -------------------------------------------------------------------------------- /include/delegate/delegate.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * delegate.h 3 | * 4 | * Created on: 8 aug. 2017 5 | * Author: Mikael Rosbacke 6 | */ 7 | 8 | #ifndef DELEGATE_DELEGATE_HPP_ 9 | #define DELEGATE_DELEGATE_HPP_ 10 | 11 | /** 12 | * Storage of a callable object for functors, free and member functions. 13 | * 14 | * Design intent is to do part of what std::function do but 15 | * without heap allocation, virtual function call and with minimal 16 | * memory footprint. (2 pointers). 17 | * Use case is embedded systems where previously raw function pointers 18 | * where used, using a void* pointer to point out a particular structure/object. 19 | * 20 | * The price is less generality. The user must keep objects alive since the 21 | * delegate only store a pointer to them. This is true for both 22 | * member functions and functors. 23 | * 24 | * Free functions and member functions are supplied as compile time template 25 | * arguments. It is required to generate the correct intermediate functions. 26 | * 27 | * Once constructed, the delegate should behave as any pointer: 28 | * - Can be copied freely. 29 | * - Can be compared to same type delegates and nullptr. 30 | * - Can be reassigned. 31 | * - Can be called. 32 | * 33 | * A default constructed delegate compare equal to nullptr. However it can be 34 | * called with the default behavior being to do nothing and return a default 35 | * constructed return object. 36 | * 37 | * Two overload sets are provided for construction: 38 | * - set : Set an existing delegate with new pointer values. 39 | * - make : Construct a new delegate. 40 | * 41 | * The following types of callables are supported: 42 | * - Functors. 43 | * - Member functions. 44 | * - Free functions. (Do not use the stored void* value) 45 | * - (Special) A free function with a void* extra first argument. That will 46 | * be passed the void* value set at delegate construction, 47 | * in addition to the arguments supplied to the call. 48 | * 49 | * Const correctness: 50 | * The delegate models a pointer in const correctness. The constness of the 51 | * delegate is different that the constness of the called objects. 52 | * Calling a member function or operator() require them to be const to be able 53 | * to call a const object. 54 | * 55 | * Both the member function and the object to call on must be set 56 | * at the same time. This is required to maintain const correctness guarantees. 57 | * The constness of the object or the member function is not part of the 58 | * delegate type. 59 | * 60 | * There is a class MemFkn for storing pointers to member functions which 61 | * will keep track of constness. It allow taking the address of a member 62 | * function at one point and store it. At a later time the MemFkn object and an 63 | * object can be set to a delegate for later call. 64 | * 65 | * The delegate do not allow storing pointers to r-value references 66 | * (temporary objects) for member and functor construction. 67 | */ 68 | 69 | #ifdef _MSVC_LANG 70 | #define DELEGATE_CPP_VERSION _MSVC_LANG 71 | #define DELEGATE_ALWAYS_INLINE __forceinline 72 | #else 73 | #define DELEGATE_CPP_VERSION __cplusplus 74 | #define DELEGATE_ALWAYS_INLINE __attribute__((always_inline)) 75 | #endif 76 | 77 | #if DELEGATE_CPP_VERSION < 201103L 78 | #error "Require at least C++11 to compile delegate" 79 | #endif 80 | 81 | #if DELEGATE_CPP_VERSION >= 201402L 82 | #define DELEGATE_CXX14CONSTEXPR constexpr 83 | #else 84 | #define DELEGATE_CXX14CONSTEXPR 85 | #endif 86 | 87 | namespace details 88 | { 89 | 90 | using nullptr_t = decltype(nullptr); 91 | 92 | template 93 | T 94 | nullReturnFunction() 95 | { 96 | return T{}; 97 | } 98 | template <> 99 | inline void 100 | nullReturnFunction() 101 | { 102 | return; 103 | } 104 | 105 | template 106 | class common; 107 | 108 | template 109 | class common 110 | { 111 | public: 112 | using FknPtr = R (*)(Args...); 113 | union DataPtr; 114 | struct FknStore; 115 | 116 | using Trampoline = R (*)(DataPtr const&, Args...); 117 | 118 | union DataPtr { 119 | constexpr DataPtr() = default; 120 | constexpr DataPtr(void* p) noexcept : v_ptr(p){}; 121 | 122 | static constexpr bool equal(Trampoline fkn, const DataPtr& lhs, 123 | const DataPtr& rhs) noexcept 124 | { 125 | // Ugly, but the m_ptr part should be optimized away on normal 126 | // platforms. 127 | return (fkn == doRuntimeFkn ? (lhs.fkn_ptr == rhs.fkn_ptr) 128 | : (lhs.v_ptr == rhs.v_ptr)); 129 | } 130 | static constexpr bool less(Trampoline fkn, const DataPtr& lhs, 131 | const DataPtr& rhs) noexcept 132 | { 133 | // Ugly, but the m_ptr part should be optimized away on normal 134 | // platforms. 135 | return fkn == doRuntimeFkn ? (lhs.fkn_ptr < rhs.fkn_ptr) 136 | : (lhs.v_ptr < rhs.v_ptr); 137 | } 138 | constexpr void* ptr() const noexcept 139 | { 140 | return v_ptr; 141 | } 142 | 143 | private: 144 | // Whole reason for 'private' is to make sure fkn_ptr is only 145 | // used when FknStore uses 'doRuntimeFkn' as adapter fkn. Will break 146 | // Aliasing rules otherwise in equal/less comparisons. 147 | friend struct FknStore; 148 | constexpr DataPtr(FknPtr p) noexcept : fkn_ptr(p){}; 149 | 150 | static R doRuntimeFkn(DataPtr const& o_arg, Args... args) 151 | { 152 | FknPtr fkn = o_arg.fkn_ptr; 153 | return fkn(args...); 154 | } 155 | 156 | void* v_ptr = nullptr; 157 | FknPtr fkn_ptr; 158 | }; 159 | 160 | inline static constexpr R doNullCB(DataPtr const&, Args...) 161 | { 162 | return nullReturnFunction(); 163 | } 164 | 165 | struct FknStore 166 | { 167 | constexpr FknStore() = default; 168 | constexpr FknStore(Trampoline fkn, void* ptr) 169 | : m_fkn(fkn), m_data(ptr){}; 170 | constexpr FknStore(FknPtr ptr) 171 | : m_fkn(ptr ? &DataPtr::doRuntimeFkn : &doNullCB), m_data(ptr) 172 | { 173 | } 174 | 175 | constexpr bool null() const noexcept 176 | { 177 | return m_fkn == &doNullCB; 178 | } 179 | 180 | Trampoline m_fkn = &doNullCB; 181 | DataPtr m_data; 182 | }; 183 | 184 | // Adapter function for the member + object calling. 185 | template 186 | inline static constexpr R doMemberCB(DataPtr const& o, Args... args) 187 | { 188 | return (static_cast(o.ptr())->*memFkn)(args...); 189 | } 190 | 191 | // Adapter function for the member + object calling. 192 | template 193 | inline static constexpr R doConstMemberCB(DataPtr const& o, Args... args) 194 | { 195 | return (static_cast(o.ptr())->*memFkn)(args...); 196 | } 197 | 198 | static constexpr bool equal(const FknStore& lhs, 199 | const FknStore& rhs) noexcept 200 | { 201 | return lhs.m_fkn == rhs.m_fkn && 202 | DataPtr::equal(lhs.m_fkn, lhs.m_data, rhs.m_data); 203 | } 204 | 205 | static constexpr bool less(const FknStore& lhs, 206 | const FknStore& rhs) noexcept 207 | { 208 | return (lhs.null() && !rhs.null()) || lhs.m_fkn < rhs.m_fkn || 209 | (lhs.m_fkn == rhs.m_fkn && 210 | DataPtr::less(lhs.m_fkn, lhs.m_data, rhs.m_data)); 211 | } 212 | 213 | // Helper struct to deduce member function types and const. 214 | template 215 | struct DeduceMemberType; 216 | 217 | template 218 | struct DeduceMemberType 219 | { 220 | using ObjType = T; 221 | static constexpr bool cnst = false; 222 | static constexpr Trampoline trampoline = 223 | &common::template doMemberCB; 224 | static constexpr void* castPtr(ObjType* obj) noexcept 225 | { 226 | return static_cast(obj); 227 | } 228 | }; 229 | template 230 | struct DeduceMemberType 231 | { 232 | using ObjType = T; 233 | static constexpr bool cnst = true; 234 | static constexpr Trampoline trampoline = 235 | &common::template doConstMemberCB; 236 | static constexpr void* castPtr(ObjType const* obj) noexcept 237 | { 238 | return const_cast(static_cast(obj)); 239 | }; 240 | }; 241 | }; 242 | 243 | } // namespace details 244 | 245 | template 246 | class delegate; 247 | 248 | /** 249 | * Simple adapter class for holding member functions and 250 | * allow them to be called similar to std::invoke. 251 | * 252 | * @param T Object type this member can be called on. 253 | * @param cnst Force stored member function to be const. 254 | * @param Signature The signature required to call this member function. 255 | * 256 | * Notes on the cnst arguments. If false, we require the member to be called 257 | * only on non const objects. Normally we match only on non const member 258 | * functions. When true we require member functions to be const, so they can be 259 | * called on both non-const/const objects. To assign a const function to a 260 | * mem_fkn which is false, use make_from_const. A separate function name is 261 | * needed to avoid ambiguous overloads w.r.t. constness. 262 | * 263 | * Current idea: Give both T and constness separately to be part of the type. 264 | * - Allow us to disambiguate const/non-const member functions when taking the 265 | * address. 266 | * 267 | * Discarded idea: T can be const/non-const. If const is not part of stored 268 | * type, we can not later use constness to disambiguate taking the address. 269 | * Still, constness of function is different from constness of object. 270 | * 271 | * -> 272 | */ 273 | template 274 | class mem_fkn; 275 | 276 | template 277 | class mem_fkn_base; 278 | 279 | template 280 | class mem_fkn_base 281 | { 282 | protected: 283 | using common = details::common; 284 | using DataPtr = typename common::DataPtr; 285 | using Trampoline = typename common::Trampoline; 286 | 287 | constexpr mem_fkn_base(Trampoline fkn) : fknPtr(fkn){}; 288 | constexpr mem_fkn_base() = default; 289 | 290 | Trampoline fknPtr = common::doNullCB; 291 | 292 | void setPtr(Trampoline t) 293 | { 294 | fknPtr = t; 295 | } 296 | 297 | public: 298 | constexpr Trampoline ptr() const 299 | { 300 | return fknPtr; 301 | } 302 | 303 | constexpr bool null() const noexcept 304 | { 305 | return fknPtr == common::doNullCB; 306 | } 307 | // Return true if a function pointer is stored. 308 | constexpr explicit operator bool() const noexcept 309 | { 310 | return !null(); 311 | } 312 | static constexpr bool equal(const mem_fkn_base& lhs, 313 | const mem_fkn_base& rhs) 314 | { 315 | return lhs.fknPtr == rhs.fknPtr; 316 | } 317 | static constexpr bool less(const mem_fkn_base& lhs, const mem_fkn_base& rhs) 318 | { 319 | return (lhs.null() && !rhs.null()) || (lhs.fknPtr < rhs.fknPtr); 320 | } 321 | }; 322 | 323 | template 324 | class mem_fkn : public mem_fkn_base 325 | { 326 | using Base = mem_fkn_base; 327 | static constexpr const bool cnst = false; 328 | using common = details::common; 329 | using DataPtr = typename common::DataPtr; 330 | using Trampoline = typename common::Trampoline; 331 | 332 | constexpr mem_fkn(Trampoline fkn) : mem_fkn_base(fkn){}; 333 | 334 | public: 335 | constexpr mem_fkn() = default; 336 | 337 | template 338 | constexpr R invoke(U&& o, Args... args) const noexcept 339 | { 340 | return Base::fknPtr(DataPtr{static_cast(&o)}, args...); 341 | } 342 | 343 | template 344 | DELEGATE_CXX14CONSTEXPR mem_fkn& set() noexcept 345 | { 346 | Base::setPtr(&common::template doMemberCB); 347 | return *this; 348 | } 349 | template 350 | DELEGATE_CXX14CONSTEXPR mem_fkn& set_from_const() noexcept 351 | { 352 | Base::fknPtr = &common::template doConstMemberCB; 353 | return *this; 354 | } 355 | template 356 | static constexpr mem_fkn make() noexcept 357 | { 358 | return mem_fkn{&common::template doMemberCB}; 359 | } 360 | template 361 | static constexpr mem_fkn make_from_const() noexcept 362 | { 363 | return mem_fkn{&common::template doConstMemberCB}; 364 | } 365 | }; 366 | 367 | template 368 | class mem_fkn : public mem_fkn_base 369 | { 370 | using Base = mem_fkn_base; 371 | using common = details::common; 372 | using Trampoline = typename common::Trampoline; 373 | 374 | constexpr mem_fkn(Trampoline fkn) : Base(fkn){}; 375 | 376 | public: 377 | constexpr mem_fkn() = default; 378 | 379 | constexpr R invoke(T const& o, Args... args) const 380 | { 381 | return Base::fknPtr(const_cast(&o), args...); 382 | } 383 | 384 | template 385 | static constexpr mem_fkn make() noexcept 386 | { 387 | return mem_fkn{&common::template doConstMemberCB}; 388 | } 389 | 390 | template 391 | DELEGATE_CXX14CONSTEXPR mem_fkn& set() noexcept 392 | { 393 | Base::fknPtr = &common::template doConstMemberCB; 394 | return *this; 395 | } 396 | }; 397 | 398 | template 399 | bool 400 | operator==(const mem_fkn& lhs, const mem_fkn& rhs) 401 | { 402 | return lhs.equal(lhs, rhs); 403 | } 404 | 405 | template 406 | bool 407 | operator!=(const mem_fkn& lhs, const mem_fkn& rhs) 408 | { 409 | return !(lhs == rhs); 410 | } 411 | 412 | template 413 | bool 414 | operator<(const mem_fkn& lhs, const mem_fkn& rhs) 415 | { 416 | return lhs.less(lhs, rhs); 417 | } 418 | 419 | template 420 | bool 421 | operator<=(const mem_fkn& lhs, const mem_fkn& rhs) 422 | { 423 | return lhs.less(lhs, rhs) || lhs.equal(lhs, rhs); 424 | } 425 | 426 | template 427 | bool 428 | operator>=(const mem_fkn& lhs, const mem_fkn& rhs) 429 | { 430 | return lhs.less(rhs, lhs) || lhs.equal(lhs, rhs); 431 | } 432 | 433 | template 434 | bool 435 | operator>(const mem_fkn& lhs, const mem_fkn& rhs) 436 | { 437 | return rhs.less(rhs, lhs); 438 | } 439 | 440 | /** 441 | * Class for storing the callable object 442 | * Stores a pointer to an adapter function and a void* pointer to the 443 | * Object. 444 | * 445 | * @param R type of the return value from calling the callback. 446 | * @param Args Argument list to the function when calling the callback. 447 | */ 448 | template 449 | class delegate 450 | { 451 | public: 452 | using common = details::common; 453 | using DataPtr = typename common::DataPtr; 454 | using FknStore = typename common::FknStore; 455 | 456 | // Signature for call to the delegate. 457 | using FknPtr = typename common::FknPtr; 458 | 459 | // Type of the function pointer for the trampoline functions. 460 | using Trampoline = typename common::Trampoline; 461 | 462 | // Adaptor function for the case where void* is not forwarded 463 | // to the caller. (Just a normal function pointer.) 464 | template 465 | inline static R doFreeCB(DataPtr const&, Args... args) 466 | { 467 | return freeFkn(args...); 468 | } 469 | 470 | // Adapter function for when the stored object is a pointer to a 471 | // callable object (stored elsewhere). Call it using operator(). 472 | template 473 | inline static R doFunctor(DataPtr const& o_arg, Args... args) 474 | { 475 | auto obj = static_cast(o_arg.ptr()); 476 | return (*obj)(args...); 477 | } 478 | 479 | template 480 | inline static R doConstFunctor(DataPtr const& o_arg, Args... args) 481 | { 482 | const Functor* obj = static_cast(o_arg.ptr()); 483 | return (*obj)(args...); 484 | } 485 | 486 | // Adapter function for the free function with extra first arg 487 | // in the called function, set at delegate construction. 488 | template 489 | inline static R dofreeFknWithObjectRef(DataPtr const& o, Args... args) 490 | { 491 | T* obj = static_cast(o.ptr()); 492 | return freeFkn(*obj, args...); 493 | } 494 | 495 | // Adapter function for the free function with extra first arg 496 | // in the called function, set at delegate construction. 497 | template 498 | inline static R dofreeFknWithObjectConstRef(DataPtr const& o, Args... args) 499 | { 500 | T const* obj = static_cast(o.ptr()); 501 | return freeFkn(*obj, args...); 502 | } 503 | 504 | // Adapter function for the free function with extra first void*arg 505 | // in the called function, set at delegate construction. 506 | template 507 | inline static R dofreeFknWithVoidPtr(DataPtr const& o, Args... args) 508 | { 509 | return freeFkn(o.ptr(), args...); 510 | } 511 | 512 | // Adapter function for the free function with extra first arg 513 | // in the called function, set at delegate construction. 514 | template 515 | inline static R dofreeFknWithVoidConstPtr(DataPtr const& o, Args... args) 516 | { 517 | return freeFkn(static_cast(o.ptr()), args...); 518 | } 519 | 520 | public: 521 | // Default construct with stored ptr == nullptr. 522 | constexpr delegate() = default; 523 | 524 | // General simple function pointer handling. Will accept stateless lambdas. 525 | // Do note it is less easy to optimize compared to static function setup. 526 | // Handle nullptr / 0 arguments as well. 527 | constexpr delegate(FknPtr fkn) noexcept : m_data(fkn) {} 528 | 529 | /** 530 | * Allow writing extensions with separate make/trampoline function. 531 | * We restrict to allowing 'void*̈́' as data pointer to make sure 532 | * not tripping up on union aliasing issues in e.g. equal function. 533 | * THe trampoline function can only access the v_ptr member of the passed 534 | * union. 535 | */ 536 | constexpr delegate(Trampoline tFkn, void* datap) noexcept 537 | : m_data(tFkn, datap) 538 | { 539 | } 540 | 541 | ~delegate() = default; 542 | 543 | DELEGATE_CXX14CONSTEXPR delegate& operator=(FknPtr fkn) noexcept 544 | { 545 | m_data = FknStore{fkn}; 546 | return *this; 547 | } 548 | 549 | // Call the stored function. Requires: bool(*this) == true; 550 | // Will call trampoline fkn which will call the final fkn. 551 | DELEGATE_ALWAYS_INLINE constexpr R operator()(Args... args) const 552 | { 553 | return m_data.m_fkn(m_data.m_data, args...); 554 | } 555 | 556 | constexpr bool null() const noexcept 557 | { 558 | return m_data.null(); 559 | } 560 | 561 | static constexpr bool equal(const delegate& lhs, 562 | const delegate& rhs) noexcept 563 | { 564 | return common::equal(lhs.m_data, rhs.m_data); 565 | } 566 | 567 | // Helper Functor for passing into std functions etc. 568 | struct Equal 569 | { 570 | constexpr bool operator()(const delegate& lhs, 571 | const delegate& rhs) const noexcept 572 | { 573 | return equal(lhs, rhs); 574 | } 575 | }; 576 | 577 | // Define a total order for purpose of sorting in maps etc. 578 | // Do not define operators since this is not a natural total order. 579 | // It will vary randomly depending on where symbols end up etc. 580 | static constexpr bool less(const delegate& lhs, 581 | const delegate& rhs) noexcept 582 | { 583 | return common::less(lhs.m_data, rhs.m_data); 584 | // Ugly, but the m_ptr part should be optimized away on normal 585 | // platforms. 586 | } 587 | 588 | // Helper Functor for passing into std::map et.al. 589 | struct Less 590 | { 591 | constexpr bool operator()(const delegate& lhs, 592 | const delegate& rhs) const noexcept 593 | { 594 | return less(lhs, rhs); 595 | } 596 | }; 597 | 598 | // Return true if a function pointer is stored. 599 | constexpr explicit operator bool() const noexcept 600 | { 601 | return !null(); 602 | } 603 | 604 | DELEGATE_CXX14CONSTEXPR void clear() noexcept 605 | { 606 | m_data = FknStore{}; 607 | } 608 | 609 | /** 610 | * Create a callback to a free function with a specific type on 611 | * the pointer. 612 | */ 613 | template 614 | DELEGATE_CXX14CONSTEXPR delegate& set() noexcept 615 | { 616 | m_data = FknStore(&doFreeCB, nullptr); 617 | return *this; 618 | } 619 | 620 | /** 621 | * Create a callback to a member function to a given object. 622 | */ 623 | template 624 | DELEGATE_CXX14CONSTEXPR delegate& set(T& tr) noexcept 625 | { 626 | m_data = FknStore(&common::template doMemberCB, 627 | static_cast(&tr)); 628 | return *this; 629 | } 630 | 631 | template 632 | DELEGATE_CXX14CONSTEXPR delegate& set(T const& tr) noexcept 633 | { 634 | m_data = FknStore(&common::template doConstMemberCB, 635 | const_cast(static_cast(&tr))); 636 | return *this; 637 | } 638 | 639 | // Delete r-values. Not interested in temporaries. 640 | template 641 | DELEGATE_CXX14CONSTEXPR delegate& set(T&&) = delete; 642 | 643 | template 644 | DELEGATE_CXX14CONSTEXPR delegate& set(T&&) = delete; 645 | 646 | /** 647 | * Create a callback to a Functor or a lambda. 648 | * NOTE : Only a pointer to the functor is stored. The 649 | * user must ensure the functor is still valid at call time. 650 | * Hence, we do not accept functor r-values. 651 | */ 652 | template 653 | DELEGATE_CXX14CONSTEXPR delegate& set(T& tr) noexcept 654 | { 655 | m_data = FknStore(&doFunctor, static_cast(&tr)); 656 | return *this; 657 | } 658 | 659 | template 660 | DELEGATE_CXX14CONSTEXPR delegate& set(T const& tr) noexcept 661 | { 662 | m_data = FknStore(&doConstFunctor, 663 | const_cast(static_cast(&tr))); 664 | return *this; 665 | } 666 | 667 | // Do not allow temporaries to be stored. 668 | template 669 | constexpr delegate& set(T&&) const = delete; 670 | 671 | DELEGATE_CXX14CONSTEXPR delegate& set(FknPtr fkn) noexcept 672 | { 673 | m_data = FknStore(fkn); 674 | return *this; 675 | } 676 | 677 | /** 678 | * Combine a MemFkn with an object to set this delegate. 679 | */ 680 | template 681 | DELEGATE_CXX14CONSTEXPR delegate& 682 | set(mem_fkn const& f, T& o) noexcept 683 | { 684 | m_data = FknStore(f.ptr(), static_cast(&o)); 685 | return *this; 686 | } 687 | template 688 | DELEGATE_CXX14CONSTEXPR delegate& 689 | set(mem_fkn const& f, T const& o) = delete; 690 | 691 | template 692 | DELEGATE_CXX14CONSTEXPR delegate& set(mem_fkn const& f, 693 | T const& o) noexcept 694 | { 695 | m_data = 696 | FknStore(f.ptr(), const_cast(static_cast(&o))); 697 | return *this; 698 | } 699 | template 700 | DELEGATE_CXX14CONSTEXPR delegate& set(mem_fkn const& f, 701 | T& o) noexcept 702 | { 703 | return set(f, static_cast(o)); 704 | } 705 | 706 | template 707 | DELEGATE_CXX14CONSTEXPR delegate& 708 | set(mem_fkn const& f, T&& o) = delete; 709 | template 710 | DELEGATE_CXX14CONSTEXPR delegate& set(mem_fkn const& f, 711 | T&& o) = delete; 712 | 713 | // C++17 allow template for non type template arguments. 714 | // Use to avoid specifying object type. 715 | #if __cplusplus >= 201703 716 | 717 | template 718 | constexpr delegate& 719 | set(typename common::template DeduceMemberType::ObjType& obj) noexcept 721 | { 722 | using DM = 723 | typename common::template DeduceMemberType; 724 | m_data = FknStore(DM::trampoline, DM::castPtr(&obj)); 725 | return *this; 726 | } 727 | template 728 | constexpr delegate& set(typename common::template DeduceMemberType< 729 | decltype(mFkn), mFkn>::ObjType const& obj) noexcept 730 | { 731 | using DM = 732 | typename common::template DeduceMemberType; 733 | m_data = FknStore(DM::trampoline, DM::castPtr(&obj)); 734 | return *this; 735 | } 736 | template 737 | constexpr delegate& set(typename common::template DeduceMemberType< 738 | decltype(mFkn), mFkn>::ObjType&& obj) = delete; 739 | #endif 740 | 741 | DELEGATE_CXX14CONSTEXPR delegate& set_fkn(FknPtr fkn) noexcept 742 | { 743 | return set(fkn); 744 | } 745 | 746 | template 747 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_void(void* ctx) noexcept 748 | { 749 | m_data = FknStore(&dofreeFknWithVoidPtr, ctx); 750 | return *this; 751 | } 752 | 753 | template 754 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_void(decltype(nullptr)) noexcept 755 | { 756 | m_data = FknStore(&dofreeFknWithVoidPtr, nullptr); 757 | return *this; 758 | } 759 | 760 | template 761 | DELEGATE_CXX14CONSTEXPR delegate& 762 | set_free_with_void(void const* ctx) noexcept 763 | { 764 | m_data = 765 | FknStore(&dofreeFknWithVoidConstPtr, const_cast(ctx)); 766 | return *this; 767 | } 768 | 769 | template 770 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_object(T& o) noexcept 771 | { 772 | m_data = FknStore(&dofreeFknWithObjectRef, 773 | const_cast(static_cast(&o))); 774 | return *this; 775 | } 776 | 777 | template 778 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_object(T& o) noexcept 779 | { 780 | m_data = FknStore(&dofreeFknWithObjectConstRef, 781 | static_cast(&o)); 782 | return *this; 783 | } 784 | 785 | template 786 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_object(T const& o) noexcept 787 | { 788 | m_data = FknStore(&dofreeFknWithObjectConstRef, 789 | const_cast(static_cast(&o))); 790 | return *this; 791 | } 792 | 793 | template 794 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_object(T const&) = delete; 795 | template 796 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_object(T&&) = delete; 797 | template 798 | DELEGATE_CXX14CONSTEXPR delegate& set_free_with_object(T&&) = delete; 799 | 800 | /** 801 | * Create a callback to a free function with a specific type on 802 | * the pointer. 803 | */ 804 | template 805 | static constexpr delegate make() noexcept 806 | { 807 | // Note: template arg can never be nullptr. 808 | return delegate{&doFreeCB, static_cast(nullptr)}; 809 | } 810 | 811 | /** 812 | * Create a callback to a member function to a given object. 813 | */ 814 | template 815 | static constexpr delegate make(T& o) noexcept 816 | { 817 | return delegate{&common::template doMemberCB, 818 | static_cast(&o)}; 819 | } 820 | 821 | template 822 | static constexpr delegate make(const T& o) noexcept 823 | { 824 | return delegate{&common::template doConstMemberCB, 825 | const_cast(static_cast(&o))}; 826 | } 827 | 828 | /** 829 | * Create a callback to a Functor or a lambda. 830 | * NOTE : Only a pointer to the functor is stored. The 831 | * user must ensure the functor is still valid at call time. 832 | * Hence, we do not accept functor r-values. 833 | */ 834 | template 835 | static constexpr delegate make(T& o) noexcept 836 | { 837 | return delegate{&doFunctor, static_cast(&o)}; 838 | } 839 | template 840 | static constexpr delegate make(T const& o) noexcept 841 | { 842 | return delegate{&doConstFunctor, 843 | const_cast(static_cast(&o))}; 844 | } 845 | template 846 | static constexpr delegate make(T&& object) = delete; 847 | 848 | static constexpr delegate make(FknPtr fkn) noexcept 849 | { 850 | return delegate{fkn}; 851 | } 852 | 853 | static constexpr delegate make_fkn(FknPtr fkn) noexcept 854 | { 855 | return delegate{fkn}; 856 | } 857 | 858 | template 859 | static constexpr delegate make_free_with_void(void* ctx) noexcept 860 | { 861 | return delegate{&dofreeFknWithVoidPtr, ctx}; 862 | } 863 | 864 | template 865 | static constexpr delegate make_free_with_void(void const* ctx) noexcept 866 | { 867 | return delegate{&dofreeFknWithVoidConstPtr, 868 | const_cast(ctx)}; 869 | } 870 | 871 | template 872 | static constexpr delegate make_free_with_void(decltype(nullptr)) noexcept 873 | { 874 | return delegate{&dofreeFknWithVoidPtr, nullptr}; 875 | } 876 | 877 | /** 878 | * Create a delegate to a free function, where the first argument is 879 | * assumed to be a reference to the object supplied as argument here. 880 | * The return value and rest of the argument must match the signature 881 | * of the delegate. 882 | */ 883 | template 884 | static constexpr delegate make_free_with_object(T& o) noexcept 885 | { 886 | return delegate{&dofreeFknWithObjectRef, 887 | static_cast(&o)}; 888 | } 889 | 890 | template 891 | static constexpr delegate make_free_with_object(T& o) noexcept 892 | { 893 | return delegate{&dofreeFknWithObjectConstRef, 894 | static_cast(&o)}; 895 | } 896 | 897 | template 898 | static constexpr delegate make_free_with_object(T const& o) noexcept 899 | { 900 | return delegate{&dofreeFknWithObjectConstRef, 901 | const_cast(static_cast(&o))}; 902 | } 903 | 904 | template 905 | static constexpr delegate make_free_with_object(T const&) = delete; 906 | template 907 | static constexpr delegate make_free_with_object(T&&) = delete; 908 | template 909 | static constexpr delegate make_free_with_object(T&&) = delete; 910 | 911 | template 912 | static constexpr delegate make(mem_fkn f, 913 | T& o) noexcept 914 | { 915 | return delegate{f.ptr(), static_cast(&o)}; 916 | } 917 | template 918 | static constexpr delegate make(mem_fkn, 919 | T const&) = delete; 920 | 921 | template 922 | static constexpr delegate make(mem_fkn f, 923 | T const& o) noexcept 924 | { 925 | return delegate{f.ptr(), 926 | const_cast(static_cast(&o))}; 927 | } 928 | template 929 | static constexpr delegate make(mem_fkn f, 930 | T& o) noexcept 931 | { 932 | return delegate{f.ptr(), static_cast(&o)}; 933 | } 934 | 935 | template 936 | static constexpr delegate make(mem_fkn, T&&) = delete; 937 | 938 | // C++17 allow template for non type template arguments. 939 | // Use to avoid specifying object type. (Getting a bit hairy here...) 940 | #if __cplusplus >= 201703 941 | template 942 | static constexpr delegate make(typename common::template DeduceMemberType< 943 | decltype(mFkn), mFkn>::ObjType& obj) noexcept 944 | { 945 | using DM = 946 | typename common::template DeduceMemberType; 947 | return delegate{DM::trampoline, DM::castPtr(&obj)}; 948 | } 949 | template 950 | static constexpr delegate 951 | make(typename common::template DeduceMemberType< 952 | decltype(mFkn), mFkn>::ObjType const& obj) noexcept 953 | { 954 | using DM = 955 | typename common::template DeduceMemberType; 956 | return delegate{DM::trampoline, DM::castPtr(&obj)}; 957 | } 958 | 959 | // Temporaries not allowed. 960 | template 961 | static constexpr delegate 962 | make(typename common::template DeduceMemberType::ObjType&&) = delete; 964 | 965 | #endif 966 | 967 | private: 968 | FknStore m_data; 969 | }; 970 | 971 | template 972 | constexpr bool 973 | operator==(const delegate& lhs, 974 | const delegate& rhs) noexcept 975 | { 976 | return delegate::equal(lhs, rhs); 977 | } 978 | 979 | template 980 | constexpr bool 981 | operator!=(const delegate& lhs, 982 | const delegate& rhs) noexcept 983 | { 984 | return !(lhs == rhs); 985 | } 986 | 987 | // Bite the bullet, this is how unique_ptr handle nullptr_t. 988 | template 989 | constexpr bool 990 | operator==(details::nullptr_t, const delegate& rhs) noexcept 991 | { 992 | return rhs.null(); 993 | } 994 | 995 | template 996 | constexpr bool 997 | operator!=(details::nullptr_t lhs, const delegate& rhs) noexcept 998 | { 999 | return !(lhs == rhs); 1000 | } 1001 | 1002 | template 1003 | constexpr bool 1004 | operator==(const delegate& lhs, details::nullptr_t) noexcept 1005 | { 1006 | return lhs.null(); 1007 | } 1008 | 1009 | template 1010 | constexpr bool 1011 | operator!=(const delegate& lhs, details::nullptr_t rhs) noexcept 1012 | { 1013 | return !(lhs == rhs); 1014 | } 1015 | 1016 | // No ordering operators ( operator< etc) defined. This delegate 1017 | // represent several classes of pointers and is not a naturally 1018 | // ordered type. Use members less, Less for explicit ordering. 1019 | 1020 | /** 1021 | * Helper macro to create a delegate for calling a member function. 1022 | * Example of use: 1023 | * 1024 | * auto cb = DELEGATE_MKMEM(void(), &SomeClass::memberFunction, obj); 1025 | * 1026 | * where 'obj' is of type 'SomeClass'. 1027 | * 1028 | * @param signature Template parameter for the delegate. 1029 | * @param memFknPtr address of member function pointer. C++ require 1030 | * full name path with addressof operator (&) 1031 | * @object object which the member function should be called on. 1032 | */ 1033 | #define DELEGATE_MKMEM(signature, memFknPtr, object) \ 1034 | (delegate::make, \ 1035 | memFkn>(object)) 1036 | 1037 | #undef DELEGATE_14CONSTEXPR 1038 | 1039 | #endif /* UTILITY_CALLBACK_H_ */ 1040 | -------------------------------------------------------------------------------- /local_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GTEST_ROOT=/usr/src/gtest 4 | 5 | rm -rf build* 6 | mkdir -p build-gcc 7 | mkdir -p build-clang 8 | 9 | cd build-gcc 10 | cmake .. -DCMAKE_TOOLCHAIN_FILE=/home/mikaelr/1_install/cmake/host_gcc_tc.cmake 11 | make 12 | make test 13 | 14 | cd ../build-clang 15 | cmake .. -DCMAKE_TOOLCHAIN_FILE=/home/mikaelr/1_install/cmake/host_clang_tc.cmake 16 | make 17 | make test 18 | 19 | -------------------------------------------------------------------------------- /test/delegate_test.cpp: -------------------------------------------------------------------------------- 1 | namespace test_ns 2 | { 3 | #include "delegate/delegate.hpp" 4 | } 5 | 6 | using test_ns::delegate; 7 | using test_ns::mem_fkn; 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #if __cplusplus < 201103L 18 | #error "Require at least C++11 to compile delegate" 19 | #endif 20 | 21 | static int 22 | freeFkn(int x) 23 | { 24 | return x + 5; 25 | } 26 | 27 | static int s_obj = 0; 28 | void 29 | testFkn12() 30 | { 31 | s_obj = 1; 32 | } 33 | 34 | void 35 | testFkn12(int) 36 | { 37 | s_obj = 2; 38 | } 39 | 40 | // Ensure the language uses the signature in the delegate type to disambiguate 41 | // function names. 42 | TEST(delegate, languageAllowPtrOverloadSet) 43 | { 44 | delegate cb; 45 | cb.set(); 46 | cb(); 47 | EXPECT_EQ(s_obj, 1); 48 | 49 | delegate cb2; 50 | cb2.set(); 51 | cb2(1); 52 | EXPECT_EQ(s_obj, 2); 53 | } 54 | 55 | TEST(delegate, Free_static_function_set_make) 56 | { 57 | auto del = delegate::make(); 58 | EXPECT_EQ(del(1), 6); 59 | 60 | del = nullptr; 61 | EXPECT_EQ(del(1), 0); 62 | 63 | del.set(); 64 | EXPECT_EQ(del(1), 6); 65 | } 66 | 67 | TEST(delegate, Free_dynamic_function_constructor) 68 | { 69 | using Del = delegate; 70 | Del del; 71 | EXPECT_FALSE(del); 72 | Del del2(freeFkn); 73 | EXPECT_EQ(del2(1), 6); 74 | 75 | // Handle implicit conversion from stateless lambda to fkn ptr. 76 | Del del3([](int x) -> int { return x + 7; }); 77 | EXPECT_EQ(del3(1), 8); 78 | 79 | auto del4 = Del{[](int x) -> int { return x + 9; }}; 80 | EXPECT_EQ(del4(1), 10); 81 | } 82 | 83 | TEST(delegate, implicit_conversion_lambda_construct_assign) 84 | { 85 | auto lam = []() { return 42; }; 86 | delegate del1{lam}; 87 | delegate del2; //= lam; 88 | del2 = lam; 89 | 90 | // This is weird. Doen't work, even though del3{ lam } works and del2 = lam 91 | // works... Does it have something to do with Dan Saks 'friends' talk? 92 | // delegate del3 = lam; 93 | // delegate del12 = []() { return 42; }; 94 | 95 | // Workaround do: 96 | delegate del10{lam}; 97 | delegate del11 = {lam}; 98 | delegate del12 = {[]() { return 42; }}; 99 | 100 | auto const lam2 = []() { return 42; }; 101 | delegate del4{lam2}; 102 | delegate del5; // = lam2; 103 | del5 = lam2; 104 | } 105 | 106 | TEST(delegate, Free_dynamic_function_set_make) 107 | { 108 | auto del = delegate::make(freeFkn); 109 | EXPECT_EQ(del(1), 6); 110 | 111 | del = nullptr; 112 | EXPECT_EQ(del(1), 0); 113 | 114 | del.set(freeFkn); 115 | EXPECT_EQ(del(1), 6); 116 | 117 | // Make sure vanilla usage of lambda works. Rely on conversion to ordinary 118 | // function pointer. (Requires explicit casting.) 119 | del.set(static_cast([](int x) -> int { return x + 9; })); 120 | EXPECT_EQ(del(1), 10); 121 | 122 | del = delegate::make( 123 | static_cast([](int x) -> int { return x + 9; })); 124 | EXPECT_EQ(del(1), 10); 125 | 126 | // Use another member name to get implicit function conversion. 127 | del.set_fkn([](int x) -> int { return x + 9; }); 128 | EXPECT_EQ(del(1), 10); 129 | 130 | del = delegate::make_fkn([](int x) -> int { return x + 9; }); 131 | EXPECT_EQ(del(1), 10); 132 | } 133 | 134 | TEST(delegate, value_semantics) 135 | { 136 | // Default construct. 137 | delegate del1; 138 | EXPECT_TRUE(del1.null()); 139 | EXPECT_EQ(del1(0), 0); 140 | 141 | auto del2 = delegate::make(); 142 | EXPECT_EQ(del2(1), 6); 143 | 144 | // Copy construct 145 | auto del3{del2}; 146 | EXPECT_EQ(del3(1), 6); 147 | EXPECT_FALSE(del3.null()); 148 | 149 | // Assignment. 150 | delegate del4; 151 | del4 = del3; 152 | EXPECT_EQ(del4(1), 6); 153 | 154 | // Equality 155 | EXPECT_TRUE(del4 == del3); 156 | EXPECT_FALSE(del4 == del1); 157 | 158 | EXPECT_FALSE(del4 != del3); 159 | EXPECT_TRUE(del4 != del1); 160 | } 161 | 162 | TEST(delegate, is_trivially_copyable) 163 | { 164 | // Note: being constexpr and trivial is mutually exclusive. 165 | // Trivial require default initialization do not set a value, 166 | // constexpr require to set a value. 167 | // We can fulfill _trivially_copyable -> this class can safely be 168 | // memcpy:ied between places. 169 | 170 | using Del = delegate; 171 | EXPECT_TRUE(std::is_trivially_copyable::value); 172 | EXPECT_TRUE(std::is_standard_layout::value); 173 | } 174 | 175 | TEST(mem_fkn, is_trivially_copyable) 176 | { 177 | // Note: being constexpr and trivial is mutually exclusive. 178 | // Trivial require default initialization do not set a value, 179 | // constexpr require to set a value. 180 | // We can fulfill _trivially_copyable -> this class can safely be 181 | // memcpy:ied between places. 182 | 183 | struct Obj 184 | { 185 | }; 186 | 187 | using MemFknT = mem_fkn; 188 | EXPECT_TRUE(std::is_trivially_copyable::value); 189 | EXPECT_TRUE(std::is_standard_layout::value); 190 | 191 | using MemFknF = mem_fkn; 192 | EXPECT_TRUE(std::is_trivially_copyable::value); 193 | EXPECT_TRUE(std::is_standard_layout::value); 194 | } 195 | 196 | TEST(delegate, nulltests) 197 | { 198 | // Default construct, Can call default constructed. 199 | delegate del; 200 | 201 | EXPECT_FALSE(del); 202 | 203 | EXPECT_TRUE(del == nullptr); 204 | EXPECT_TRUE(nullptr == del); 205 | 206 | EXPECT_FALSE(del != nullptr); 207 | EXPECT_FALSE(nullptr != del); 208 | 209 | bool t = static_cast(del); 210 | EXPECT_FALSE(t); 211 | 212 | delegate del2(nullptr); 213 | EXPECT_FALSE(del2); 214 | 215 | delegate del3(0); 216 | EXPECT_FALSE(del3); 217 | 218 | struct Functor 219 | { 220 | void operator()() {} 221 | }; 222 | 223 | Functor f; 224 | del.set(f); 225 | 226 | EXPECT_TRUE(del); 227 | 228 | EXPECT_FALSE(del == nullptr); 229 | EXPECT_FALSE(nullptr == del); 230 | 231 | EXPECT_TRUE(del != nullptr); 232 | EXPECT_TRUE(nullptr != del); 233 | 234 | bool t2 = static_cast(del); 235 | EXPECT_TRUE(t2); 236 | 237 | del.clear(); 238 | EXPECT_FALSE(del); 239 | 240 | del.set(f); 241 | EXPECT_TRUE(del); 242 | 243 | del = delegate{}; 244 | EXPECT_FALSE(del); 245 | } 246 | 247 | struct MemberCheck 248 | { 249 | int member(int i) 250 | { 251 | return i + 1; 252 | } 253 | int cmember(int i) const 254 | { 255 | return i + 2; 256 | } 257 | }; 258 | 259 | struct Functor 260 | { 261 | int operator()(int x) 262 | { 263 | return x + 3; 264 | }; 265 | int operator()(int x) const 266 | { 267 | return x + 4; 268 | }; 269 | 270 | // No const version available. 271 | void operator()(){}; 272 | }; 273 | 274 | TEST(delegate, Functor_const_variants_set) 275 | { 276 | Functor f; 277 | const Functor cf; 278 | 279 | delegate del; 280 | del.set(f); 281 | EXPECT_EQ(del(2), 5); 282 | del.set(cf); 283 | EXPECT_EQ(del(2), 6); 284 | 285 | // Must not compile. require operator() const 286 | // delegate del2; del2.set(cf); 287 | 288 | // Must not compile. No storage of pointer to temporary. 289 | // del.set(Functor{}); 290 | // del.set(const Functor{}); 291 | } 292 | 293 | TEST(delegate, Functor_const_variants_make) 294 | { 295 | Functor f; 296 | const Functor cf; 297 | 298 | delegate del; 299 | del = delegate::make(f); 300 | EXPECT_EQ(del(2), 5); 301 | del = delegate::make(cf); 302 | EXPECT_EQ(del(2), 6); 303 | 304 | // Short notation. 305 | auto del2 = del.make(f); 306 | EXPECT_EQ(del2(2), 5); 307 | 308 | // Must not compile. require operator() const 309 | // delegate del3 = delegate::make(cf); 310 | 311 | // Must not compile. No storage of pointer to temporary. 312 | // del.make(Functor{}); 313 | // del.make(const Functor{}); 314 | } 315 | 316 | TEST(delegate, Member_const_variants_set) 317 | { 318 | MemberCheck mc; 319 | const MemberCheck cmc; 320 | 321 | delegate del; 322 | del.set(mc); 323 | int res = del(1); 324 | EXPECT_EQ(res, 2); 325 | 326 | // Must not compile. Need const member for const object. 327 | // del.set(cmc); 328 | 329 | del.set(mc); 330 | res = del(1); 331 | EXPECT_EQ(res, 3); 332 | 333 | del.set(cmc); 334 | res = del(1); 335 | EXPECT_EQ(res, 3); 336 | 337 | // Must not compile. Do not allow storing pointer to temporary. 338 | // del.set(MemberCheck{}); 339 | // del.set(MemberCheck{}); 340 | // del.set(const MemberCheck{}); 341 | // del.set(const MemberCheck{}); 342 | } 343 | 344 | #if __cplusplus >= 201703 345 | TEST(delegate, Member_short_const_variants_set) 346 | { 347 | MemberCheck mc; 348 | const MemberCheck cmc; 349 | 350 | delegate del; 351 | del.set<&MemberCheck::member>(mc); 352 | int res = del(1); 353 | EXPECT_EQ(res, 2); 354 | 355 | // Must not compile. Need const member for const object. 356 | // del.set<&MemberCheck::member>(cmc); 357 | 358 | del.set<&MemberCheck::cmember>(mc); 359 | res = del(1); 360 | EXPECT_EQ(res, 3); 361 | 362 | del.set<&MemberCheck::cmember>(cmc); 363 | res = del(1); 364 | EXPECT_EQ(res, 3); 365 | 366 | // Must not compile. Do not allow storing pointer to temporary. 367 | // del.set<&MemberCheck::member>(MemberCheck{}); 368 | // del.set<&MemberCheck::cmember>(MemberCheck{}); 369 | // del.set<&MemberCheck::member>(const MemberCheck{}); 370 | // del.set<&MemberCheck::cmember>(const MemberCheck{}); 371 | } 372 | #endif 373 | 374 | TEST(delegate, Member_const_variants_make) 375 | { 376 | MemberCheck mc; 377 | const MemberCheck cmc; 378 | 379 | delegate del = 380 | delegate::make(mc); 381 | EXPECT_EQ(del(1), 2); 382 | 383 | // Must not compile. Need const member for const object. 384 | // del.make(cmc); 385 | 386 | del = delegate::make(mc); 387 | EXPECT_EQ(del(1), 3); 388 | 389 | del = delegate::make(cmc); 390 | EXPECT_EQ(del(1), 3); 391 | 392 | // Must not compile. Do not allow storing pointer to temporary. 393 | // del = delegate::make(MemberCheck{}); del = 395 | // delegate::make(MemberCheck{}); del = 397 | // delegate::make(const 398 | // MemberCheck{}); del = delegate::make(const MemberCheck{}); 400 | } 401 | 402 | #if __cplusplus >= 201703 403 | TEST(delegate, Member_short_const_variants_make) 404 | { 405 | MemberCheck mc; 406 | const MemberCheck cmc; 407 | 408 | delegate del = delegate::make<&MemberCheck::member>(mc); 409 | EXPECT_EQ(del(1), 2); 410 | 411 | // Must not compile. Need const member for const object. 412 | // del.make(cmc); 413 | 414 | del = delegate::make<&MemberCheck::cmember>(mc); 415 | EXPECT_EQ(del(1), 3); 416 | 417 | del = delegate::make<&MemberCheck::cmember>(cmc); 418 | EXPECT_EQ(del(1), 3); 419 | 420 | // Must not compile. Do not allow storing pointer to temporary. 421 | // del = delegate::make<&MemberCheck::member>(MemberCheck{}); 422 | // del = delegate::make<&MemberCheck::cmember>(MemberCheck{}); 423 | // del = delegate::make<&MemberCheck::member>(const 424 | // MemberCheck{}); del = 425 | // delegate::make<&MemberCheck::cmember>(const MemberCheck{}); 426 | } 427 | #endif 428 | 429 | // Make sure we can store member function pointer with correct const 430 | // correctness. 431 | TEST(delegate, MemFkn_member_intermediate_storage) 432 | { 433 | MemberCheck mc; 434 | const MemberCheck cmc; 435 | 436 | delegate del; 437 | 438 | mem_fkn memberFkn; 439 | memberFkn.set<&MemberCheck::member>(); 440 | 441 | // Must not compile. Do not want to deduce non const member fkn with true 442 | // cnst. 443 | // MemFkn cmemberFkn{ 444 | // delegate::memFkn()}; 445 | 446 | del.set(memberFkn, mc); 447 | int res = del(1); 448 | EXPECT_EQ(res, 2); 449 | 450 | del.clear(); 451 | 452 | del = delegate::make(memberFkn, mc); 453 | res = del(1); 454 | EXPECT_EQ(res, 2); 455 | 456 | // Must not compile. Non const member fkn, const obj. 457 | // del.set(memberFkn, cmc); 458 | // del.set(memberFkn, MemberCheck{}); 459 | // del = delegate::make(memberFkn, cmc); 460 | // del = delegate::make(memberFkn, MemberCheck{}); 461 | 462 | auto cmemberFkn = 463 | mem_fkn::make<&MemberCheck::cmember>(); 464 | del.set(cmemberFkn, mc); 465 | res = del(1); 466 | EXPECT_EQ(res, 3); 467 | 468 | del.clear(); 469 | 470 | del = delegate::make(cmemberFkn, mc); 471 | res = del(1); 472 | EXPECT_EQ(res, 3); 473 | 474 | del.set(cmemberFkn, cmc); 475 | res = del(1); 476 | EXPECT_EQ(res, 3); 477 | 478 | del = delegate::make(cmemberFkn, cmc); 479 | res = del(1); 480 | EXPECT_EQ(res, 3); 481 | 482 | // Must not compile. Const member fkn, 483 | // del.set(cmemberFkn, MemberCheck{}); 484 | // del = delegate::make(cmemberFkn, MemberCheck{}); 485 | 486 | #if 0 487 | // Make sure convenience notation works. 488 | auto memberFkn3 = del.memFkn(); 489 | del.set(memberFkn3, mc); 490 | res = del(1); 491 | EXPECT_EQ(res, 2); 492 | 493 | auto memberFkn4 = del.memFkn(); 494 | del.set(memberFkn4, cmc); 495 | res = del(1); 496 | EXPECT_EQ(res, 3); 497 | #endif 498 | } 499 | 500 | #if __cplusplus >= 201703 501 | // Make sure we can store member function pointer with correct const 502 | // correctness. 503 | TEST(delegate, MemFkn_short_member_intermediate_storage) 504 | { 505 | MemberCheck mc; 506 | const MemberCheck cmc; 507 | 508 | delegate del; 509 | mem_fkn memberFkn; 510 | memberFkn.set<&MemberCheck::member>(); 511 | 512 | // Must not compile. Do not want to deduce non const member fkn with true 513 | // cnst. 514 | // MemFkn cmemberFkn{ 515 | // delegate::memFkn<&MemberCheck::member>()}; 516 | 517 | del.set(memberFkn, mc); 518 | int res = del(1); 519 | EXPECT_EQ(res, 2); 520 | 521 | del.clear(); 522 | 523 | del = delegate::make(memberFkn, mc); 524 | res = del(1); 525 | EXPECT_EQ(res, 2); 526 | 527 | mem_fkn cmemberFkn; 528 | cmemberFkn.set<&MemberCheck::cmember>(); 529 | 530 | del.set(cmemberFkn, mc); 531 | res = del(1); 532 | EXPECT_EQ(res, 3); 533 | 534 | del.set(cmemberFkn, cmc); 535 | res = del(1); 536 | EXPECT_EQ(res, 3); 537 | 538 | del.clear(); 539 | 540 | #if 0 541 | // Make sure convenience notation works. 542 | auto memberFkn3 = del.memFkn<&MemberCheck::member>(); 543 | del.set(memberFkn3, mc); 544 | res = del(1); 545 | EXPECT_EQ(res, 2); 546 | 547 | auto memberFkn4 = del.memFkn<&MemberCheck::cmember>(); 548 | del.set(memberFkn4, cmc); 549 | res = del(1); 550 | EXPECT_EQ(res, 3); 551 | #endif 552 | } 553 | 554 | TEST(delegate, can_be_called_by_invoke) 555 | { 556 | MemberCheck mc; 557 | const MemberCheck cmc; 558 | 559 | auto del = delegate::make<&MemberCheck::member>(mc); 560 | auto res = std::invoke(del, 1); 561 | EXPECT_EQ(res, 2); 562 | 563 | del.set<&MemberCheck::cmember>(cmc); 564 | EXPECT_EQ(std::invoke(del, 1), 3); 565 | } 566 | 567 | #endif 568 | 569 | void 570 | testFkn() 571 | { 572 | } 573 | 574 | #if __cplusplus >= 201402L 575 | #define CXX_14CONSTEXPR constexpr 576 | #else 577 | #define CXX_14CONSTEXPR 578 | #endif 579 | 580 | struct TestMember 581 | { 582 | void member() {} 583 | void cmember() const {} 584 | }; 585 | static TestMember tm; 586 | static const TestMember ctm; 587 | 588 | static Functor s_f; 589 | 590 | struct CFunctor 591 | { 592 | void operator()() const {}; 593 | }; 594 | 595 | static const CFunctor s_cf; 596 | 597 | TEST(delegate, test_constexpr) 598 | { 599 | // Default construct. 600 | auto constexpr del = delegate{}; 601 | (void)del; 602 | 603 | // Set: 604 | // Free function 605 | auto CXX_14CONSTEXPR del2 = delegate{}.set(); 606 | (void)del2; 607 | 608 | // Member function 609 | auto CXX_14CONSTEXPR del4 = 610 | delegate{}.set(tm); 611 | (void)del4; 612 | auto CXX_14CONSTEXPR del6 = 613 | delegate{}.set(ctm); 614 | (void)del6; 615 | 616 | // Functor 617 | auto CXX_14CONSTEXPR del8 = delegate{}.set(s_f); 618 | (void)del8; 619 | 620 | auto CXX_14CONSTEXPR del10 = delegate{}.set(s_cf); 621 | (void)del10; 622 | 623 | // mem_fkn 624 | auto constexpr memFkn = 625 | mem_fkn::make<&TestMember::member>(); 626 | auto constexpr memFkn2 = 627 | mem_fkn::make<&TestMember::cmember>(); 628 | 629 | auto CXX_14CONSTEXPR del12 = delegate{}.set(memFkn, tm); 630 | (void)del12; 631 | 632 | auto CXX_14CONSTEXPR del14 = delegate{}.set(memFkn2, ctm); 633 | (void)del14; 634 | 635 | // Make: 636 | // Free function 637 | auto constexpr del3 = delegate::make(); 638 | (void)del3; 639 | 640 | // Member function 641 | auto constexpr del5 = 642 | delegate::make(tm); 643 | (void)del5; 644 | 645 | auto constexpr del7 = 646 | delegate::make(ctm); 647 | (void)del7; 648 | 649 | // Functor 650 | auto constexpr del9 = delegate::make(s_f); 651 | (void)del9; 652 | auto constexpr del11 = delegate::make(s_cf); 653 | (void)del11; 654 | 655 | #if 0 656 | // MemFkn 657 | auto constexpr del13 = delegate::make(memFkn, s_f); 658 | (void)del13; 659 | auto constexpr del15 = delegate::make(memFkn2, s_cf); 660 | (void)del15; 661 | #endif 662 | 663 | // Member function, short c++17 notation. 664 | #if __cplusplus >= 201703 665 | auto constexpr del16 = delegate{}.set<&TestMember::member>(tm); 666 | (void)del16; 667 | auto constexpr del18 = delegate{}.set<&TestMember::cmember>(ctm); 668 | (void)del18; 669 | 670 | auto constexpr del17 = delegate::make<&TestMember::member>(tm); 671 | (void)del17; 672 | auto constexpr del19 = delegate::make<&TestMember::cmember>(ctm); 673 | (void)del19; 674 | 675 | #if 0 676 | auto constexpr memFkn3 = delegate::memFkn<&TestMember::member>(); 677 | (void)memFkn3; 678 | auto constexpr memFkn4 = delegate::memFkn<&TestMember::cmember>(); 679 | (void)memFkn4; 680 | #endif 681 | 682 | #endif 683 | } 684 | 685 | struct Base 686 | { 687 | virtual int memb(int i) 688 | { 689 | return i + 1; 690 | } 691 | virtual int cmemb(int i) const 692 | { 693 | return i + 2; 694 | } 695 | }; 696 | 697 | struct Derived : public Base 698 | { 699 | virtual int memb(int i) 700 | { 701 | return i + 3; 702 | } 703 | virtual int cmemb(int i) const 704 | { 705 | return i + 4; 706 | } 707 | }; 708 | 709 | TEST(delegate, test_virtual_dispatch) 710 | { 711 | Derived d; 712 | Base& b = d; 713 | 714 | delegate del; 715 | del.set(b); 716 | EXPECT_EQ(del(1), 4); 717 | 718 | const Base& cb = d; 719 | del.set(cb); 720 | // derived called. 721 | EXPECT_EQ(del(1), 5); 722 | } 723 | 724 | struct TestReturn 725 | { 726 | ~TestReturn() 727 | { 728 | val++; 729 | } 730 | static int val; 731 | }; 732 | 733 | int TestReturn::val = 0; 734 | 735 | TEST(delegate, ensure_nullptr_return_default_constructed_object) 736 | { 737 | // Default construct, Can call default constructed. 738 | delegate del; 739 | // And call. 740 | del(); 741 | 742 | // Make sure we get a default constructed return value. 743 | delegate del2; 744 | 745 | EXPECT_EQ(TestReturn::val, 0); 746 | del2(); 747 | EXPECT_EQ(TestReturn::val, 1); 748 | } 749 | 750 | TEST(delegate, ensure_return_type_is_the_correct_type) 751 | { 752 | delegate cb; 753 | EXPECT_FALSE(cb); 754 | 755 | // delegate supports calls to a null delegate. will do nothing. 756 | // and return a default constructed value. 757 | auto t = cb(4); 758 | 759 | // try to deduce that we got a proper uint16_t type back. 760 | using ArgT = decltype(t); 761 | EXPECT_TRUE(std::numeric_limits::min() == 0); 762 | EXPECT_TRUE(std::numeric_limits::max() == 0xffff); 763 | EXPECT_TRUE(std::numeric_limits::is_signed == false); 764 | EXPECT_TRUE(std::numeric_limits::is_exact == true); 765 | EXPECT_TRUE(std::numeric_limits::is_integer == true); 766 | EXPECT_TRUE(t == 0); 767 | } 768 | 769 | TEST(delegate, testMemberDisambiguateConst) 770 | { 771 | struct ConstCheck 772 | { 773 | int member(int i) 774 | { 775 | return i + 1; 776 | } 777 | int member(int i) const 778 | { 779 | return i + 2; 780 | } 781 | }; 782 | 783 | ConstCheck obj; 784 | const ConstCheck cobj; 785 | 786 | delegate del; 787 | 788 | // Ambiguous name w.r.t. const. Disambiguate on object. 789 | del.set(obj); 790 | int res = del(1); 791 | EXPECT_EQ(res, 2); 792 | 793 | // Ambiguous name w.r.t. const. Disambiguate on object. 794 | del.set(cobj); 795 | res = del(1); 796 | EXPECT_EQ(res, 3); 797 | } 798 | 799 | TEST(delegate, test_lambda_support) 800 | { 801 | struct Functor_ 802 | { 803 | int operator()(int x, int y) 804 | { 805 | return x + y; 806 | } 807 | }; 808 | 809 | Functor_ fkn; 810 | 811 | // Create simple callback object with operator(). 812 | auto cb = delegate::make(fkn); 813 | EXPECT_EQ(cb(5, 3), 8); 814 | 815 | // Lambda should be similar. 816 | auto t = [](int x, int y) -> int { return x + y; }; 817 | auto cb3 = delegate::make(t); 818 | EXPECT_EQ(cb3(5, 3), 8); 819 | } 820 | 821 | TEST(delegate, testLambdaConstFunctionConst2) 822 | { 823 | struct FunctorC 824 | { 825 | int operator()(int x, int y) const 826 | { 827 | return x + y; 828 | } 829 | }; 830 | 831 | const FunctorC fkn; 832 | const FunctorC fkn2 = fkn; 833 | (void)fkn2; 834 | 835 | // Create simple callback object with operator(). 836 | auto cb = delegate::make(fkn); 837 | 838 | auto res = cb(5, 3); 839 | assert(res == 8); 840 | 841 | auto t = [](int x, int y) -> int { return x + y; }; 842 | auto cb2 = delegate::make(t); 843 | 844 | res = cb2(6, 5); 845 | assert(res == 11); 846 | 847 | auto lambda = [](int x, int y) -> int { return x + y; }; 848 | auto cb3 = delegate::make(lambda); 849 | 850 | res = cb3(6, 5); 851 | assert(res == 11); 852 | } 853 | 854 | TEST(delegate, Make_sure_our_union_is_a_void_size) 855 | { 856 | delegate del; 857 | EXPECT_EQ(sizeof del, 2 * sizeof(void*)); 858 | // If this fails, it could also be because sizeof fknptr != sizeof (void*) 859 | } 860 | 861 | #include 862 | 863 | TEST(delegate, special_case_that_should_work_uniqueptr) 864 | { 865 | // Make sure we can construct unique_ptr which keep tight control on making 866 | // copies of itself. 867 | delegate(int)> del; 868 | auto tmp = del(10); 869 | EXPECT_EQ(tmp, nullptr); 870 | 871 | auto t = [](int x) -> std::unique_ptr // No make_unique in C++11. 872 | { 873 | std::unique_ptr t2{new int}; 874 | *t2 = x; 875 | return t2; 876 | }; 877 | del.set(t); 878 | 879 | std::unique_ptr up = del(12); 880 | EXPECT_EQ(*up, 12); 881 | } 882 | 883 | #include 884 | 885 | static int 886 | freeFkn2(int x) 887 | { 888 | return x + 6; 889 | } 890 | 891 | TEST(delegate, can_store_in_a_set) 892 | { 893 | using Del = delegate; 894 | auto del = Del::make(); 895 | auto del2 = Del::make(); 896 | EXPECT_NE(del, del2); 897 | EXPECT_NE(del.less(del, del2), del2.less(del2, del)); 898 | 899 | std::set testSet; 900 | testSet.insert(Del::make()); 901 | testSet.insert(Del::make()); 902 | EXPECT_EQ(testSet.size(), 2u); 903 | } 904 | 905 | struct TestObj 906 | { 907 | TestObj() = default; 908 | TestObj(int x) : m_val(x) {} 909 | 910 | int m_val = 3; 911 | 912 | int add(int x) 913 | { 914 | return m_val + x; 915 | } 916 | 917 | int addc(int x) const 918 | { 919 | return m_val + x; 920 | } 921 | }; 922 | 923 | int 924 | adder(TestObj& o, int val) 925 | { 926 | return o.m_val + val; 927 | } 928 | 929 | void 930 | testFreeFunctionWithPtr() 931 | { 932 | TestObj o; 933 | 934 | // Create simple callback to a normal free function. 935 | auto cb = delegate::make_free_with_object(o); 936 | 937 | o.m_val = 6; 938 | int res = cb(3); 939 | assert(res == 9); 940 | 941 | o.m_val = 3; 942 | res = cb(9); 943 | assert(res == 12); 944 | } 945 | 946 | TEST(oldtests, testrun) 947 | { 948 | testFreeFunctionWithPtr(); 949 | } 950 | 951 | struct MCheck 952 | { 953 | int member(int i) 954 | { 955 | return i + 1; 956 | } 957 | int cmember(int i) const 958 | { 959 | return i + 2; 960 | } 961 | int constcheck(int i) 962 | { 963 | return i + 1; 964 | } 965 | int constcheck(int i) const 966 | { 967 | return i + 2; 968 | } 969 | }; 970 | 971 | TEST(mem_fkn, develop) 972 | { 973 | MCheck mc; 974 | const MCheck cmc; 975 | 976 | mem_fkn mf; 977 | 978 | mf = mf.make<&MCheck::member>(); 979 | EXPECT_EQ(mf.invoke(mc, 1), 2); 980 | 981 | mf = mf.make_from_const<&MCheck::cmember>(); 982 | EXPECT_EQ(mf.invoke(mc, 1), 3); 983 | 984 | mf = mf.make<&MCheck::member>(); 985 | // EXPECT_EQ(mf.invoke(cmc, 1), 2); 986 | 987 | // Not ambiguous when taking address. 988 | mf = mf.make<&MCheck::constcheck>(); 989 | EXPECT_EQ(mf.invoke(mc, 1), 2); 990 | 991 | mem_fkn cmf; 992 | 993 | // cmf = cmf.make<&MemberCheck::member>(); 994 | cmf = cmf.make<&MCheck::cmember>(); 995 | EXPECT_EQ(cmf.invoke(mc, 1), 3); 996 | EXPECT_EQ(cmf.invoke(cmc, 1), 3); 997 | 998 | // Not ambiguous when taking address. 999 | cmf = cmf.make<&MCheck::constcheck>(); 1000 | EXPECT_EQ(cmf.invoke(mc, 1), 3); 1001 | EXPECT_EQ(cmf.invoke(cmc, 1), 3); 1002 | } 1003 | 1004 | TEST(mem_fkn, value_based) 1005 | { 1006 | mem_fkn mf; 1007 | EXPECT_FALSE(mf); 1008 | EXPECT_TRUE(!mf); 1009 | EXPECT_TRUE(mf == mf); 1010 | EXPECT_FALSE(mf != mf); 1011 | EXPECT_FALSE(mf < mf); 1012 | EXPECT_TRUE(mf <= mf); 1013 | EXPECT_TRUE(mf >= mf); 1014 | EXPECT_FALSE(mf > mf); 1015 | 1016 | mem_fkn mf2 = mf2.make<&MCheck::member>(); 1017 | EXPECT_FALSE(!mf2); 1018 | EXPECT_TRUE(mf2); 1019 | 1020 | EXPECT_TRUE(mf != mf2); 1021 | EXPECT_FALSE(mf == mf2); 1022 | EXPECT_TRUE(mf < mf2); 1023 | EXPECT_TRUE(mf <= mf2); 1024 | EXPECT_FALSE(mf >= mf2); 1025 | EXPECT_FALSE(mf > mf2); 1026 | 1027 | EXPECT_TRUE(mf2 != mf); 1028 | EXPECT_FALSE(mf2 == mf); 1029 | EXPECT_FALSE(mf2 <= mf); 1030 | EXPECT_FALSE(mf2 < mf); 1031 | EXPECT_TRUE(mf2 > mf); 1032 | EXPECT_TRUE(mf2 >= mf); 1033 | 1034 | mem_fkn mf3 = mf2.make<&MCheck::constcheck>(); 1035 | 1036 | EXPECT_TRUE(mf2 != mf3); 1037 | EXPECT_FALSE(mf2 == mf3); 1038 | EXPECT_FALSE(mf2 <= mf3 && mf2 >= mf3); 1039 | EXPECT_TRUE(mf2 <= mf3 || mf2 >= mf3); 1040 | EXPECT_FALSE(mf2 < mf3 && mf2 > mf3); 1041 | EXPECT_TRUE(mf2 < mf3 || mf2 > mf3); 1042 | } 1043 | 1044 | TEST(mem_fkn, value_based_const) 1045 | { 1046 | mem_fkn mf; 1047 | EXPECT_FALSE(mf); 1048 | EXPECT_TRUE(!mf); 1049 | EXPECT_TRUE(mf == mf); 1050 | EXPECT_FALSE(mf != mf); 1051 | EXPECT_FALSE(mf < mf); 1052 | EXPECT_TRUE(mf <= mf); 1053 | EXPECT_TRUE(mf >= mf); 1054 | EXPECT_FALSE(mf > mf); 1055 | 1056 | mem_fkn mf2 = mf2.make<&MCheck::cmember>(); 1057 | EXPECT_FALSE(!mf2); 1058 | EXPECT_TRUE(mf2); 1059 | 1060 | EXPECT_TRUE(mf != mf2); 1061 | EXPECT_FALSE(mf == mf2); 1062 | EXPECT_TRUE(mf < mf2); 1063 | EXPECT_TRUE(mf <= mf2); 1064 | EXPECT_FALSE(mf >= mf2); 1065 | EXPECT_FALSE(mf > mf2); 1066 | 1067 | EXPECT_TRUE(mf2 != mf); 1068 | EXPECT_FALSE(mf2 == mf); 1069 | EXPECT_FALSE(mf2 <= mf); 1070 | EXPECT_FALSE(mf2 < mf); 1071 | EXPECT_TRUE(mf2 > mf); 1072 | EXPECT_TRUE(mf2 >= mf); 1073 | 1074 | mem_fkn mf3 = mf2.make<&MCheck::constcheck>(); 1075 | 1076 | EXPECT_TRUE(mf2 != mf3); 1077 | EXPECT_FALSE(mf2 == mf3); 1078 | EXPECT_FALSE(mf2 <= mf3 && mf2 >= mf3); 1079 | EXPECT_TRUE(mf2 <= mf3 || mf2 >= mf3); 1080 | EXPECT_FALSE(mf2 < mf3 && mf2 > mf3); 1081 | EXPECT_TRUE(mf2 < mf3 || mf2 > mf3); 1082 | } 1083 | 1084 | // See that the set/make for aiding in reworking old C code works. 1085 | // Assume some driver (act as a service) which offer callbacks to be registered. 1086 | // Assume it offers a void* context pointer that will be passed on. 1087 | // In the case where a context pointer is not needed/unwanted a normal function 1088 | // pointer would work better. Do note it is required for member function calls 1089 | // anyway. 1090 | // 1091 | // The delegate should help with this workflow: 1092 | // 0. Make sure the driver / user code actually offers this context pointer in 1093 | // callbacks. 1094 | // Get your C code to compile with C++ compiler. 1095 | // 1. Ensure the driver do not expose the void*/fkn pointer directly. Have a 1096 | // setter function 1097 | // accepting void* / free function pointer. 1098 | // 2. Replace the raw function pointer / void* pointer in driver with a 1099 | // delegate. 1100 | // Set it in the setter function above. Use 'set_freefkn_with_void()' in 1101 | // setter function. Let the driver call delegate instead of old pointers. 1102 | // 3. Possibly convert the driver to a class if it is not done yet. 1103 | // 4. Offer member function to register delegates. (expose internal 1104 | // delegate/offer to copy in 1105 | // a full delegate.) 1106 | // 1107 | // At this point the driver is converted to a C++ class, while the user code 1108 | // Is still mostly C. You can now convert User code one at a time. 1109 | // Converting user code to classes can be done in 2 steps: 1110 | // 1. Convert the struct to a class, but keep external functions external for 1111 | // now. 1112 | // 2. Convert all usage of void* ctx into the proper class type. Use 1113 | // 'set_free_with_object' to get type safety in the callbacks. 1114 | // 3. Convert free functions to member functions as needed to enforce 1115 | // invariants. 1116 | // Once a callback target becomes a member change registration to 1117 | // a normal 'set' with member function name. 1118 | 1119 | int 1120 | fknWithObject(MemberCheck& mc, int val) 1121 | { 1122 | return mc.member(val); 1123 | } 1124 | 1125 | int 1126 | fknWithConstObject(MemberCheck const& cmc, int val) 1127 | { 1128 | return cmc.cmember(val); 1129 | } 1130 | 1131 | int 1132 | fknWithVoid(void* ctx, int val) 1133 | { 1134 | return static_cast(ctx)->member(val); 1135 | } 1136 | 1137 | int 1138 | fknWithConstVoid(void const* cctx, int val) 1139 | { 1140 | return static_cast(cctx)->cmember(val); 1141 | } 1142 | 1143 | 1144 | int 1145 | fknWithVoidOverload(void* , int val) 1146 | { 1147 | return val - 1; 1148 | } 1149 | 1150 | int 1151 | fknWithVoidOverload(const void*, int val) 1152 | { 1153 | return val - 2; 1154 | } 1155 | 1156 | 1157 | TEST(delegate, with_void) 1158 | { 1159 | MemberCheck mc; 1160 | MemberCheck const cmc; 1161 | 1162 | using Del = delegate; 1163 | Del del; 1164 | 1165 | del.set_free_with_void(static_cast(&mc)); 1166 | EXPECT_EQ(del(1), 2); 1167 | 1168 | //del.set_free_with_void(static_cast(&cmc)); 1169 | //EXPECT_EQ(del(1), 2); 1170 | 1171 | del.set_free_with_void(static_cast(&mc)); 1172 | EXPECT_EQ(del(1), 3); 1173 | 1174 | del.set_free_with_void(static_cast(&cmc)); 1175 | EXPECT_EQ(del(1), 3); 1176 | 1177 | // Make sure we handle const overloading reasonable. 1178 | del.set_free_with_void(static_cast(&mc)); 1179 | EXPECT_EQ(del(0), -1); 1180 | del.set_free_with_void(static_cast(&mc)); 1181 | EXPECT_EQ(del(0), -2); 1182 | 1183 | // nullptr will resolve to non const variant. User will need to cast 1184 | // if it doesn't work. 1185 | del.set_free_with_void(nullptr); 1186 | EXPECT_EQ(del(0), -1); 1187 | 1188 | 1189 | del = Del::make_free_with_void(static_cast(&mc)); 1190 | EXPECT_EQ(del(1), 2); 1191 | 1192 | //del = Del::make_free_with_void(static_cast(&cmc)); EXPECT_EQ(del(1), 2); 1194 | 1195 | del = Del::make_free_with_void( 1196 | static_cast(&mc)); 1197 | EXPECT_EQ(del(1), 3); 1198 | 1199 | del = Del::make_free_with_void( 1200 | static_cast(&cmc)); 1201 | EXPECT_EQ(del(1), 3); 1202 | 1203 | // Make sure we handle const overloading reasonable. 1204 | del = Del::make_free_with_void(static_cast(&mc)); 1205 | EXPECT_EQ(del(0), -1); 1206 | del = Del::make_free_with_void(static_cast(&mc)); 1207 | EXPECT_EQ(del(0), -2); 1208 | 1209 | // nullptr will resolve to non const variant. User will need to cast 1210 | // if it doesn't work. It is a choice. 1211 | // This is usually used with older code where const often is not used as often. 1212 | del = Del::make_free_with_void(nullptr); 1213 | EXPECT_EQ(del(0), -1); 1214 | } 1215 | 1216 | TEST(delegate, with_object) 1217 | { 1218 | MemberCheck mc; 1219 | MemberCheck const cmc; 1220 | 1221 | using Del = delegate; 1222 | Del del; 1223 | 1224 | del.set_free_with_object(mc); 1225 | EXPECT_EQ(del(1), 2); 1226 | 1227 | // del.set_free_with_object(cmc); 1228 | // EXPECT_EQ(del(1), 2); 1229 | 1230 | del.set_free_with_object(cmc); 1231 | EXPECT_EQ(del(1), 3); 1232 | 1233 | del.set_free_with_object(cmc); 1234 | EXPECT_EQ(del(1), 3); 1235 | 1236 | del = Del::make_free_with_object(mc); 1237 | EXPECT_EQ(del(1), 2); 1238 | 1239 | // del = Del::make_free_with_object(cmc); 1240 | // EXPECT_EQ(del(1), 2); 1241 | 1242 | del = Del::make_free_with_object(cmc); 1243 | EXPECT_EQ(del(1), 3); 1244 | 1245 | del = Del::make_free_with_object(cmc); 1246 | EXPECT_EQ(del(1), 3); 1247 | } 1248 | 1249 | static delegate 1250 | make_exchange(int& store) 1251 | { 1252 | using Del = delegate; 1253 | typename Del::Trampoline adapterFkn = [](const typename Del::DataPtr& v, 1254 | int val) -> int { 1255 | int* p = static_cast(v.ptr()); 1256 | std::swap(*p, val); 1257 | return val; 1258 | }; 1259 | return Del{adapterFkn, static_cast(&store)}; 1260 | } 1261 | 1262 | TEST(delegate, use_extension) 1263 | { 1264 | int t = 2; 1265 | delegate del = make_exchange(t); 1266 | EXPECT_EQ(t, 2); 1267 | EXPECT_EQ(del(5), 2); 1268 | EXPECT_EQ(t, 5); 1269 | } 1270 | 1271 | 1272 | TEST(delegate, Store_delegate_in_std_function) 1273 | { 1274 | using Del = delegate; 1275 | auto del = Del::make(); 1276 | EXPECT_EQ(del(1), 6); 1277 | 1278 | // Can call delegate within a std::function. 1279 | std::function sFkn{del}; 1280 | EXPECT_EQ(sFkn(5), 10); 1281 | 1282 | // Determine if the delegate require heap allocation. 1283 | 1284 | Del* d_p = sFkn.target(); 1285 | auto fn_base = reinterpret_cast(&sFkn); 1286 | auto fn_next = reinterpret_cast(&sFkn + 1); 1287 | auto fn_del = reinterpret_cast(d_p); 1288 | 1289 | if (fn_del >= fn_base && fn_del < fn_next) 1290 | { 1291 | std::cout << "Delegate within std::function. SOO active." << std::endl; 1292 | } 1293 | else 1294 | { 1295 | std::cout << "Delegate outside std::function. Heap allocated." << std::endl; 1296 | } 1297 | } 1298 | --------------------------------------------------------------------------------