├── .gitignore ├── tests ├── cmake-pkg-demo │ ├── readme.md │ ├── CMakeLists.txt │ └── main.cpp ├── CMakeLists.txt ├── benchmark │ ├── CMakeLists.txt │ ├── testmain.cpp │ ├── test.h │ ├── b6_callbacklist_add_remove_callbacks.cpp │ ├── b2_map_vs_unordered_map.cpp │ ├── b8_eventqueue_anydata.cpp │ └── b7_callbacklist_vs_function_list.cpp ├── tutorial │ ├── CMakeLists.txt │ ├── tutorial.h │ ├── tutorialmain.cpp │ ├── tutorial_hetereventdispatcher.cpp │ ├── tutorial_hetercallbacklist.cpp │ └── tutorial_callbacklist.cpp ├── unittest │ ├── testmain.cpp │ ├── CMakeLists.txt │ ├── test_dispatcher_multithread.cpp │ ├── test.h │ ├── test_heterdispatcher_multithread.cpp │ ├── test_eventmaker.cpp │ ├── test_conditionalfunctor.cpp │ ├── test_callbacklist_util.h │ ├── test_queue_ordered_list.cpp │ ├── test_heterdispatcher_ctors.cpp │ ├── test_anyid.cpp │ └── test_heterqueue_basic.cpp └── build │ └── makefile ├── cmake └── eventppConfig.cmake.in ├── doc ├── cn │ ├── readme.md │ ├── introduction.md │ ├── tutorial_eventqueue.md │ ├── tutorial_callbacklist.md │ ├── tutorial_eventdispatcher.md │ ├── callbacklist.md │ └── mixins.md ├── build_for_development.md ├── eventutil.md ├── conditionalfunctor.md ├── heterogeneous.md ├── orderedqueuelist.md ├── counterremover.md ├── eventmaker.md ├── install.md ├── introduction.md ├── conditionalremover.md ├── tutorial_callbacklist.md ├── tip_sample_use_cases.md ├── faq.md ├── tutorial_eventdispatcher.md └── hetercallbacklist.md ├── license ├── .github └── workflows │ └── main.yml ├── include └── eventpp │ ├── utilities │ ├── conditionalfunctor.h │ ├── orderedqueuelist.h │ ├── argumentadapter.h │ ├── eventutil.h │ └── anyid.h │ ├── mixins │ ├── mixinfilter.h │ └── mixinheterfilter.h │ ├── internal │ ├── eventqueue_i.h │ └── typeutil_i.h │ └── eventpolicies.h └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | tests/build/temp* 2 | tests/build/project* 3 | tests/coverage 4 | build 5 | .vscode 6 | -------------------------------------------------------------------------------- /tests/cmake-pkg-demo/readme.md: -------------------------------------------------------------------------------- 1 | # Demo for CMake package usage 2 | 3 | This folder demonstrates how to use eventpp as CMake package. 4 | See the project readme.md for how to build the demo program. 5 | 6 | -------------------------------------------------------------------------------- /cmake/eventppConfig.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component(eventpp_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | if (NOT TARGET eventpp::eventpp) 3 | include("${eventpp_CMAKE_DIR}/eventppTargets.cmake") 4 | endif () 5 | 6 | set(eventpp_LIBRARIES eventpp::eventpp) 7 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | project(eventpptest) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | set(THIRDPARTY_PATH ../../thirdparty) 8 | 9 | include_directories(../include) 10 | 11 | if(MSVC) 12 | add_definitions(/W4) 13 | else() 14 | add_definitions(-Wall -Wextra -Wpedantic) 15 | endif() 16 | 17 | enable_testing() 18 | 19 | add_subdirectory(tutorial) 20 | add_subdirectory(unittest) 21 | add_subdirectory(benchmark) 22 | 23 | -------------------------------------------------------------------------------- /tests/cmake-pkg-demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(eventpppkgdemo) 2 | 3 | cmake_minimum_required(VERSION 3.2) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | set(TARGET pkgdemo) 8 | 9 | set(SRC 10 | main.cpp 11 | ) 12 | 13 | add_executable( 14 | ${TARGET} 15 | ${SRC} 16 | ) 17 | 18 | find_package(eventpp CONFIG REQUIRED) 19 | target_link_libraries(${TARGET} PRIVATE eventpp::eventpp) 20 | 21 | # need this because https://github.com/Microsoft/vcpkg/issues/798 22 | find_path(EVENTPP_INCLUDE_DIR eventpp/eventqueue.h) 23 | include_directories(${EVENTPP_INCLUDE_DIR}) 24 | -------------------------------------------------------------------------------- /tests/benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TARGET_BENCHMARK benchmark) 2 | 3 | set(SRC_BENCHMARK 4 | testmain.cpp 5 | b1_callbacklist_invoking_vs_cpp.cpp 6 | b2_map_vs_unordered_map.cpp 7 | b3_b5_eventqueue.cpp 8 | b6_callbacklist_add_remove_callbacks.cpp 9 | b7_callbacklist_vs_function_list.cpp 10 | b8_eventqueue_anydata.cpp 11 | ) 12 | 13 | add_executable( 14 | ${TARGET_BENCHMARK} 15 | ${SRC_BENCHMARK} 16 | ) 17 | 18 | set(THREADS_PREFER_PTHREAD_FLAG ON) 19 | find_package(Threads REQUIRED) 20 | target_link_libraries(${TARGET_BENCHMARK} Threads::Threads) 21 | 22 | -------------------------------------------------------------------------------- /tests/tutorial/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TARGET_TUTORIAL tutorial) 2 | 3 | set(SRC_TUTORIAL 4 | tutorialmain.cpp 5 | tutorial_callbacklist.cpp 6 | tutorial_eventdispatcher.cpp 7 | tutorial_eventqueue.cpp 8 | tutorial_hetercallbacklist.cpp 9 | tutorial_hetereventdispatcher.cpp 10 | tutorial_argumentadapter.cpp 11 | tutorial_anydata.cpp 12 | tip_use_type_as_id.cpp 13 | ) 14 | 15 | add_executable( 16 | ${TARGET_TUTORIAL} 17 | ${SRC_TUTORIAL} 18 | ) 19 | 20 | set(THREADS_PREFER_PTHREAD_FLAG ON) 21 | find_package(Threads REQUIRED) 22 | target_link_libraries(${TARGET_TUTORIAL} Threads::Threads) 23 | 24 | -------------------------------------------------------------------------------- /doc/cn/readme.md: -------------------------------------------------------------------------------- 1 | # eventpp -- C++ 事件派发和回调列表开源库 2 | 3 | 本文档还有待完善,以下是一些已经翻译的中文文档。 4 | 非常感谢 [marsCatXdu](https://github.com/marsCatXdu) 的中文翻译。 5 | 6 | 文档 7 | 8 | * 核心类和函数库 9 | * [概述](introduction.md) 10 | * [CallbackList 教程](tutorial_callbacklist.md) 11 | * [EventDispatcher 教程](tutorial_eventdispatcher.md) 12 | * [EventQueue 教程](tutorial_eventqueue.md) 13 | * [CallbackList 类参考手册](callbacklist.md) 14 | * [EventDispatcher 类参考手册](eventdispatcher.md) 15 | * [EventQueue 类参考手册](eventqueue.md) 16 | * [Policies -- 配置 eventpp](policies.md) 17 | * [Mixins -- 扩展 eventpp](mixins.md) 18 | 19 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | eventpp library 2 | 3 | Copyright (C) 2018 Wang Qi (wqking) 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | Test: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [windows-latest, ubuntu-latest, macos-latest] 11 | build-type: [Release] 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Configure 16 | run: | 17 | mkdir -p build 18 | cmake -DCMAKE_BUILD_TYPE="${{ matrix.build-type }}" -B build -S tests 19 | 20 | - name: Build Tests 21 | run: cmake --build build --config "${{ matrix.build-type }}" -j 22 | 23 | - name: Run unit tests 24 | run: cd build && ctest --build-config "${{ matrix.build-type }}" --progress --verbose -------------------------------------------------------------------------------- /tests/tutorial/tutorial.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef TUTORIAL_H 15 | #define TUTORIAL_H 16 | 17 | #include "../catch.hpp" 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /tests/benchmark/testmain.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 15 | #include "test.h" 16 | -------------------------------------------------------------------------------- /tests/unittest/testmain.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 15 | #include "test.h" 16 | -------------------------------------------------------------------------------- /tests/tutorial/tutorialmain.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 15 | #include "tutorial.h" 16 | -------------------------------------------------------------------------------- /tests/benchmark/test.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef TEST_H 15 | #define TEST_H 16 | 17 | #include "../catch.hpp" 18 | 19 | #include 20 | #include 21 | 22 | template 23 | uint64_t measureElapsedTime(F f) 24 | { 25 | std::chrono::steady_clock::time_point t = std::chrono::steady_clock::now(); 26 | f(); 27 | return std::chrono::duration_cast(std::chrono::steady_clock::now() - t).count(); 28 | } 29 | 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /doc/build_for_development.md: -------------------------------------------------------------------------------- 1 | # Build eventpp for development 2 | 3 | The library itself is header only and doesn't need building. 4 | If you want to improve eventpp, you need to build the test code. 5 | 6 | There are three parts of code to test the library, 7 | 8 | - unittests: tests the library. They require C++17 since it uses generic lambda and `std::any` (the library itself only requires C++11). 9 | - tutorials: sample code to demonstrate how to use the library. They require C++11. If you want to have a quick study on how to use the library, you can look at the tutorials. 10 | - benchmarks: measure the library performance. 11 | 12 | All parts are in the `tests` folder. 13 | 14 | All three parts require CMake to build, and there is a makefile to ease the building. 15 | Go to folder `tests/build`, then run `make` with different target. 16 | - `make vc19` #generate solution files for Microsoft Visual Studio 2019, then open eventpptest.sln in folder project_vc19 17 | - `make vc17` #generate solution files for Microsoft Visual Studio 2017, then open eventpptest.sln in folder project_vc17 18 | - `make vc15` #generate solution files for Microsoft Visual Studio 2015, then open eventpptest.sln in folder project_vc15 19 | - `make mingw` #build using MinGW 20 | - `make linux` #build on Linux 21 | - `make mingw_coverage` #build using MinGW and generate code coverage report 22 | 23 | -------------------------------------------------------------------------------- /tests/unittest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TARGET_TEST unittest) 2 | 3 | set(SRC_TEST 4 | testmain.cpp 5 | test_callbacklist_basic.cpp 6 | test_callbacklist_ctors.cpp 7 | test_callbacklist_multithread.cpp 8 | test_dispatcher_basic.cpp 9 | test_dispatcher_ctors.cpp 10 | test_dispatcher_multithread.cpp 11 | test_queue_basic.cpp 12 | test_queue_ctors.cpp 13 | test_queue_multithread.cpp 14 | test_queue_ordered_list.cpp 15 | test_hetercallbacklist_basic.cpp 16 | test_hetercallbacklist_ctors.cpp 17 | test_heterdispatcher_basic.cpp 18 | test_heterdispatcher_ctors.cpp 19 | test_heterdispatcher_multithread.cpp 20 | test_heterqueue_basic.cpp 21 | test_eventutil.cpp 22 | test_eventmaker.cpp 23 | test_conditionalremover.cpp 24 | test_counterremover.cpp 25 | test_scopedremover.cpp 26 | test_no_extra_copy_move.cpp 27 | test_argumentadapter.cpp 28 | test_conditionalfunctor.cpp 29 | test_anyid.cpp 30 | test_anydata.cpp 31 | ) 32 | 33 | add_executable( 34 | ${TARGET_TEST} 35 | ${SRC_TEST} 36 | ) 37 | 38 | set(THREADS_PREFER_PTHREAD_FLAG ON) 39 | find_package(Threads REQUIRED) 40 | target_link_libraries(${TARGET_TEST} Threads::Threads) 41 | 42 | set_target_properties(${TARGET_TEST} PROPERTIES CXX_STANDARD 17) 43 | 44 | if(CMAKE_COMPILER_IS_GNUCXX) 45 | if(coverage) 46 | set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") 47 | endif() 48 | endif() 49 | 50 | add_test(NAME ${TARGET_TEST} COMMAND ${TARGET_TEST}) 51 | -------------------------------------------------------------------------------- /include/eventpp/utilities/conditionalfunctor.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef CONDITIONALFUNCTOR_H_828958739581 15 | #define CONDITIONALFUNCTOR_H_828958739581 16 | 17 | #include 18 | 19 | namespace eventpp { 20 | 21 | template 22 | struct ConditionalFunctor 23 | { 24 | ConditionalFunctor(Func f, Condition condition) 25 | : func(std::move(f)), condition(std::move(condition)) 26 | { 27 | } 28 | 29 | template 30 | void operator() (A &&...args) { 31 | if(condition(args...)) { 32 | func(std::forward(args)...); 33 | } 34 | } 35 | 36 | Func func; 37 | Condition condition; 38 | }; 39 | 40 | template 41 | ConditionalFunctor conditionalFunctor(Func func, Condition condition) 42 | { 43 | return ConditionalFunctor(std::move(func), std::move(condition)); 44 | } 45 | 46 | 47 | } //namespace eventpp 48 | 49 | #endif 50 | 51 | -------------------------------------------------------------------------------- /tests/benchmark/b6_callbacklist_add_remove_callbacks.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "eventpp/callbacklist.h" 16 | 17 | TEST_CASE("b6, CallbackList add/remove callbacks") 18 | { 19 | std::cout << std::endl << "b6, CallbackList add/remove callbacks" << std::endl; 20 | 21 | using CL = eventpp::CallbackList; 22 | constexpr size_t callbackCount = 1000; 23 | constexpr size_t iterateCount = 1000 * 100; 24 | CL callbackList; 25 | std::vector handleList(callbackCount); 26 | const uint64_t time = measureElapsedTime( 27 | [callbackCount, iterateCount, &callbackList, &handleList]() { 28 | for(size_t iterate = 0; iterate < iterateCount; ++iterate) { 29 | for(size_t i = 0; i < callbackCount; ++i) { 30 | handleList[i] = callbackList.append([]() {}); 31 | } 32 | for(size_t i = 0; i < callbackCount; ++i) { 33 | callbackList.remove(handleList[i]); 34 | } 35 | } 36 | }); 37 | 38 | std::cout 39 | << "CallbackList add/remove callbacks," 40 | << " callbackCount: " << callbackCount 41 | << " iterateCount: " << iterateCount 42 | << " time: " << time 43 | << std::endl; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /tests/cmake-pkg-demo/main.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Include the head 15 | #include "eventpp/eventqueue.h" 16 | 17 | #include 18 | 19 | int main() 20 | { 21 | eventpp::EventQueue &)> queue; 22 | 23 | queue.appendListener(3, [](const std::string & s, std::unique_ptr & n) { 24 | std::cout << "Got event 3, s is " << s << " n is " << *n << std::endl; 25 | }); 26 | // The listener prototype doesn't need to be exactly same as the dispatcher. 27 | // It would be find as long as the arguments is compatible with the dispatcher. 28 | queue.appendListener(5, [](std::string s, const std::unique_ptr & n) { 29 | std::cout << "Got event 5, s is " << s << " n is " << *n << std::endl; 30 | }); 31 | queue.appendListener(5, [](const std::string & s, std::unique_ptr & n) { 32 | std::cout << "Got another event 5, s is " << s << " n is " << *n << std::endl; 33 | }); 34 | 35 | // Enqueue the events, the first argument is always the event type. 36 | // The listeners are not triggered during enqueue. 37 | queue.enqueue(3, "Hello", std::unique_ptr(new int(38))); 38 | queue.enqueue(5, "World", std::unique_ptr(new int(58))); 39 | 40 | // Process the event queue, dispatch all queued events. 41 | queue.process(); 42 | 43 | return 0; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /include/eventpp/mixins/mixinfilter.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef MIXINFILTER_H_713231680355 15 | #define MIXINFILTER_H_713231680355 16 | 17 | #include "../callbacklist.h" 18 | 19 | namespace eventpp { 20 | 21 | template 22 | class MixinFilter : public Base 23 | { 24 | private: 25 | using super = Base; 26 | 27 | using BoolReferencePrototype = typename internal_::ReplaceReturnType< 28 | typename internal_::TransformArguments< 29 | typename super::Prototype, 30 | std::add_lvalue_reference 31 | >::Type, 32 | bool 33 | >::Type; 34 | 35 | using Filter = std::function; 36 | using FilterList = CallbackList; 37 | 38 | public: 39 | using FilterHandle = typename FilterList::Handle; 40 | 41 | public: 42 | FilterHandle appendFilter(const Filter & filter) 43 | { 44 | return filterList.append(filter); 45 | } 46 | 47 | bool removeFilter(const FilterHandle & filterHandle) 48 | { 49 | return filterList.remove(filterHandle); 50 | } 51 | 52 | template 53 | bool mixinBeforeDispatch(Args && ...args) const { 54 | if(! filterList.empty()) { 55 | if(! filterList.forEachIf([&args...](typename FilterList::Callback & callback) { 56 | return callback(args...); 57 | }) 58 | ) { 59 | return false; 60 | } 61 | } 62 | 63 | return true; 64 | } 65 | 66 | private: 67 | FilterList filterList; 68 | }; 69 | 70 | 71 | } //namespace eventpp 72 | 73 | 74 | #endif 75 | 76 | -------------------------------------------------------------------------------- /include/eventpp/mixins/mixinheterfilter.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef MIXINHETERFILTER_H_990158796753 15 | #define MIXINHETERFILTER_H_990158796753 16 | 17 | #include "../hetercallbacklist.h" 18 | 19 | namespace eventpp { 20 | 21 | template 22 | class MixinHeterFilter : public Base 23 | { 24 | private: 25 | using super = Base; 26 | 27 | using BoolReferencePrototypeList = typename internal_::ReplaceReturnTypeList< 28 | typename internal_::TransformArgumentsList< 29 | typename super::PrototypeList, 30 | std::add_lvalue_reference 31 | >::Type, 32 | bool 33 | >::Type; 34 | 35 | using FilterList = HeterCallbackList; 36 | 37 | public: 38 | using FilterHandle = typename FilterList::Handle; 39 | 40 | public: 41 | template 42 | FilterHandle appendFilter(const Callback & filter) 43 | { 44 | return filterList.append(filter); 45 | } 46 | 47 | bool removeFilter(const FilterHandle & filterHandle) 48 | { 49 | return filterList.remove(filterHandle); 50 | } 51 | 52 | template 53 | bool mixinBeforeDispatch(Args && ...args) const { 54 | if(! filterList.template forEachIf([&args...](const typename std::function & callback) -> bool { 55 | return callback(std::forward(args)...); 56 | }) 57 | ) { 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | private: 65 | FilterList filterList; 66 | }; 67 | 68 | 69 | } //namespace eventpp 70 | 71 | 72 | #endif 73 | 74 | -------------------------------------------------------------------------------- /include/eventpp/utilities/orderedqueuelist.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ORDEREDQUEUELIST_H_862875671799 15 | #define ORDEREDQUEUELIST_H_862875671799 16 | 17 | #include "../eventpolicies.h" 18 | 19 | #include 20 | 21 | namespace eventpp { 22 | 23 | struct OrderedQueueListCompare 24 | { 25 | template 26 | bool operator() (const T & a, const T & b) const { 27 | return a.event < b.event; 28 | } 29 | }; 30 | 31 | template 32 | class OrderedQueueList : private std::list 33 | { 34 | private: 35 | using super = std::list; 36 | 37 | public: 38 | using iterator = typename super::iterator; 39 | using const_iterator = typename super::const_iterator; 40 | using super::empty; 41 | using super::begin; 42 | using super::end; 43 | using super::front; 44 | using super::swap; 45 | using super::emplace_back; 46 | 47 | void splice(const_iterator pos, OrderedQueueList & other) { 48 | super::splice(pos, other); 49 | doSort(); 50 | } 51 | 52 | void splice(const_iterator pos, OrderedQueueList & other, const_iterator it) { 53 | super::splice(pos, other, it); 54 | doSort(); 55 | } 56 | 57 | private: 58 | void doSort() { 59 | auto compare = Compare(); 60 | this->sort([compare](const T & a, const T & b) { 61 | // a and b may be empty if they are recycled to free list. 62 | if(a.empty()) { 63 | if(b.empty()) { 64 | return false; 65 | } 66 | return true; 67 | } 68 | else if(b.empty()) { 69 | return false; 70 | } 71 | 72 | return compare(a.get(), b.get()); 73 | }); 74 | } 75 | }; 76 | 77 | 78 | } //namespace eventpp 79 | 80 | #endif 81 | 82 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(eventpp VERSION 0.1.3) 3 | 4 | add_library(eventpp INTERFACE) 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | include(GNUInstallDirs) 8 | target_include_directories( 9 | eventpp INTERFACE 10 | $ 11 | $ 12 | ) 13 | 14 | add_library(eventpp::eventpp ALIAS eventpp) 15 | 16 | # Checks if eventpp is the main project or if it is 17 | # being built as a subproject (using add_subdirectory/FetchContent). 18 | set(MAIN_PROJECT OFF) 19 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 20 | set(MAIN_PROJECT ON) 21 | endif() 22 | 23 | # Installation 24 | # ------------ 25 | if (POLICY CMP0077) 26 | # Allow CMake 3.13+ to override options when using add_subdirectory/FetchContent. 27 | cmake_policy(SET CMP0077 NEW) 28 | endif (POLICY CMP0077) 29 | 30 | option(EVENTPP_INSTALL "Enable installation" ${MAIN_PROJECT}) 31 | 32 | if (EVENTPP_INSTALL) 33 | # Install the library 34 | install( 35 | TARGETS eventpp 36 | EXPORT eventppTargets 37 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 38 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 39 | ) 40 | 41 | # Install the headers 42 | install( 43 | DIRECTORY include/ 44 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 45 | ) 46 | 47 | # (Generate and) install the target import file 48 | install( 49 | EXPORT eventppTargets 50 | NAMESPACE eventpp:: 51 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eventpp 52 | ) 53 | 54 | # Generate the package version file 55 | include(CMakePackageConfigHelpers) 56 | write_basic_package_version_file( 57 | ${CMAKE_CURRENT_BINARY_DIR}/eventppConfigVersion.cmake 58 | VERSION ${PROJECT_VERSION} 59 | COMPATIBILITY AnyNewerVersion 60 | ) 61 | 62 | # Generate the package configuration file, that allows other 63 | # CMake projects to find the library with find_package() 64 | configure_package_config_file( 65 | cmake/eventppConfig.cmake.in 66 | ${CMAKE_CURRENT_BINARY_DIR}/eventppConfig.cmake 67 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eventpp 68 | ) 69 | 70 | # Install the package version and configuration files 71 | install( 72 | FILES 73 | ${CMAKE_CURRENT_BINARY_DIR}/eventppConfig.cmake 74 | ${CMAKE_CURRENT_BINARY_DIR}/eventppConfigVersion.cmake 75 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eventpp 76 | ) 77 | 78 | # Install readme and license 79 | install( 80 | FILES 81 | readme.md 82 | license 83 | DESTINATION ${CMAKE_INSTALL_DATADIR}/eventpp 84 | ) 85 | endif() 86 | -------------------------------------------------------------------------------- /tests/unittest/test_dispatcher_multithread.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "eventpp/eventdispatcher.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | TEST_CASE("EventDispatcher, multi threading, int, void (int)") 23 | { 24 | using ED = eventpp::EventDispatcher; 25 | ED dispatcher; 26 | 27 | constexpr int threadCount = 256; 28 | constexpr int eventCountPerThread = 1024 * 4; 29 | constexpr int itemCount = threadCount * eventCountPerThread; 30 | 31 | std::vector eventList(itemCount); 32 | std::iota(eventList.begin(), eventList.end(), 0); 33 | std::shuffle(eventList.begin(), eventList.end(), std::mt19937(std::random_device()())); 34 | 35 | std::vector dataList(itemCount); 36 | std::vector handleList(itemCount); 37 | 38 | std::vector threadList; 39 | 40 | for(int i = 0; i < threadCount; ++i) { 41 | threadList.emplace_back([i, eventCountPerThread, &dispatcher, &eventList, &handleList, &dataList]() { 42 | for(int k = i * eventCountPerThread; k < (i + 1) * eventCountPerThread; ++k) { 43 | handleList[k] = dispatcher.appendListener(eventList[k], [&dispatcher, k, &dataList, &eventList, &handleList](const int e) { 44 | dataList[k] += e; 45 | dispatcher.removeListener(eventList[k], handleList[k]); 46 | }); 47 | } 48 | }); 49 | } 50 | for(int i = 0; i < threadCount; ++i) { 51 | threadList[i].join(); 52 | } 53 | 54 | threadList.clear(); 55 | for(int i = 0; i < threadCount; ++i) { 56 | threadList.emplace_back([i, eventCountPerThread, &dispatcher, &eventList]() { 57 | for(int k = i * eventCountPerThread; k < (i + 1) * eventCountPerThread; ++k) { 58 | dispatcher.dispatch(eventList[k]); 59 | } 60 | }); 61 | } 62 | for(int i = 0; i < threadCount; ++i) { 63 | threadList[i].join(); 64 | } 65 | 66 | std::sort(eventList.begin(), eventList.end()); 67 | std::sort(dataList.begin(), dataList.end()); 68 | 69 | REQUIRE(eventList == dataList); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /doc/eventutil.md: -------------------------------------------------------------------------------- 1 | # Utilities reference 2 | 3 | ## Header 4 | 5 | eventpp/utilities/eventutil.h 6 | 7 | ## API reference 8 | 9 | ```c++ 10 | template 11 | bool removeListener( 12 | DispatcherType & dispatcher, 13 | const typename DispatcherType::Event & event, 14 | const typename DispatcherType::Callback & listener 15 | ); 16 | ``` 17 | The function finds `listener` of `event` in `dispatcher`, if it finds one, removes the listener and returns true, otherwise returns false. 18 | Note: the function only removes the first found listener. To remove more than one listeners, repeat calling this function until it returns false. 19 | This function requires the listener be able to be compared with equal operator (==). 20 | 21 | ```c++ 22 | template 23 | bool removeListener( 24 | CallbackListType & callbackList, 25 | const typename CallbackListType::Callback & callback 26 | ); 27 | ``` 28 | The function finds `callback` in `callbackList`, if it finds one, removes the callback and returns true, otherwise returns false. 29 | Note: the function only removes the first found callback. To remove more than one callbacks, repeat calling this function until it returns false. 30 | This function requires the callback be able to be compared with equal operator (==). 31 | 32 | ```c++ 33 | template 34 | bool hasListener( 35 | DispatcherType & dispatcher, 36 | const typename DispatcherType::Event & event, 37 | const typename DispatcherType::Callback & listener 38 | ); 39 | ``` 40 | The function finds `listener` of `event` in `dispatcher`, returns true if it finds any one, otherwise returns false. 41 | This function requires the listener be able to be compared with equal operator (==). 42 | 43 | ```c++ 44 | template 45 | bool hasAnyListener( 46 | DispatcherType & dispatcher, 47 | const typename DispatcherType::Event & event 48 | ); 49 | ``` 50 | The function finds any listener of `event` in `dispatcher`, returns true if it finds any one, otherwise returns false. 51 | 52 | ```c++ 53 | template 54 | bool hasListener( 55 | CallbackListType & callbackList, 56 | const typename CallbackListType::Callback & callback 57 | ); 58 | ``` 59 | The function finds `callback` in `callbackList`, returns true if it finds one, otherwise returns false. 60 | This function requires the callback be able to be compared with equal operator (==). 61 | 62 | ```c++ 63 | template 64 | bool hasAnyListener( 65 | CallbackListType & callbackList 66 | ); 67 | ``` 68 | The function finds any callback in `callbackList`, returns true if it finds any one, otherwise returns false. 69 | -------------------------------------------------------------------------------- /doc/conditionalfunctor.md: -------------------------------------------------------------------------------- 1 | # Conditional functor reference 2 | 3 | ## Description 4 | 5 | The header file `eventpp/utilities/conditionalfunctor.h` contains utilities that can check certain condition then decide whether to invoke the underlying function. 6 | 7 | For example, 8 | 9 | ```c++ 10 | eventpp::CallbackList callbackList; 11 | 12 | callbackList.append( 13 | eventpp::conditionalFunctor( 14 | [](const int value) { 15 | std::cout << "We should get value 1." << here; 16 | }, 17 | [](const int value) { 18 | return value == 1; 19 | } 20 | ) 21 | ); 22 | callbackList(2); // not trigger the callback 23 | callbackList(1); // trigger the callback 24 | ``` 25 | 26 | ## Header 27 | 28 | eventpp/utilities/conditionalfunctor.h 29 | 30 | ## API reference 31 | 32 | ```c++ 33 | template 34 | ConditionalFunctor conditionalFunctor(Func func, Condition condition); 35 | ``` 36 | 37 | Function `conditionalFunctor` receives a function `func`, and a conditional function `condition`, and return a functor object of `ConditionalFunctor`. `ConditionalFunctor` has a function invoking operator that forwards all arguments to `condition`, if `condition` returns true, `func` will be invoked with same arguments, if `condition` return false, `func` will not be invoked. The return value of `conditionalFunctor` can be passed to CallbackList, EventDispatcher, or EventQueue. 38 | 39 | Below is the example code to demonstrate how to use `conditionalFunctor`, it's grabbed from the unit tests. 40 | 41 | ```c++ 42 | 43 | eventpp::CallbackList callbackList; 44 | 45 | std::vector dataList(3); 46 | callbackList.append( 47 | eventpp::conditionalFunctor( 48 | [&dataList](const int index) { 49 | ++dataList[index]; 50 | }, 51 | [](const int index) { 52 | return index == 0; 53 | } 54 | ) 55 | ); 56 | callbackList.append( 57 | eventpp::conditionalFunctor( 58 | [&dataList](const int index) { 59 | ++dataList[index]; 60 | }, 61 | [](const int index) { 62 | return index == 1; 63 | } 64 | ) 65 | ); 66 | callbackList.append( 67 | eventpp::conditionalFunctor( 68 | [&dataList](const int index) { 69 | ++dataList[index]; 70 | }, 71 | [](const int index) { 72 | return index == 2; 73 | } 74 | ) 75 | ); 76 | 77 | REQUIRE(dataList == std::vector{ 0, 0, 0 }); 78 | 79 | callbackList(2); 80 | REQUIRE(dataList == std::vector{ 0, 0, 1 }); 81 | 82 | callbackList(0); 83 | REQUIRE(dataList == std::vector{ 1, 0, 1 }); 84 | 85 | callbackList(1); 86 | REQUIRE(dataList == std::vector{ 1, 1, 1 }); 87 | 88 | ``` 89 | -------------------------------------------------------------------------------- /tests/unittest/test.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef TEST_H 15 | #define TEST_H 16 | 17 | #include "../catch.hpp" 18 | 19 | template 20 | struct EraseArgs1 21 | { 22 | template 23 | explicit EraseArgs1(const C & callable) : callable(callable) 24 | { 25 | } 26 | 27 | template 28 | ReturnType operator() (First &&, Args && ...args) 29 | { 30 | callable(std::forward(args)...); 31 | } 32 | 33 | Callable callable; 34 | }; 35 | 36 | template 37 | EraseArgs1 eraseArgs1(const Callable & callable) 38 | { 39 | return EraseArgs1(callable); 40 | } 41 | 42 | template 43 | bool checkAllWeakPtrAreFreed(const T & nodeList) 44 | { 45 | for(const auto & node : nodeList) { 46 | if(node.lock()) { 47 | return false; 48 | } 49 | } 50 | 51 | return true; 52 | } 53 | 54 | // Can be converted from int implicitly 55 | struct FromInt 56 | { 57 | FromInt() : value(0) {} 58 | FromInt(const int value) : value(value) {} 59 | 60 | int value; 61 | }; 62 | 63 | // Can convert to int implicitly 64 | struct ToInt 65 | { 66 | ToInt() : value(0) {} 67 | explicit ToInt(const int value) : value(value) {} 68 | 69 | operator int() const { return value; } 70 | 71 | int value; 72 | }; 73 | 74 | // return 0 -- no order, 1 -- ascend -1 -- descend 75 | template 76 | int detectDataListOrder(Iterator from, Iterator to) 77 | { 78 | if(from == to) { 79 | return 0; 80 | } 81 | auto previous = *from; 82 | ++from; 83 | bool ascend = false; 84 | bool descend = false; 85 | while(from != to) { 86 | auto current = *from; 87 | ++from; 88 | if(current > previous) { 89 | ascend = true; 90 | } 91 | if(current < previous) { 92 | descend = true; 93 | } 94 | if(ascend && descend) { 95 | return 0; 96 | } 97 | previous = current; 98 | } 99 | if(ascend) { 100 | return 1; 101 | } 102 | if(descend) { 103 | return -1; 104 | } 105 | return 0; 106 | } 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /tests/unittest/test_heterdispatcher_multithread.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "eventpp/hetereventdispatcher.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | TEST_CASE("HeterEventDispatcher, multi threading") 23 | { 24 | using ED = eventpp::HeterEventDispatcher >; 25 | ED dispatcher; 26 | 27 | constexpr int threadCount = 256; 28 | constexpr int eventCountPerThread = 1024 * 4; 29 | constexpr int itemCount = threadCount * eventCountPerThread; 30 | 31 | std::vector eventList(itemCount); 32 | std::iota(eventList.begin(), eventList.end(), 0); 33 | std::shuffle(eventList.begin(), eventList.end(), std::mt19937(std::random_device()())); 34 | 35 | std::vector dataList(itemCount); 36 | std::vector handleList(itemCount * 2); 37 | 38 | std::vector threadList; 39 | 40 | for(int i = 0; i < threadCount; ++i) { 41 | threadList.emplace_back([i, eventCountPerThread, &dispatcher, &eventList, &handleList, &dataList, itemCount]() { 42 | for(int k = i * eventCountPerThread; k < (i + 1) * eventCountPerThread; ++k) { 43 | handleList[k] = dispatcher.appendListener(eventList[k], [&dispatcher, k, &dataList, &eventList, &handleList](const int e) { 44 | dataList[k] += e; 45 | dispatcher.removeListener(eventList[k], handleList[k]); 46 | }); 47 | handleList[k + itemCount] = dispatcher.appendListener( 48 | eventList[k], [&dispatcher, k, &dataList, &eventList, &handleList, itemCount](const int /*e*/, const int n) { 49 | dataList[k] += n; 50 | dispatcher.removeListener(eventList[k], handleList[k + itemCount]); 51 | }); 52 | } 53 | }); 54 | } 55 | for(int i = 0; i < threadCount; ++i) { 56 | threadList[i].join(); 57 | } 58 | 59 | threadList.clear(); 60 | for(int i = 0; i < threadCount; ++i) { 61 | threadList.emplace_back([i, eventCountPerThread, &dispatcher, &eventList]() { 62 | for(int k = i * eventCountPerThread; k < (i + 1) * eventCountPerThread; ++k) { 63 | dispatcher.dispatch(eventList[k], eventList[k]); 64 | dispatcher.dispatch(eventList[k], eventList[k], 5); 65 | } 66 | }); 67 | } 68 | for(int i = 0; i < threadCount; ++i) { 69 | threadList[i].join(); 70 | } 71 | 72 | std::sort(eventList.begin(), eventList.end()); 73 | std::sort(dataList.begin(), dataList.end()); 74 | for(int & e : eventList) { 75 | e += 5; 76 | } 77 | 78 | REQUIRE(eventList == dataList); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /doc/cn/introduction.md: -------------------------------------------------------------------------------- 1 | # eventpp 库介绍 2 | 3 | eventpp 有三个核心的类:CallbackList, EventDispatcher 和 EventQueue ,三者各有其特定的用途。 4 | 5 | ## CallbackList 类 6 | 7 | CallbackList 是 eventpp 中最基础的类,EventDispatcher 和 EventQueue 类的实现都建立在 CallbackList 之上。 8 | 9 | CallbackList 会维护一个回调函数列表。当一个 CallbackList 被调用时,该 CallbackList 会逐个调用其中的回调函数。CallbackList 可以类比为 Qt 中的信号槽系统,以及某些 Windows API 中的回调函数指针(比如 `ReadFileEx` 中的 IpCompletionRoutine ) 10 | 11 | 这里的“回调函数”可以是任何能够回调的目标——函数、函数指针、成员函数指针、lambda表达式、函数对象等。 12 | 13 | eventpp 中的 CallbackList 相当于是 Qt 这类事件系统中的 “信号”,但 eventpp 中并没有特定的相当于 “槽”(回调函数)的东西 —— 在 eventpp 中,任何可调用的目标都可以是槽(回调函数) 14 | 15 | 当应用场景中的事件种类很少时,直接用 CallbackList 即可满足需求:为每个事件创建一个 CallbackList,每个 CallbackList 都可以有不同的原型( prototype ),例如: 16 | 17 | ```c++ 18 | eventpp::CallbackList onStart; 19 | eventpp::CallbackList onStop; 20 | ``` 21 | 22 | 当程序中有成百上千个事件时( GUI 和游戏程序中经常会有这么多的事件),上面这种写法就难以应付了——为每个事件都单独创建 CallbackList 肯定不是什么好选择。针对存在大量事件的场景,eventpp 设计了 EventDispatcher 23 | 24 | ## EventDispatcher 类 25 | 26 | EventDispatcher 类似于 `std::map` ,能根据 EventType 寻找对应 CallbackList。 27 | 28 | EventDispatcher 维护一个 `` 映射表。在进行事件分发时, EventDispatcher 会根据事件类型(EventType)查找并调用对应的回调列表(CallbackList) 。该调用过程是同步的,监听器会在 `EventDispatcher::dispatch` 被调用时触发。 29 | 30 | EventDispatcher 适用于事件种类繁多或无法预先确定事件数量的场景。每个事件都可以根据事件类型进行分类,例如: 31 | 32 | ```c++ 33 | enum class MyEventType 34 | { 35 | redraw, 36 | mouseDown, 37 | mouseUp, 38 | // 后面也许还有 2000 个事件 39 | }; 40 | 41 | struct MyEvent 42 | { 43 | MyEventType type; 44 | // 此处定义所有事件可能会需要的数据 45 | }; 46 | 47 | struct MyEventPolicies 48 | { 49 | static MyEventType getEvent(const MyEvent &e) { 50 | return e.type; 51 | } 52 | }; 53 | 54 | eventpp::EventDispatcher dispatcher; 55 | dispatcher.dispatch(MyEvent { MyEventType::redraw }); 56 | ``` 57 | 58 | (提示:若想了解上面代码中的 `MyEventPolicies` ,请阅读https://github.com/wqking/eventpp/blob/master/doc/policies.md 文档。本篇文档中可以暂且只关注分发器: `eventpp::EventDispatcher dispatcher`) 59 | 60 | EventDispatcher 的缺点是,分发器中所有事件都必须有相同的回调原型(例如示例代码中的 `void(const MyEvnet &)`)。通常可以通过下面的方案来规避该缺点:将 Event 基类作为回调函数的参数,然后所有的事件都通过继承 Event 来传递他们自己的数据。在示例代码中,`MyEvent` 就是事件的基类,回调函数接收 `const MyEvent &` 。 61 | 62 | ## EventQueue 类 63 | 64 | EventQueue 包含了 EventDispatcher 的所有特性,并在此基础上添加了事件队列。注意:EventQueue 并不是 EventDispatcher 的子类,因此不要进行类型转换。 65 | 66 | EventQueue 是异步的。事件会在调用 `EventQueue::enqueue` 时被保存在队列中,并在 `EventQueue::process` 被调用时被处理。 67 | 68 | EventQueue 相当于 Qt 中的事件系统(QEvent)或 Windows API 中的消息处理( message processing )。 69 | 70 | ```c++ 71 | eventpp::EventQueue queue; 72 | 73 | // 将事件加入队列。第一个参数永远是事件类型 74 | // 监听器不会在进入队列期间被触发 75 | queue.enqueue(3, "Hello", true); 76 | queue.enqueue(5, "World", false); 77 | 78 | // 处理事件队列,运行队列中的所有事件 79 | queue.process(); 80 | ``` 81 | 82 | ## 线程安全 83 | 84 | 所有类都是线程安全的,可以在多个线程中同时调用所有的公共函数。如果出现问题,请到 eventpp 的项目仓库报告 bug。 85 | 本库能够确保每个单独函数调用的整体性,如 `EventDispatcher::appendListener`, `CallbackList::remove` ,但无法确保多个线程中的操作执行顺序。例如,若在某个线程分发一个事件的同一时刻,另一个线程移除了一个监听器(listener),则被移除的监听器仍有可能在其被移除之后触发。 86 | 87 | ## 异常安全 88 | 89 | 所有的类都不会抛出异常。但库代码所依赖的代码可能会在下列情况发生时抛出异常: 90 | 91 | 1. 内存耗尽,无法分配新的内存空间; 92 | 2. 监听器(回调函数)在复制、移动、对比或调用时抛出异常 93 | 94 | 几乎所有操作都有很强的异常安全性,这意味着下层数据抛出异常时能够保持其原始值。唯一的例外是 `EventQueue::process`:在抛出异常时,剩余的事件就不再会被分发了,而且队列会被清空。 95 | 96 | 97 | -------------------------------------------------------------------------------- /tests/unittest/test_eventmaker.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "eventpp/utilities/eventmaker.h" 16 | 17 | enum class EventType 18 | { 19 | draw, 20 | update, 21 | message, 22 | keyDown, 23 | keyUp 24 | }; 25 | 26 | class Event 27 | { 28 | public: 29 | explicit Event(const EventType type) : type(type) {} 30 | virtual ~Event() {} 31 | 32 | EventType getType() const { 33 | return type; 34 | } 35 | 36 | private: 37 | EventType type; 38 | }; 39 | 40 | EVENTPP_MAKE_EVENT(EventDraw, Event, EventType::draw, (std::string, getText, setText), (int, getX), (double, getSize)); 41 | 42 | TEST_CASE("eventmake, simple EventDraw") 43 | { 44 | EventDraw e("Hello", 98, 3.5); 45 | REQUIRE(e.getType() == EventType::draw); 46 | REQUIRE(e.getText() == "Hello"); 47 | REQUIRE(e.getX() == 98); 48 | REQUIRE(e.getSize() == 3.5); 49 | 50 | e.setText("world"); 51 | REQUIRE(e.getText() == "world"); 52 | } 53 | 54 | template 55 | EVENTPP_MAKE_EVENT(EventKey, Event, eventType, (int, getKey)); 56 | 57 | TEST_CASE("eventmake, templated EventKey") 58 | { 59 | EventKey eventKeyUp(0); 60 | REQUIRE(eventKeyUp.getType() == EventType::keyUp); 61 | } 62 | 63 | template 64 | class TemplatedEvent 65 | { 66 | public: 67 | TemplatedEvent(const EventType type, const int c) : type(type), c(c) {} 68 | virtual ~TemplatedEvent() {} 69 | 70 | EventType getType() const { 71 | return type; 72 | } 73 | 74 | int getA() const { 75 | return A; 76 | } 77 | 78 | int getB() const { 79 | return B; 80 | } 81 | 82 | int getC() const { 83 | return c; 84 | } 85 | 86 | private: 87 | EventType type; 88 | int c; 89 | }; 90 | 91 | EVENTPP_MAKE_EVENT(EventTemplatedDraw, (TemplatedEvent<3, 8>), (EventType::draw, 9), (std::string, getText, setText), (int, getX), (double, getSize)); 92 | TEST_CASE("eventmake, TemplatedEvent") 93 | { 94 | EventTemplatedDraw e("Hello", 98, 3.5); 95 | 96 | REQUIRE(e.getA() == 3); 97 | REQUIRE(e.getB() == 8); 98 | REQUIRE(e.getC() == 9); 99 | 100 | REQUIRE(e.getType() == EventType::draw); 101 | REQUIRE(e.getText() == "Hello"); 102 | REQUIRE(e.getX() == 98); 103 | REQUIRE(e.getSize() == 3.5); 104 | 105 | e.setText("world"); 106 | REQUIRE(e.getText() == "world"); 107 | } 108 | 109 | #define G(type, name) (type, get ## name) 110 | #define GS(type, name) (type, get ## name, set ## name) 111 | 112 | EVENTPP_MAKE_EVENT(EventDrawGS, Event, EventType::draw, 113 | GS(std::string, Text), 114 | G(int, X), 115 | G(double, Size) 116 | ); 117 | 118 | TEST_CASE("eventmake, EventDrawGS with auto getter/setter") 119 | { 120 | EventDrawGS e("Hello", 98, 3.5); 121 | REQUIRE(e.getType() == EventType::draw); 122 | REQUIRE(e.getText() == "Hello"); 123 | REQUIRE(e.getX() == 98); 124 | REQUIRE(e.getSize() == 3.5); 125 | 126 | e.setText("world"); 127 | REQUIRE(e.getText() == "world"); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /tests/tutorial/tutorial_hetereventdispatcher.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Include the head 15 | #include "eventpp/hetereventdispatcher.h" 16 | 17 | #include "tutorial.h" 18 | 19 | #include 20 | 21 | namespace { 22 | 23 | // This is the definition of event types 24 | enum class EventType 25 | { 26 | // for MouseEvent 27 | mouse, 28 | 29 | // for KeyboardEvent 30 | keyboard, 31 | }; 32 | 33 | // This is the base event. It has a getType function to return the actual event type. 34 | class Event 35 | { 36 | public: 37 | explicit Event(const EventType type) : type(type) { 38 | } 39 | 40 | virtual ~Event() { 41 | } 42 | 43 | EventType getType() const { 44 | return type; 45 | } 46 | 47 | private: 48 | EventType type; 49 | }; 50 | 51 | class MouseEvent : public Event 52 | { 53 | public: 54 | MouseEvent(const int x, const int y) 55 | : Event(EventType::mouse), x(x), y(y) 56 | { 57 | } 58 | 59 | int getX() const { return x; } 60 | int getY() const { return y; } 61 | 62 | private: 63 | int x; 64 | int y; 65 | }; 66 | 67 | class KeyboardEvent : public Event 68 | { 69 | public: 70 | explicit KeyboardEvent(const int key) 71 | : Event(EventType::keyboard), key(key) 72 | { 73 | } 74 | 75 | int getKey() const { return key; } 76 | 77 | private: 78 | int key; 79 | }; 80 | 81 | // We are going to dispatch event objects directly without specify the event type explicitly, so we need to let eventpp 82 | // know how to get the event type from the event object. The event policy does that. 83 | // Note ArgumentPassingMode must be eventpp::ArgumentPassingIncludeEvent here, 84 | // the heterogeneous doesn't support eventpp::ArgumentPassingAutoDetect. 85 | struct EventPolicy 86 | { 87 | using ArgumentPassingMode = eventpp::ArgumentPassingIncludeEvent; 88 | 89 | template 90 | static EventType getEvent(const E & event) { 91 | return event.getType(); 92 | } 93 | }; 94 | 95 | TEST_CASE("HeterEventDispatcher tutorial 1, basic") 96 | { 97 | std::cout << std::endl << "HeterEventDispatcher tutorial 1, basic" << std::endl; 98 | 99 | // The namespace is eventpp 100 | // the second parameter is a HeterTuple of the listener prototypes. 101 | eventpp::HeterEventDispatcher, 105 | EventPolicy 106 | > dispatcher; 107 | 108 | dispatcher.appendListener(EventType::mouse, [](const MouseEvent & event) { 109 | std::cout << "Received mouse event, x=" << event.getX() << " y=" << event.getY() << std::endl; 110 | }); 111 | dispatcher.appendListener(EventType::keyboard, [](const KeyboardEvent & event) { 112 | std::cout << "Received keyboard event, key=" << (char)event.getKey() << std::endl; 113 | }); 114 | 115 | dispatcher.dispatch(MouseEvent(5, 38)); 116 | dispatcher.dispatch(KeyboardEvent('W')); 117 | } 118 | 119 | 120 | 121 | } // namespace 122 | -------------------------------------------------------------------------------- /include/eventpp/internal/eventqueue_i.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTQUEUE_I_H 2 | #define EVENTQUEUE_I_H 3 | 4 | #include 5 | #include 6 | 7 | namespace eventpp { 8 | 9 | namespace internal_ { 10 | 11 | template 12 | struct IndexSequence 13 | { 14 | }; 15 | 16 | template 17 | struct MakeIndexSequence : MakeIndexSequence 18 | { 19 | }; 20 | 21 | template 22 | struct MakeIndexSequence<0, Indexes...> 23 | { 24 | using Type = IndexSequence; 25 | }; 26 | 27 | template 28 | struct CounterGuard 29 | { 30 | explicit CounterGuard(T & v) : value(v) { 31 | ++value; 32 | } 33 | 34 | ~CounterGuard() { 35 | --value; 36 | } 37 | 38 | T & value; 39 | }; 40 | 41 | using DtorFunc = void (*)(void *); 42 | 43 | template 44 | void commonDtor(void * instance) 45 | { 46 | reinterpret_cast(instance)->~T(); 47 | } 48 | 49 | // used by EventQueue 50 | template 51 | class BufferedItem 52 | { 53 | public: 54 | using ValueType = T; 55 | 56 | public: 57 | explicit BufferedItem() : buffer(), dtor(nullptr) 58 | { 59 | } 60 | 61 | ~BufferedItem() 62 | { 63 | if(dtor != nullptr) { 64 | clear(); 65 | } 66 | } 67 | 68 | BufferedItem(BufferedItem &&) = delete; 69 | BufferedItem(const BufferedItem &) = delete; 70 | BufferedItem & operator = (const BufferedItem &) = delete; 71 | 72 | void set(T && item) { 73 | assert(dtor == nullptr); 74 | 75 | new (buffer.data()) T(std::forward(item)); 76 | dtor = &commonDtor; 77 | } 78 | 79 | T & get() { 80 | assert(dtor != nullptr); 81 | 82 | return *reinterpret_cast(buffer.data()); 83 | } 84 | 85 | const T & get() const { 86 | assert(dtor != nullptr); 87 | 88 | return *reinterpret_cast(buffer.data()); 89 | } 90 | 91 | void clear() { 92 | assert(dtor != nullptr); 93 | 94 | dtor(buffer.data()); 95 | dtor = nullptr; 96 | } 97 | 98 | bool empty() const { 99 | return dtor == nullptr; 100 | } 101 | 102 | private: 103 | std::array buffer; 104 | DtorFunc dtor; 105 | }; 106 | 107 | 108 | // used by HeterEventQueue 109 | template 110 | class BufferedUnion 111 | { 112 | public: 113 | explicit BufferedUnion() : buffer(), dtor(nullptr) 114 | { 115 | } 116 | 117 | ~BufferedUnion() 118 | { 119 | if(dtor != nullptr) { 120 | clear(); 121 | } 122 | } 123 | 124 | BufferedUnion(BufferedUnion &&) = delete; 125 | BufferedUnion(const BufferedUnion &) = delete; 126 | BufferedUnion & operator = (const BufferedUnion &) = delete; 127 | 128 | template 129 | void set(U && item) { 130 | static_assert(sizeof(U) <= Size, "Item is too large to fit in BufferedUnion"); 131 | assert(dtor == nullptr); 132 | 133 | new (buffer.data()) U(std::forward(item)); 134 | dtor = &commonDtor; 135 | } 136 | 137 | template 138 | U & get() { 139 | assert(dtor != nullptr); 140 | 141 | return *reinterpret_cast(buffer.data()); 142 | } 143 | 144 | template 145 | const U & get() const { 146 | assert(dtor != nullptr); 147 | 148 | return *reinterpret_cast(buffer.data()); 149 | } 150 | 151 | void clear() { 152 | assert(dtor != nullptr); 153 | 154 | dtor(buffer.data()); 155 | dtor = nullptr; 156 | } 157 | 158 | bool empty() const { 159 | return dtor == nullptr; 160 | } 161 | 162 | private: 163 | std::array buffer; 164 | DtorFunc dtor; 165 | }; 166 | 167 | 168 | } //namespace internal_ 169 | 170 | } //namespace eventpp 171 | 172 | 173 | #endif 174 | 175 | -------------------------------------------------------------------------------- /tests/tutorial/tutorial_hetercallbacklist.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Include the head 15 | #include "eventpp/hetercallbacklist.h" 16 | 17 | #include "tutorial.h" 18 | 19 | #include 20 | 21 | TEST_CASE("HeterCallbackList tutorial 1, basic") 22 | { 23 | std::cout << std::endl << "HeterCallbackList tutorial 1, basic" << std::endl; 24 | 25 | // The namespace is eventpp 26 | // the first parameter is a HeterTuple of the listener prototypes. 27 | eventpp::HeterCallbackList > callbackList; 28 | 29 | // Add a callback. 30 | // []() {} is the callback. 31 | // Lambda is not required, any function or std::function 32 | // or whatever function object with the required prototype is fine. 33 | callbackList.append([]() { 34 | std::cout << "Got callback for void()." << std::endl; 35 | }); 36 | callbackList.append([](int) { 37 | std::cout << "Got callback for void(int)." << std::endl; 38 | }); 39 | 40 | // Invoke the callback list 41 | // Output: Got callback for void(). 42 | callbackList(); 43 | // Output: Got callback for void(int). 44 | callbackList(5); 45 | } 46 | 47 | TEST_CASE("HeterCallbackList tutorial 2, for each") 48 | { 49 | std::cout << std::endl << "HeterCallbackList tutorial 2, for each" << std::endl; 50 | 51 | using CL = eventpp::HeterCallbackList >; 52 | CL callbackList; 53 | 54 | // Add some callbacks. 55 | callbackList.append([]() { 56 | std::cout << "Got callback void()." << std::endl; 57 | }); 58 | callbackList.append([]() { 59 | std::cout << "Got callback void() again." << std::endl; 60 | }); 61 | callbackList.append([](int /*a*/) { 62 | std::cout << "Got callback void(int a)." << std::endl; 63 | }); 64 | callbackList.append([](int /*a*/) { 65 | std::cout << "Got callback void(int a) again." << std::endl; 66 | }); 67 | 68 | // Now call forEach to remove the second callback 69 | // forEach has one template parameter, here is void(). It's which prototype to iterate for. 70 | // The forEach callback prototype is void(const HeterCallbackList::Handle & handle, const std::function & callback) 71 | int index = 0; 72 | callbackList.forEach([&callbackList, &index](const CL::Handle & handle, const std::function & /*callback*/) { 73 | std::cout << "forEach(Handle, Callback), invoked " << index << std::endl; 74 | if(index == 1) { 75 | callbackList.remove(handle); 76 | std::cout << "forEach(Handle, Callback), removed second callback" << std::endl; 77 | } 78 | ++index; 79 | }); 80 | 81 | // The forEach callback prototype is void(const std::function & callback) 82 | // We can also use C++14 generic lambda to simplify the callback, for example, 83 | // callbackList.forEach([&callbackList, &index](const auto & callback) 84 | callbackList.forEach([&callbackList, &index](const std::function & callback) { 85 | std::cout << "forEach(Callback), invoked" << std::endl; 86 | callback(3); 87 | }); 88 | 89 | // Invoke the callback list 90 | callbackList(); 91 | } 92 | 93 | -------------------------------------------------------------------------------- /tests/benchmark/b2_map_vs_unordered_map.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace { 22 | 23 | int getRandomeInt() 24 | { 25 | static std::random_device rd; 26 | static std::mt19937 engine(rd()); 27 | static std::uniform_int_distribution<> dist; 28 | return dist(engine); 29 | } 30 | 31 | int getRandomeInt(const int max) 32 | { 33 | return getRandomeInt() % max; 34 | } 35 | 36 | int getRandomeInt(const int min, const int max) 37 | { 38 | if(min >= max) { 39 | return min; 40 | } 41 | return min + getRandomeInt() % (max - min); 42 | } 43 | 44 | std::string generateRandomString(const int length){ 45 | static std::string possibleCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 46 | std::string result(length, 0); 47 | for(int i = 0; i < length; i++){ 48 | result[i] = possibleCharacters[getRandomeInt((int)possibleCharacters.size())]; 49 | } 50 | return result; 51 | } 52 | 53 | } //unnamed namespace 54 | 55 | TEST_CASE("b2, std::map vs std::unordered_map") 56 | { 57 | std::cout << std::endl << "b2, std::map vs std::unordered_map" << std::endl; 58 | 59 | constexpr int stringCount = 1000 * 1000; 60 | std::vector stringList(stringCount); 61 | for(auto & s : stringList) { 62 | s = generateRandomString(getRandomeInt(3, 10)); 63 | } 64 | 65 | constexpr int iterateCount = 1000 * 1000 * 1; 66 | 67 | uint64_t mapInsertTime = 0; 68 | uint64_t mapLookupTime = 0; 69 | { 70 | std::map map; 71 | mapInsertTime = measureElapsedTime([iterateCount, stringCount, &map, &stringList]() { 72 | for(int i = 0; i < iterateCount; ++i) { 73 | map[stringList[i % stringCount]] = i; 74 | } 75 | }); 76 | 77 | mapLookupTime = measureElapsedTime([iterateCount, stringCount, &map, &stringList]() { 78 | for(int i = iterateCount - 1; i >= 0; --i) { 79 | if(map.find(stringList[i % stringCount]) == map.end()) { 80 | stringList[i] = stringList[i]; 81 | } 82 | } 83 | }); 84 | } 85 | 86 | uint64_t unorderedMapInsertTime = 0; 87 | uint64_t unorderedMapLookupTime = 0; 88 | { 89 | std::unordered_map map; 90 | unorderedMapInsertTime = measureElapsedTime([iterateCount, stringCount, &map, &stringList]() { 91 | for(int i = 0; i < iterateCount; ++i) { 92 | map[std::hash()(stringList[i % stringCount])] = i; 93 | } 94 | }); 95 | 96 | unorderedMapLookupTime = measureElapsedTime([iterateCount, stringCount, &map, &stringList]() { 97 | for(int i = stringCount - 1; i >= 0; --i) { 98 | if(map.find(std::hash()(stringList[i])) == map.end()) { 99 | stringList[i] = stringList[i]; 100 | } 101 | } 102 | }); 103 | } 104 | std::cout << "Map: insert " << mapInsertTime << " lookup " << mapLookupTime << std::endl; 105 | std::cout << "UnordereMap: insert " << unorderedMapInsertTime << " lookup " << unorderedMapLookupTime << std::endl; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /tests/unittest/test_conditionalfunctor.cpp: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "eventpp/utilities/conditionalfunctor.h" 16 | #include "eventpp/callbacklist.h" 17 | 18 | TEST_CASE("ConditionalFunctor, lambda") 19 | { 20 | eventpp::CallbackList callbackList; 21 | 22 | std::vector dataList(3); 23 | 24 | callbackList.append( 25 | eventpp::conditionalFunctor( 26 | [&dataList](const int index) { 27 | ++dataList[index]; 28 | }, 29 | [](const int index) { 30 | return index == 0; 31 | } 32 | ) 33 | ); 34 | callbackList.append( 35 | eventpp::conditionalFunctor( 36 | [&dataList](const int index) { 37 | ++dataList[index]; 38 | }, 39 | [](const int index) { 40 | return index == 1; 41 | } 42 | ) 43 | ); 44 | callbackList.append( 45 | eventpp::conditionalFunctor( 46 | [&dataList](const int index) { 47 | ++dataList[index]; 48 | }, 49 | [](const int index) { 50 | return index == 2; 51 | } 52 | ) 53 | ); 54 | 55 | REQUIRE(dataList == std::vector{ 0, 0, 0 }); 56 | 57 | callbackList(2); 58 | REQUIRE(dataList == std::vector{ 0, 0, 1 }); 59 | 60 | callbackList(0); 61 | REQUIRE(dataList == std::vector{ 1, 0, 1 }); 62 | 63 | callbackList(1); 64 | REQUIRE(dataList == std::vector{ 1, 1, 1 }); 65 | } 66 | 67 | void conditionalFunctorIncreaseOne(std::vector & dataList, const int index) 68 | { 69 | ++dataList[index]; 70 | } 71 | 72 | TEST_CASE("ConditionalFunctor, free function") 73 | { 74 | eventpp::CallbackList &, int)> callbackList; 75 | 76 | std::vector dataList(3); 77 | 78 | callbackList.append( 79 | eventpp::conditionalFunctor(&conditionalFunctorIncreaseOne, 80 | [](std::vector &, const int index) { 81 | return index == 0; 82 | } 83 | ) 84 | ); 85 | callbackList.append( 86 | eventpp::conditionalFunctor(&conditionalFunctorIncreaseOne, 87 | [](std::vector &, const int index) { 88 | return index == 1; 89 | } 90 | ) 91 | ); 92 | callbackList.append( 93 | eventpp::conditionalFunctor(&conditionalFunctorIncreaseOne, 94 | [](std::vector &, const int index) { 95 | return index == 2; 96 | } 97 | ) 98 | ); 99 | 100 | REQUIRE(dataList == std::vector{ 0, 0, 0 }); 101 | 102 | callbackList(dataList, 2); 103 | REQUIRE(dataList == std::vector{ 0, 0, 1 }); 104 | 105 | callbackList(dataList, 0); 106 | REQUIRE(dataList == std::vector{ 1, 0, 1 }); 107 | 108 | callbackList(dataList, 1); 109 | REQUIRE(dataList == std::vector{ 1, 1, 1 }); 110 | } 111 | 112 | TEST_CASE("ConditionalFunctor, std::shared_ptr") 113 | { 114 | using Ptr = std::shared_ptr; 115 | eventpp::CallbackList callbackList; 116 | callbackList.append( 117 | eventpp::conditionalFunctor( 118 | [](Ptr ptr) { 119 | REQUIRE(ptr); // Be sure ptr is not moved 120 | REQUIRE(*ptr == 5); 121 | }, 122 | [](Ptr ptr) { 123 | return *ptr == 5; 124 | } 125 | ) 126 | ); 127 | callbackList(std::make_shared(2)); 128 | callbackList(std::make_shared(5)); 129 | } 130 | -------------------------------------------------------------------------------- /include/eventpp/utilities/argumentadapter.h: -------------------------------------------------------------------------------- 1 | // eventpp library 2 | // Copyright (C) 2018 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/eventpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ARGUMENTADAPTER_H_566280692673 15 | #define ARGUMENTADAPTER_H_566280692673 16 | 17 | #include 18 | 19 | namespace eventpp { 20 | 21 | namespace adapter_internal_ { 22 | 23 | template 24 | struct StaticCast 25 | { 26 | template 27 | static T cast(U && value) 28 | { 29 | return static_cast(value); 30 | } 31 | }; 32 | 33 | template 34 | struct StaticCast > 35 | { 36 | template 37 | static std::shared_ptr cast(U && value) 38 | { 39 | return std::static_pointer_cast(value); 40 | } 41 | }; 42 | 43 | template 44 | struct IsSharedPtr 45 | { 46 | enum { value = false }; 47 | }; 48 | 49 | template 50 | struct IsSharedPtr > 51 | { 52 | enum { value = true }; 53 | }; 54 | 55 | template 56 | struct IsAnySharedPtr 57 | { 58 | enum { value = false }; 59 | }; 60 | 61 | template 62 | struct IsAnySharedPtr 63 | { 64 | enum { value = IsSharedPtr::value 65 | || IsAnySharedPtr::value }; 66 | }; 67 | 68 | } //namespace adapter_internal_ 69 | 70 | template 71 | struct ArgumentAdapter; 72 | 73 | template 74 | struct ArgumentAdapter < 75 | Func, 76 | R(Args...), 77 | typename std::enable_if::value>::type 78 | > 79 | { 80 | explicit ArgumentAdapter(Func f) 81 | : func(std::move(f)) 82 | { 83 | } 84 | 85 | template 86 | void operator() (A &&...args) { 87 | func(std::forward(static_cast(args))...); 88 | } 89 | 90 | Func func; 91 | }; 92 | 93 | template 94 | struct ArgumentAdapter < 95 | Func, 96 | R(Args...), 97 | typename std::enable_if::value>::type 98 | > 99 | { 100 | explicit ArgumentAdapter(Func f) 101 | : func(std::move(f)) 102 | { 103 | } 104 | 105 | template 106 | void operator() (A &&...args) { 107 | func(std::forward(adapter_internal_::StaticCast::cast(args))...); 108 | } 109 | 110 | Func func; 111 | }; 112 | 113 | template