Compare commits

...

3 Commits

Author SHA1 Message Date
dbb86258e1 add Observer interface 2023-07-10 22:47:34 +09:00
096cd189d6 add new thirdparty, gtest 2023-07-10 22:47:34 +09:00
d6e62f1ed6 specify C++ version as 23 2023-07-10 22:47:34 +09:00
6 changed files with 184 additions and 1 deletions

View File

@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.20)
project(nf7 C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 23)
add_subdirectory(thirdparty EXCLUDE_FROM_ALL)
include(cmake/git_hash.cmake)
add_subdirectory(iface)

View File

@ -1,10 +1,25 @@
add_library(nf7_iface)
target_include_directories(nf7_iface PUBLIC ${PROJECT_SOURCE_DIR})
target_link_libraries(nf7_iface PRIVATE git_hash)
target_sources(nf7_iface
PRIVATE
version.cc
PUBLIC
observer.hh
version.hh
)
add_executable(nf7_iface_test)
target_sources(nf7_iface_test
PRIVATE
observer_test.hh
observer_test.cc
)
target_link_libraries(nf7_iface_test
PRIVATE
nf7_iface
GTest::gmock_main
GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(nf7_iface_test)

86
iface/observer.hh Normal file
View File

@ -0,0 +1,86 @@
// No copyright
// ---- this header provides a way to implement Observer pattern easily
#pragma once
#include <cassert>
#include <unordered_set>
#include <utility>
namespace nf7::iface {
// T is notified to Observer<T> by Observer<T>::Target.
// All observers should be based on this.
template <typename T>
class Observer {
public:
class Target;
inline explicit Observer(Target&);
inline virtual ~Observer() noexcept;
protected:
virtual void Notify(const T&) {}
virtual void NotifyWithMove(T&& v) { Notify(v); }
virtual void NotifyDestruction(const T* = nullptr) {}
private:
Target& target_;
};
// All objects which can be observed by Observer<T> must be based on this.
template <typename T>
class Observer<T>::Target {
public:
friend class Observer<T>;
Target() = default;
virtual ~Target() noexcept {
for (auto obs : obs_) {
obs->NotifyDestruction();
}
}
Target(const Target&) = delete;
Target(Target&&) = delete;
Target& operator=(const Target&) = delete;
Target& operator=(Target&&) = delete;
protected:
void Notify(T&& v) const noexcept {
if (1 == obs_.size()) {
auto first = *obs_.begin();
first->NotifyWithMove(std::move(v));
return;
}
for (auto obs : obs_) {
obs->Notify(v);
}
}
bool observed(const T* = nullptr) const noexcept { return !obs_.empty(); }
private:
void Register(Observer<T>& obs) {
obs_.insert(&obs);
}
void Unregister(const Observer<T>& obs) noexcept {
obs_.erase(&const_cast<Observer<T>&>(obs));
}
private:
std::unordered_set<Observer<T>*> obs_;
};
template <typename T>
Observer<T>::Observer(Target& target) : target_(target) {
target_.Register(*this);
}
template <typename T>
Observer<T>::~Observer() noexcept {
target_.Unregister(*this);
}
} // namespace nf7::iface

41
iface/observer_test.cc Normal file
View File

@ -0,0 +1,41 @@
// No copyright
#include "iface/observer.hh"
#include "iface/observer_test.hh"
#include <gtest/gtest.h>
#include <cstdint>
#include <optional>
using namespace nf7::iface;
using namespace nf7::iface::test;
TEST(Observer, NotifyWithMove) {
ObserverTargetMock<int32_t> target;
ObserverMock<int32_t> sut {target};
EXPECT_CALL(sut, NotifyWithMove(111)).Times(1);
target.Notify(int32_t {111});
}
TEST(Observer, NotifyWithRef) {
ObserverTargetMock<int32_t> target;
ObserverMock<int32_t> sut1 {target};
ObserverMock<int32_t> sut2 {target};
EXPECT_CALL(sut1, Notify(111)).Times(1);
EXPECT_CALL(sut2, Notify(111)).Times(1);
target.Notify(int32_t {111});
}
TEST(Observer, NotifyDestruction) {
std::optional<ObserverTargetMock<int32_t>> target;
target.emplace();
ObserverMock<int32_t> sut {*target};
EXPECT_CALL(sut, NotifyDestruction(testing::_)).Times(1);
target = std::nullopt;
}

29
iface/observer_test.hh Normal file
View File

@ -0,0 +1,29 @@
// No copyright
#pragma once
#include "iface/observer.hh"
#include <gmock/gmock.h>
namespace nf7::iface::test {
template <typename T>
class ObserverMock : public Observer<T> {
public:
explicit ObserverMock(Observer<T>::Target& target) : Observer<T>(target) {
}
MOCK_METHOD1(Notify, void(const T&));
MOCK_METHOD1(NotifyWithMove, void(T&&));
MOCK_METHOD1(NotifyDestruction, void(const T*));
};
template <typename T>
class ObserverTargetMock : public Observer<T>::Target {
public:
using Observer<T>::Target::Target;
using Observer<T>::Target::Notify;
};
} // namespace nf7::iface::test

10
thirdparty/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,10 @@
include(FetchContent)
# ---- gtest (BSD-3-Clause)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.13.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)