├── Emaject ├── tests │ ├── tests.cpp │ ├── unused.cpp │ ├── lambda_install.cpp │ ├── helloworld.cpp │ ├── from_resolve.cpp │ ├── with_arg.cpp │ ├── from_instance.cpp │ ├── transient.cpp │ ├── ctor_inject.cpp │ ├── field_inject.cpp │ ├── from_factory.cpp │ ├── inject_taits.cpp │ ├── method_inject.cpp │ ├── use_id.cpp │ ├── single.cpp │ └── cached.cpp ├── .runsettings ├── Emaject.vcxproj.filters ├── Emaject.vcxproj └── include │ └── Emaject.hpp ├── LICENSE ├── Emaject.sln ├── .gitignore └── README.md /Emaject/tests/tests.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" -------------------------------------------------------------------------------- /Emaject/.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tests 5 | .* 6 | 7 | -------------------------------------------------------------------------------- /Emaject/tests/unused.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "catch.hpp" 4 | 5 | namespace 6 | { 7 | using emaject::Container; 8 | using emaject::IInstaller; 9 | using emaject::Injector; 10 | 11 | class ICounter 12 | { 13 | public: 14 | virtual ~ICounter() = default; 15 | virtual int countUp() = 0; 16 | }; 17 | 18 | struct CounterInstaller : IInstaller 19 | { 20 | void onBinding(Container* c) const 21 | { 22 | // Unused 23 | c->bind() 24 | .unused() 25 | .asTransient(); 26 | } 27 | }; 28 | 29 | TEST_CASE("unuused") 30 | { 31 | Injector injector; 32 | injector.install(); 33 | 34 | { 35 | auto counter = injector.resolve(); 36 | REQUIRE(counter == nullptr); 37 | } 38 | { 39 | auto counter = injector.resolve(); 40 | REQUIRE(counter == nullptr); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Emaject/tests/lambda_install.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "catch.hpp" 6 | 7 | namespace 8 | { 9 | using emaject::Container; 10 | using emaject::IInstaller; 11 | using emaject::Injector; 12 | 13 | class IPrinter 14 | { 15 | public: 16 | virtual ~IPrinter() = default; 17 | virtual void println(std::string_view str) const = 0; 18 | }; 19 | 20 | class CoutPrinter : public IPrinter 21 | { 22 | public: 23 | void println(std::string_view str) const override 24 | { 25 | std::cout << "[lambda_install]" << str << std::endl; 26 | } 27 | }; 28 | 29 | TEST_CASE("lambda_install") 30 | { 31 | Injector injector; 32 | injector.install([](Container* c) { 33 | c->bind() 34 | .to() 35 | .asCached(); 36 | }); 37 | 38 | auto printer = injector.resolve(); 39 | printer->println("Hello World"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tyanmahou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Emaject/tests/helloworld.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "catch.hpp" 6 | 7 | namespace 8 | { 9 | using emaject::Container; 10 | using emaject::IInstaller; 11 | using emaject::Injector; 12 | 13 | class IPrinter 14 | { 15 | public: 16 | virtual ~IPrinter() = default; 17 | virtual void println(std::string_view str) const = 0; 18 | }; 19 | 20 | class CoutPrinter : public IPrinter 21 | { 22 | public: 23 | void println(std::string_view str) const override 24 | { 25 | std::cout << "[hello world]" << str << std::endl; 26 | } 27 | }; 28 | 29 | struct CoutInstaller : IInstaller 30 | { 31 | void onBinding(Container* c) const 32 | { 33 | c->bind() 34 | .to() 35 | .asCached(); 36 | } 37 | }; 38 | 39 | TEST_CASE("hello world") 40 | { 41 | Injector injector; 42 | injector.install(); 43 | 44 | auto printer = injector.resolve(); 45 | printer->println("Hello World"); 46 | } 47 | } -------------------------------------------------------------------------------- /Emaject/tests/from_resolve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "catch.hpp" 5 | 6 | namespace 7 | { 8 | using emaject::Container; 9 | using emaject::IInstaller; 10 | using emaject::Injector; 11 | 12 | 13 | class IFoo 14 | { 15 | public: 16 | virtual ~IFoo() = default; 17 | }; 18 | class IFooFoo : public IFoo 19 | { 20 | }; 21 | class FooFoo : public IFooFoo 22 | { 23 | }; 24 | struct FooInstaller : IInstaller 25 | { 26 | void onBinding(Container* c) const 27 | { 28 | c->bind() 29 | .to() 30 | .asCached(); 31 | 32 | c->bind() 33 | .to() 34 | .fromResolve() 35 | .asCached(); 36 | } 37 | }; 38 | TEST_CASE("from_resolve") 39 | { 40 | Injector injector; 41 | injector.install(); 42 | 43 | { 44 | auto foo = injector.resolve(); 45 | auto foofoo = injector.resolve(); 46 | 47 | REQUIRE(foo != nullptr); 48 | REQUIRE(foo == foofoo); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Emaject/tests/with_arg.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "catch.hpp" 4 | 5 | namespace 6 | { 7 | using emaject::Container; 8 | using emaject::IInstaller; 9 | using emaject::Injector; 10 | 11 | class ICounter 12 | { 13 | public: 14 | virtual ~ICounter() = default; 15 | virtual int countUp() = 0; 16 | }; 17 | 18 | class Counter : public ICounter 19 | { 20 | int m_count; 21 | public: 22 | Counter(int count = 0): 23 | m_count(count) 24 | {} 25 | 26 | int countUp() override 27 | { 28 | return ++m_count; 29 | } 30 | }; 31 | 32 | struct CounterInstaller : IInstaller 33 | { 34 | void onBinding(Container* c) const 35 | { 36 | c->bind() 37 | .to() 38 | .withArgs(100) 39 | .asTransient(); 40 | } 41 | }; 42 | 43 | TEST_CASE("with_arg") 44 | { 45 | Injector injector; 46 | injector.install(); 47 | 48 | { 49 | auto counter = injector.resolve(); 50 | REQUIRE(counter->countUp() == 101); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Emaject/tests/from_instance.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "catch.hpp" 3 | 4 | namespace 5 | { 6 | using emaject::Container; 7 | using emaject::IInstaller; 8 | using emaject::Injector; 9 | 10 | 11 | class Singleton 12 | { 13 | public: 14 | static std::shared_ptr Instance() 15 | { 16 | static auto instance = std::shared_ptr(new Singleton()); 17 | return instance; 18 | } 19 | private: 20 | Singleton() = default; 21 | Singleton(const Singleton& other) = delete; 22 | Singleton& operator=(const Singleton& other) = delete; 23 | }; 24 | struct SingletonInstaller : IInstaller 25 | { 26 | void onBinding(Container* c) const 27 | { 28 | c->bind() 29 | .toSelf() 30 | .fromInstance(Singleton::Instance()) 31 | .asSingle(); 32 | } 33 | }; 34 | TEST_CASE("from_instance") 35 | { 36 | Injector injector; 37 | injector.install(); 38 | { 39 | auto singleton = injector.resolve(); 40 | REQUIRE(Singleton::Instance().get() == singleton.get()); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Emaject/tests/transient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "catch.hpp" 5 | 6 | namespace 7 | { 8 | using emaject::Container; 9 | using emaject::IInstaller; 10 | using emaject::Injector; 11 | 12 | class ICounter 13 | { 14 | public: 15 | virtual ~ICounter() = default; 16 | virtual int countUp() = 0; 17 | }; 18 | 19 | class Counter : public ICounter 20 | { 21 | int m_count = 0; 22 | public: 23 | int countUp() override 24 | { 25 | return ++m_count; 26 | } 27 | }; 28 | 29 | struct CounterInstaller : IInstaller 30 | { 31 | void onBinding(Container* c) const 32 | { 33 | c->bind() 34 | .to() 35 | .asTransient(); 36 | } 37 | }; 38 | 39 | TEST_CASE("transient") 40 | { 41 | Injector injector; 42 | injector.install(); 43 | 44 | { 45 | auto counter = injector.resolve(); // new instance 46 | REQUIRE(counter->countUp() == 1); 47 | } 48 | { 49 | auto counter = injector.resolve(); // new instance 50 | REQUIRE(counter->countUp() == 1); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Emaject/tests/ctor_inject.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "catch.hpp" 6 | 7 | namespace 8 | { 9 | using emaject::Container; 10 | using emaject::IInstaller; 11 | using emaject::Injector; 12 | 13 | class IPrinter 14 | { 15 | public: 16 | virtual ~IPrinter() = default; 17 | virtual void println(std::string_view str) const = 0; 18 | }; 19 | 20 | class CoutPrinter : public IPrinter 21 | { 22 | public: 23 | void println(std::string_view str) const override 24 | { 25 | } 26 | }; 27 | 28 | struct CoutInstaller : IInstaller 29 | { 30 | void onBinding(Container* c) const 31 | { 32 | c->bind() 33 | .to() 34 | .asCached(); 35 | } 36 | }; 37 | 38 | class HelloWorld 39 | { 40 | public: 41 | INJECT_CTOR(HelloWorld(std::shared_ptr printer)) : 42 | m_printer(printer) 43 | {} 44 | 45 | void greet() const 46 | { 47 | m_printer->println("[ctor_inject] Hello World"); 48 | } 49 | private: 50 | std::shared_ptr m_printer; 51 | }; 52 | 53 | TEST_CASE("ctor_inject") 54 | { 55 | Injector injector; 56 | injector.install(); 57 | 58 | auto helloWorld = injector.instantiate(); 59 | helloWorld->greet(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Emaject.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Emaject", "Emaject\Emaject.vcxproj", "{7026BEA5-2773-4939-988B-B380A50E16ED}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Debug|x64.ActiveCfg = Debug|x64 17 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Debug|x64.Build.0 = Debug|x64 18 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Debug|x86.ActiveCfg = Debug|Win32 19 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Debug|x86.Build.0 = Debug|Win32 20 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Release|x64.ActiveCfg = Release|x64 21 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Release|x64.Build.0 = Release|x64 22 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Release|x86.ActiveCfg = Release|Win32 23 | {7026BEA5-2773-4939-988B-B380A50E16ED}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {81DCED82-43B2-4970-B8B6-0D3298C48A5F} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Emaject/tests/field_inject.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "catch.hpp" 6 | namespace 7 | { 8 | using emaject::Container; 9 | using emaject::IInstaller; 10 | using emaject::Injector; 11 | 12 | class IPrinter 13 | { 14 | public: 15 | virtual ~IPrinter() = default; 16 | virtual void println(std::string_view str) const = 0; 17 | }; 18 | 19 | class CoutPrinter : public IPrinter 20 | { 21 | public: 22 | void println(std::string_view str) const override 23 | { 24 | std::cout << str << std::endl; 25 | } 26 | }; 27 | 28 | struct CoutInstaller : IInstaller 29 | { 30 | void onBinding(Container* c) const 31 | { 32 | c->bind() 33 | .to() 34 | .asCached(); 35 | } 36 | }; 37 | 38 | class HelloWorld 39 | { 40 | public: 41 | HelloWorld() = default; 42 | 43 | void greet() const 44 | { 45 | m_printer->println("[field_inject] Hello World"); 46 | } 47 | private: 48 | [[INJECT(m_printer)]] 49 | std::shared_ptr m_printer; 50 | }; 51 | 52 | TEST_CASE("field_inject") 53 | { 54 | static_assert(emaject::detail::IsAutoInjectable); 55 | Injector injector; 56 | injector.install(); 57 | 58 | auto helloWorld = injector.instantiate(); 59 | helloWorld->greet(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Emaject/tests/from_factory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "catch.hpp" 5 | 6 | namespace 7 | { 8 | using emaject::Container; 9 | using emaject::IInstaller; 10 | using emaject::Injector; 11 | 12 | class IFoo 13 | { 14 | public: 15 | virtual ~IFoo() = default; 16 | virtual int value() const = 0; 17 | }; 18 | class Foo : public IFoo 19 | { 20 | public: 21 | Foo(int value) : 22 | m_value(value) 23 | { 24 | } 25 | int value() const override 26 | { 27 | return m_value; 28 | } 29 | private: 30 | int m_value; 31 | }; 32 | 33 | struct FooInstaller : IInstaller 34 | { 35 | void onBinding(Container* c) const 36 | { 37 | // No arg Factory 38 | c->bind() 39 | .toSelf() 40 | .fromFactory([] { 41 | return std::make_shared(1); 42 | }) 43 | .asCached(); 44 | 45 | // Container arg Factory 46 | c->bind() 47 | .toSelf() 48 | .fromFactory([](Container* c) { 49 | return std::make_shared(c->resolve()->value() + 1); 50 | }) 51 | .asCached(); 52 | } 53 | }; 54 | TEST_CASE("from_factory") 55 | { 56 | Injector injector; 57 | injector.install(); 58 | 59 | { 60 | auto foo0 = injector.resolve(); 61 | auto foo1 = injector.resolve(); 62 | REQUIRE(foo0->value() == 1); 63 | REQUIRE(foo1->value() == 2); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Emaject/tests/inject_taits.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "catch.hpp" 6 | 7 | namespace 8 | { 9 | using emaject::Container; 10 | using emaject::IInstaller; 11 | using emaject::Injector; 12 | 13 | class IPrinter 14 | { 15 | public: 16 | virtual ~IPrinter() = default; 17 | virtual void println(std::string_view str) const = 0; 18 | }; 19 | 20 | class CoutPrinter : public IPrinter 21 | { 22 | public: 23 | void println(std::string_view str) const override 24 | { 25 | std::cout << str << std::endl; 26 | } 27 | }; 28 | 29 | struct CoutInstaller : IInstaller 30 | { 31 | void onBinding(Container* c) const 32 | { 33 | c->bind() 34 | .to() 35 | .asCached(); 36 | } 37 | }; 38 | 39 | class HelloWorld 40 | { 41 | public: 42 | HelloWorld() 43 | {} 44 | 45 | void greet() const 46 | { 47 | m_printer->println("[inject trairs] Hello World"); 48 | } 49 | 50 | void setPrinter(std::shared_ptr printer) 51 | { 52 | m_printer = printer; 53 | } 54 | private: 55 | std::shared_ptr m_printer; 56 | }; 57 | } 58 | 59 | namespace emaject 60 | { 61 | template<> 62 | struct InjectTraits 63 | { 64 | void onInject(HelloWorld* value, Container* c) 65 | { 66 | value->setPrinter(c->resolve()); 67 | } 68 | }; 69 | } 70 | 71 | namespace 72 | { 73 | TEST_CASE("inject trairs") 74 | { 75 | Injector injector; 76 | injector.install(); 77 | 78 | auto helloWorld = injector.instantiate(); 79 | helloWorld->greet(); 80 | } 81 | } -------------------------------------------------------------------------------- /Emaject/tests/method_inject.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "catch.hpp" 6 | 7 | namespace 8 | { 9 | using emaject::Container; 10 | using emaject::IInstaller; 11 | using emaject::Injector; 12 | 13 | class Hoge 14 | { 15 | public: 16 | Hoge(int v): 17 | value(v) 18 | {} 19 | int value; 20 | }; 21 | 22 | class Foo 23 | { 24 | public: 25 | Foo(int v) : 26 | value(v) 27 | {} 28 | int value; 29 | }; 30 | 31 | class HogeFoo 32 | { 33 | public: 34 | int value() const 35 | { 36 | return m_hoge->value + m_foo->value; 37 | } 38 | int value2() const 39 | { 40 | return m_hoge2->value + m_foo2->value; 41 | } 42 | private: 43 | [[INJECT(setHogeAndFoo)]] 44 | void setHogeAndFoo( 45 | const std::shared_ptr& hoge, 46 | const std::shared_ptr& foo 47 | ) { 48 | m_hoge = hoge; 49 | m_foo = foo; 50 | } 51 | [[INJECT(setHogeAndFoo2, 1, 2)]] 52 | void setHogeAndFoo2( 53 | const std::shared_ptr& hoge, 54 | const std::shared_ptr& foo 55 | ) { 56 | m_hoge2 = hoge; 57 | m_foo2 = foo; 58 | } 59 | private: 60 | std::shared_ptr m_hoge; 61 | std::shared_ptr m_foo; 62 | 63 | std::shared_ptr m_hoge2; 64 | std::shared_ptr m_foo2; 65 | }; 66 | struct HogeFooInstaller : IInstaller 67 | { 68 | void onBinding(Container* c) const 69 | { 70 | c->bind().withArgs(20).asTransient(); 71 | c->bind().withArgs(100).asTransient(); 72 | 73 | c->bind().withArgs(10).asTransient(); 74 | c->bind().withArgs(200).asTransient(); 75 | } 76 | }; 77 | 78 | TEST_CASE("method_inject") 79 | { 80 | Injector injector; 81 | injector.install(); 82 | 83 | auto hogeFoo = injector.instantiate(); 84 | REQUIRE(hogeFoo->value() == 120); 85 | REQUIRE(hogeFoo->value2() == 210); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Emaject/tests/use_id.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include "catch.hpp" 7 | 8 | namespace 9 | { 10 | using emaject::Container; 11 | using emaject::IInstaller; 12 | using emaject::Injector; 13 | 14 | class IPrinter 15 | { 16 | public: 17 | virtual ~IPrinter() = default; 18 | virtual void println(std::string_view str) const = 0; 19 | }; 20 | 21 | class CoutPrinter : public IPrinter 22 | { 23 | public: 24 | void println(std::string_view str) const override 25 | { 26 | std::cout << str << " for cout" << std::endl; 27 | } 28 | }; 29 | 30 | class PrintfPrinter : public IPrinter 31 | { 32 | public: 33 | void println(std::string_view str) const override 34 | { 35 | std::printf("%s for printf\n", str.data()); 36 | } 37 | }; 38 | 39 | struct CoutInstaller : IInstaller 40 | { 41 | void onBinding(Container* c) const 42 | { 43 | // ID = 0 -> CoutPrinter 44 | c->bind() 45 | .to() 46 | .asCached(); 47 | } 48 | }; 49 | struct PrintfInstaller : IInstaller 50 | { 51 | void onBinding(Container* c) const 52 | { 53 | // ID = 1 -> PrintfPrinter 54 | c->bind() 55 | .to() 56 | .asCached(); 57 | } 58 | }; 59 | class HelloWorld 60 | { 61 | public: 62 | HelloWorld() = default; 63 | 64 | void greet() const 65 | { 66 | m_printer0->println("Hello World"); 67 | m_printer1->println("Hello World"); 68 | } 69 | private: 70 | [[INJECT(m_printer0)]] // ID = 0 71 | std::shared_ptr m_printer0; 72 | 73 | [[INJECT(m_printer1, 1)]] 74 | std::shared_ptr m_printer1; 75 | }; 76 | 77 | TEST_CASE("use_id") 78 | { 79 | Injector injector; 80 | injector 81 | .install() 82 | .install(); 83 | 84 | auto helloWorld = injector.instantiate(); 85 | helloWorld->greet(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Emaject/tests/single.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "catch.hpp" 4 | 5 | namespace 6 | { 7 | using emaject::Container; 8 | using emaject::IInstaller; 9 | using emaject::Injector; 10 | 11 | class ICounter 12 | { 13 | public: 14 | virtual ~ICounter() = default; 15 | virtual int countUp() = 0; 16 | }; 17 | class IPrinter 18 | { 19 | public: 20 | virtual ~IPrinter() = default; 21 | virtual void println(std::string_view str) const = 0; 22 | }; 23 | class PrintCounter : public ICounter, public IPrinter 24 | { 25 | int m_count = 0; 26 | public: 27 | int countUp() override 28 | { 29 | return ++m_count; 30 | } 31 | void println(std::string_view str) const override 32 | { 33 | std::cout << "[single]" << str << " " << m_count << std::endl; 34 | } 35 | }; 36 | 37 | struct CounterInstaller : IInstaller 38 | { 39 | void onBinding(Container* c) const 40 | { 41 | c->bind() 42 | .to() 43 | .asSingle(); 44 | 45 | // failed 46 | c->bind() 47 | .to() 48 | .asCached(); 49 | 50 | // failed 51 | c->bind() 52 | .to() 53 | .asTransient(); 54 | } 55 | }; 56 | 57 | TEST_CASE("single") 58 | { 59 | Injector injector; 60 | injector.install(); 61 | 62 | { 63 | auto counter = injector.resolve(); // new instance(single) 64 | REQUIRE(counter != nullptr); 65 | REQUIRE(counter->countUp() == 1); 66 | } 67 | { 68 | auto counter1 = injector.resolve(); // cached 69 | auto counter2 = injector.resolve(); // cached 70 | REQUIRE(counter1 == counter2); 71 | } 72 | { 73 | auto counter = injector.resolve(); // failed 74 | REQUIRE(counter == nullptr); 75 | } 76 | { 77 | auto printer = injector.resolve(); // failed 78 | REQUIRE(printer == nullptr); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Emaject/tests/cached.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "catch.hpp" 5 | 6 | namespace 7 | { 8 | using emaject::Container; 9 | using emaject::IInstaller; 10 | using emaject::Injector; 11 | 12 | class ICounter 13 | { 14 | public: 15 | virtual ~ICounter() = default; 16 | virtual int countUp() = 0; 17 | }; 18 | 19 | class Counter : public ICounter 20 | { 21 | int m_count = 0; 22 | public: 23 | int countUp() override 24 | { 25 | return ++m_count; 26 | } 27 | }; 28 | 29 | struct Test 30 | { 31 | [[INJECT(c1)]] 32 | std::shared_ptr c1; 33 | [[INJECT(c1_2)]] 34 | std::shared_ptr c1_2; 35 | [[INJECT(c2, 2)]] 36 | std::shared_ptr c2; 37 | [[INJECT(c2_2, 2)]] 38 | std::shared_ptr c2_2; 39 | 40 | [[INJECT(c3)]] 41 | std::shared_ptr c3; 42 | }; 43 | struct CounterInstaller : IInstaller 44 | { 45 | void onBinding(Container* c) const 46 | { 47 | c->bind() 48 | .to() 49 | .asCached(); 50 | 51 | c->bind() 52 | .to() 53 | .asCached(); 54 | 55 | c->bind() 56 | .asCached(); 57 | } 58 | }; 59 | TEST_CASE("cached") 60 | { 61 | Injector injector; 62 | injector.install(); 63 | 64 | { 65 | auto counter = injector.resolve(); // new instance 66 | REQUIRE(counter->countUp() == 1); 67 | } 68 | { 69 | auto counter = injector.resolve(); // used cache 70 | REQUIRE(counter->countUp() == 2); 71 | } 72 | { 73 | auto test = injector.instantiate(); 74 | REQUIRE(test->c1 != nullptr); 75 | REQUIRE(test->c2 != nullptr); 76 | REQUIRE(test->c3 != nullptr); 77 | 78 | REQUIRE(test->c1 == test->c1_2); 79 | REQUIRE(test->c1 != test->c2); 80 | REQUIRE(test->c1 != test->c2_2); 81 | REQUIRE(test->c1 != test->c3); 82 | 83 | REQUIRE(test->c2 == test->c2_2); 84 | REQUIRE(test->c3 == test->c3); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Emaject/Emaject.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | ヘッダー ファイル 20 | 21 | 22 | ヘッダー ファイル 23 | 24 | 25 | 26 | 27 | ソース ファイル 28 | 29 | 30 | ソース ファイル 31 | 32 | 33 | ソース ファイル 34 | 35 | 36 | ソース ファイル 37 | 38 | 39 | ソース ファイル 40 | 41 | 42 | ソース ファイル 43 | 44 | 45 | ソース ファイル 46 | 47 | 48 | ソース ファイル 49 | 50 | 51 | ソース ファイル 52 | 53 | 54 | ソース ファイル 55 | 56 | 57 | ソース ファイル 58 | 59 | 60 | ソース ファイル 61 | 62 | 63 | ソース ファイル 64 | 65 | 66 | ソース ファイル 67 | 68 | 69 | ソース ファイル 70 | 71 | 72 | ソース ファイル 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Skip to content 2 | Search or jump to… 3 | 4 | Pull requests 5 | Issues 6 | Marketplace 7 | Explore 8 | 9 | @tyanmahou 10 | tyanmahou 11 | / 12 | Magico 13 | 1 14 | 6 15 | 0 16 | Code 17 | Issues 18 | Pull requests 19 | Actions 20 | Projects 21 | Wiki 22 | Security 23 | Insights 24 | Settings 25 | Magico/.gitignore 26 | 27 | mahou gitignore add 28 | Latest commit 1cc0884 on 5 Sep 2017 29 | History 30 | 0 contributors 31 | 299 lines (242 sloc) 5.16 KB 32 | 33 | ## Ignore Visual Studio temporary files, build results, and 34 | ## files generated by popular Visual Studio add-ons. 35 | ## 36 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 37 | 38 | # User-specific files 39 | *.suo 40 | *.user 41 | *.userosscache 42 | *.sln.docstates 43 | 44 | # User-specific files (MonoDevelop/Xamarin Studio) 45 | *.userprefs 46 | 47 | # Build results 48 | [Dd]ebug/ 49 | [Dd]ebugPublic/ 50 | [Rr]elease/ 51 | [Rr]eleases/ 52 | x64/ 53 | x86/ 54 | bld/ 55 | [Bb]in/ 56 | [Oo]bj/ 57 | [Ll]og/ 58 | 59 | # Visual Studio 2015 cache/options directory 60 | .vs/ 61 | # Uncomment if you have tasks that create the project's static files in wwwroot 62 | #wwwroot/ 63 | 64 | # MSTest test Results 65 | [Tt]est[Rr]esult*/ 66 | [Bb]uild[Ll]og.* 67 | 68 | # NUNIT 69 | *.VisualState.xml 70 | TestResult.xml 71 | 72 | # Build Results of an ATL Project 73 | [Dd]ebugPS/ 74 | [Rr]eleasePS/ 75 | dlldata.c 76 | 77 | # Benchmark Results 78 | BenchmarkDotNet.Artifacts/ 79 | 80 | # .NET Core 81 | project.lock.json 82 | project.fragment.lock.json 83 | artifacts/ 84 | **/Properties/launchSettings.json 85 | 86 | *_i.c 87 | *_p.c 88 | *_i.h 89 | *.ilk 90 | *.meta 91 | *.obj 92 | *.pch 93 | *.pdb 94 | *.pgc 95 | *.pgd 96 | *.rsp 97 | *.sbr 98 | *.tlb 99 | *.tli 100 | *.tlh 101 | *.tmp 102 | *.tmp_proj 103 | *.log 104 | *.vspscc 105 | *.vssscc 106 | .builds 107 | *.pidb 108 | *.svclog 109 | *.scc 110 | 111 | # Chutzpah Test files 112 | _Chutzpah* 113 | 114 | # Visual C++ cache files 115 | ipch/ 116 | *.aps 117 | *.ncb 118 | *.opendb 119 | *.opensdf 120 | *.sdf 121 | *.cachefile 122 | *.VC.db 123 | *.VC.VC.opendb 124 | 125 | # Visual Studio profiler 126 | *.psess 127 | *.vsp 128 | *.vspx 129 | *.sap 130 | 131 | # TFS 2012 Local Workspace 132 | $tf/ 133 | 134 | # Guidance Automation Toolkit 135 | *.gpState 136 | 137 | # ReSharper is a .NET coding add-in 138 | _ReSharper*/ 139 | *.[Rr]e[Ss]harper 140 | *.DotSettings.user 141 | 142 | # JustCode is a .NET coding add-in 143 | .JustCode 144 | 145 | # TeamCity is a build add-in 146 | _TeamCity* 147 | 148 | # DotCover is a Code Coverage Tool 149 | *.dotCover 150 | 151 | # AxoCover is a Code Coverage Tool 152 | .axoCover/* 153 | !.axoCover/settings.json 154 | 155 | # Visual Studio code coverage results 156 | *.coverage 157 | *.coveragexml 158 | 159 | # NCrunch 160 | _NCrunch_* 161 | .*crunch*.local.xml 162 | nCrunchTemp_* 163 | 164 | # MightyMoose 165 | *.mm.* 166 | AutoTest.Net/ 167 | 168 | # Web workbench (sass) 169 | .sass-cache/ 170 | 171 | # Installshield output folder 172 | [Ee]xpress/ 173 | 174 | # DocProject is a documentation generator add-in 175 | DocProject/buildhelp/ 176 | DocProject/Help/*.HxT 177 | DocProject/Help/*.HxC 178 | DocProject/Help/*.hhc 179 | DocProject/Help/*.hhk 180 | DocProject/Help/*.hhp 181 | DocProject/Help/Html2 182 | DocProject/Help/html 183 | 184 | # Click-Once directory 185 | publish/ 186 | 187 | # Publish Web Output 188 | *.[Pp]ublish.xml 189 | *.azurePubxml 190 | # Note: Comment the next line if you want to checkin your web deploy settings, 191 | # but database connection strings (with potential passwords) will be unencrypted 192 | *.pubxml 193 | *.publishproj 194 | 195 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 196 | # checkin your Azure Web App publish settings, but sensitive information contained 197 | # in these scripts will be unencrypted 198 | PublishScripts/ 199 | 200 | # NuGet Packages 201 | *.nupkg 202 | # The packages folder can be ignored because of Package Restore 203 | **/packages/* 204 | # except build/, which is used as an MSBuild target. 205 | !**/packages/build/ 206 | # Uncomment if necessary however generally it will be regenerated when needed 207 | #!**/packages/repositories.config 208 | # NuGet v3's project.json files produces more ignorable files 209 | *.nuget.props 210 | *.nuget.targets 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | 227 | # Visual Studio cache files 228 | # files ending in .cache can be ignored 229 | *.[Cc]ache 230 | # but keep track of directories ending in .cache 231 | !*.[Cc]ache/ 232 | 233 | # Others 234 | ClientBin/ 235 | ~$* 236 | *~ 237 | *.dbmdl 238 | *.dbproj.schemaview 239 | *.jfm 240 | *.pfx 241 | *.publishsettings 242 | orleans.codegen.cs 243 | 244 | # Since there are multiple workflows, uncomment next line to ignore bower_components 245 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 246 | #bower_components/ 247 | 248 | # RIA/Silverlight projects 249 | Generated_Code/ 250 | 251 | # Backup & report files from converting an old project file 252 | # to a newer Visual Studio version. Backup files are not needed, 253 | # because we have git ;-) 254 | _UpgradeReport_Files/ 255 | Backup*/ 256 | UpgradeLog*.XML 257 | UpgradeLog*.htm 258 | 259 | # SQL Server files 260 | *.mdf 261 | *.ldf 262 | *.ndf 263 | 264 | # Business Intelligence projects 265 | *.rdl.data 266 | *.bim.layout 267 | *.bim_*.settings 268 | 269 | # Microsoft Fakes 270 | FakesAssemblies/ 271 | 272 | # GhostDoc plugin setting file 273 | *.GhostDoc.xml 274 | 275 | # Node.js Tools for Visual Studio 276 | .ntvs_analysis.dat 277 | node_modules/ 278 | 279 | # Typescript v1 declaration files 280 | typings/ 281 | 282 | # Visual Studio 6 build log 283 | *.plg 284 | 285 | # Visual Studio 6 workspace options file 286 | *.opt 287 | 288 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 289 | *.vbw 290 | 291 | # Visual Studio LightSwitch build output 292 | **/*.HTMLClient/GeneratedArtifacts 293 | **/*.DesktopClient/GeneratedArtifacts 294 | **/*.DesktopClient/ModelManifest.xml 295 | **/*.Server/GeneratedArtifacts 296 | **/*.Server/ModelManifest.xml 297 | _Pvt_Extensions 298 | 299 | # Paket dependency manager 300 | .paket/paket.exe 301 | paket-files/ 302 | 303 | # FAKE - F# Make 304 | .fake/ 305 | 306 | # JetBrains Rider 307 | .idea/ 308 | *.sln.iml 309 | 310 | # CodeRush 311 | .cr/ 312 | 313 | # Python Tools for Visual Studio (PTVS) 314 | __pycache__/ 315 | *.pyc 316 | 317 | # Cake - Uncomment if you are using it 318 | # tools/** 319 | # !tools/packages.config 320 | 321 | # Tabs Studio 322 | *.tss 323 | 324 | # Telerik's JustMock configuration file 325 | *.jmconfig 326 | 327 | # BizTalk build output 328 | *.btp.cs 329 | *.btm.cs 330 | *.odx.cs 331 | *.xsd.cs 332 | © 2020 GitHub, Inc. 333 | Terms 334 | Privacy 335 | Security 336 | Status 337 | Help 338 | Contact GitHub 339 | Pricing 340 | API 341 | Training 342 | Blog 343 | About 344 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emaject 2 | C++20 DI Library 3 | 4 | ## About 5 | 6 | "Emaject" is a `Dependency Injection` library for C++20 only with a header; 7 | 8 | ## How to 9 | 10 | Header Include Only `Emaject.hpp` 11 | 12 | ## Example 13 | 14 | ```cpp 15 | class IPrinter 16 | { 17 | public: 18 | virtual ~IPrinter() = default; 19 | virtual void println(std::string_view str) const = 0; 20 | }; 21 | 22 | class CoutPrinter : public IPrinter 23 | { 24 | public: 25 | void println(std::string_view str) const override 26 | { 27 | std::cout << str << std::endl; 28 | } 29 | }; 30 | 31 | struct CoutInstaller : IInstaller 32 | { 33 | void onBinding(Container* c) const 34 | { 35 | c->bind() 36 | .to() 37 | .asCached(); 38 | } 39 | }; 40 | 41 | int main() 42 | { 43 | Injector injector; 44 | injector.install(); 45 | 46 | auto printer = injector.resolve(); 47 | printer->println("Hello World"); 48 | } 49 | ``` 50 | ### Field Inject 51 | 52 | This is executed after the constructor. 53 | 54 | ```cpp 55 | class HelloWorld 56 | { 57 | public: 58 | HelloWorld() = default; 59 | 60 | void greet() const 61 | { 62 | m_printer->println("Hello World"); 63 | } 64 | private: 65 | [[INJECT(m_printer)]] 66 | std::shared_ptr m_printer; 67 | }; 68 | 69 | int main() 70 | { 71 | Injector injector; 72 | injector.install(); 73 | 74 | auto helloWorld = injector.instantiate(); 75 | helloWorld->greet(); 76 | } 77 | ``` 78 | ### Method Inject 79 | 80 | This is executed at the same timing as Field Inject. 81 | 82 | ```cpp 83 | class HelloWorld 84 | { 85 | public: 86 | HelloWorld() = default; 87 | 88 | void greet() const 89 | { 90 | m_printer->println("Hello World"); 91 | } 92 | private: 93 | [[INJECT(setPrinter)]] 94 | void setPrinter(std::shared_ptr printer) 95 | { 96 | m_printer = printer; 97 | } 98 | std::shared_ptr m_printer; 99 | }; 100 | ``` 101 | 102 | ### Constructor Inject 103 | 104 | ```cpp 105 | class HelloWorld 106 | { 107 | public: 108 | INJECT_CTOR(HelloWorld(std::shared_ptr printer)): 109 | m_printer(printer) 110 | {} 111 | 112 | void greet() const 113 | { 114 | m_printer->println("Hello World"); 115 | } 116 | private: 117 | std::shared_ptr m_printer; 118 | }; 119 | ``` 120 | ### Traits Inject 121 | 122 | You can also inject using `InjectTraits`. 123 | This is executed after the constructor and Field Inject. 124 | 125 | 126 | ```cpp 127 | namespace emaject 128 | { 129 | template<> 130 | struct InjectTraits 131 | { 132 | void onInject(HelloWorld* value, Container* c) 133 | { 134 | value->setPrinter(c->resolve()); 135 | } 136 | }; 137 | } 138 | ``` 139 | 140 | ### ID 141 | 142 | You can use an ID for binding. If not specified, it will be `0`. 143 | 144 | ```cpp 145 | class PrintfPrinter : public IPrinter 146 | { 147 | public: 148 | void println(std::string_view str) const override 149 | { 150 | std::printf("%s for printf", str.data()); 151 | } 152 | }; 153 | 154 | struct PrintfInstaller : IInstaller 155 | { 156 | void onBinding(Container* c) const 157 | { 158 | // ID = 1 -> PrintfPrinter 159 | c->bind() 160 | .to() 161 | .asCached(); 162 | } 163 | }; 164 | ``` 165 | You can specify the ID with field inject. 166 | 167 | ```cpp 168 | class HelloWorld 169 | { 170 | public: 171 | HelloWorld() = default; 172 | 173 | void greet() const 174 | { 175 | m_printer0->println("Hello World"); 176 | m_printer1->println("Hello World"); 177 | } 178 | private: 179 | [[INJECT(m_printer0)]] // default ID = 0 180 | std::shared_ptr m_printer0; 181 | 182 | [[INJECT(m_printer1, 1)]] // ID = 1 183 | std::shared_ptr m_printer1; 184 | }; 185 | ``` 186 | 187 | and method inject. 188 | 189 | ```cpp 190 | class HelloWorld 191 | { 192 | public: 193 | HelloWorld() = default; 194 | 195 | void greet() const 196 | { 197 | m_printer0->println("Hello World"); 198 | m_printer1->println("Hello World"); 199 | } 200 | private: 201 | [[INJECT(setPrinter, 0, 1)]] 202 | void setPrinter(std::shared_ptr printer0, std::shared_ptr printer1) 203 | { 204 | m_printer0 = printer0; 205 | m_printer1 = printer1; 206 | } 207 | std::shared_ptr m_printer0; 208 | std::shared_ptr m_printer1; 209 | }; 210 | ``` 211 | or resolve 212 | 213 | ```cpp 214 | container->resolve(); 215 | ``` 216 | 217 | ### Scope 218 | 219 | #### Transient 220 | 221 | If you use `asTransient()`, an instance will be created for each injection. 222 | 223 | ```cpp 224 | struct CounterInstaller : IInstaller 225 | { 226 | void onBinding(Container* c) const 227 | { 228 | c->bind() 229 | .to() 230 | .asTransient(); 231 | } 232 | }; 233 | 234 | int main() 235 | { 236 | Injector injector; 237 | injector.install(); 238 | 239 | { 240 | auto counter = injector.resolve(); // new instance 241 | std::cout << counter->countUp() << std::endl; // 1 242 | } 243 | { 244 | auto counter = injector.resolve(); // new instance 245 | std::cout << counter->countUp() << std::endl; // 1 246 | } 247 | } 248 | ``` 249 | 250 | #### Cached 251 | 252 | If you use `asCached`, the one cached by the ID will be reused. 253 | 254 | ```cpp 255 | struct CounterInstaller : IInstaller 256 | { 257 | void onBinding(Container* c) const 258 | { 259 | container->bind() 260 | .to() 261 | .asCached(); 262 | } 263 | }; 264 | 265 | int main() 266 | { 267 | Injector injector; 268 | injector.install(); 269 | 270 | { 271 | auto counter = injector.resolve(); // new instance 272 | std::cout << counter->countUp() << std::endl; // 1 273 | } 274 | { 275 | auto counter = injector.resolve(); // use cache 276 | std::cout << counter->countUp() << std::endl; // 2 277 | } 278 | } 279 | ``` 280 | 281 | #### Single 282 | 283 | If you use `asSingle`, the cached one will be reused and you can't bind to same type. 284 | 285 | ```cpp 286 | struct CounterInstaller : IInstaller 287 | { 288 | void onBinding(Container* c) const 289 | { 290 | c->bind() 291 | .to() 292 | .asSingle(); 293 | 294 | // failed Counter is Already bind to 295 | c->bind() 296 | .to() 297 | .asSingle(); 298 | } 299 | }; 300 | 301 | int main() 302 | { 303 | Injector injector; 304 | injector.install(); 305 | 306 | { 307 | auto counter = injector.resolve(); // new instance 308 | std::cout << counter->countUp() << std::endl; // 1 309 | } 310 | { 311 | auto counter = injector.resolve(); // used cache 312 | std::cout << counter->countUp() << std::endl; // 2 313 | } 314 | { 315 | auto counter = injector.resolve(); // nullptr 316 | } 317 | } 318 | ``` -------------------------------------------------------------------------------- /Emaject/Emaject.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {7026bea5-2773-4939-988b-b380a50e16ed} 25 | Emaject 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(ProjectDir)\include;$(IncludePath) 76 | 77 | 78 | false 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | 88 | Level3 89 | true 90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 91 | true 92 | stdcpplatest 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Level3 102 | true 103 | true 104 | true 105 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | stdcpplatest 108 | 109 | 110 | Console 111 | true 112 | true 113 | true 114 | 115 | 116 | 117 | 118 | Level3 119 | true 120 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 121 | true 122 | 123 | 124 | Console 125 | true 126 | 127 | 128 | 129 | 130 | Level3 131 | true 132 | true 133 | true 134 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | 137 | 138 | Console 139 | true 140 | true 141 | true 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /Emaject/include/Emaject.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace emaject 11 | { 12 | class Container; 13 | 14 | template 15 | struct InjectTraits 16 | { 17 | //void onInject(Type* value, Container* c) 18 | }; 19 | 20 | namespace detail 21 | { 22 | template 23 | struct AutoInjector; 24 | 25 | template 26 | struct MethodInjector; 27 | 28 | template 29 | concept TraitsInjectable = requires(Type * t, Container * c) 30 | { 31 | InjectTraits{}.onInject(t, c); 32 | }; 33 | 34 | template 35 | concept AutoInjectable = requires(Type * t, Container * c) 36 | { 37 | AutoInjector{}.onInject(t, c); 38 | }; 39 | 40 | template 41 | concept MethodInjectable = requires(Type * t, Container * c) 42 | { 43 | requires std::is_member_function_pointer_v; 44 | typename MethodInjector::Type; 45 | MethodInjector{}.onInject(t, c); 46 | }; 47 | 48 | template 49 | concept CtorInjectable = requires() 50 | { 51 | typename Type::CtorInject; 52 | }; 53 | 54 | template 55 | concept DefaultInstantiatable = (sizeof...(Args) == 0 && detail::CtorInjectable) || std::constructible_from; 56 | } 57 | 58 | /// 59 | /// Container 60 | /// 61 | class Container 62 | { 63 | public: 64 | template 65 | [[nodiscard]] auto bind() 66 | { 67 | return Binder(this); 68 | } 69 | 70 | template 71 | [[nodiscard]] std::shared_ptr instantiate(Args&&... args) 72 | { 73 | auto ret = this->makeInstance(std::forward(args)...); 74 | this->inject(ret.get()); 75 | return ret; 76 | } 77 | template 78 | [[nodiscard]] std::shared_ptr resolve() 79 | { 80 | auto itr = m_bindInfos.find(typeid(Tag)); 81 | if (itr == m_bindInfos.end()) { 82 | return nullptr; 83 | } 84 | auto&& [factory, createKind, cache] = std::any_cast&>(itr->second); 85 | if (createKind != ScopeKind::Transient) { 86 | if (!cache) { 87 | cache = factory(this); 88 | } 89 | return std::static_pointer_cast(cache); 90 | } 91 | return std::static_pointer_cast(factory(this)); 92 | } 93 | 94 | template 95 | void inject(Type* value) 96 | { 97 | if constexpr (detail::AutoInjectable) { 98 | if (value) { 99 | detail::AutoInjector{}.onInject(value, this); 100 | } 101 | } 102 | if constexpr (detail::TraitsInjectable) { 103 | if (value) { 104 | InjectTraits{}.onInject(value, this); 105 | } 106 | } 107 | } 108 | private: 109 | enum class ScopeKind 110 | { 111 | Transient, 112 | Cached, 113 | Single 114 | }; 115 | template 116 | struct Tag {}; 117 | 118 | template 119 | using Factory = std::function(Container*)>; 120 | 121 | template 122 | struct BindInfo 123 | { 124 | Factory factory; 125 | ScopeKind kind; 126 | std::shared_ptr cache; 127 | }; 128 | struct DerefInfo 129 | { 130 | bool isSingle = false; 131 | std::vector bindIds; 132 | }; 133 | template 134 | class Binder 135 | { 136 | public: 137 | Binder(Container* c) : 138 | m_container(c) 139 | {} 140 | public: 141 | template 142 | [[nodiscard]] auto to() const 143 | { 144 | return FromDescriptor(m_container); 145 | } 146 | [[nodiscard]] auto toSelf() const 147 | { 148 | return FromDescriptor(m_container); 149 | } 150 | public: 151 | [[nodiscard]] auto fromNew() const requires detail::DefaultInstantiatable 152 | { 153 | return toSelf().fromNew(); 154 | } 155 | template 156 | [[nodiscard]] auto withArgs(Args&&... args) const requires std::constructible_from 157 | { 158 | return toSelf().withArgs(std::forward(args)...); 159 | } 160 | [[nodiscard]] auto fromInstance(const std::shared_ptr& instance) const 161 | { 162 | return toSelf().fromInstance(instance); 163 | } 164 | [[nodiscard]] auto fromFactory(const Factory& factory) const 165 | { 166 | return toSelf().fromFactory(factory); 167 | } 168 | [[nodiscard]] auto fromFactory(const std::function()>& factory) const 169 | { 170 | return toSelf().fromFactory(factory); 171 | } 172 | [[nodiscard]] auto unused() const 173 | { 174 | return toSelf().unused(); 175 | } 176 | public: 177 | bool asTransient() const requires detail::DefaultInstantiatable 178 | { 179 | return fromNew().asTransient(); 180 | } 181 | bool asCached() const requires detail::DefaultInstantiatable 182 | { 183 | return fromNew().asCached(); 184 | } 185 | bool asSingle() const requires detail::DefaultInstantiatable 186 | { 187 | return fromNew().asSingle(); 188 | } 189 | private: 190 | Container* m_container; 191 | }; 192 | 193 | template 194 | class FromDescriptor 195 | { 196 | public: 197 | FromDescriptor(Container* c) : 198 | m_container(c) 199 | {} 200 | public: 201 | [[nodiscard]] auto fromNew() const requires detail::DefaultInstantiatable 202 | { 203 | return fromFactory([] (Container* c){ 204 | return c->instantiate(); 205 | }); 206 | } 207 | template 208 | [[nodiscard]] auto withArgs(Args&&... args) const requires std::constructible_from 209 | { 210 | return fromFactory([...args = std::forward(args)](Container* c){ 211 | // not allow move becouse transient 212 | auto ret = std::make_shared(args...); 213 | c->inject(ret.get()); 214 | return ret; 215 | }); 216 | } 217 | [[nodiscard]] auto fromInstance(const std::shared_ptr& instance) const 218 | { 219 | return fromFactory([i = instance] { 220 | return i; 221 | }); 222 | } 223 | [[nodiscard]] auto fromFactory(const Factory& factory) const 224 | { 225 | return ScopeDescriptor(m_container, factory); 226 | } 227 | [[nodiscard]] auto fromFactory(const std::function()>& factory) const 228 | { 229 | return fromFactory([factory]([[maybe_unused]] Container* container) { 230 | return factory(); 231 | }); 232 | } 233 | template 234 | [[nodiscard]] auto fromResolve() const 235 | { 236 | return fromFactory([](Container* c) { 237 | return c->resolve(); 238 | }); 239 | } 240 | [[nodiscard]] auto unused() const 241 | { 242 | return fromFactory([]{ 243 | return nullptr; 244 | }); 245 | } 246 | public: 247 | bool asTransient() const requires detail::DefaultInstantiatable 248 | { 249 | return fromNew().asTransient(); 250 | } 251 | bool asCached() const requires detail::DefaultInstantiatable 252 | { 253 | return fromNew().asCached(); 254 | } 255 | bool asSingle() const requires detail::DefaultInstantiatable 256 | { 257 | return fromNew().asSingle(); 258 | } 259 | private: 260 | Container* m_container; 261 | }; 262 | template 263 | class ScopeDescriptor 264 | { 265 | public: 266 | ScopeDescriptor(Container* c, const Factory& f) : 267 | m_container(c), 268 | m_factory(f) 269 | {} 270 | public: 271 | bool asTransient() const 272 | { 273 | return m_container 274 | ->regist({ m_factory, ScopeKind::Transient, nullptr }); 275 | } 276 | bool asCached() const 277 | { 278 | return m_container 279 | ->regist({ m_factory, ScopeKind::Cached, nullptr }); 280 | } 281 | bool asSingle() const 282 | { 283 | return m_container 284 | ->regist({ m_factory, ScopeKind::Single, nullptr }); 285 | } 286 | private: 287 | Container* m_container; 288 | Factory m_factory; 289 | }; 290 | 291 | template 292 | struct Instantiater 293 | { 294 | template 295 | struct ctor {}; 296 | template 297 | struct ctor 298 | { 299 | auto operator()(Container* c) const 300 | { 301 | return std::make_shared(c->resolve::element_type>()...); 302 | } 303 | }; 304 | 305 | template 306 | auto operator()(Container* c, Args&&... args) const 307 | { 308 | if constexpr (sizeof...(Args) == 0 && detail::CtorInjectable) { 309 | return ctor{}(c); 310 | } else if constexpr (std::constructible_from) { 311 | return std::make_shared(std::forward(args)...); 312 | } else { 313 | return nullptr; 314 | } 315 | } 316 | }; 317 | private: 318 | template 319 | [[nodiscard]] std::shared_ptr makeInstance(Args&&... args) 320 | { 321 | return Instantiater{}(this, std::forward(args)...); 322 | } 323 | 324 | template 325 | bool regist(const BindInfo& info) 326 | { 327 | auto& deref = m_derefInfos[typeid(To)]; 328 | if (deref.isSingle) { 329 | return false; 330 | } 331 | const auto& id = typeid(Tag); 332 | if (m_bindInfos.find(id) != m_bindInfos.end()) { 333 | return false; 334 | } 335 | if (info.kind == ScopeKind::Single) { 336 | if (!deref.bindIds.empty()) { 337 | return false; 338 | } 339 | deref.isSingle = true; 340 | } 341 | m_bindInfos[id] = info; 342 | deref.bindIds.push_back(id); 343 | return true; 344 | } 345 | private: 346 | std::unordered_map m_bindInfos; 347 | std::unordered_map m_derefInfos; 348 | }; 349 | 350 | /// 351 | /// Installer 352 | /// 353 | struct IInstaller 354 | { 355 | virtual ~IInstaller() = default; 356 | virtual void onBinding(Container* c) const = 0; 357 | }; 358 | 359 | /// 360 | /// Injector 361 | /// 362 | class Injector 363 | { 364 | public: 365 | Injector() : 366 | m_container(std::make_shared()) 367 | {} 368 | 369 | template 370 | Injector& install(Args&&... args) requires std::is_base_of_v 371 | { 372 | Installer(std::forward(args)...).onBinding(m_container.get()); 373 | return *this; 374 | } 375 | 376 | Injector& install(const std::function& installer) 377 | { 378 | installer(m_container.get()); 379 | return *this; 380 | } 381 | 382 | template 383 | [[nodiscard]] std::shared_ptr instantiate(Args&&... args) requires detail::DefaultInstantiatable 384 | { 385 | return m_container->instantiate(std::forward(args)...); 386 | } 387 | template 388 | [[nodiscard]] std::shared_ptr resolve() 389 | { 390 | return m_container->resolve(); 391 | } 392 | private: 393 | std::shared_ptr m_container; 394 | }; 395 | 396 | 397 | //---------------------------------------- 398 | // Auto Injector 399 | //---------------------------------------- 400 | namespace detail 401 | { 402 | #if __clang__ 403 | inline constexpr size_t AUTO_INJECT_MAX_LINES = 256; 404 | #else 405 | inline constexpr size_t AUTO_INJECT_MAX_LINES = 500; 406 | #endif 407 | template 408 | struct AutoInjectLine 409 | { 410 | Container* container; 411 | }; 412 | 413 | template 414 | concept AutoInjectCallable = requires(Type t, AutoInjectLine l) 415 | { 416 | t | l; 417 | }; 418 | 419 | template 420 | constexpr std::index_sequence operator+(std::index_sequence, std::index_sequence) 421 | { 422 | return {}; 423 | } 424 | template 425 | constexpr auto filter_seq() 426 | { 427 | if constexpr (AutoInjectCallable) { 428 | return std::index_sequence{}; 429 | } else { 430 | return std::index_sequence<>{}; 431 | } 432 | } 433 | template 434 | constexpr auto make_sequence_impl(std::index_sequence) 435 | { 436 | return (filter_seq() + ...); 437 | } 438 | 439 | template 440 | constexpr auto make_sequence() 441 | { 442 | return make_sequence_impl(std::make_index_sequence()); 443 | } 444 | 445 | template 446 | concept IsAutoInjectable = decltype(make_sequence())::size() > 0; 447 | 448 | template 449 | void auto_inject(Type& ret, Container* c) 450 | { 451 | ret | AutoInjectLine{c}; 452 | } 453 | template 454 | void auto_inject_all_impl(Type& ret, Container* c, std::index_sequence) 455 | { 456 | (auto_inject(ret, c), ...); 457 | } 458 | template 459 | void auto_inject_all(Type& ret, Container* c) 460 | { 461 | auto_inject_all_impl(ret, c, make_sequence()); 462 | } 463 | 464 | template 465 | struct AutoInjector 466 | {}; 467 | 468 | template 469 | struct AutoInjector 470 | { 471 | void onInject(Type* value, Container* c) 472 | { 473 | detail::auto_inject_all(*value, c); 474 | } 475 | }; 476 | 477 | template 478 | struct InjectedType{}; 479 | 480 | template 481 | struct InjectedType 482 | { 483 | using type = Type; 484 | }; 485 | 486 | template 487 | struct InjectedType 488 | { 489 | using type = Type; 490 | }; 491 | 492 | template 493 | struct MethodInjector 494 | { 495 | template 496 | struct impl 497 | { 498 | }; 499 | template 500 | struct impl 501 | { 502 | using resolves_type = std::tuple; 503 | 504 | template 505 | struct IDType 506 | { 507 | static constexpr int id = ID; 508 | }; 509 | using resolves_id = std::tuple...>; 510 | 511 | template 512 | struct TypeId 513 | { 514 | using type = typename std::decay_t>::element_type; 515 | static consteval int id() 516 | { 517 | if constexpr (Index < sizeof...(IDs)) { 518 | return std::tuple_element_t::id; 519 | } else { 520 | return 0; 521 | } 522 | } 523 | }; 524 | template 525 | auto resolve(Container* c) 526 | { 527 | return c->resolve::type, TypeId::id()>(); 528 | } 529 | template 530 | auto onInject(Type* value, Container* c, std::index_sequence) 531 | { 532 | return (value->*MemP)(resolve(c)...); 533 | } 534 | auto onInject(Type* value, Container* c) 535 | { 536 | return onInject(value, c, std::make_index_sequence()); 537 | } 538 | }; 539 | using Type = typename InjectedType::type; 540 | 541 | auto onInject(Type* t, Container* c) 542 | { 543 | return impl{}.onInject(t, c); 544 | } 545 | }; 546 | template 547 | struct FieldInjector 548 | { 549 | using Type = typename InjectedType::type; 550 | 551 | void onInject(Type* t, Container* c) 552 | { 553 | t->*MemP = c->resolve*MemP)>::element_type, ID>(); 554 | } 555 | }; 556 | template 557 | void AutoInject(Type* value, Container* c) 558 | { 559 | if constexpr (::emaject::detail::MethodInjectable) { 560 | MethodInjector{}.onInject(value, c); 561 | } else { 562 | FieldInjector{}.onInject(value, c); 563 | } 564 | } 565 | } 566 | } 567 | 568 | //---------------------------------------- 569 | // Macro 570 | //---------------------------------------- 571 | #if _MSC_VER 572 | #define INJECT_IMPL(value, line, ...) ]]\ 573 | friend auto operator|(auto& a, const ::emaject::detail::AutoInjectLine& l){\ 574 | static_assert(line < ::emaject::detail::AUTO_INJECT_MAX_LINES);\ 575 | using ThisType = std::decay_t;\ 576 | ::emaject::detail::AutoInject(&a, l.container);\ 577 | }[[ 578 | #else 579 | #define INJECT_IMPL(value, line, ...) ]]\ 580 | friend auto operator|(auto& a, const ::emaject::detail::AutoInjectLine& l){\ 581 | static_assert(line < ::emaject::detail::AUTO_INJECT_MAX_LINES);\ 582 | using ThisType = std::decay_t;\ 583 | ::emaject::detail::AutoInject(&a, l.container);\ 584 | }[[ 585 | #endif 586 | #define INJECT(value, ...) INJECT_IMPL(value, __LINE__, __VA_ARGS__) 587 | 588 | #define INJECT_CTOR(ctor) using CtorInject = ctor; ctor 589 | --------------------------------------------------------------------------------