add Container

This commit is contained in:
falsycat 2023-07-15 12:45:06 +09:00
parent 52abaeea1b
commit 6cda20c93c
3 changed files with 199 additions and 0 deletions

View File

@ -9,6 +9,7 @@ target_sources(nf7_iface
PRIVATE
version.cc
PUBLIC
common/container.hh
common/future.hh
common/observer.hh
version.hh
@ -17,6 +18,7 @@ target_sources(nf7_iface
add_executable(nf7_iface_test)
target_sources(nf7_iface_test
PRIVATE
common/container_test.cc
common/future_test.cc
common/observer_test.hh
common/observer_test.cc

123
iface/common/container.hh Normal file
View File

@ -0,0 +1,123 @@
// No copyright
#pragma once
#include <cassert>
#include <cstdint>
#include <functional>
#include <memory>
#include <typeindex>
#include <unordered_map>
#include <utility>
#include <vector>
#include "iface/common/exception.hh"
namespace nf7 {
template <typename I>
class Container final {
public:
using Factory = std::function<std::shared_ptr<I>(Container<I>&)>;
template <typename I2, typename T>
static std::pair<std::type_index, Factory> MakePair() noexcept {
static_assert(std::is_base_of_v<I, I2>,
"registerable interface must be based on "
"container common interface");
static_assert(std::is_base_of_v<I2, T>,
"registerable concrete type must be based on "
"an interface to be being registered");
static_assert(std::is_constructible_v<T, Container<I>&>,
"registerable concrete type must be "
"constructible with container");
return std::pair<std::type_index, Factory>{
typeid(I2), [](auto& x) { return std::make_shared<T>(x); }};
}
static std::shared_ptr<Container<I>> Make(
std::unordered_map<std::type_index, Factory>&& factories) {
try {
return std::make_shared<Container<I>>(std::move(factories));
} catch (const std::bad_alloc&) {
throw Exception {"memory shortage"};
}
}
Container() = delete;
explicit Container(std::unordered_map<std::type_index, Factory>&& factories) noexcept
: factories_(std::move(factories)) { }
Container(const Container&) = delete;
Container(Container&&) = delete;
Container& operator=(const Container&) = delete;
Container& operator=(Container&&) = delete;
template <typename I2>
void Get(std::shared_ptr<I2>& out) {
out = Get<I2>();
}
template <typename I2>
std::shared_ptr<I2> Get() {
auto ptr = GetOr<I2>();
if (nullptr == ptr) {
throw Exception {"missing dependency"};
}
return ptr;
}
template <typename I2>
void GetOr(std::shared_ptr<I2>& out,
const std::shared_ptr<I2>& def = nullptr) {
out = GetOr<I2>(def);
}
template <typename I2>
std::shared_ptr<I2> GetOr(const std::shared_ptr<I2>& def = nullptr) {
auto ptr = GetOr(typeid(I2), def);
auto casted_ptr = std::dynamic_pointer_cast<I2>(ptr);
assert(nullptr == ptr || nullptr != casted_ptr);
return casted_ptr;
}
std::shared_ptr<I> GetOr(
std::type_index idx, const std::shared_ptr<I>& def = nullptr) {
const auto obj_itr = objs_.find(idx);
if (objs_.end() != obj_itr) {
return obj_itr->second;
}
const auto factory_itr = factories_.find(idx);
if (factories_.end() != factory_itr) {
assert(nest_ < 1000 &&
"circular dependency detected in container factory");
++nest_;
auto obj = factory_itr->second(*this);
--nest_;
try {
const auto [itr, added] = objs_.insert({idx, std::move(obj)});
(void) itr;
(void) added;
assert(added);
return itr->second;
} catch (...) {
throw Exception {"memory shortage"};
}
}
return def;
}
template <typename T>
bool installed() const noexcept {
return installed(typeid(T));
}
bool installed(std::type_index idx) const noexcept {
return factories_.contains(idx);
}
private:
std::unordered_map<std::type_index, Factory> factories_;
std::unordered_map<std::type_index, std::shared_ptr<I>> objs_;
uint32_t nest_ = 0;
};
} // namespace nf7

View File

@ -0,0 +1,74 @@
// No copyright
#include "iface/common/container.hh"
#include <gtest/gtest.h>
#include <string>
class Object {
public:
virtual ~Object() = default;
};
using SUT = nf7::Container<Object>;
class IA : public Object { };
class IB : public Object { };
class A : public IA {
public:
explicit A(SUT&) noexcept { }
};
class B : public IB {
public:
explicit B(SUT& sut) { sut.Get(a_); }
private:
std::shared_ptr<IA> a_;
};
class BRecursive : public IB {
public:
explicit BRecursive(SUT& sut) { sut.Get(b_); }
private:
std::shared_ptr<IB> b_;
};
TEST(Container, FetchIsolated) {
SUT sut {{
SUT::MakePair<IA, A>(),
}};
auto ptr = sut.Get<IA>();
EXPECT_TRUE(std::dynamic_pointer_cast<A>(ptr));
}
TEST(Container, FetchDepending) {
SUT sut {{
SUT::MakePair<IA, A>(),
SUT::MakePair<IB, B>(),
}};
auto ptr = sut.Get<IB>();
EXPECT_TRUE(std::dynamic_pointer_cast<B>(ptr));
}
TEST(Container, FetchUnknown) {
SUT sut {{}};
EXPECT_THROW(sut.Get<IA>(), nf7::Exception);
}
TEST(Container, FetchUnknownDepending) {
SUT sut {{
SUT::MakePair<IB, B>(),
}};
EXPECT_THROW(sut.Get<IB>(), nf7::Exception);
}
TEST(Container, CheckInstalled) {
SUT sut {{
SUT::MakePair<IA, A>(),
}};
EXPECT_TRUE(sut.installed<IA>());
EXPECT_FALSE(sut.installed<IB>());
}
#if !defined(NDEBUG)
TEST(Container, DeathByFetchRecursive) {
SUT sut {{
SUT::MakePair<IB, BRecursive>(),
}};
ASSERT_DEATH_IF_SUPPORTED(sut.Get<IB>(), "");
}
#endif