aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/object_factory
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/object_factory
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/object_factory')
-rw-r--r--library/cpp/object_factory/object_factory.cpp1
-rw-r--r--library/cpp/object_factory/object_factory.h243
-rw-r--r--library/cpp/object_factory/object_factory_ut.cpp189
-rw-r--r--library/cpp/object_factory/ut/ya.make15
-rw-r--r--library/cpp/object_factory/ya.make9
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()