mirror of
https://github.com/gelldur/EventBus.git
synced 2024-12-25 18:00:48 +08:00
Reorganize project structure
commit f7dd4172cf535cf52601a8819cf5c8bfabcd1fe4 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 31 10:26:27 2018 +0200 Improve Travis script Fixed after reordering project structure commit d054e5c91762da15defa458404e355d7c670e301 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Mon Jul 30 15:11:21 2018 +0200 Update Travis CI for linux and OSX commit 63395f5a7e3dd9f2a52b2d6a254da89ec1d6e5e9 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Fri Jul 27 14:38:40 2018 +0200 Secure EventBus from wrong usage For example user previously could do such thing: bus.listen<const MyEvent>(...) bus.listen<MyEvent>(...) Those we 2 different events :/ commit f9195316d3ba6313ee425e3194b65b32fe52d641 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Thu Jul 26 12:44:17 2018 +0200 Update for better managing Debug/Release Updated project for easy switch between debug/release versions of library Thanks to that we can do only find_package and don't have to care about if's switching between debug/release Thanks: https://github.com/forexample/package-example commit 7d708959d9e96176875ca882f0f69a72622added Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Wed Jul 25 13:45:55 2018 +0200 Update clang-format style commit beb1d3b863379490f321e43f3e42ab272954ea67 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 16:05:16 2018 +0200 Add some docs commit ca450dfeee2d4bc604bbb9bf0599f373c21a4173 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 15:49:52 2018 +0200 Remove not needed includes commit 6473b80e8e60408675bcc4adc88653390576c4bd Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 15:41:57 2018 +0200 Code format commit 8abb56e1dd4b71df2b05bb34bef0530567e4ff2e Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 15:41:40 2018 +0200 Improve performance Thanks to that we don't need RTTI commit 1feacbb1f9ae6a5ac2209a6dc1df5c868ead8fd4 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 15:14:16 2018 +0200 Remove trash commit b5dc5c05589b969dd61eb65b68e4cdce69c5a5fb Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 15:00:15 2018 +0200 Fix include path commit 9939fd09805191f0bdada6cb85193a291d519116 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 14:51:02 2018 +0200 Update install lib commit 9eaa09f9ec5a29045b03ffc7632878863a2b2b9b Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 14:50:47 2018 +0200 Fix commit 7a5b3323af0b728f7e511ac22ff5027c6d06402e Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 14:16:12 2018 +0200 Update README commit beb6599ee4385fdffc747dc866db46e160be1358 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 14:16:05 2018 +0200 Add performance compare to Poco::NotifactionCenter commit 1d25b997580a9ee09c9db86135b4ca9e1b1a10c6 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 14:14:08 2018 +0200 Update clang-format commit 4f4cb4a7e8a849c067a42085eb3e76c3df894bc7 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 13:04:30 2018 +0200 Remove bad flag It is only working for GCC commit 66a7945084607f94d9d0c803008398e8d281fd06 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 13:03:15 2018 +0200 Remove deprecated stuff It was breaking encapsulation commit 1e7500607b42bff3632250f623888b95a503dfd0 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 13:01:37 2018 +0200 Update sample commit 11a146bb9145fa55f9b9a39a9e033387007a7151 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 13:01:27 2018 +0200 Add clang-format rules commit 685562c632d9751f50a2f05b92ef9ebf53a5d6e0 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 12:47:10 2018 +0200 Reorganize project layout Inspired by: https://www.youtube.com/watch?v=6sWec7b0JIc commit 40d1d6487814730533d7dd7cbedbaf2b4e34ef19 Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 12:45:55 2018 +0200 Remove Catch2 submodule Switch to own dependency commit ca21df04f392adcb027a5b4f25ffac085b51f48c Author: Dawid Drozd <dawid.drozd@mobica.com> Date: Tue Jul 24 11:28:51 2018 +0200 Remove old code
This commit is contained in:
parent
7ae52d926d
commit
3688c145cb
127
.clang-format
Normal file
127
.clang-format
Normal file
@ -0,0 +1,127 @@
|
||||
# Checkout config tool: https://zed0.co.uk/clang-format-configurator/
|
||||
# Or http://cf.monofraps.net/
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
# https://github.com/01org/parameter-framework/blob/master/.clang-format
|
||||
|
||||
# Tested on: clang-format version 6.0.1
|
||||
|
||||
|
||||
# Common settings
|
||||
BasedOnStyle: WebKit
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
UseTab: Always
|
||||
ColumnLimit: 100
|
||||
|
||||
# Other languages JavaScript, Proto
|
||||
|
||||
---
|
||||
Language: Cpp
|
||||
|
||||
# http://releases.llvm.org/6.0.1/tools/clang/docs/ClangFormatStyleOptions.html#disabling-formatting-on-a-piece-of-code
|
||||
# int formatted_code;
|
||||
# // clang-format off
|
||||
# void unformatted_code ;
|
||||
# // clang-format on
|
||||
# void formatted_code_again;
|
||||
|
||||
DisableFormat: false
|
||||
Standard: Cpp11
|
||||
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: true
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
|
||||
# Configure each individual brace in BraceWrapping
|
||||
BreakBeforeBraces: Custom
|
||||
# Control of individual brace wrapping cases
|
||||
BraceWrapping: {
|
||||
AfterClass: 'true'
|
||||
AfterControlStatement: 'true'
|
||||
AfterEnum : 'true'
|
||||
AfterFunction : 'true'
|
||||
AfterNamespace : 'true'
|
||||
AfterStruct : 'true'
|
||||
AfterUnion : 'true'
|
||||
BeforeCatch : 'true'
|
||||
BeforeElse : 'true'
|
||||
IndentBraces : 'false'
|
||||
AfterExternBlock : 'true'
|
||||
SplitEmptyFunction : 'false'
|
||||
SplitEmptyRecord : 'false'
|
||||
SplitEmptyNamespace : 'true'
|
||||
}
|
||||
|
||||
BreakAfterJavaFieldAnnotations: true
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
BreakStringLiterals: true
|
||||
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
IndentCaseLabels: false
|
||||
FixNamespaceComments: true
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
JavaScriptQuotes: Double
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Left
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: Never
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
|
||||
SortUsingDeclarations: true
|
||||
SortIncludes: true
|
||||
|
||||
# Comments are for developers, they should arrange them
|
||||
ReflowComments: false
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
||||
---
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "test/Catch"]
|
||||
path = test/Catch
|
||||
url = https://github.com/philsquared/Catch.git
|
||||
[submodule "performance/benchmark"]
|
||||
path = performance/benchmark
|
||||
url = https://github.com/google/benchmark.git
|
||||
|
71
.travis.yml
71
.travis.yml
@ -1,37 +1,54 @@
|
||||
os: linux
|
||||
dist: trusty
|
||||
sudo: require
|
||||
language: cpp
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-precise-3.9
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- clang-3.9
|
||||
- gcc-6
|
||||
- g++-6
|
||||
common_sources: &all_sources
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty
|
||||
- llvm-toolchain-trusty-3.9
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- llvm-toolchain-trusty-5.0
|
||||
- llvm-toolchain-trusty-6.0
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
env: COMPILER=g++-6
|
||||
exclude: # On OSX g++ is a symlink to clang++ by default
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
- compiler: clang
|
||||
env: COMPILER=clang++-3.9
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['gcc-7', 'g++-7', 'cmake']
|
||||
env: COMPILER='g++-7'
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-6.0', 'libstdc++-6-dev', 'cmake']
|
||||
env: COMPILER='clang++-6.0'
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9.4
|
||||
compiler: clang
|
||||
env: COMPILER='clang++'
|
||||
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi
|
||||
|
||||
before_script:
|
||||
- if [ "$COMPILER" = "g++-6" ]; then export CXX="g++-6" CC="gcc-6"; fi
|
||||
- if [ "$COMPILER" = "clang++-3.9" ]; then export CXX="clang++-3.9" CC="clang-3.9"; fi
|
||||
# Install catch 2 dependency
|
||||
- wget https://github.com/catchorg/Catch2/archive/v2.2.3.zip
|
||||
- unzip v2.2.3.zip && cd Catch2-2.2.3 && mkdir -p build/ && cd build/
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release .. -DCMAKE_INSTALL_PREFIX=~/.local/ && cmake --build . --target install
|
||||
- cd ../..
|
||||
|
||||
script:
|
||||
- mkdir build-debug && cd build-debug && cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=YES .. && cmake --build .
|
||||
- ./test/EventBusTest # Run tests
|
||||
- cd .. # exit from build-debug
|
||||
- mkdir build-release && cd build-release && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=YES .. && cmake --build .
|
||||
- ./test/EventBusTest # Run tests
|
||||
#Build & Install library
|
||||
- (mkdir -p lib/build-debug/ && cd lib/build-debug && cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=~/.local/ .. && cmake --build . --target install)
|
||||
- (mkdir -p lib/build-release/ && cd lib/build-release && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/.local/ .. && cmake --build . --target install)
|
||||
# Run tests
|
||||
- (mkdir -p test/build-debug/ && cd test/build-debug && cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=~/.local/ .. && cmake --build . && ctest -V .)
|
||||
- (mkdir -p test/build-release/ && cd test/build-release && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/.local/ .. && cmake --build . && ctest -V .)
|
||||
|
||||
|
@ -1,66 +1,20 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.2 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
OPTION(PERFORMANCE "Enable/Disable performance subdirectory" OFF)
|
||||
# Layout of project is inspired by: https://youtu.be/6sWec7b0JIc?t=20m50s
|
||||
# This top level CMakeLists should be used for development
|
||||
|
||||
# BUILD_SHARED_LIBS can controll build type!
|
||||
PROJECT(EventBus
|
||||
VERSION 2.2.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
project(EventBusDev)
|
||||
|
||||
ADD_LIBRARY(EventBus
|
||||
src/eventbus/EventCollector.cpp include/eventbus/EventCollector.h
|
||||
include/eventbus/EventBus.h
|
||||
)
|
||||
ADD_LIBRARY(Dexode::EventBus ALIAS EventBus)
|
||||
add_subdirectory(lib/)
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(EventBus PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
|
||||
$<INSTALL_INTERFACE:include/>
|
||||
PRIVATE src/
|
||||
)
|
||||
enable_testing()
|
||||
add_subdirectory(test/)
|
||||
add_subdirectory(sample/)
|
||||
add_subdirectory(performance/)
|
||||
|
||||
TARGET_COMPILE_OPTIONS(EventBus PRIVATE
|
||||
target_compile_options(EventBus PUBLIC
|
||||
-Wall -pedantic
|
||||
-Wnon-virtual-dtor
|
||||
-Werror
|
||||
-Wno-error=deprecated-declarations
|
||||
)
|
||||
|
||||
IF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
TARGET_COMPILE_OPTIONS(EventBus PRIVATE
|
||||
-Wno-error=unknown-argument
|
||||
-Wno-error=unused-command-line-argument
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
SET_TARGET_PROPERTIES(EventBus PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
)
|
||||
|
||||
IF(NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
TARGET_COMPILE_FEATURES(EventBus
|
||||
PUBLIC cxx_auto_type
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
INSTALL(TARGETS EventBus EXPORT EventBusConfig
|
||||
ARCHIVE DESTINATION lib/
|
||||
LIBRARY DESTINATION lib/
|
||||
RUNTIME DESTINATION bin/
|
||||
INCLUDES DESTINATION include/
|
||||
)
|
||||
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/ FILES_MATCHING PATTERN "*.h*")
|
||||
INSTALL(EXPORT EventBusConfig
|
||||
DESTINATION cmake/
|
||||
NAMESPACE Dexode::
|
||||
)
|
||||
EXPORT(TARGETS EventBus FILE EventBusConfig.cmake)
|
||||
|
||||
IF(BUILD_TESTING)
|
||||
ADD_SUBDIRECTORY(test/)
|
||||
ENDIF()
|
||||
|
||||
IF(PERFORMANCE)
|
||||
ADD_SUBDIRECTORY(performance/)
|
||||
ENDIF()
|
||||
|
14
README.md
14
README.md
@ -180,8 +180,12 @@ TARGET_LINK_LIBRARIES(MyExecutable PUBLIC Dexode::EventBus)
|
||||
Also if you want you can install library and add it at any way you want.
|
||||
Eg.
|
||||
```commandline
|
||||
mkdir Release && cd Release
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./install ..
|
||||
mkdir -p lib/build/
|
||||
cd lib/build
|
||||
cmake -DCMAKE_BUILD_TYPE=Relase -DCMAKE_INSTALL_PREFIX=~/.local/ ..
|
||||
|
||||
cmake --build . --target install
|
||||
# OR
|
||||
make && make install
|
||||
```
|
||||
|
||||
@ -205,6 +209,7 @@ checkNotifyFor10kListenersWhenNoOneListens_CCNotificationCenter 127388 ns
|
||||
- Thread safe EventBus ?
|
||||
- Verbose messages for easy debugging ?
|
||||
- Generating graph flow ?
|
||||
- Add nice documentation [like in POCO](https://pocoproject.org/slides/090-NotificationsEvents.pdf)
|
||||
- ...
|
||||
|
||||
# Issues ?
|
||||
@ -217,6 +222,11 @@ Please report here issue / question / whatever in 99% I will answer ;)
|
||||
- [swietlana](https://github.com/swietlana) for english correction and support ;)
|
||||
- [ruslo](https://github.com/ruslo) for this great example: https://github.com/forexample/package-example
|
||||
|
||||
## For modern cmake refer
|
||||
|
||||
- https://github.com/forexample/package-example
|
||||
- https://www.youtube.com/watch?v=6sWec7b0JIc
|
||||
|
||||
# License
|
||||
|
||||
EventBus source code can be used according to the **Apache License, Version 2.0**.
|
||||
|
104
lib/CMakeLists.txt
Normal file
104
lib/CMakeLists.txt
Normal file
@ -0,0 +1,104 @@
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
# BUILD_SHARED_LIBS can controll build type!
|
||||
project(EventBus
|
||||
VERSION 2.2.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
# Dependencies
|
||||
# No dependencies for EventBus yay!
|
||||
|
||||
# Introduce variables:
|
||||
# * CMAKE_INSTALL_LIBDIR
|
||||
# * CMAKE_INSTALL_BINDIR
|
||||
# * CMAKE_INSTALL_INCLUDEDIR
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Layout. This works for all platforms:
|
||||
# * <prefix>/lib*/cmake/<PROJECT-NAME>
|
||||
# * <prefix>/lib*/
|
||||
# * <prefix>/include/
|
||||
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
|
||||
# Library definition
|
||||
add_library(EventBus
|
||||
src/EventCollector.cpp include/eventbus/EventCollector.h
|
||||
include/eventbus/EventBus.h
|
||||
)
|
||||
add_library(Dexode::EventBus ALIAS EventBus)
|
||||
|
||||
target_compile_features(EventBus PUBLIC cxx_std_14)
|
||||
|
||||
target_include_directories(EventBus PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
|
||||
$<INSTALL_INTERFACE:include/>
|
||||
PRIVATE lib/src/
|
||||
)
|
||||
|
||||
# Add definitions for targets
|
||||
# Values:
|
||||
# * Debug: -DEVENTBUS_DEBUG=1
|
||||
# * Release: -DEVENTBUS_DEBUG=0
|
||||
# * other: -DEVENTBUS_DEBUG=0
|
||||
target_compile_definitions(EventBus PUBLIC "EVENTBUS_DEBUG=$<CONFIG:Debug>")
|
||||
|
||||
|
||||
# Configuration
|
||||
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
|
||||
set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
|
||||
set(namespace "Dexode::")
|
||||
|
||||
# Targets:
|
||||
# * <prefix>/lib/libEventBus.a
|
||||
# * header location after install: <prefix>/include/eventbus/EventBus.h
|
||||
# * headers can be included by C++ code `#include <eventbus/EventBus.h>`
|
||||
install(TARGETS EventBus
|
||||
EXPORT "${TARGETS_EXPORT_NAME}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
|
||||
# Export headers
|
||||
install(DIRECTORY include/eventbus
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
|
||||
# Include module with fuction 'write_basic_package_version_file'
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# Configure '<PROJECT-NAME>ConfigVersion.cmake'
|
||||
# Use:
|
||||
# * PROJECT_VERSION
|
||||
write_basic_package_version_file(
|
||||
"${version_config}" COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
# Configure '<PROJECT-NAME>Config.cmake'
|
||||
# Use variables:
|
||||
# * TARGETS_EXPORT_NAME
|
||||
# * PROJECT_NAME
|
||||
configure_package_config_file(
|
||||
"cmake/Config.cmake.in"
|
||||
"${project_config}"
|
||||
INSTALL_DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
# Config
|
||||
# * <prefix>/lib/cmake/EventBusventBusConfig.cmake
|
||||
# * <prefix>/lib/cmake/EventBus/EventBusConfigVersion.cmake
|
||||
install(
|
||||
FILES "${project_config}" "${version_config}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
# Config
|
||||
# * <prefix>/lib/cmake/EventBus/EventBusTargets.cmake
|
||||
install(EXPORT "${TARGETS_EXPORT_NAME}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
NAMESPACE "${namespace}"
|
||||
)
|
4
lib/cmake/Config.cmake.in
Normal file
4
lib/cmake/Config.cmake.in
Normal file
@ -0,0 +1,4 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
@ -5,15 +5,28 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
namespace Dexode
|
||||
{
|
||||
|
||||
template <typename>
|
||||
void type_id() // Helper for getting "type id"
|
||||
{}
|
||||
|
||||
using type_id_t = void (*)(); // Function pointer
|
||||
|
||||
class EventBus
|
||||
{
|
||||
template <class Event>
|
||||
constexpr void validateEvent()
|
||||
{
|
||||
static_assert(std::is_const<Event>::value == false, "Struct must be without const");
|
||||
static_assert(std::is_volatile<Event>::value == false, "Struct must be without volatile");
|
||||
static_assert(std::is_reference<Event>::value == false, "Struct must be without reference");
|
||||
static_assert(std::is_pointer<Event>::value == false, "Struct must be without pointer");
|
||||
}
|
||||
|
||||
public:
|
||||
EventBus() = default;
|
||||
|
||||
@ -35,9 +48,11 @@ public:
|
||||
* @param callback - your callback to handle event
|
||||
* @return token used for unlisten
|
||||
*/
|
||||
template<typename Event>
|
||||
template <typename Event>
|
||||
int listen(const std::function<void(const Event&)>& callback)
|
||||
{
|
||||
validateEvent<Event>();
|
||||
|
||||
const int token = ++_tokener;
|
||||
listen<Event>(token, callback);
|
||||
return token;
|
||||
@ -48,15 +63,17 @@ public:
|
||||
* @param token - unique token for identification receiver. Simply pass token from @see EventBus::listen
|
||||
* @param callback - your callback to handle event
|
||||
*/
|
||||
template<typename Event>
|
||||
template <typename Event>
|
||||
void listen(const int token, const std::function<void(const Event&)>& callback)
|
||||
{
|
||||
validateEvent<Event>();
|
||||
|
||||
using Vector = VectorImpl<Event>;
|
||||
|
||||
assert(callback && "callback should be valid");//Check for valid object
|
||||
assert(callback && "callback should be valid"); //Check for valid object
|
||||
|
||||
std::unique_ptr<VectorInterface>& vector = _callbacks[getTypeId<Event>()];
|
||||
if (vector == nullptr)
|
||||
std::unique_ptr<VectorInterface>& vector = _callbacks[type_id<Event>];
|
||||
if(vector == nullptr)
|
||||
{
|
||||
vector.reset(new Vector{});
|
||||
}
|
||||
@ -70,7 +87,7 @@ public:
|
||||
*/
|
||||
void unlistenAll(const int token)
|
||||
{
|
||||
for (auto& element : _callbacks)
|
||||
for(auto& element : _callbacks)
|
||||
{
|
||||
element.second->remove(token);
|
||||
}
|
||||
@ -80,11 +97,13 @@ public:
|
||||
* @tparam Event - type you want to unlisten. @see Notiier::listen
|
||||
* @param token - token from EventBus::listen
|
||||
*/
|
||||
template<typename Event>
|
||||
template <typename Event>
|
||||
void unlisten(const int token)
|
||||
{
|
||||
auto found = _callbacks.find(getTypeId<Event>());
|
||||
if (found != _callbacks.end())
|
||||
validateEvent<Event>();
|
||||
|
||||
auto found = _callbacks.find(type_id<Event>);
|
||||
if(found != _callbacks.end())
|
||||
{
|
||||
found->second->remove(token);
|
||||
}
|
||||
@ -95,15 +114,17 @@ public:
|
||||
*
|
||||
* @param event your event struct
|
||||
*/
|
||||
template<typename Event>
|
||||
template <typename Event>
|
||||
void notify(const Event& event)
|
||||
{
|
||||
using Vector = VectorImpl<Event>;
|
||||
const auto typeId = getTypeId<Event>();//TODO think about constexpr
|
||||
auto found = _callbacks.find(typeId);
|
||||
if (found == _callbacks.end())
|
||||
using CleanEventType = typename std::remove_const<Event>::type;
|
||||
validateEvent<CleanEventType>();
|
||||
|
||||
using Vector = VectorImpl<CleanEventType>;
|
||||
auto found = _callbacks.find(type_id<CleanEventType>);
|
||||
if(found == _callbacks.end())
|
||||
{
|
||||
return;// no such notifications
|
||||
return; // no such notifications
|
||||
}
|
||||
|
||||
std::unique_ptr<VectorInterface>& vector = found->second;
|
||||
@ -111,7 +132,7 @@ public:
|
||||
Vector* vectorImpl = static_cast<Vector*>(vector.get());
|
||||
|
||||
vectorImpl->beginTransaction();
|
||||
for (const auto& element : vectorImpl->container)
|
||||
for(const auto& element : vectorImpl->container)
|
||||
{
|
||||
element.second(event);
|
||||
}
|
||||
@ -126,7 +147,7 @@ private:
|
||||
virtual void remove(const int token) = 0;
|
||||
};
|
||||
|
||||
template<typename Event>
|
||||
template <typename Event>
|
||||
struct VectorImpl : public VectorInterface
|
||||
{
|
||||
using CallbackType = std::function<void(const Event&)>;
|
||||
@ -139,19 +160,18 @@ private:
|
||||
|
||||
virtual void remove(const int token) override
|
||||
{
|
||||
if (inTransaction > 0)
|
||||
if(inTransaction > 0)
|
||||
{
|
||||
toRemove.push_back(token);
|
||||
return;
|
||||
}
|
||||
|
||||
//Invalidation rules: https://stackoverflow.com/questions/6438086/iterator-invalidation-rules
|
||||
auto removeFrom = std::remove_if(container.begin(), container.end()
|
||||
, [token](const ContainerElement& element)
|
||||
{
|
||||
return element.first == token;
|
||||
});
|
||||
if (removeFrom != container.end())
|
||||
auto removeFrom = std::remove_if(
|
||||
container.begin(), container.end(), [token](const ContainerElement& element) {
|
||||
return element.first == token;
|
||||
});
|
||||
if(removeFrom != container.end())
|
||||
{
|
||||
container.erase(removeFrom, container.end());
|
||||
}
|
||||
@ -159,7 +179,7 @@ private:
|
||||
|
||||
void add(const int token, const CallbackType& callback)
|
||||
{
|
||||
if (inTransaction > 0)
|
||||
if(inTransaction > 0)
|
||||
{
|
||||
toAdd.emplace_back(token, callback);
|
||||
}
|
||||
@ -177,20 +197,20 @@ private:
|
||||
void commitTransaction()
|
||||
{
|
||||
--inTransaction;
|
||||
if (inTransaction > 0)
|
||||
if(inTransaction > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
inTransaction = 0;
|
||||
|
||||
if (toAdd.empty() == false)
|
||||
if(toAdd.empty() == false)
|
||||
{
|
||||
container.insert(container.end(), toAdd.begin(), toAdd.end());
|
||||
toAdd.clear();
|
||||
}
|
||||
if (toRemove.empty() == false)
|
||||
if(toRemove.empty() == false)
|
||||
{
|
||||
for (auto token : toRemove)
|
||||
for(auto token : toRemove)
|
||||
{
|
||||
remove(token);
|
||||
}
|
||||
@ -200,14 +220,7 @@ private:
|
||||
};
|
||||
|
||||
int _tokener = 0;
|
||||
std::map<std::size_t, std::unique_ptr<VectorInterface>> _callbacks;
|
||||
|
||||
template<typename T>
|
||||
static std::size_t getTypeId()
|
||||
{
|
||||
//std::hash<std::string>{}(typeid(T).name() is slower
|
||||
return typeid(T).hash_code();
|
||||
}
|
||||
std::map<type_id_t, std::unique_ptr<VectorInterface>> _callbacks;
|
||||
};
|
||||
|
||||
} /* namespace Dexode */
|
@ -11,36 +11,6 @@
|
||||
namespace Dexode
|
||||
{
|
||||
|
||||
class [[deprecated("This class will be removed, is breaking API")]] BusAttorney
|
||||
{
|
||||
public:
|
||||
BusAttorney(std::shared_ptr<EventBus> bus)
|
||||
: _bus(std::move(bus))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all listeners for event
|
||||
*
|
||||
* @param event your event struct
|
||||
*/
|
||||
template<typename Event>
|
||||
[[deprecated]]
|
||||
void notify(const Event& event) const
|
||||
{
|
||||
_bus->notify(event);
|
||||
}
|
||||
|
||||
[[deprecated]]
|
||||
std::shared_ptr<EventBus> extract() const
|
||||
{
|
||||
return _bus;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<EventBus> _bus;
|
||||
};
|
||||
|
||||
class EventCollector
|
||||
{
|
||||
public:
|
||||
@ -93,10 +63,6 @@ public:
|
||||
|
||||
bool isUsing(const std::shared_ptr<EventBus>& bus) const;
|
||||
|
||||
///I wan't explicitly say getBus. Ok we could add method for notify but this is more explicit
|
||||
[[deprecated("This method will be removed, is breaking encapsulation")]]
|
||||
BusAttorney getBus() const;
|
||||
|
||||
private:
|
||||
int _token = 0;
|
||||
std::shared_ptr<EventBus> _bus;
|
@ -49,6 +49,7 @@ EventCollector::~EventCollector()
|
||||
|
||||
EventCollector& EventCollector::operator=(const EventCollector& other)
|
||||
{
|
||||
std::unique_ptr<int> vector3;
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
@ -86,11 +87,6 @@ void EventCollector::unlistenAll()
|
||||
}
|
||||
}
|
||||
|
||||
BusAttorney EventCollector::getBus() const
|
||||
{
|
||||
return BusAttorney{_bus};
|
||||
}
|
||||
|
||||
bool EventCollector::isUsing(const std::shared_ptr<EventBus>& bus) const
|
||||
{
|
||||
return _bus == bus;
|
@ -1,17 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
# http://www.levelofindirection.com/journal/2010/12/28/unit-testing-in-c-and-objective-c-just-got-easier.html
|
||||
# Thanks for CATCH!
|
||||
|
||||
ADD_SUBDIRECTORY(benchmark/)
|
||||
find_package(Poco COMPONENTS Foundation Util)
|
||||
|
||||
add_subdirectory(benchmark/)
|
||||
|
||||
if (NOT TARGET Dexode::EventBus)
|
||||
find_package(EventBus CONFIG REQUIRED)
|
||||
endif ()
|
||||
|
||||
# If you want to compare with CCNotificationCenter read about it in README and uncomment line below
|
||||
#INCLUDE(cocos2d-x-compare/Cocos2dxCompare.cmake)
|
||||
|
||||
ADD_EXECUTABLE(EventBusPerformance
|
||||
eventbus/EventBusPerformance.cpp
|
||||
add_executable(EventBusPerformance
|
||||
src/EventBusPerformance.cpp
|
||||
${CCNOTIFICATION_CENTER_SRC}
|
||||
$<$<BOOL:${Poco_FOUND}>:src/PocoNotificationCenterPerformance.cpp>
|
||||
)
|
||||
|
||||
TARGET_COMPILE_OPTIONS(EventBusPerformance PUBLIC
|
||||
target_compile_options(EventBusPerformance PUBLIC
|
||||
-Wall -pedantic
|
||||
-Wno-unused-private-field
|
||||
-Wnon-virtual-dtor
|
||||
@ -19,27 +28,22 @@ TARGET_COMPILE_OPTIONS(EventBusPerformance PUBLIC
|
||||
-Werror
|
||||
)
|
||||
|
||||
SET(EVENTBUS_DEBUG_FLAGS
|
||||
set(EVENTBUS_DEBUG_FLAGS
|
||||
-O0 -fno-inline
|
||||
-DDEBUG
|
||||
-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
|
||||
)
|
||||
|
||||
SET(EVENTBUS_RELEASE_FLAGS
|
||||
-DNDEBUG
|
||||
)
|
||||
|
||||
TARGET_COMPILE_OPTIONS(EventBusPerformance PUBLIC "$<$<CONFIG:DEBUG>:${EVENTBUS_DEBUG_FLAGS}>")
|
||||
TARGET_COMPILE_OPTIONS(EventBusPerformance PUBLIC "$<$<CONFIG:RELEASE>:${EVENTBUS_RELEASE_FLAGS}>")
|
||||
target_compile_options(EventBusPerformance PUBLIC "$<$<CONFIG:DEBUG>:${EVENTBUS_DEBUG_FLAGS}>")
|
||||
|
||||
SET_TARGET_PROPERTIES(EventBusPerformance PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
)
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(EventBusPerformance PUBLIC
|
||||
./
|
||||
target_include_directories(EventBusPerformance PUBLIC
|
||||
src/
|
||||
${CCNOTIFICATION_CENTER_INCLUDE}
|
||||
)
|
||||
|
||||
TARGET_LINK_LIBRARIES(EventBusPerformance PUBLIC Dexode::EventBus benchmark)
|
||||
target_link_libraries(EventBusPerformance PUBLIC
|
||||
Dexode::EventBus
|
||||
benchmark
|
||||
$<$<BOOL:${Poco_FOUND}>:Poco::Foundation>
|
||||
$<$<BOOL:${Poco_FOUND}>:Poco::Util>
|
||||
)
|
@ -1,284 +0,0 @@
|
||||
//
|
||||
// Created by Dawid Drozd aka Gelldur on 05.08.17.
|
||||
//
|
||||
#include <random>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <eventbus/EventBus.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void checkNListeners(benchmark::State& state, const int listenersCount)
|
||||
{
|
||||
Dexode::EventBus bus;
|
||||
int sum = 0;
|
||||
|
||||
struct SimpleEvent
|
||||
{
|
||||
int value;
|
||||
};
|
||||
|
||||
for (int i = 0; i < listenersCount; ++i)
|
||||
{
|
||||
bus.listen<SimpleEvent>([&](const SimpleEvent& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value * 2);
|
||||
});
|
||||
}
|
||||
|
||||
const auto event = SimpleEvent{2};
|
||||
while (state.KeepRunning())//Performance area!
|
||||
{
|
||||
bus.notify(event);
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
void checkSimpleNotification(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 1);
|
||||
}
|
||||
|
||||
void check10Listeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 10);
|
||||
}
|
||||
|
||||
void check100Listeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 100);
|
||||
}
|
||||
|
||||
void check1kListeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 1000);
|
||||
}
|
||||
|
||||
void call1kLambdas_compare(benchmark::State& state)
|
||||
{
|
||||
int sum = 0;
|
||||
std::vector<std::function<void(int)>> callbacks;
|
||||
callbacks.reserve(1000);
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
callbacks.emplace_back([&](int value)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += value * 2);
|
||||
});
|
||||
}
|
||||
|
||||
while (state.KeepRunning())//Performance area!
|
||||
{
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
// for (auto& callback :callbacks)
|
||||
{
|
||||
callbacks[i](2);
|
||||
}
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
struct SimpleEvent
|
||||
{
|
||||
int value = N;
|
||||
};
|
||||
|
||||
void checkNNotificationsForNListeners(benchmark::State& state, const int notificationsCount, const int listenersCount)
|
||||
{
|
||||
std::mt19937 generator(311281);
|
||||
std::uniform_int_distribution<int> uniformDistribution(0, notificationsCount - 1);
|
||||
|
||||
Dexode::EventBus bus;
|
||||
int sum = 0;
|
||||
for (int i = 0; i < listenersCount; ++i)//We register M listeners for N notifications using uniform distribution
|
||||
{
|
||||
const auto which = uniformDistribution(generator);
|
||||
//We generate here N different notifications
|
||||
switch (which)
|
||||
{
|
||||
case 0:
|
||||
bus.listen<SimpleEvent<0>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
bus.listen<SimpleEvent<1>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
bus.listen<SimpleEvent<2>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 3:
|
||||
bus.listen<SimpleEvent<3>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 4:
|
||||
bus.listen<SimpleEvent<4>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 5:
|
||||
bus.listen<SimpleEvent<5>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 6:
|
||||
bus.listen<SimpleEvent<6>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 7:
|
||||
bus.listen<SimpleEvent<7>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 8:
|
||||
bus.listen<SimpleEvent<8>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 9:
|
||||
bus.listen<SimpleEvent<9>>([&](const auto& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2);//we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
while (state.KeepRunning())//Performance area!
|
||||
{
|
||||
//Pick random notification
|
||||
const auto which = uniformDistribution(generator);
|
||||
const auto number = uniformDistribution(generator);
|
||||
//We generate here N different notifications
|
||||
switch (which)
|
||||
{
|
||||
case 0:
|
||||
bus.notify(SimpleEvent<0>{number});
|
||||
break;
|
||||
case 1:
|
||||
bus.notify(SimpleEvent<1>{number});
|
||||
break;
|
||||
case 2:
|
||||
bus.notify(SimpleEvent<2>{number});
|
||||
break;
|
||||
case 3:
|
||||
bus.notify(SimpleEvent<3>{number});
|
||||
break;
|
||||
case 4:
|
||||
bus.notify(SimpleEvent<4>{number});
|
||||
break;
|
||||
case 5:
|
||||
bus.notify(SimpleEvent<5>{number});
|
||||
break;
|
||||
case 6:
|
||||
bus.notify(SimpleEvent<6>{number});
|
||||
break;
|
||||
case 7:
|
||||
bus.notify(SimpleEvent<7>{number});
|
||||
break;
|
||||
case 8:
|
||||
bus.notify(SimpleEvent<8>{number});
|
||||
break;
|
||||
case 9:
|
||||
bus.notify(SimpleEvent<9>{number});
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
void check10NotificationsFor1kListeners(benchmark::State& state)
|
||||
{
|
||||
checkNNotificationsForNListeners(state, 10, 1000);
|
||||
}
|
||||
|
||||
//Sorry not available
|
||||
//void check100NotificationsFor1kListeners(benchmark::State& state)
|
||||
//{
|
||||
// checkNNotificationsForNListeners(state, 100, 1000);
|
||||
//}
|
||||
//
|
||||
//void check1kNotificationsFor1kListeners(benchmark::State& state)
|
||||
//{
|
||||
// checkNNotificationsForNListeners(state, 1000, 1000);
|
||||
//}
|
||||
//
|
||||
//void check100NotificationsFor10kListeners(benchmark::State& state)
|
||||
//{
|
||||
// checkNNotificationsForNListeners(state, 100, 10000);
|
||||
//}
|
||||
|
||||
void checkNotifyFor10kListenersWhenNoOneListens(benchmark::State& state)
|
||||
{
|
||||
Dexode::EventBus bus;
|
||||
int sum = 0;
|
||||
struct SimpleEvent
|
||||
{
|
||||
int value;
|
||||
};
|
||||
struct UnknownEvent
|
||||
{
|
||||
int value;
|
||||
};
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
bus.listen<SimpleEvent>([&](const SimpleEvent& event)
|
||||
{
|
||||
benchmark::DoNotOptimize(sum += event.value * 2);
|
||||
});
|
||||
}
|
||||
|
||||
const auto unknownEvent = UnknownEvent{2};
|
||||
while (state.KeepRunning())//Performance area!
|
||||
{
|
||||
bus.notify(unknownEvent);
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BENCHMARK(call1kLambdas_compare);
|
||||
|
||||
BENCHMARK(checkSimpleNotification);
|
||||
BENCHMARK(check10Listeners);
|
||||
BENCHMARK(check100Listeners);
|
||||
BENCHMARK(check1kListeners);
|
||||
BENCHMARK(check10NotificationsFor1kListeners);
|
||||
//BENCHMARK(check100NotificationsFor1kListeners); //Not available
|
||||
//BENCHMARK(check1kNotificationsFor1kListeners); //Not available
|
||||
//BENCHMARK(check100NotificationsFor10kListeners); //Not available
|
||||
BENCHMARK(checkNotifyFor10kListenersWhenNoOneListens);
|
||||
|
||||
BENCHMARK_MAIN()
|
269
performance/src/EventBusPerformance.cpp
Normal file
269
performance/src/EventBusPerformance.cpp
Normal file
@ -0,0 +1,269 @@
|
||||
//
|
||||
// Created by Dawid Drozd aka Gelldur on 05.08.17.
|
||||
//
|
||||
#include <random>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <eventbus/EventBus.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void checkNListeners(benchmark::State& state, const int listenersCount)
|
||||
{
|
||||
Dexode::EventBus bus;
|
||||
int sum = 0;
|
||||
|
||||
struct SimpleEvent
|
||||
{
|
||||
int value;
|
||||
};
|
||||
|
||||
for(int i = 0; i < listenersCount; ++i)
|
||||
{
|
||||
bus.listen<SimpleEvent>(
|
||||
[&](const SimpleEvent& event) { benchmark::DoNotOptimize(sum += event.value * 2); });
|
||||
}
|
||||
|
||||
while(state.KeepRunning()) // Performance area!
|
||||
{
|
||||
const auto event = SimpleEvent{2};
|
||||
bus.notify(event);
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
void checkSimpleNotification(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 1);
|
||||
}
|
||||
|
||||
void check10Listeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 10);
|
||||
}
|
||||
|
||||
void check100Listeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 100);
|
||||
}
|
||||
|
||||
void check1kListeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 1000);
|
||||
}
|
||||
|
||||
void call1kLambdas_compare(benchmark::State& state)
|
||||
{
|
||||
int sum = 0;
|
||||
std::vector<std::function<void(int)>> callbacks;
|
||||
callbacks.reserve(1000);
|
||||
for(int i = 0; i < 1000; ++i)
|
||||
{
|
||||
callbacks.emplace_back([&](int value) { benchmark::DoNotOptimize(sum += value * 2); });
|
||||
}
|
||||
|
||||
while(state.KeepRunning()) // Performance area!
|
||||
{
|
||||
for(int i = 0; i < 1000; ++i)
|
||||
// for (auto& callback :callbacks)
|
||||
{
|
||||
callbacks[i](2);
|
||||
}
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
struct SimpleEvent
|
||||
{
|
||||
int value = N;
|
||||
};
|
||||
|
||||
void checkNNotificationsForNListeners(benchmark::State& state,
|
||||
const int notificationsCount,
|
||||
const int listenersCount)
|
||||
{
|
||||
std::mt19937 generator(311281);
|
||||
std::uniform_int_distribution<int> uniformDistribution(0, notificationsCount - 1);
|
||||
|
||||
Dexode::EventBus bus;
|
||||
int sum = 0;
|
||||
for(int i = 0; i < listenersCount;
|
||||
++i) // We register M listeners for N notifications using uniform distribution
|
||||
{
|
||||
const auto which = uniformDistribution(generator);
|
||||
// We generate here N different notifications
|
||||
switch(which)
|
||||
{
|
||||
case 0:
|
||||
bus.listen<SimpleEvent<0>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
bus.listen<SimpleEvent<1>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
bus.listen<SimpleEvent<2>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 3:
|
||||
bus.listen<SimpleEvent<3>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 4:
|
||||
bus.listen<SimpleEvent<4>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 5:
|
||||
bus.listen<SimpleEvent<5>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 6:
|
||||
bus.listen<SimpleEvent<6>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 7:
|
||||
bus.listen<SimpleEvent<7>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 8:
|
||||
bus.listen<SimpleEvent<8>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
case 9:
|
||||
bus.listen<SimpleEvent<9>>([&](const auto& event) {
|
||||
benchmark::DoNotOptimize(sum += event.value *
|
||||
2); // we use it to prevent some? optimizations
|
||||
});
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
while(state.KeepRunning()) // Performance area!
|
||||
{
|
||||
// Pick random notification
|
||||
const auto which = uniformDistribution(generator);
|
||||
const auto number = uniformDistribution(generator);
|
||||
// We generate here N different notifications
|
||||
switch(which)
|
||||
{
|
||||
case 0:
|
||||
bus.notify(SimpleEvent<0>{number});
|
||||
break;
|
||||
case 1:
|
||||
bus.notify(SimpleEvent<1>{number});
|
||||
break;
|
||||
case 2:
|
||||
bus.notify(SimpleEvent<2>{number});
|
||||
break;
|
||||
case 3:
|
||||
bus.notify(SimpleEvent<3>{number});
|
||||
break;
|
||||
case 4:
|
||||
bus.notify(SimpleEvent<4>{number});
|
||||
break;
|
||||
case 5:
|
||||
bus.notify(SimpleEvent<5>{number});
|
||||
break;
|
||||
case 6:
|
||||
bus.notify(SimpleEvent<6>{number});
|
||||
break;
|
||||
case 7:
|
||||
bus.notify(SimpleEvent<7>{number});
|
||||
break;
|
||||
case 8:
|
||||
bus.notify(SimpleEvent<8>{number});
|
||||
break;
|
||||
case 9:
|
||||
bus.notify(SimpleEvent<9>{number});
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
void check10NotificationsFor1kListeners(benchmark::State& state)
|
||||
{
|
||||
checkNNotificationsForNListeners(state, 10, 1000);
|
||||
}
|
||||
|
||||
// Sorry not available
|
||||
// void check100NotificationsFor1kListeners(benchmark::State& state)
|
||||
//{
|
||||
// checkNNotificationsForNListeners(state, 100, 1000);
|
||||
//}
|
||||
//
|
||||
// void check1kNotificationsFor1kListeners(benchmark::State& state)
|
||||
//{
|
||||
// checkNNotificationsForNListeners(state, 1000, 1000);
|
||||
//}
|
||||
//
|
||||
// void check100NotificationsFor10kListeners(benchmark::State& state)
|
||||
//{
|
||||
// checkNNotificationsForNListeners(state, 100, 10000);
|
||||
//}
|
||||
|
||||
void checkNotifyFor10kListenersWhenNoOneListens(benchmark::State& state)
|
||||
{
|
||||
Dexode::EventBus bus;
|
||||
int sum = 0;
|
||||
struct SimpleEvent
|
||||
{
|
||||
int value;
|
||||
};
|
||||
struct UnknownEvent
|
||||
{
|
||||
int value;
|
||||
};
|
||||
for(int i = 0; i < 10000; ++i)
|
||||
{
|
||||
bus.listen<SimpleEvent>(
|
||||
[&](const SimpleEvent& event) { benchmark::DoNotOptimize(sum += event.value * 2); });
|
||||
}
|
||||
|
||||
const auto unknownEvent = UnknownEvent{2};
|
||||
while(state.KeepRunning()) // Performance area!
|
||||
{
|
||||
bus.notify(unknownEvent);
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(call1kLambdas_compare);
|
||||
|
||||
BENCHMARK(checkSimpleNotification);
|
||||
BENCHMARK(check10Listeners);
|
||||
BENCHMARK(check100Listeners);
|
||||
BENCHMARK(check1kListeners);
|
||||
BENCHMARK(check10NotificationsFor1kListeners);
|
||||
// BENCHMARK(check100NotificationsFor1kListeners); //Not available
|
||||
// BENCHMARK(check1kNotificationsFor1kListeners); //Not available
|
||||
// BENCHMARK(check100NotificationsFor10kListeners); //Not available
|
||||
BENCHMARK(checkNotifyFor10kListenersWhenNoOneListens);
|
||||
|
||||
BENCHMARK_MAIN()
|
101
performance/src/PocoNotificationCenterPerformance.cpp
Normal file
101
performance/src/PocoNotificationCenterPerformance.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
//
|
||||
// Created by Dawid Drozd aka Gelldur on 24.07.18.
|
||||
//
|
||||
#include <functional>
|
||||
#include <random>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <Poco/NObserver.h>
|
||||
#include <Poco/Notification.h>
|
||||
#include <Poco/NotificationCenter.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <class T>
|
||||
struct Wrapper : public Poco::Notification
|
||||
{
|
||||
T data;
|
||||
|
||||
Wrapper(T data)
|
||||
: data(std::move(data))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class Egg>
|
||||
class Target
|
||||
{
|
||||
public:
|
||||
Target(std::function<void(const Egg&)> callback)
|
||||
: _callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
void handle(const Poco::AutoPtr<Wrapper<Egg>>& event)
|
||||
{
|
||||
_callback((*event.get()).data);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(const Egg&)> _callback;
|
||||
};
|
||||
|
||||
void checkNListeners(benchmark::State& state, const int listenersCount)
|
||||
{
|
||||
Poco::NotificationCenter bus;
|
||||
int sum = 0;
|
||||
|
||||
struct SimpleEvent
|
||||
{
|
||||
int value;
|
||||
};
|
||||
|
||||
using MyEvent = Wrapper<SimpleEvent>;
|
||||
using Listener = Target<SimpleEvent>;
|
||||
|
||||
std::vector<Listener> targets;
|
||||
targets.reserve(listenersCount + 1);
|
||||
|
||||
for(int i = 0; i < listenersCount; ++i)
|
||||
{
|
||||
targets.emplace_back(
|
||||
[&](const SimpleEvent& event) { benchmark::DoNotOptimize(sum += event.value * 2); });
|
||||
|
||||
bus.addObserver(Poco::NObserver<Listener, MyEvent>(targets.back(), &Listener::handle));
|
||||
}
|
||||
|
||||
while(state.KeepRunning()) // Performance area!
|
||||
{
|
||||
const Poco::AutoPtr<MyEvent> event = new Wrapper<SimpleEvent>{SimpleEvent{2}};
|
||||
bus.postNotification(event);
|
||||
}
|
||||
state.counters["sum"] = sum;
|
||||
}
|
||||
|
||||
void Poco_checkSimpleNotification(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 1);
|
||||
}
|
||||
|
||||
void Poco_check10Listeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 10);
|
||||
}
|
||||
|
||||
void Poco_check100Listeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 100);
|
||||
}
|
||||
|
||||
void Poco_check1kListeners(benchmark::State& state)
|
||||
{
|
||||
checkNListeners(state, 1000);
|
||||
}
|
||||
|
||||
BENCHMARK(Poco_checkSimpleNotification);
|
||||
BENCHMARK(Poco_check10Listeners);
|
||||
BENCHMARK(Poco_check100Listeners);
|
||||
BENCHMARK(Poco_check1kListeners);
|
||||
}
|
@ -1,23 +1,13 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.2 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
PROJECT(Sample LANGUAGES CXX)
|
||||
project(Sample LANGUAGES CXX)
|
||||
|
||||
ADD_EXECUTABLE(Sample
|
||||
add_executable(Sample
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
TARGET_COMPILE_OPTIONS(Sample PUBLIC
|
||||
-Wall -pedantic
|
||||
-Wno-unused-private-field
|
||||
-Wnon-virtual-dtor
|
||||
-Wno-gnu
|
||||
-Werror
|
||||
)
|
||||
if(NOT TARGET Dexode::EventBus)
|
||||
find_package(EventBus CONFIG REQUIRED)
|
||||
endif()
|
||||
|
||||
SET_TARGET_PROPERTIES(Sample PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
)
|
||||
|
||||
ADD_SUBDIRECTORY(../ EventBus/)
|
||||
|
||||
TARGET_LINK_LIBRARIES(Sample PRIVATE Dexode::EventBus)
|
||||
target_link_libraries(Sample PRIVATE Dexode::EventBus)
|
||||
|
@ -2,45 +2,48 @@
|
||||
* @brief Sample code to play with!
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <eventbus/EventBus.h>
|
||||
#include <eventbus/EventCollector.h>
|
||||
|
||||
namespace Event //Example namespace for events
|
||||
namespace Event // Example namespace for events
|
||||
{
|
||||
|
||||
struct Gold { int value = 0; }; //Event that will be proceed when our gold changes
|
||||
|
||||
struct Gold // Event that will be proceed when our gold changes
|
||||
{
|
||||
int value = 0;
|
||||
};
|
||||
}
|
||||
|
||||
enum class Monster
|
||||
{
|
||||
Frog, Tux, Shark
|
||||
Frog,
|
||||
Tux,
|
||||
Shark
|
||||
};
|
||||
|
||||
class CharacterController // some kind of character controller
|
||||
class Character
|
||||
{
|
||||
public:
|
||||
CharacterController(const std::shared_ptr<Dexode::EventBus>& eventBus)
|
||||
: _bus{eventBus}
|
||||
Character(const std::shared_ptr<Dexode::EventBus>& eventBus)
|
||||
: _bus{eventBus}
|
||||
{
|
||||
}
|
||||
|
||||
void kill(Monster monsterType)
|
||||
{
|
||||
if (Monster::Frog == monsterType)
|
||||
if(Monster::Frog == monsterType)
|
||||
{
|
||||
_gold += 1;
|
||||
}
|
||||
else if (Monster::Tux == monsterType)
|
||||
else if(Monster::Tux == monsterType)
|
||||
{
|
||||
_gold += 100;
|
||||
}
|
||||
else if (Monster::Shark == monsterType)
|
||||
else if(Monster::Shark == monsterType)
|
||||
{
|
||||
_gold += 25;
|
||||
}
|
||||
@ -48,7 +51,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
int _gold = 0;//Controller stores how much gold we have
|
||||
int _gold = 0;
|
||||
std::shared_ptr<Dexode::EventBus> _bus;
|
||||
};
|
||||
|
||||
@ -56,21 +59,20 @@ class UIWallet
|
||||
{
|
||||
public:
|
||||
UIWallet(const std::shared_ptr<Dexode::EventBus>& eventBus)
|
||||
: _listener{eventBus}
|
||||
: _listener{eventBus}
|
||||
{
|
||||
}
|
||||
|
||||
void onDraw()//Example "draw" of UI
|
||||
void onDraw() // Example "draw" of UI
|
||||
{
|
||||
std::cout << "Gold:" << _gold << std::endl;
|
||||
}
|
||||
|
||||
void onEnter()// We could also do such things in ctor and dtor but a lot of UI has something like this
|
||||
void onEnter() // We could also do such things in ctor and dtor but a lot of UI has something
|
||||
// like this
|
||||
{
|
||||
_listener.listen<Event::Gold>([this](const auto& event)
|
||||
{
|
||||
_gold = std::to_string(event.value);
|
||||
});
|
||||
_listener.listen<Event::Gold>(
|
||||
[this](const auto& event) { _gold = std::to_string(event.value); });
|
||||
}
|
||||
|
||||
void onExit()
|
||||
@ -83,23 +85,17 @@ private:
|
||||
Dexode::EventCollector _listener;
|
||||
};
|
||||
|
||||
class ShopButton // Shop button is only enabled when we have some gold (odd decision but for sample good :P)
|
||||
class ShopButton // Shop button is only enabled when we have some gold (odd decision but for sample
|
||||
// good :P)
|
||||
{
|
||||
public:
|
||||
ShopButton(const std::shared_ptr<Dexode::EventBus>& eventBus)
|
||||
: _listener{eventBus}
|
||||
: _listener{eventBus}
|
||||
{
|
||||
}
|
||||
|
||||
void onEnter()
|
||||
{
|
||||
//We can use lambda or bind your choice
|
||||
_listener.listen<Event::Gold>(std::bind(&ShopButton::onGoldUpdated, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void onExit()
|
||||
{
|
||||
_listener.unlistenAll();//unlistenAll is also called when listener is destroyed
|
||||
// We can use lambda or bind your choice
|
||||
_listener.listen<Event::Gold>(
|
||||
std::bind(&ShopButton::onGoldUpdated, this, std::placeholders::_1));
|
||||
// Also we use RAII idiom to handle unlisten
|
||||
}
|
||||
|
||||
bool isEnabled() const
|
||||
@ -114,32 +110,36 @@ private:
|
||||
void onGoldUpdated(const Event::Gold& event)
|
||||
{
|
||||
_isEnabled = event.value > 0;
|
||||
std::cout << "Shop button is:" << _isEnabled << std::endl;//some kind of logs
|
||||
std::cout << "Shop button is:" << _isEnabled << std::endl; // some kind of logs
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::shared_ptr<Dexode::EventBus> eventBus = std::shared_ptr<Dexode::EventBus>(new Dexode::EventBus{});
|
||||
std::shared_ptr<Dexode::EventBus> eventBus = std::make_shared<Dexode::EventBus>();
|
||||
|
||||
CharacterController characterController{eventBus};
|
||||
Character characterController{eventBus};
|
||||
|
||||
UIWallet wallet{eventBus};//UIWallet doesn't know anything about character or even who store gold
|
||||
ShopButton shopButton{eventBus};//ShopButton doesn't know anything about character
|
||||
UIWallet wallet{eventBus}; // UIWallet doesn't know anything about character
|
||||
// or even who store gold
|
||||
ShopButton shopButton{eventBus}; // ShopButton doesn't know anything about character
|
||||
{
|
||||
wallet.onEnter();
|
||||
shopButton.onEnter();
|
||||
}
|
||||
|
||||
characterController.kill(Monster::Tux);
|
||||
|
||||
wallet.onDraw();
|
||||
{
|
||||
characterController.kill(Monster::Tux);
|
||||
}
|
||||
wallet.onDraw();
|
||||
|
||||
//It is easy to test UI eg.
|
||||
// It is easy to test UI eg.
|
||||
eventBus->notify(Event::Gold{1});
|
||||
assert(shopButton.isEnabled() == true);
|
||||
eventBus->notify(Event::Gold{0});
|
||||
assert(shopButton.isEnabled() == false);
|
||||
|
||||
wallet.onExit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,203 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <list>
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
#error This library needs at least a C++11 compliant compiler
|
||||
#endif
|
||||
|
||||
// This version isn't transaction safe as normal EventBus
|
||||
|
||||
namespace Dexode
|
||||
{
|
||||
|
||||
class EventBus
|
||||
{
|
||||
public:
|
||||
EventBus() = default;
|
||||
|
||||
~EventBus()
|
||||
{
|
||||
_callbacks.clear();
|
||||
}
|
||||
|
||||
EventBus(const EventBus&) = delete;
|
||||
EventBus(EventBus&&) = delete;
|
||||
|
||||
EventBus& operator=(EventBus&&) = delete;
|
||||
EventBus& operator=(const EventBus&) = delete;
|
||||
|
||||
/**
|
||||
* Register listener for event. Returns token used for unlisten.
|
||||
*
|
||||
* @tparam Event - type you want to listen for
|
||||
* @param callback - your callback to handle event
|
||||
* @return token used for unlisten
|
||||
*/
|
||||
template<typename Event>
|
||||
int listen(const std::function<void(const Event&)>& callback)
|
||||
{
|
||||
const int token = ++_tokener;
|
||||
listen<Event>(token, callback);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam Event - type you want to listen for
|
||||
* @param token - unique token for identification receiver. Simply pass token from @see EventBus::listen
|
||||
* @param callback - your callback to handle event
|
||||
*/
|
||||
template<typename Event>
|
||||
void listen(const int token, const std::function<void(const Event&)>& callback)
|
||||
{
|
||||
using Vector = VectorImpl<Event>;
|
||||
|
||||
assert(callback && "callback should be valid");//Check for valid object
|
||||
|
||||
std::unique_ptr<VectorInterface>& vector = _callbacks[getTypeId<Event>()];
|
||||
if (vector == nullptr)
|
||||
{
|
||||
vector.reset(new Vector{});
|
||||
}
|
||||
|
||||
assert(dynamic_cast<Vector*>(vector.get()));
|
||||
Vector* vectorImpl = static_cast<Vector*>(vector.get());
|
||||
vectorImpl->container.emplace_back(std::make_pair(callback, token));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param token - token from EventBus::listen
|
||||
*/
|
||||
void unlistenAll(const int token)
|
||||
{
|
||||
for (auto& element : _callbacks)
|
||||
{
|
||||
element.second->remove(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam Event - type you want to unlisten. @see Notiier::listen
|
||||
* @param token - token from EventBus::listen
|
||||
*/
|
||||
template<typename Event>
|
||||
void unlisten(const int token)
|
||||
{
|
||||
auto found = _callbacks.find(getTypeId<Event>());
|
||||
if (found != _callbacks.end())
|
||||
{
|
||||
found->second->remove(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all listeners for event
|
||||
*
|
||||
* @param event your event struct
|
||||
*/
|
||||
template<typename Event>
|
||||
void notify(const Event& event)
|
||||
{
|
||||
using Vector = VectorImpl<Event>;
|
||||
// constexpr auto typeId = getTypeId<Event>();
|
||||
const auto typeId = getTypeId<Event>();
|
||||
auto found = _callbacks.find(typeId);
|
||||
if (found == _callbacks.end())
|
||||
{
|
||||
return;// no such notifications
|
||||
}
|
||||
|
||||
std::unique_ptr<VectorInterface>& vector = found->second;
|
||||
assert(dynamic_cast<Vector*>(vector.get()));
|
||||
Vector* vectorImpl = static_cast<Vector*>(vector.get());
|
||||
|
||||
if (vectorImpl->container.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = vectorImpl->container.begin();
|
||||
while (it != vectorImpl->container.end())
|
||||
{
|
||||
vectorImpl->currentIterator = std::next(it);
|
||||
it->first(event);//Order may change eg. some unlisten during call
|
||||
it = vectorImpl->currentIterator;
|
||||
}
|
||||
vectorImpl->currentIterator = vectorImpl->container.end();
|
||||
}
|
||||
|
||||
private:
|
||||
struct VectorInterface
|
||||
{
|
||||
virtual ~VectorInterface() = default;
|
||||
|
||||
virtual void remove(const int token) = 0;
|
||||
virtual void removeAll() = 0;
|
||||
};
|
||||
|
||||
template<typename Event>
|
||||
struct VectorImpl : public VectorInterface
|
||||
{
|
||||
using CallbackType = std::function<void(const Event&)>;
|
||||
using ContainerType = std::list<std::pair<CallbackType, int>>;
|
||||
ContainerType container;
|
||||
typename ContainerType::iterator currentIterator = container.end();
|
||||
|
||||
//Invalidation rules: https://stackoverflow.com/questions/6438086/iterator-invalidation-rules
|
||||
virtual void removeAll() override
|
||||
{
|
||||
container.clear();
|
||||
currentIterator = container.end();
|
||||
}
|
||||
|
||||
virtual void remove(const int token) override
|
||||
{
|
||||
auto removeIfPredicate = [token](const std::pair<CallbackType, int>& element)
|
||||
{
|
||||
return element.second == token;
|
||||
};
|
||||
//It is safe to remove
|
||||
if (currentIterator == container.end())
|
||||
{
|
||||
container.remove_if(removeIfPredicate);
|
||||
return;
|
||||
}
|
||||
|
||||
//During iteration
|
||||
ContainerType unvisited;
|
||||
unvisited.splice(unvisited.begin(), container, currentIterator, container.end());
|
||||
|
||||
container.remove_if(removeIfPredicate);//could invalidate currentIterator
|
||||
unvisited.remove_if(removeIfPredicate);
|
||||
|
||||
const auto distance = unvisited.size();
|
||||
container.splice(container.end(), unvisited);
|
||||
|
||||
if (container.empty())
|
||||
{
|
||||
currentIterator = container.end();
|
||||
return;
|
||||
}
|
||||
currentIterator = std::prev(container.end(), distance);
|
||||
}
|
||||
};
|
||||
|
||||
int _tokener = 0;
|
||||
std::map<std::size_t, std::unique_ptr<VectorInterface>> _callbacks;
|
||||
|
||||
template<typename T>
|
||||
static const std::size_t getTypeId()
|
||||
{
|
||||
//std::hash<std::string>{}(typeid(T).name() is slower
|
||||
return typeid(T).hash_code();
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace Dexode */
|
@ -1,42 +1,53 @@
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
# http://www.levelofindirection.com/journal/2010/12/28/unit-testing-in-c-and-objective-c-just-got-easier.html
|
||||
# Thanks for CATCH!
|
||||
|
||||
ADD_EXECUTABLE(EventBusTest
|
||||
eventbus/EventCollectorTest.cpp
|
||||
eventbus/NotifierTest.cpp
|
||||
project(EventBusTest)
|
||||
|
||||
# Dependencies
|
||||
enable_testing()
|
||||
if (NOT TARGET Dexode::EventBus)
|
||||
find_package(EventBus CONFIG REQUIRED)
|
||||
endif ()
|
||||
|
||||
# From 2.3.X they broke back compatibility
|
||||
find_package(Catch2 2.2 REQUIRED)
|
||||
|
||||
# Target definition
|
||||
add_executable(EventBusTest
|
||||
src/EventCollectorTest.cpp
|
||||
src/NotifierTest.cpp
|
||||
)
|
||||
|
||||
SET_TARGET_PROPERTIES(EventBusTest PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
)
|
||||
|
||||
TARGET_COMPILE_OPTIONS(EventBusTest PUBLIC
|
||||
target_compile_options(EventBusTest PUBLIC
|
||||
-Wall -pedantic
|
||||
-Wno-unused-private-field
|
||||
-Wnon-virtual-dtor
|
||||
-Wno-gnu
|
||||
-Werror
|
||||
-Wno-error=deprecated-declarations
|
||||
)
|
||||
IF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
TARGET_COMPILE_OPTIONS(EventBusTest PRIVATE
|
||||
-Wno-error=unknown-argument
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
SET(EVENTBUS_DEBUG_FLAGS
|
||||
# Don't do such thing:
|
||||
# if(CMAKE_BUILD_TYPE STREQUAL DEBUG)
|
||||
# ....
|
||||
# else()
|
||||
# ...
|
||||
# endif()
|
||||
#
|
||||
# Instead do this way: (It will work for Visual Studio)
|
||||
# target_compile_definitions(foo PRIVATE "VERBOSITY=$<IF:$<BOOL:${VERBOSE}>,30,10>")
|
||||
|
||||
|
||||
set(EVENTBUS_DEBUG_FLAGS
|
||||
-O0 -fno-inline
|
||||
-DDEBUG
|
||||
-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
|
||||
#-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
|
||||
)
|
||||
|
||||
SET(EVENTBUS_RELEASE_FLAGS
|
||||
-DNDEBUG
|
||||
)
|
||||
target_compile_options(EventBusTest PUBLIC "$<$<CONFIG:DEBUG>:${EVENTBUS_DEBUG_FLAGS}>")
|
||||
|
||||
TARGET_COMPILE_OPTIONS(EventBusTest PUBLIC "$<$<CONFIG:DEBUG>:${EVENTBUS_DEBUG_FLAGS}>")
|
||||
TARGET_COMPILE_OPTIONS(EventBusTest PUBLIC "$<$<CONFIG:RELEASE>:${EVENTBUS_RELEASE_FLAGS}>")
|
||||
target_link_libraries(EventBusTest PUBLIC Dexode::EventBus Catch2::Catch)
|
||||
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(EventBusTest PUBLIC Catch/single_include/)
|
||||
TARGET_LINK_LIBRARIES(EventBusTest PUBLIC Dexode::EventBus)
|
||||
|
||||
add_test(NAME EventBus.UnitTests COMMAND EventBusTest)
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit ee67ac6b7c3595251342671485c65cf81d725896
|
Loading…
x
Reference in New Issue
Block a user