improve container implementation
This commit is contained in:
parent
ab9e7f4a3d
commit
9bae60011e
@ -29,22 +29,17 @@ class EnvFixture : public ::testing::Test {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename I>
|
template <typename I>
|
||||||
void Install(const std::shared_ptr<I>& o) {
|
void Install(SimpleEnv::ObjectOrFactory&& v) {
|
||||||
fmap_.emplace(typeid(I), [o](auto&) { return o; });
|
map_.emplace(typeid(I), std::move(v));
|
||||||
}
|
|
||||||
template <typename I>
|
|
||||||
void Install(std::function<std::shared_ptr<I>(Env&)>&& factory) {
|
|
||||||
fmap_.emplace(typeid(I), std::move(factory));
|
|
||||||
}
|
}
|
||||||
template <typename I, typename T>
|
template <typename I, typename T>
|
||||||
void Install() {
|
void Install() {
|
||||||
fmap_.emplace(
|
map_.insert(SimpleEnv::MakeItem<I, T>());
|
||||||
typeid(I), [](auto& env) { return std::make_shared<T>(env); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
env_.emplace(std::move(fmap_));
|
env_.emplace(std::move(map_));
|
||||||
}
|
}
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
env_ = std::nullopt;
|
env_ = std::nullopt;
|
||||||
@ -54,7 +49,7 @@ class EnvFixture : public ::testing::Test {
|
|||||||
Env& env() noexcept { return *env_; }
|
Env& env() noexcept { return *env_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SimpleEnv::FactoryMap fmap_;
|
SimpleEnv::Map map_;
|
||||||
std::optional<SimpleEnv> env_;
|
std::optional<SimpleEnv> env_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,18 +6,16 @@
|
|||||||
#include "iface/env.hh"
|
#include "iface/env.hh"
|
||||||
|
|
||||||
|
|
||||||
static inline std::shared_ptr<nf7::Env> MakeEmptyEnv() {
|
static inline bool MatchPair(
|
||||||
return std::make_shared<nf7::SimpleEnv>(nf7::SimpleEnv::FactoryMap {});
|
const std::optional<nf7::subsys::MetaEnv::Pair>& a,
|
||||||
}
|
const nf7::subsys::MetaEnv::Pair& b) {
|
||||||
static inline bool MatchPair(const std::optional<nf7::subsys::MetaEnv::Pair>& a,
|
|
||||||
const nf7::subsys::MetaEnv::Pair& b) {
|
|
||||||
return a && a->first == b.first && &a->second == &b.second;
|
return a && a->first == b.first && &a->second == &b.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MetaEnv, FindOrByName) {
|
TEST(MetaEnv, FindOrByName) {
|
||||||
const auto a = MakeEmptyEnv();
|
const auto a = std::make_shared<nf7::SimpleEnv>();
|
||||||
const auto b = MakeEmptyEnv();
|
const auto b = std::make_shared<nf7::SimpleEnv>();
|
||||||
const auto c = MakeEmptyEnv();
|
const auto c = std::make_shared<nf7::SimpleEnv>();
|
||||||
|
|
||||||
nf7::core::MetaEnv sut {
|
nf7::core::MetaEnv sut {
|
||||||
{
|
{
|
||||||
@ -35,9 +33,9 @@ TEST(MetaEnv, FindOrByName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(MetaEnv, FindOrByIndex) {
|
TEST(MetaEnv, FindOrByIndex) {
|
||||||
const auto a = MakeEmptyEnv();
|
const auto a = std::make_shared<nf7::SimpleEnv>();
|
||||||
const auto b = MakeEmptyEnv();
|
const auto b = std::make_shared<nf7::SimpleEnv>();
|
||||||
const auto c = MakeEmptyEnv();
|
const auto c = std::make_shared<nf7::SimpleEnv>();
|
||||||
|
|
||||||
nf7::core::MetaEnv sut {
|
nf7::core::MetaEnv sut {
|
||||||
{
|
{
|
||||||
@ -54,9 +52,9 @@ TEST(MetaEnv, FindOrByIndex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(MetaEnv, FetchAll) {
|
TEST(MetaEnv, FetchAll) {
|
||||||
const auto a = MakeEmptyEnv();
|
const auto a = std::make_shared<nf7::SimpleEnv>();
|
||||||
const auto b = MakeEmptyEnv();
|
const auto b = std::make_shared<nf7::SimpleEnv>();
|
||||||
const auto c = MakeEmptyEnv();
|
const auto c = std::make_shared<nf7::SimpleEnv>();
|
||||||
|
|
||||||
nf7::core::MetaEnv sut {
|
nf7::core::MetaEnv sut {
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "iface/common/exception.hh"
|
#include "iface/common/exception.hh"
|
||||||
@ -27,7 +28,6 @@ class Container {
|
|||||||
Container& operator=(Container&&) = delete;
|
Container& operator=(Container&&) = delete;
|
||||||
|
|
||||||
virtual std::shared_ptr<I> Get(std::type_index) = 0;
|
virtual std::shared_ptr<I> Get(std::type_index) = 0;
|
||||||
virtual bool installed(std::type_index) const noexcept = 0;
|
|
||||||
|
|
||||||
template <typename I2>
|
template <typename I2>
|
||||||
void Get(std::shared_ptr<I2>& out) {
|
void Get(std::shared_ptr<I2>& out) {
|
||||||
@ -57,42 +57,34 @@ class Container {
|
|||||||
const auto& ret = Get(idx);
|
const auto& ret = Get(idx);
|
||||||
return nullptr != ret? ret: def;
|
return nullptr != ret? ret: def;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool installed() const noexcept {
|
|
||||||
return installed(typeid(T));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename I>
|
template <typename I>
|
||||||
class NullContainer : public Container<I> {
|
class NullContainer : public Container<I> {
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<NullContainer> instance()
|
static inline const auto kInstance = std::make_shared<NullContainer>();
|
||||||
try {
|
|
||||||
static const auto kInstance = std::make_shared<NullContainer>();
|
|
||||||
return kInstance;
|
|
||||||
} catch (const std::bad_alloc&) {
|
|
||||||
throw MemoryException {};
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NullContainer() = default;
|
NullContainer() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<I> Get(std::type_index) override { return nullptr; }
|
std::shared_ptr<I> Get(std::type_index) override { return nullptr; }
|
||||||
bool installed(std::type_index) const noexcept override { return false; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename I>
|
template <typename I>
|
||||||
class SimpleContainer : public Container<I> {
|
class SimpleContainer : public Container<I> {
|
||||||
public:
|
public:
|
||||||
using Factory = std::function<std::shared_ptr<I>(Container<I>&)>;
|
using Object = std::shared_ptr<I>;
|
||||||
using FactoryPair = std::pair<std::type_index, Factory>;
|
using Factory = std::function<Object(Container<I>&)>;
|
||||||
using FactoryMap = std::unordered_map<std::type_index, Factory>;
|
|
||||||
using ObjectMap = std::unordered_map<std::type_index, std::shared_ptr<I>>;
|
|
||||||
|
|
||||||
|
using ObjectOrFactory = std::variant<Object, Factory>;
|
||||||
|
|
||||||
|
using MapItem = std::pair<std::type_index, ObjectOrFactory>;
|
||||||
|
using Map = std::unordered_map<std::type_index, ObjectOrFactory>;
|
||||||
|
|
||||||
|
public:
|
||||||
template <typename I2, typename T>
|
template <typename I2, typename T>
|
||||||
static FactoryPair MakePair() noexcept {
|
static MapItem MakeItem() noexcept {
|
||||||
static_assert(std::is_base_of_v<I, I2>,
|
static_assert(std::is_base_of_v<I, I2>,
|
||||||
"registerable interface must be based on "
|
"registerable interface must be based on "
|
||||||
"container common interface");
|
"container common interface");
|
||||||
@ -102,62 +94,47 @@ class SimpleContainer : public Container<I> {
|
|||||||
static_assert(std::is_constructible_v<T, Container<I>&>,
|
static_assert(std::is_constructible_v<T, Container<I>&>,
|
||||||
"registerable concrete type must be "
|
"registerable concrete type must be "
|
||||||
"constructible with container");
|
"constructible with container");
|
||||||
return FactoryPair {
|
return MapItem {
|
||||||
typeid(I2), [](auto& x) { return std::make_shared<T>(x); }};
|
typeid(I2),
|
||||||
}
|
[](auto& x) { return std::make_shared<T>(x); },
|
||||||
static std::shared_ptr<Container<I>> Make(FactoryMap&& factories) {
|
};
|
||||||
try {
|
|
||||||
return std::make_shared<Container<I>>(std::move(factories));
|
|
||||||
} catch (const std::bad_alloc&) {
|
|
||||||
throw MemoryException {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleContainer() = delete;
|
public:
|
||||||
SimpleContainer(FactoryMap&& factories,
|
SimpleContainer(Map&& m = {},
|
||||||
Container<I>& fb = *NullContainer<I>::instance()) noexcept
|
Container<I>& fb = *NullContainer<I>::kInstance) noexcept
|
||||||
: fallback_(fb), factories_(std::move(factories)) { }
|
: fallback_(fb), map_(std::move(m)) { }
|
||||||
|
|
||||||
std::shared_ptr<I> Get(std::type_index idx) override {
|
public:
|
||||||
const auto obj_itr = objs_.find(idx);
|
Object Get(std::type_index idx) override {
|
||||||
if (objs_.end() != obj_itr) {
|
assert(nest_ < 1000 && "circular dependency detected");
|
||||||
return obj_itr->second;
|
|
||||||
|
auto itr = map_.find(idx);
|
||||||
|
if (map_.end() == itr) {
|
||||||
|
return fallback_.Get(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto factory_itr = factories_.find(idx);
|
auto& v = itr->second;
|
||||||
if (factories_.end() != factory_itr) {
|
auto ret = Object {nullptr};
|
||||||
assert(nest_ < 1000 &&
|
if (std::holds_alternative<Object>(v)) {
|
||||||
"circular dependency detected in container factory");
|
ret = std::get<Object>(v);
|
||||||
|
} else {
|
||||||
++nest_;
|
++nest_;
|
||||||
auto obj = factory_itr->second(*this);
|
ret = std::get<Factory>(v)(*this);
|
||||||
--nest_;
|
--nest_;
|
||||||
|
v = ret;
|
||||||
try {
|
|
||||||
const auto [itr, added] = objs_.insert({idx, std::move(obj)});
|
|
||||||
(void) itr;
|
|
||||||
(void) added;
|
|
||||||
assert(added);
|
|
||||||
return itr->second;
|
|
||||||
} catch (...) {
|
|
||||||
throw MemoryException {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return fallback_.Get(idx);
|
return nullptr != ret? ret:
|
||||||
}
|
throw Exception {"the specified interface is hidden"};
|
||||||
|
|
||||||
bool installed(std::type_index idx) const noexcept override {
|
|
||||||
return factories_.contains(idx) || fallback_.installed(idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using Container<I>::Get;
|
using Container<I>::Get;
|
||||||
using Container<I>::GetOr;
|
using Container<I>::GetOr;
|
||||||
using Container<I>::installed;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Container<I>& fallback_;
|
Container<I>& fallback_;
|
||||||
|
|
||||||
FactoryMap factories_;
|
Map map_;
|
||||||
ObjectMap objs_;
|
|
||||||
|
|
||||||
uint32_t nest_ = 0;
|
uint32_t nest_ = 0;
|
||||||
};
|
};
|
||||||
|
@ -35,15 +35,15 @@ class BRecursive : public IB {
|
|||||||
|
|
||||||
TEST(SimpleContainer, FetchIsolated) {
|
TEST(SimpleContainer, FetchIsolated) {
|
||||||
SUT sut {{
|
SUT sut {{
|
||||||
SUT::MakePair<IA, A>(),
|
SUT::MakeItem<IA, A>(),
|
||||||
}};
|
}};
|
||||||
auto ptr = sut.Get<IA>();
|
auto ptr = sut.Get<IA>();
|
||||||
EXPECT_TRUE(std::dynamic_pointer_cast<A>(ptr));
|
EXPECT_TRUE(std::dynamic_pointer_cast<A>(ptr));
|
||||||
}
|
}
|
||||||
TEST(SimpleContainer, FetchDepending) {
|
TEST(SimpleContainer, FetchDepending) {
|
||||||
SUT sut {{
|
SUT sut {{
|
||||||
SUT::MakePair<IA, A>(),
|
SUT::MakeItem<IA, A>(),
|
||||||
SUT::MakePair<IB, B>(),
|
SUT::MakeItem<IB, B>(),
|
||||||
}};
|
}};
|
||||||
auto ptr = sut.Get<IB>();
|
auto ptr = sut.Get<IB>();
|
||||||
EXPECT_TRUE(std::dynamic_pointer_cast<B>(ptr));
|
EXPECT_TRUE(std::dynamic_pointer_cast<B>(ptr));
|
||||||
@ -54,21 +54,14 @@ TEST(SimpleContainer, FetchUnknown) {
|
|||||||
}
|
}
|
||||||
TEST(SimpleContainer, FetchUnknownDepending) {
|
TEST(SimpleContainer, FetchUnknownDepending) {
|
||||||
SUT sut {{
|
SUT sut {{
|
||||||
SUT::MakePair<IB, B>(),
|
SUT::MakeItem<IB, B>(),
|
||||||
}};
|
}};
|
||||||
EXPECT_THROW(sut.Get<IB>(), nf7::Exception);
|
EXPECT_THROW(sut.Get<IB>(), nf7::Exception);
|
||||||
}
|
}
|
||||||
TEST(SimpleContainer, CheckInstalled) {
|
|
||||||
SUT sut {{
|
|
||||||
SUT::MakePair<IA, A>(),
|
|
||||||
}};
|
|
||||||
EXPECT_TRUE(sut.installed<IA>());
|
|
||||||
EXPECT_FALSE(sut.installed<IB>());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(SimpleContainer, FetchWithFallback) {
|
TEST(SimpleContainer, FetchWithFallback) {
|
||||||
SUT fb {{
|
SUT fb {{
|
||||||
SUT::MakePair<IA, A>(),
|
SUT::MakeItem<IA, A>(),
|
||||||
}};
|
}};
|
||||||
SUT sut {{}, fb};
|
SUT sut {{}, fb};
|
||||||
auto ptr = sut.Get<IA>();
|
auto ptr = sut.Get<IA>();
|
||||||
@ -79,19 +72,11 @@ TEST(SimpleContainer, FetchUnknownWithFallback) {
|
|||||||
SUT sut {{}, fb};
|
SUT sut {{}, fb};
|
||||||
EXPECT_THROW(sut.Get<IA>(), nf7::Exception);
|
EXPECT_THROW(sut.Get<IA>(), nf7::Exception);
|
||||||
}
|
}
|
||||||
TEST(SimpleContainer, CheckInstalledWithFallback) {
|
|
||||||
SUT fb {{
|
|
||||||
SUT::MakePair<IA, A>(),
|
|
||||||
}};
|
|
||||||
SUT sut {{}, fb};
|
|
||||||
EXPECT_TRUE(sut.installed<IA>());
|
|
||||||
EXPECT_FALSE(sut.installed<IB>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
TEST(SimpleContainer, DeathByFetchRecursive) {
|
TEST(SimpleContainer, DeathByFetchRecursive) {
|
||||||
SUT sut {{
|
SUT sut {{
|
||||||
SUT::MakePair<IB, BRecursive>(),
|
SUT::MakeItem<IB, BRecursive>(),
|
||||||
}};
|
}};
|
||||||
ASSERT_DEATH_IF_SUPPORTED(sut.Get<IB>(), "");
|
ASSERT_DEATH_IF_SUPPORTED(sut.Get<IB>(), "");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user