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:
Dawid Drozd 2018-07-31 11:33:35 +02:00
parent 7ae52d926d
commit 3688c145cb
21 changed files with 834 additions and 759 deletions

127
.clang-format Normal file
View 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
View File

@ -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

View File

@ -1,37 +1,54 @@
os: linux
dist: trusty
sudo: require
language: cpp
sudo: false
addons:
apt:
sources:
- llvm-toolchain-precise-3.9
common_sources: &all_sources
- ubuntu-toolchain-r-test
packages:
- clang-3.9
- gcc-6
- g++-6
- 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 .)

View File

@ -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()

View File

@ -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
View 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}"
)

View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
check_required_components("@PROJECT_NAME@")

View File

@ -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;
@ -38,6 +51,8 @@ public:
template <typename Event>
int listen(const std::function<void(const Event&)>& callback)
{
validateEvent<Event>();
const int token = ++_tokener;
listen<Event>(token, callback);
return token;
@ -51,11 +66,13 @@ public:
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
std::unique_ptr<VectorInterface>& vector = _callbacks[getTypeId<Event>()];
std::unique_ptr<VectorInterface>& vector = _callbacks[type_id<Event>];
if(vector == nullptr)
{
vector.reset(new Vector{});
@ -83,7 +100,9 @@ public:
template <typename Event>
void unlisten(const int token)
{
auto found = _callbacks.find(getTypeId<Event>());
validateEvent<Event>();
auto found = _callbacks.find(type_id<Event>);
if(found != _callbacks.end())
{
found->second->remove(token);
@ -98,9 +117,11 @@ public:
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);
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
@ -146,9 +167,8 @@ private:
}
//Invalidation rules: https://stackoverflow.com/questions/6438086/iterator-invalidation-rules
auto removeFrom = std::remove_if(container.begin(), container.end()
, [token](const ContainerElement& element)
{
auto removeFrom = std::remove_if(
container.begin(), container.end(), [token](const ContainerElement& element) {
return element.first == token;
});
if(removeFrom != container.end())
@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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>
)

View File

@ -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()

View 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()

View 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);
}

View File

@ -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)

View File

@ -2,30 +2,33 @@
* @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
{
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)
Character(const std::shared_ptr<Dexode::EventBus>& eventBus)
: _bus{eventBus}
{
}
@ -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;
};
@ -65,12 +68,11 @@ public:
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}
{
}
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
_listener.listen<Event::Gold>(
std::bind(&ShopButton::onGoldUpdated, this, std::placeholders::_1));
// Also we use RAII idiom to handle unlisten
}
bool isEnabled() const
@ -120,19 +116,21 @@ private:
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
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();
}
wallet.onDraw();
{
characterController.kill(Monster::Tux);
}
wallet.onDraw();
// It is easy to test UI eg.
@ -141,5 +139,7 @@ int main(int argc, char* argv[])
eventBus->notify(Event::Gold{0});
assert(shopButton.isEnabled() == false);
wallet.onExit();
return 0;
}

View File

@ -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 */

View File

@ -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