diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/object_factory | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/object_factory')
-rw-r--r-- | library/cpp/object_factory/object_factory.cpp | 1 | ||||
-rw-r--r-- | library/cpp/object_factory/object_factory.h | 243 | ||||
-rw-r--r-- | library/cpp/object_factory/object_factory_ut.cpp | 189 | ||||
-rw-r--r-- | library/cpp/object_factory/ut/ya.make | 15 | ||||
-rw-r--r-- | library/cpp/object_factory/ya.make | 9 |
5 files changed, 457 insertions, 0 deletions
diff --git a/library/cpp/object_factory/object_factory.cpp b/library/cpp/object_factory/object_factory.cpp new file mode 100644 index 0000000000..3ef5bd9d18 --- /dev/null +++ b/library/cpp/object_factory/object_factory.cpp @@ -0,0 +1 @@ +#include "object_factory.h" diff --git a/library/cpp/object_factory/object_factory.h b/library/cpp/object_factory/object_factory.h new file mode 100644 index 0000000000..96cc11bcfd --- /dev/null +++ b/library/cpp/object_factory/object_factory.h @@ -0,0 +1,243 @@ +#pragma once + +#include <util/system/guard.h> +#include <util/system/rwlock.h> +#include <util/generic/map.h> +#include <util/generic/set.h> +#include <util/generic/singleton.h> +#include <util/generic/yexception.h> + +namespace NObjectFactory { + template <class TProduct, class... TArgs> + class IFactoryObjectCreator { + public: + virtual TProduct* Create(TArgs... args) const = 0; + virtual ~IFactoryObjectCreator() { + } + }; + + template <class TProduct> + class IFactoryObjectCreator<TProduct, void> { + public: + virtual TProduct* Create(void) const = 0; + virtual ~IFactoryObjectCreator() { + } + }; + +#define FACTORY_OBJECT_NAME(Name) \ + static TString GetTypeName() { \ + return #Name; \ + } \ + virtual TString GetType() const override { \ + return #Name; \ + } + + template <class TBaseProduct, class TDerivedProduct, class... TArgs> + class TFactoryObjectCreator: public IFactoryObjectCreator<TBaseProduct, TArgs...> { + TDerivedProduct* Create(TArgs... args) const override { + return new TDerivedProduct(std::forward<TArgs>(args)...); + } + }; + + template <class TBaseProduct, class TDerivedProduct> + class TFactoryObjectCreator<TBaseProduct, TDerivedProduct, void>: public IFactoryObjectCreator<TBaseProduct, void> { + TDerivedProduct* Create() const override { + return new TDerivedProduct(); + } + }; + + template <class P, class K, class... TArgs> + class IObjectFactory { + public: + typedef P TProduct; + typedef K TKey; + + public: + template <class TDerivedProduct> + void Register(const TKey& key, IFactoryObjectCreator<TProduct, TArgs...>* creator) { + if (!creator) + ythrow yexception() << "Please specify non-null creator for " << key; + + TWriteGuard guard(CreatorsLock); + if (!Creators.insert(typename ICreators::value_type(key, creator)).second) + ythrow yexception() << "Product with key " << key << " already registered"; + } + + template <class TDerivedProduct> + void Register(const TKey& key) { + Register<TDerivedProduct>(key, new TFactoryObjectCreator<TProduct, TDerivedProduct, TArgs...>); + } + + void GetKeys(TSet<TKey>& keys) const { + TReadGuard guard(CreatorsLock); + keys.clear(); + for (typename ICreators::const_iterator i = Creators.begin(), e = Creators.end(); i != e; ++i) { + keys.insert(i->first); + } + } + + protected: + IFactoryObjectCreator<TProduct, TArgs...>* GetCreator(const TKey& key) const { + TReadGuard guard(CreatorsLock); + typename ICreators::const_iterator i = Creators.find(key); + return i == Creators.end() ? nullptr : i->second.Get(); + } + + bool HasImpl(const TKey& key) const { + TReadGuard guard(CreatorsLock); + return Creators.find(key) != Creators.end(); + } + + private: + typedef TSimpleSharedPtr<IFactoryObjectCreator<TProduct, TArgs...>> ICreatorPtr; + typedef TMap<TKey, ICreatorPtr> ICreators; + ICreators Creators; + TRWMutex CreatorsLock; + }; + + template <class TProduct, class TKey> + class TObjectFactory: public IObjectFactory<TProduct, TKey, void> { + public: + TProduct* Create(const TKey& key) const { + IFactoryObjectCreator<TProduct, void>* creator = IObjectFactory<TProduct, TKey, void>::GetCreator(key); + return creator == nullptr ? nullptr : creator->Create(); + } + + static TString KeysDebugString() { + TSet<TString> keys; + Singleton<TObjectFactory<TProduct, TKey>>()->GetKeys(keys); + TString keysStr; + for (auto&& k : keys) { + keysStr += k + " "; + } + return keysStr; + } + + static TProduct* Construct(const TKey& key, const TKey& defKey) { + TProduct* result = Singleton<TObjectFactory<TProduct, TKey>>()->Create(key); + if (!result && !!defKey) { + result = Singleton<TObjectFactory<TProduct, TKey>>()->Create(defKey); + } + return result; + } + + static TProduct* Construct(const TKey& key) { + TProduct* result = Singleton<TObjectFactory<TProduct, TKey>>()->Create(key); + return result; + } + + static THolder<TProduct> VerifiedConstruct(const TKey& key) { + auto result = MakeHolder(key); + Y_VERIFY(result, "Construct by factory failed"); + return result; + } + + template<class... Args> + static THolder<TProduct> MakeHolder(Args&&... args) { + return THolder<TProduct>(Construct(std::forward<Args>(args)...)); + } + + static bool Has(const TKey& key) { + return Singleton<TObjectFactory<TProduct, TKey>>()->HasImpl(key); + } + + static void GetRegisteredKeys(TSet<TKey>& keys) { + return Singleton<TObjectFactory<TProduct, TKey>>()->GetKeys(keys); + } + + static TSet<TKey> GetRegisteredKeys() { + TSet<TKey> keys; + Singleton<TObjectFactory<TProduct, TKey>>()->GetKeys(keys); + return keys; + } + + template <class TDerivedProduct> + static TSet<TKey> GetRegisteredKeys() { + TSet<TKey> registeredKeys(GetRegisteredKeys()); + TSet<TKey> fileredKeys; + std::copy_if(registeredKeys.begin(), registeredKeys.end(), std::inserter(fileredKeys, fileredKeys.end()), [](const TKey& key) { + THolder<TProduct> objectHolder(Construct(key)); + return !!dynamic_cast<const TDerivedProduct*>(objectHolder.Get()); + }); + return fileredKeys; + } + + template <class Product> + class TRegistrator { + public: + TRegistrator(const TKey& key, IFactoryObjectCreator<TProduct, void>* creator) { + Singleton<TObjectFactory<TProduct, TKey>>()->template Register<Product>(key, creator); + } + + TRegistrator(const TKey& key) { + Singleton<TObjectFactory<TProduct, TKey>>()->template Register<Product>(key); + } + + TRegistrator() + : TRegistrator(Product::GetTypeName()) + { + } + }; + }; + + template <class TProduct, class TKey, class... TArgs> + class TParametrizedObjectFactory: public IObjectFactory<TProduct, TKey, TArgs...> { + public: + TProduct* Create(const TKey& key, TArgs... args) const { + IFactoryObjectCreator<TProduct, TArgs...>* creator = IObjectFactory<TProduct, TKey, TArgs...>::GetCreator(key); + return creator == nullptr ? nullptr : creator->Create(std::forward<TArgs>(args)...); + } + + static bool Has(const TKey& key) { + return Singleton<TParametrizedObjectFactory<TProduct, TKey, TArgs...>>()->HasImpl(key); + } + + static TProduct* Construct(const TKey& key, TArgs... args) { + return Singleton<TParametrizedObjectFactory<TProduct, TKey, TArgs...>>()->Create(key, std::forward<TArgs>(args)...); + } + + template <class... Args> + static THolder<TProduct> VerifiedConstruct(Args&&... args) { + auto result = MakeHolder(std::forward<Args>(args)...); + Y_VERIFY(result, "Construct by factory failed"); + return result; + } + + template<class... Args> + static THolder<TProduct> MakeHolder(Args&&... args) { + return THolder<TProduct>(Construct(std::forward<Args>(args)...)); + } + + static void GetRegisteredKeys(TSet<TKey>& keys) { + return Singleton<TParametrizedObjectFactory<TProduct, TKey, TArgs...>>()->GetKeys(keys); + } + + static TSet<TKey> GetRegisteredKeys() { + TSet<TKey> keys; + Singleton<TParametrizedObjectFactory<TProduct, TKey, TArgs...>>()->GetKeys(keys); + return keys; + } + + template <class Product> + class TRegistrator { + public: + TRegistrator(const TKey& key, IFactoryObjectCreator<TProduct, TArgs...>* creator) { + Singleton<TParametrizedObjectFactory<TProduct, TKey, TArgs...>>()->template Register<Product>(key, creator); + } + + TRegistrator(const TKey& key) { + Singleton<TParametrizedObjectFactory<TProduct, TKey, TArgs...>>()->template Register<Product>(key); + } + + TRegistrator() + : TRegistrator(Product::GetTypeName()) + { + } + + TString GetName() const { + return Product::GetTypeName(); + } + }; + }; + +} diff --git a/library/cpp/object_factory/object_factory_ut.cpp b/library/cpp/object_factory/object_factory_ut.cpp new file mode 100644 index 0000000000..06fb0739ff --- /dev/null +++ b/library/cpp/object_factory/object_factory_ut.cpp @@ -0,0 +1,189 @@ +#include <library/cpp/object_factory/object_factory.h> +#include <library/cpp/testing/unittest/registar.h> + +#include <util/generic/noncopyable.h> +#include <util/generic/string.h> +#include <util/generic/ptr.h> + +using namespace NObjectFactory; + +struct TArgument { + TString Name; + void* Discarded; +}; + +class ICommonInterface { +public: + virtual ~ICommonInterface() { + } + + virtual TString GetValue() const = 0; +}; + +class TDirectOrder: public ICommonInterface { +public: + TDirectOrder(const TString& provider, float factor, TArgument& argument) + : Provider(provider) + , Factor(factor) + , Argument(argument) + { + } + + TString GetValue() const override { + return Provider + ToString(Factor) + Argument.Name; + } + +private: + const TString Provider; + const float Factor; + const TArgument Argument; +}; + +class TInverseOrder: public ICommonInterface { +public: + TInverseOrder(const TString& provider, float factor, TArgument& argument) + : Provider(provider) + , Factor(factor) + , Argument(argument) + { + } + + TString GetValue() const override { + return Argument.Name + ToString(Factor) + Provider; + } + +private: + const TString Provider; + const float Factor; + const TArgument Argument; +}; + +struct TDirectOrderCreator: public IFactoryObjectCreator<ICommonInterface, const TString&, float, TArgument&> { + ICommonInterface* Create(const TString& provider, float factor, TArgument& argument) const override { + ++CallsCounter; + return new TDirectOrder(provider, factor, argument); + } + + static int CallsCounter; +}; +int TDirectOrderCreator::CallsCounter = 0; + +using TTestFactory = TParametrizedObjectFactory<ICommonInterface, TString, const TString&, float, TArgument&>; + +static TTestFactory::TRegistrator<TDirectOrder> Direct("direct", new TDirectOrderCreator); +static TTestFactory::TRegistrator<TInverseOrder> Inverse("inverse"); + + + +class IMoveableOnlyInterface { +public: + virtual ~IMoveableOnlyInterface() { + } + + virtual TString GetValue() const = 0; +}; + +class TMoveableOnly: public IMoveableOnlyInterface, public TMoveOnly { +public: + TMoveableOnly(TString&& value) + : Value(value) + {} + + TString GetValue() const override { + return Value; + } + +private: + const TString Value; +}; + + +using TMoveableOnlyFactory = TParametrizedObjectFactory<IMoveableOnlyInterface, TString, TString&&>; + +static TMoveableOnlyFactory::TRegistrator<TMoveableOnly> MoveableOnlyReg("move"); + + + +class TMoveableOnly2: public IMoveableOnlyInterface, public TMoveOnly { +public: + TMoveableOnly2(THolder<TString>&& value) + : Value(std::move(value)) + {} + + TString GetValue() const override { + return *Value; + } + +private: + const THolder<TString> Value; +}; + + +using TMoveableOnly2Factory = TParametrizedObjectFactory<IMoveableOnlyInterface, TString, THolder<TString>&&>; + +static TMoveableOnly2Factory::TRegistrator<TMoveableOnly2> MoveableOnly2Reg("move2"); + +class TDirectOrderDifferentSignature : public TDirectOrder { +public: + TDirectOrderDifferentSignature(const TString& provider, TArgument& argument) : + TDirectOrder(provider, 0.01f, argument) + { + } + +}; + +struct TDirectOrderDSCreator: public IFactoryObjectCreator<ICommonInterface, const TString&, float, TArgument&> { + ICommonInterface* Create(const TString& provider, float factor, TArgument& argument) const override { + Y_UNUSED(factor); + return new TDirectOrderDifferentSignature(provider, argument); + } +}; + + +static TTestFactory::TRegistrator<TDirectOrderDifferentSignature> DirectDs("direct_ds", new TDirectOrderDSCreator); + +Y_UNIT_TEST_SUITE(TestObjectFactory) { + Y_UNIT_TEST(TestParametrized) { + TArgument directArg{"Name", nullptr}; + TArgument inverseArg{"Fake", nullptr}; + THolder<ICommonInterface> direct(TTestFactory::Construct("direct", "prov", 0.42, directArg)); + THolder<ICommonInterface> inverse(TTestFactory::Construct("inverse", "prov2", 1, inverseArg)); + + UNIT_ASSERT(!!direct); + UNIT_ASSERT(!!inverse); + + UNIT_ASSERT(direct->GetValue() == "prov0.42Name"); + UNIT_ASSERT(inverse->GetValue() == "Fake1prov2"); + + UNIT_ASSERT_EQUAL(TDirectOrderCreator::CallsCounter, 1); + } + + Y_UNIT_TEST(TestMoveableOnly) { + TString v = "value1"; + + THolder<IMoveableOnlyInterface> moveableOnly(TMoveableOnlyFactory::Construct("move", std::move(v))); + + UNIT_ASSERT(!!moveableOnly); + + UNIT_ASSERT(moveableOnly->GetValue() == "value1"); + } + + Y_UNIT_TEST(TestMoveableOnly2) { + THolder<TString> v = MakeHolder<TString>("value2"); + + THolder<IMoveableOnlyInterface> moveableOnly2(TMoveableOnly2Factory::Construct("move2", std::move(v))); + + UNIT_ASSERT(!!moveableOnly2); + + UNIT_ASSERT(moveableOnly2->GetValue() == "value2"); + } + + Y_UNIT_TEST(TestDifferentSignature) { + TArgument directArg{"Name", nullptr}; + THolder<ICommonInterface> directDs(TTestFactory::Construct("direct_ds", "prov", 0.42, directArg)); + + UNIT_ASSERT(!!directDs); + + UNIT_ASSERT_EQUAL(directDs->GetValue(), "prov0.01Name"); + } +} diff --git a/library/cpp/object_factory/ut/ya.make b/library/cpp/object_factory/ut/ya.make new file mode 100644 index 0000000000..5a870072fc --- /dev/null +++ b/library/cpp/object_factory/ut/ya.make @@ -0,0 +1,15 @@ +UNITTEST() + +OWNER(svshevtsov) + +PEERDIR( + ADDINCL library/cpp/object_factory +) + +SRCDIR(library/cpp/object_factory) + +SRCS( + object_factory_ut.cpp +) + +END() diff --git a/library/cpp/object_factory/ya.make b/library/cpp/object_factory/ya.make new file mode 100644 index 0000000000..bb93f75c23 --- /dev/null +++ b/library/cpp/object_factory/ya.make @@ -0,0 +1,9 @@ +LIBRARY() + +OWNER(ivanmorozov) + +SRCS( + object_factory.cpp +) + +END() |