├── WORKSPACE ├── test ├── main.cpp ├── mock │ ├── iqux.hpp │ ├── ifizz.hpp │ ├── iservices.hpp │ ├── qux.hpp │ ├── fizz.hpp │ ├── symbols.hpp │ └── services.hpp ├── speed.cpp ├── inspection.cpp ├── constant_resolve.cpp ├── auto_resolve.cpp └── dynamic_resolve.cpp ├── example ├── simple │ ├── api │ │ ├── settings.hpp │ │ ├── service.hpp │ │ ├── symbols.hpp │ │ └── logger.hpp │ ├── src │ │ ├── mock_logger.hpp │ │ ├── settings.hpp │ │ ├── service.hpp │ │ ├── main.cpp │ │ └── logger.hpp │ └── BUILD └── profiling │ ├── BUILD │ └── main.cpp ├── .bazelrc ├── third_party └── amalgamate │ ├── config.json │ ├── CHANGES.md │ ├── LICENSE.md │ ├── README.md │ └── amalgamate.py ├── include └── mosure │ ├── factory.hpp │ ├── exceptions │ └── resolution.hpp │ ├── context.hpp │ ├── symbol.hpp │ ├── injectable.hpp │ ├── interfaces │ └── icontainer.hpp │ ├── meta.hpp │ ├── container.hpp │ ├── binding.hpp │ ├── inversify.hpp │ └── resolver.hpp ├── .github └── workflows │ ├── macos.yml │ ├── ubuntu.yml │ └── windows.yml ├── .gitignore ├── LICENSE ├── BUILD ├── README.md └── single_include └── mosure └── inversify.hpp /WORKSPACE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 2 | #define CATCH_CONFIG_MAIN 3 | #include 4 | -------------------------------------------------------------------------------- /example/simple/api/settings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | struct ISettings { 7 | LogLevel logLevel; 8 | }; 9 | -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | build:windows --copt="/std:c++17" --copt="/W4" --copt="/O2" 2 | build:linux --copt="-std=c++17" --copt="-O3" 3 | build:macos --copt="-std=c++17" --copt="-O3" 4 | -------------------------------------------------------------------------------- /example/simple/api/service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | struct IService { 7 | virtual void run() = 0; 8 | }; 9 | 10 | using IServicePtr = std::shared_ptr; 11 | -------------------------------------------------------------------------------- /third_party/amalgamate/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": "Dependency injection for modern C++", 3 | "target": "single_include/mosure/inversify.hpp", 4 | "sources": [ 5 | "include/mosure/inversify.hpp" 6 | ], 7 | "include_paths": ["include"] 8 | } 9 | -------------------------------------------------------------------------------- /test/mock/iqux.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | struct IQux { 7 | virtual ~IQux() = default; 8 | 9 | virtual int buzz() = 0; 10 | }; 11 | 12 | using IQuxUniquePtr = std::unique_ptr; 13 | using IQuxSharedPtr = std::shared_ptr; 14 | -------------------------------------------------------------------------------- /example/profiling/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary") 2 | 3 | 4 | cc_binary( 5 | name = "profiling", 6 | srcs = [ 7 | "main.cpp", 8 | ], 9 | deps = [ 10 | "//:inversify", 11 | ], 12 | visibility = ["//visibility:public"] 13 | ) 14 | -------------------------------------------------------------------------------- /include/mosure/factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | 8 | namespace mosure::inversify { 9 | 10 | template 11 | using Factory = std::function&)>; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /test/mock/ifizz.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | struct IFizz { 7 | virtual ~IFizz() = default; 8 | 9 | virtual int buzz() = 0; 10 | 11 | int counter { 0 }; 12 | }; 13 | 14 | using IFizzUniquePtr = std::unique_ptr; 15 | using IFizzSharedPtr = std::shared_ptr; 16 | -------------------------------------------------------------------------------- /include/mosure/exceptions/resolution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | namespace mosure::inversify::exceptions { 8 | 9 | struct ResolutionException : public std::runtime_error { 10 | explicit ResolutionException(const std::string& msg) : std::runtime_error(msg) { } 11 | }; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /include/mosure/context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace mosure::inversify { 7 | 8 | template 9 | class Container; 10 | 11 | template 12 | struct Context { 13 | inversify::IContainer& container; 14 | }; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /test/mock/iservices.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | struct IServiceA { 7 | virtual int foo() = 0; 8 | }; 9 | using ServiceAPtr = std::shared_ptr; 10 | 11 | struct IServiceB { 12 | virtual int foo() = 0; 13 | }; 14 | using ServiceBPtr = std::shared_ptr; 15 | 16 | struct IServiceC { 17 | virtual int foo() = 0; 18 | }; 19 | using ServiceCPtr = std::shared_ptr; 20 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: macos 3 | 4 | jobs: 5 | test: 6 | strategy: 7 | matrix: 8 | os: [macos-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | 14 | - uses: bazelbuild/setup-bazelisk@v2 15 | 16 | - name: Test 17 | run: bazel run test --enable_platform_specific_config 18 | -------------------------------------------------------------------------------- /include/mosure/symbol.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | namespace mosure::inversify { 8 | 9 | template 10 | struct Symbol { 11 | static_assert( 12 | !std::is_abstract(), 13 | "inversify::Container cannot bind/get abstract class value (use a smart pointer instead)." 14 | ); 15 | 16 | using value = Interface; 17 | }; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: ubuntu 3 | 4 | jobs: 5 | test: 6 | strategy: 7 | matrix: 8 | os: [ubuntu-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | 14 | - uses: bazelbuild/setup-bazelisk@v2 15 | 16 | - name: Test 17 | run: bazel run test --enable_platform_specific_config 18 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: windows 3 | 4 | jobs: 5 | test: 6 | strategy: 7 | matrix: 8 | os: [windows-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | 14 | - uses: bazelbuild/setup-bazelisk@v2 15 | 16 | - name: Test 17 | run: bazel run test --enable_platform_specific_config 18 | -------------------------------------------------------------------------------- /example/simple/api/symbols.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | namespace inversify = mosure::inversify; 11 | namespace symbols { 12 | using logger = inversify::Symbol; 13 | using service = inversify::Symbol; 14 | using settings = inversify::Symbol; 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .vscode/ 35 | bazel-* 36 | 37 | callgrind.out.* 38 | -------------------------------------------------------------------------------- /third_party/amalgamate/CHANGES.md: -------------------------------------------------------------------------------- 1 | The following changes have been made to the code with respect to : 2 | 3 | - Resolved inspection results from PyCharm: 4 | - replaced tabs with spaces 5 | - added encoding annotation 6 | - reindented file to remove trailing whitespaces 7 | - unused import `sys` 8 | - membership check 9 | - made function from `_is_within` 10 | - removed unused variable `actual_path` 11 | -------------------------------------------------------------------------------- /example/simple/src/mock_logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | 9 | class MockLogger : public ILogger { 10 | public: 11 | void info(const std::string&) const override { }; 12 | void warn(const std::string&) const override { }; 13 | void error(const std::string&) const override { }; 14 | void log(LogLevel, const std::string&) const override { }; 15 | }; 16 | -------------------------------------------------------------------------------- /example/simple/api/logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | enum LogLevel { 8 | INFO, 9 | WARN, 10 | ERROR 11 | }; 12 | 13 | struct ILogger { 14 | virtual void info(const std::string& message) const = 0; 15 | virtual void warn(const std::string& message) const = 0; 16 | virtual void error(const std::string& message) const = 0; 17 | 18 | virtual void log(LogLevel level, const std::string& message) const = 0; 19 | }; 20 | 21 | using ILoggerPtr = std::shared_ptr; 22 | -------------------------------------------------------------------------------- /test/mock/qux.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "mock/ifizz.hpp" 6 | #include "mock/iqux.hpp" 7 | #include "mock/symbols.hpp" 8 | 9 | 10 | namespace inversify = mosure::inversify; 11 | 12 | struct Qux : IQux { 13 | Qux(IFizzSharedPtr fizz) 14 | : 15 | fizz_(fizz) 16 | { } 17 | 18 | int buzz() override { 19 | return fizz_->buzz() + 5; 20 | } 21 | 22 | IFizzSharedPtr fizz_; 23 | }; 24 | 25 | template <> 26 | struct inversify::Injectable 27 | : inversify::Inject< 28 | symbols::autoFizzShared 29 | > 30 | { }; 31 | -------------------------------------------------------------------------------- /example/simple/src/settings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | 10 | struct Settings : ISettings { 11 | Settings() { 12 | logLevel = LogLevel::INFO; 13 | 14 | if(const char* level_str = std::getenv("LOG_LEVEL")) { 15 | auto val = std::string(level_str); 16 | 17 | if (val == "WARN") { 18 | logLevel = LogLevel::WARN; 19 | } else if (val == "ERROR") { 20 | logLevel = LogLevel::ERROR; 21 | } 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /test/mock/fizz.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "mock/ifizz.hpp" 6 | #include "mock/symbols.hpp" 7 | 8 | 9 | namespace inversify = mosure::inversify; 10 | 11 | struct Fizz : IFizz { 12 | Fizz(int foo, double bar) 13 | : 14 | foo_(foo), 15 | bar_(bar) 16 | { } 17 | 18 | int buzz() override { 19 | counter++; 20 | 21 | return foo_; 22 | } 23 | 24 | int foo_; 25 | double bar_; 26 | }; 27 | 28 | template <> 29 | struct inversify::Injectable 30 | : inversify::Inject< 31 | symbols::foo, 32 | symbols::bar 33 | > 34 | { }; 35 | -------------------------------------------------------------------------------- /example/simple/src/service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | class Service : public IService { 11 | public: 12 | Service(ILoggerPtr logger) 13 | : 14 | logger_(logger) 15 | { } 16 | 17 | void run() override { 18 | logger_->info("Info message."); 19 | logger_->warn("Warn message."); 20 | logger_->error("Error message."); 21 | } 22 | 23 | private: 24 | ILoggerPtr logger_; 25 | }; 26 | 27 | namespace inversify = mosure::inversify; 28 | template <> 29 | struct inversify::Injectable 30 | : inversify::Inject< 31 | symbols::logger 32 | > 33 | { }; -------------------------------------------------------------------------------- /example/simple/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") 2 | 3 | 4 | cc_library( 5 | name = "simple_api", 6 | hdrs = glob([ 7 | "api/*.hpp", 8 | "src/*.hpp", 9 | ]), 10 | includes = [ 11 | ".", 12 | ], 13 | visibility = ["//visibility:public"] 14 | ) 15 | 16 | cc_library( 17 | name = "simple_src_hdrs", 18 | hdrs = glob([ 19 | "src/*.hpp", 20 | ]), 21 | includes = [ 22 | ".", 23 | ], 24 | visibility = ["//visibility:public"] 25 | ) 26 | 27 | cc_binary( 28 | name = "simple", 29 | srcs = glob([ 30 | "src/*.cpp", 31 | ]), 32 | deps = [ 33 | ":simple_api", 34 | ":simple_src_hdrs", 35 | "//:inversify", 36 | ], 37 | visibility = ["//visibility:public"] 38 | ) 39 | -------------------------------------------------------------------------------- /example/simple/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace inversify = mosure::inversify; 12 | 13 | int main() { 14 | inversify::Container< 15 | symbols::logger, 16 | symbols::service, 17 | symbols::settings 18 | > container; 19 | 20 | container.bind().to().inSingletonScope(); 21 | container.bind().to(); 22 | container.bind().to().inSingletonScope(); 23 | 24 | //container.bind(symbols::logger).to().inSingletonScope(); 25 | 26 | container.get()->run(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /include/mosure/injectable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | 10 | namespace mosure::inversify { 11 | 12 | template 13 | struct Inject { 14 | static_assert( 15 | meta::valid_symbol_types_v, 16 | "inversify::Injectable dependencies must be of type inversify::Symbol" 17 | ); 18 | 19 | #ifdef INVERSIFY_BINDING_INSPECTION 20 | using value = std::tuple; 21 | #endif 22 | 23 | template 24 | inline static auto resolve(const inversify::Context& context) { 25 | return std::make_tuple(context.container.template get()...); 26 | } 27 | }; 28 | 29 | template > 30 | struct Injectable: Inject { }; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /include/mosure/interfaces/icontainer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | 8 | namespace mosure::inversify { 9 | 10 | template < 11 | typename T, 12 | typename... SymbolTypes 13 | > 14 | class BindingTo; 15 | 16 | template < 17 | template class Implementation, 18 | typename... SymbolTypes 19 | > 20 | class IContainer { 21 | public: 22 | template 23 | inversify::BindingTo& bind() { 24 | auto crtpImplementation = static_cast *>(this); 25 | 26 | return crtpImplementation->template bind(); 27 | } 28 | 29 | template 30 | typename T::value get() { 31 | auto crtpImplementation = static_cast *>(this); 32 | 33 | return crtpImplementation->template get(); 34 | } 35 | }; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /test/mock/symbols.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "mock/ifizz.hpp" 8 | #include "mock/iqux.hpp" 9 | #include "mock/iservices.hpp" 10 | 11 | 12 | namespace inversify = mosure::inversify; 13 | 14 | namespace symbols { 15 | using foo = inversify::Symbol; 16 | struct foo2 : inversify::Symbol {}; // make the symbol unique by defining a struct :/ 17 | 18 | using bar = inversify::Symbol; 19 | using fizz = inversify::Symbol; 20 | using fizzFactory = inversify::Symbol>; 21 | 22 | using autoFizzUnique = inversify::Symbol; 23 | using autoFizzShared = inversify::Symbol; 24 | 25 | using autoQuxUnique = inversify::Symbol; 26 | 27 | using symbolA = inversify::Symbol; 28 | using symbolB = inversify::Symbol; 29 | using symbolC = inversify::Symbol; 30 | } 31 | -------------------------------------------------------------------------------- /example/profiling/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | 6 | namespace inversify = mosure::inversify; 7 | 8 | using int_symbol = inversify::Symbol; 9 | 10 | struct Service { 11 | int val; 12 | 13 | Service() : val(0) { } 14 | Service(int val) : val(val) { } 15 | 16 | void foo() { 17 | std::cout << "hello: " << val << std::endl; 18 | } 19 | }; 20 | 21 | template <> 22 | struct inversify::Injectable 23 | : inversify::Inject< 24 | int_symbol 25 | > 26 | { }; 27 | 28 | using test_symbol = inversify::Symbol; 29 | 30 | 31 | template 32 | void to_profile(Container& container) { 33 | container.template get().foo(); 34 | } 35 | 36 | int main() { 37 | inversify::Container< 38 | int_symbol, 39 | test_symbol 40 | > container; 41 | 42 | container.bind().toConstantValue(3); 43 | container.bind().to(); 44 | 45 | to_profile(container); 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mitchell Mosure 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 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | 4 | cc_library( 5 | name = "catch2", 6 | copts = [ 7 | "-std=c++11", 8 | ], 9 | hdrs = [ 10 | "third_party/catch2/catch.hpp", 11 | ], 12 | includes = [ 13 | "third_party", 14 | ], 15 | visibility = ["//visibility:public"], 16 | ) 17 | 18 | cc_library( 19 | name = "inversify", 20 | copts = [ 21 | "-std=c++17", 22 | "-pedantic-errors", 23 | "-Wall", 24 | "-Wextra", 25 | "-Wshadow", 26 | "-Wunused", 27 | ], 28 | hdrs = glob([ 29 | "include/**/*.hpp", 30 | ]), 31 | includes = [ 32 | "include", 33 | ], 34 | visibility = ["//visibility:public"] 35 | ) 36 | 37 | cc_library( 38 | name = "test_mocks", 39 | hdrs = glob([ 40 | "test/mock/*.hpp", 41 | ]), 42 | includes = [ 43 | "test", 44 | ], 45 | visibility = ["//visibility:public"] 46 | ) 47 | 48 | cc_binary( 49 | name = "test", 50 | copts = [ 51 | "-DINVERSIFY_BINDING_INSPECTION", 52 | ], 53 | srcs = glob([ 54 | "test/**/*.cpp", 55 | ]), 56 | deps = [ 57 | ":catch2", 58 | ":inversify", 59 | ":test_mocks", 60 | ], 61 | visibility = ["//visibility:public"] 62 | ) 63 | -------------------------------------------------------------------------------- /include/mosure/meta.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | 10 | namespace mosure::inversify::meta { 11 | 12 | template< 13 | typename T, 14 | typename... Types 15 | > 16 | inline constexpr bool contains_v = std::disjunction_v< 17 | std::is_same... 18 | >; 19 | 20 | 21 | template 22 | struct is_empty : std::false_type { }; 23 | 24 | template<> 25 | struct is_empty<> : std::true_type { }; 26 | 27 | template 28 | inline constexpr bool is_empty_v = is_empty::value; 29 | 30 | 31 | template < 32 | typename T, 33 | template typename Template 34 | > 35 | struct is_specialization : std::false_type { }; 36 | 37 | template < 38 | template typename Template, 39 | typename... Args 40 | > 41 | struct is_specialization, Template> : std::true_type { }; 42 | 43 | 44 | template 45 | inline constexpr bool valid_symbol_types_v = std::conjunction_v< 46 | meta::is_specialization... 47 | > || std::conjunction_v< 48 | std::is_base_of, Types>... 49 | >; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /test/mock/services.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "mock/iservices.hpp" 4 | #include "mock/symbols.hpp" 5 | 6 | 7 | namespace inversify = mosure::inversify; 8 | 9 | struct ServiceA : IServiceA { 10 | ServiceA(int foo) 11 | : 12 | foo_(foo) 13 | { } 14 | 15 | inline int foo() override { 16 | return foo_; 17 | } 18 | 19 | int foo_; 20 | }; 21 | 22 | struct ServiceB : IServiceB { 23 | ServiceB(ServiceAPtr serviceA) 24 | : 25 | serviceA_(serviceA) 26 | { } 27 | 28 | inline int foo() override { 29 | return serviceA_->foo(); 30 | } 31 | 32 | ServiceAPtr serviceA_; 33 | }; 34 | 35 | struct ServiceC : IServiceC { 36 | ServiceC(ServiceBPtr serviceB) 37 | : 38 | serviceB_(serviceB) 39 | { } 40 | 41 | inline int foo() override { 42 | return serviceB_->foo(); 43 | } 44 | 45 | ServiceBPtr serviceB_; 46 | }; 47 | 48 | template <> 49 | struct inversify::Injectable 50 | : inversify::Inject< 51 | symbols::foo 52 | > 53 | { }; 54 | 55 | template <> 56 | struct inversify::Injectable 57 | : inversify::Inject< 58 | symbols::symbolA 59 | > 60 | { }; 61 | 62 | template <> 63 | struct inversify::Injectable 64 | : inversify::Inject< 65 | symbols::symbolB 66 | > 67 | { }; 68 | -------------------------------------------------------------------------------- /test/speed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 6 | #include 7 | 8 | #include 9 | 10 | #include "mock/symbols.hpp" 11 | #include "mock/services.hpp" 12 | 13 | 14 | namespace inversify = mosure::inversify; 15 | 16 | SCENARIO("container resolves automatic values quickly", "[performance]") { 17 | 18 | GIVEN("A container with an auto binding chain of size 3") { 19 | inversify::Container< 20 | symbols::foo, 21 | symbols::symbolA, 22 | symbols::symbolB, 23 | symbols::symbolC 24 | > container; 25 | 26 | container.bind().toConstantValue(10); 27 | container.bind().to(); 28 | container.bind().to(); 29 | container.bind().to(); 30 | 31 | BENCHMARK("container.get()") { 32 | return container.get(); 33 | }; 34 | 35 | BENCHMARK("manual injection with nested std::make_shared(...)") { 36 | return std::make_shared( 37 | std::make_shared( 38 | std::make_shared( 39 | 10 40 | ) 41 | ) 42 | ); 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/inspection.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 2 | #include 3 | 4 | #include 5 | 6 | #include "mock/fizz.hpp" 7 | #include "mock/qux.hpp" 8 | #include "mock/symbols.hpp" 9 | 10 | 11 | namespace inversify = mosure::inversify; 12 | 13 | SCENARIO("container inspection yields proper metadata", "[inspection]") { 14 | 15 | GIVEN("A container with automatic nested bindings") { 16 | inversify::Container< 17 | symbols::foo, 18 | symbols::bar, 19 | symbols::autoFizzShared, 20 | symbols::autoQuxUnique 21 | > container; 22 | 23 | container.bind().toConstantValue(10); 24 | container.bind().toConstantValue(1.618); 25 | 26 | container.bind().to().inSingletonScope(); 27 | 28 | auto quxResolverPtr = container.bind().to().getResolver(); 29 | 30 | WHEN("the inspection is done") { 31 | auto dep_tuple = quxResolverPtr->getDependencies(); 32 | 33 | THEN("the correct values are returned") { 34 | REQUIRE(!dep_tuple.get()); 35 | REQUIRE(!dep_tuple.get()); 36 | REQUIRE(!!dep_tuple.get()); 37 | REQUIRE(!dep_tuple.get()); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /third_party/amalgamate/LICENSE.md: -------------------------------------------------------------------------------- 1 | amalgamate.py - Amalgamate C source and header files 2 | Copyright (c) 2012, Erik Edlund 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Erik Edlund, nor the names of its contributors may 15 | be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /include/mosure/container.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace mosure::inversify { 12 | 13 | template 14 | class Container 15 | : public inversify::IContainer 16 | { 17 | public: 18 | static_assert( 19 | meta::valid_symbol_types_v, 20 | "inversify::Container symbols must be of type inversify::Symbol" 21 | ); 22 | 23 | static_assert( 24 | !meta::is_empty_v, 25 | "inversify::Container must register at least one symbol" 26 | ); 27 | 28 | using BindingMap = std::tuple< 29 | inversify::Binding< 30 | SymbolTypes, 31 | SymbolTypes... 32 | >... 33 | >; 34 | 35 | template 36 | inline inversify::BindingTo& bind() { 37 | static_assert( 38 | meta::contains_v, 39 | "inversify::Container symbol not registered" 40 | ); 41 | 42 | return std::get< 43 | inversify::Binding 44 | >(bindings_); 45 | } 46 | 47 | template 48 | inline typename T::value get() { 49 | static_assert( 50 | meta::contains_v, 51 | "inversify::Container symbol not registered" 52 | ); 53 | 54 | return std::get< 55 | inversify::Binding 56 | >(bindings_).resolve(context_); 57 | } 58 | 59 | private: 60 | BindingMap bindings_ {}; 61 | inversify::Context context_ { *this }; 62 | }; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /example/simple/src/logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | class Logger : public ILogger { 13 | public: 14 | Logger(ISettings settings) 15 | : 16 | settings_(settings) 17 | { } 18 | 19 | void info(const std::string& message) const override { 20 | log(LogLevel::INFO, message); 21 | }; 22 | 23 | void warn(const std::string& message) const override { 24 | log(LogLevel::WARN, message); 25 | }; 26 | 27 | void error(const std::string& message) const override { 28 | log(LogLevel::ERROR, message); 29 | }; 30 | 31 | void log(LogLevel level, const std::string& message) const override { 32 | if (settings_.logLevel > level) { 33 | return; 34 | } 35 | 36 | std::string level_prefix; 37 | switch (level) { 38 | case LogLevel::INFO: 39 | level_prefix = "INFO"; 40 | break; 41 | case LogLevel::WARN: 42 | level_prefix = "WARN"; 43 | break; 44 | case LogLevel::ERROR: 45 | level_prefix = "ERROR"; 46 | break; 47 | } 48 | 49 | std::cout << "[ExampleApp] [" << level_prefix << "] - " << message << std::endl; 50 | }; 51 | 52 | private: 53 | ISettings settings_; 54 | }; 55 | 56 | namespace inversify = mosure::inversify; 57 | template <> 58 | struct inversify::Injectable 59 | : inversify::Inject< 60 | symbols::settings 61 | > 62 | { }; 63 | -------------------------------------------------------------------------------- /include/mosure/binding.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | namespace mosure::inversify { 13 | 14 | template 15 | class BindingScope { 16 | public: 17 | void inSingletonScope() { 18 | resolver_ = std::make_shared>(resolver_); 19 | } 20 | 21 | #ifdef INVERSIFY_BINDING_INSPECTION 22 | auto getResolver() const { 23 | return resolver_; 24 | } 25 | #endif 26 | 27 | protected: 28 | inversify::ResolverPtr resolver_; 29 | }; 30 | 31 | template 32 | class BindingTo 33 | : public BindingScope 34 | { 35 | public: 36 | void toConstantValue(T&& value) { 37 | this->resolver_ = std::make_shared>(value); 38 | } 39 | 40 | BindingScope& toDynamicValue(inversify::Factory&& factory) { 41 | this->resolver_ = std::make_shared>(factory); 42 | 43 | return *this; 44 | } 45 | 46 | template 47 | BindingScope& to() { 48 | this->resolver_ = std::make_shared>(); 49 | 50 | return *this; 51 | } 52 | }; 53 | 54 | template 55 | class Binding 56 | : public BindingTo 57 | { 58 | public: 59 | inline typename T::value resolve(const inversify::Context& context) { 60 | if (!this->resolver_) { 61 | throw inversify::exceptions::ResolutionException("inversify::Resolver not found. Malformed binding."); 62 | } 63 | 64 | return this->resolver_->resolve(context); 65 | } 66 | }; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /test/constant_resolve.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 2 | #include 3 | 4 | #include 5 | 6 | #include "mock/symbols.hpp" 7 | 8 | 9 | namespace inversify = mosure::inversify; 10 | 11 | SCENARIO("container resolves constant values", "[resolve]") { 12 | 13 | GIVEN("A container with constant binding") { 14 | inversify::Container< 15 | symbols::foo 16 | > container; 17 | 18 | container.bind().toConstantValue(10); 19 | 20 | WHEN("the dependency is resolved") { 21 | auto result = container.get(); 22 | 23 | THEN("the correct value is returned") { 24 | REQUIRE(result == 10); 25 | } 26 | } 27 | 28 | WHEN("the binding is redefined") { 29 | container.bind().toConstantValue(20); 30 | 31 | WHEN("the dependency is resolved") { 32 | auto result = container.get(); 33 | 34 | THEN("the updated value is returned") { 35 | REQUIRE(result == 20); 36 | } 37 | } 38 | } 39 | } 40 | 41 | GIVEN("A container with constant bindings of the same type") { 42 | inversify::Container< 43 | symbols::foo, 44 | symbols::foo2 45 | > container; 46 | 47 | container.bind().toConstantValue(10); 48 | container.bind().toConstantValue(20); 49 | 50 | WHEN("the foo is resolved") { 51 | auto result = container.get(); 52 | 53 | THEN("the correct value is returned") { 54 | REQUIRE(result == 10); 55 | } 56 | } 57 | 58 | WHEN("the foo2 is resolved") { 59 | auto result = container.get(); 60 | 61 | THEN("the correct value is returned") { 62 | REQUIRE(result == 20); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /include/mosure/inversify.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | IoC and Dependency Injection for Modern C++ 3 | Version 1.0.0 4 | https://github.com/mosure/inversifycpp 5 | 6 | MIT License 7 | 8 | Copyright (c) 2020 Mitchell Mosure 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | #pragma once 30 | 31 | #include // function 32 | #include // make_shared, make_unique, shared_ptr, unique_ptr 33 | #include // runtime_error 34 | #include // string 35 | #include // make_from_tuple, tuple 36 | #include // apply, conjunction_v, disjunction_v, false_type, is_copy_constructable, is_same, true_type 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | 49 | 50 | /** 51 | @brief namespace for Mitchell Mosure - InversifyCpp 52 | @see https://github.com/mosure 53 | @since version 1.0.0 54 | */ 55 | namespace mosure::inversify { } 56 | -------------------------------------------------------------------------------- /third_party/amalgamate/README.md: -------------------------------------------------------------------------------- 1 | 2 | # amalgamate.py - Amalgamate C source and header files 3 | 4 | Origin: https://bitbucket.org/erikedlund/amalgamate 5 | 6 | Mirror: https://github.com/edlund/amalgamate 7 | 8 | `amalgamate.py` aims to make it easy to use SQLite-style C source and header 9 | amalgamation in projects. 10 | 11 | For more information, please refer to: http://sqlite.org/amalgamation.html 12 | 13 | ## Here be dragons 14 | 15 | `amalgamate.py` is quite dumb, it only knows the bare minimum about C code 16 | required in order to be able to handle trivial include directives. It can 17 | produce weird results for unexpected code. 18 | 19 | Things to be aware of: 20 | 21 | `amalgamate.py` will not handle complex include directives correctly: 22 | 23 | #define HEADER_PATH "path/to/header.h" 24 | #include HEADER_PATH 25 | 26 | In the above example, `path/to/header.h` will not be included in the 27 | amalgamation (HEADER_PATH is never expanded). 28 | 29 | `amalgamate.py` makes the assumption that each source and header file which 30 | is not empty will end in a new-line character, which is not immediately 31 | preceded by a backslash character (see 5.1.1.2p1.2 of ISO C99). 32 | 33 | `amalgamate.py` should be usable with C++ code, but raw string literals from 34 | C++11 will definitely cause problems: 35 | 36 | R"delimiter(Terrible raw \ data " #include )delimiter" 37 | R"delimiter(Terrible raw \ data " escaping)delimiter" 38 | 39 | In the examples above, `amalgamate.py` will stop parsing the raw string literal 40 | when it encounters the first quotation mark, which will produce unexpected 41 | results. 42 | 43 | ## Installing amalgamate.py 44 | 45 | Python v.2.7.0 or higher is required. 46 | 47 | `amalgamate.py` can be tested and installed using the following commands: 48 | 49 | ./test.sh && sudo -k cp ./amalgamate.py /usr/local/bin/ 50 | 51 | ## Using amalgamate.py 52 | 53 | amalgamate.py [-v] -c path/to/config.json -s path/to/source/dir \ 54 | [-p path/to/prologue.(c|h)] 55 | 56 | * The `-c, --config` option should specify the path to a JSON config file which 57 | lists the source files, include paths and where to write the resulting 58 | amalgamation. Have a look at `test/source.c.json` and `test/include.h.json` 59 | to see two examples. 60 | 61 | * The `-s, --source` option should specify the path to the source directory. 62 | This is useful for supporting separate source and build directories. 63 | 64 | * The `-p, --prologue` option should specify the path to a file which will be 65 | added to the beginning of the amalgamation. It is optional. 66 | 67 | -------------------------------------------------------------------------------- /test/auto_resolve.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 2 | #include 3 | 4 | #include 5 | 6 | #include "mock/fizz.hpp" 7 | #include "mock/qux.hpp" 8 | #include "mock/symbols.hpp" 9 | 10 | 11 | namespace inversify = mosure::inversify; 12 | 13 | SCENARIO("container resolves automatic values", "[resolve]") { 14 | 15 | GIVEN("A container with automatic unique_ptr binding") { 16 | inversify::Container< 17 | symbols::foo, 18 | symbols::bar, 19 | symbols::autoFizzUnique, 20 | symbols::autoFizzShared 21 | > container; 22 | 23 | container.bind().toConstantValue(10); 24 | container.bind().toConstantValue(1.618); 25 | 26 | container.bind().to(); 27 | 28 | WHEN("the dependency is resolved") { 29 | auto result = container.get(); 30 | auto foo = result->buzz(); 31 | 32 | THEN("the correct value is returned") { 33 | REQUIRE(foo == 10); 34 | REQUIRE(result->counter == 1); 35 | } 36 | } 37 | 38 | WHEN("multiple dependencies are resolved") { 39 | auto result1 = container.get(); 40 | auto result2 = container.get(); 41 | 42 | result1->buzz(); 43 | result2->buzz(); 44 | 45 | THEN("the values are unique") { 46 | REQUIRE(result1->counter == 1); 47 | REQUIRE(result2->counter == 1); 48 | REQUIRE(result1 != result2); 49 | } 50 | } 51 | } 52 | 53 | GIVEN("A container with automatic singleton shared_ptr binding") { 54 | inversify::Container< 55 | symbols::foo, 56 | symbols::bar, 57 | symbols::autoFizzUnique, 58 | symbols::autoFizzShared 59 | > container; 60 | 61 | container.bind().toConstantValue(10); 62 | container.bind().toConstantValue(1.618); 63 | 64 | container.bind().to().inSingletonScope(); 65 | 66 | WHEN("the dependency is resolved") { 67 | auto result = container.get(); 68 | auto foo = result->buzz(); 69 | 70 | THEN("the correct value is returned") { 71 | REQUIRE(foo == 10); 72 | REQUIRE(result->counter == 1); 73 | } 74 | } 75 | 76 | WHEN("multiple dependencies are resolved") { 77 | auto result1 = container.get(); 78 | auto result2 = container.get(); 79 | 80 | result1->buzz(); 81 | result2->buzz(); 82 | 83 | THEN("the values are equal") { 84 | REQUIRE(result1->counter == 2); 85 | REQUIRE(result2->counter == 2); 86 | REQUIRE(result1 == result2); 87 | } 88 | } 89 | } 90 | 91 | GIVEN("A container with automatic nested bindings") { 92 | inversify::Container< 93 | symbols::foo, 94 | symbols::bar, 95 | symbols::autoFizzShared, 96 | symbols::autoQuxUnique 97 | > container; 98 | 99 | container.bind().toConstantValue(10); 100 | container.bind().toConstantValue(1.618); 101 | 102 | container.bind().to().inSingletonScope(); 103 | 104 | container.bind().to(); 105 | 106 | WHEN("the dependency is resolved") { 107 | auto result = container.get(); 108 | auto foo = result->buzz(); 109 | 110 | THEN("the correct value is returned") { 111 | REQUIRE(foo == 15); 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /test/dynamic_resolve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 5 | #include 6 | 7 | #include 8 | 9 | #include "mock/fizz.hpp" 10 | #include "mock/symbols.hpp" 11 | 12 | 13 | namespace inversify = mosure::inversify; 14 | 15 | SCENARIO("container resolves dynamic values", "[resolve]") { 16 | 17 | GIVEN("A container with dynamic binding") { 18 | inversify::Container< 19 | symbols::foo, 20 | symbols::bar, 21 | symbols::fizz, 22 | symbols::fizzFactory 23 | > container; 24 | 25 | container.bind().toConstantValue(3); 26 | container.bind().toDynamicValue([](auto& ctx) { 27 | auto foo = ctx.container.template get(); 28 | 29 | return foo * 1.5; 30 | }); 31 | 32 | WHEN("the dependency is resolved") { 33 | auto result = container.get(); 34 | 35 | THEN("the correct value is returned") { 36 | REQUIRE(result == 4.5); 37 | } 38 | } 39 | 40 | WHEN("the binding is redefined") { 41 | container.bind().toDynamicValue([](auto& ctx) { 42 | auto foo = ctx.container.template get(); 43 | 44 | return foo * 2.5; 45 | }); 46 | 47 | WHEN("the dependency is resolved") { 48 | auto result = container.get(); 49 | 50 | THEN("the updated value is returned") { 51 | REQUIRE(result == 7.5); 52 | } 53 | } 54 | } 55 | } 56 | 57 | GIVEN("A container with factory binding") { 58 | inversify::Container< 59 | symbols::foo, 60 | symbols::bar, 61 | symbols::fizz, 62 | symbols::fizzFactory 63 | > container; 64 | 65 | container.bind().toConstantValue(10); 66 | container.bind().toConstantValue(1.618); 67 | 68 | container.bind().toDynamicValue( 69 | [](auto& ctx) { 70 | return [&]() { 71 | auto foo = ctx.container.template get(); 72 | auto bar = ctx.container.template get(); 73 | 74 | auto fizz = std::make_unique(foo, bar); 75 | 76 | return fizz; 77 | }; 78 | } 79 | ); 80 | 81 | WHEN("the dependency is resolved") { 82 | auto factory = container.get(); 83 | 84 | WHEN("the factory is called") { 85 | auto result = factory(); 86 | 87 | THEN("A valid object is created") { 88 | auto foo = result->buzz(); 89 | 90 | REQUIRE(foo == 10); 91 | REQUIRE(result->counter == 1); 92 | } 93 | } 94 | } 95 | } 96 | 97 | GIVEN("A container with singleton dynamic binding") { 98 | inversify::Container< 99 | symbols::foo, 100 | symbols::bar, 101 | symbols::fizz, 102 | symbols::fizzFactory 103 | > container; 104 | 105 | container.bind().toConstantValue(10); 106 | container.bind().toConstantValue(1.618); 107 | 108 | container.bind().toDynamicValue( 109 | [](auto& ctx) { 110 | auto foo = ctx.container.template get(); 111 | auto bar = ctx.container.template get(); 112 | 113 | auto fizz = std::make_shared(foo, bar); 114 | 115 | return fizz; 116 | } 117 | ).inSingletonScope(); 118 | 119 | WHEN("multiple dependencies are resolved") { 120 | auto fizz1 = container.get(); 121 | auto fizz2 = container.get(); 122 | 123 | THEN("both dependency pointers are equal") { 124 | REQUIRE(fizz1 == fizz2); 125 | } 126 | } 127 | } 128 | 129 | GIVEN("A container with resolution dynamic binding") { 130 | inversify::Container< 131 | symbols::foo, 132 | symbols::bar, 133 | symbols::fizz, 134 | symbols::fizzFactory 135 | > container; 136 | 137 | container.bind().toConstantValue(10); 138 | container.bind().toConstantValue(1.618); 139 | 140 | container.bind().toDynamicValue( 141 | [](auto& ctx) { 142 | auto foo = ctx.container.template get(); 143 | auto bar = ctx.container.template get(); 144 | 145 | auto fizz = std::make_unique(foo, bar); 146 | 147 | return fizz; 148 | } 149 | ); 150 | 151 | WHEN("multiple dependencies are resolved") { 152 | auto fizz1 = container.get(); 153 | auto fizz2 = container.get(); 154 | 155 | THEN("dependencies are unique") { 156 | REQUIRE(fizz1 != fizz2); 157 | } 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /include/mosure/resolver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef INVERSIFY_BINDING_INSPECTION 9 | #include 10 | #include 11 | #include 12 | #endif 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | namespace mosure::inversify { 21 | 22 | #ifdef INVERSIFY_BINDING_INSPECTION 23 | template 24 | struct Dependency { 25 | using symbol = Symbol; 26 | 27 | operator bool() { return dependent; } 28 | 29 | bool dependent; 30 | }; 31 | 32 | template 33 | class DependencyTuple { 34 | public: 35 | template 36 | auto get() { 37 | return std::get>(values); 38 | } 39 | 40 | template 41 | void set(bool value) { 42 | std::get>(values).dependent = value; 43 | } 44 | 45 | std::tuple< 46 | inversify::Dependency< 47 | SymbolTypes 48 | >... 49 | > values; 50 | }; 51 | #endif 52 | 53 | 54 | template < 55 | typename T, 56 | typename... SymbolTypes 57 | > 58 | class Resolver { 59 | public: 60 | inline virtual T resolve(const inversify::Context&) = 0; 61 | 62 | #ifdef INVERSIFY_BINDING_INSPECTION 63 | inline virtual std::string getResolverLabel() const = 0; 64 | 65 | inline virtual std::string getImplementationLabel() const { 66 | return typeid(T).name(); 67 | }; 68 | 69 | inline virtual inversify::DependencyTuple getDependencies() const { 70 | return DependencyTuple{}; 71 | } 72 | #endif 73 | }; 74 | 75 | template < 76 | typename T, 77 | typename... SymbolTypes 78 | > 79 | using ResolverPtr = std::shared_ptr>; 80 | 81 | 82 | template < 83 | typename T, 84 | typename... SymbolTypes 85 | > 86 | class ConstantResolver 87 | : public Resolver { 88 | public: 89 | explicit ConstantResolver(T value) : value_(value) { } 90 | 91 | inline T resolve(const inversify::Context&) override { 92 | return value_; 93 | } 94 | 95 | #ifdef INVERSIFY_BINDING_INSPECTION 96 | inline virtual std::string getResolverLabel() const override { 97 | return "constant"; 98 | } 99 | #endif 100 | 101 | private: 102 | T value_; 103 | }; 104 | 105 | 106 | template < 107 | typename T, 108 | typename... SymbolTypes 109 | > 110 | class DynamicResolver 111 | : public Resolver { 112 | public: 113 | explicit DynamicResolver(inversify::Factory factory) : factory_(factory) { } 114 | 115 | inline T resolve(const inversify::Context& context) override { 116 | return factory_(context); 117 | } 118 | 119 | #ifdef INVERSIFY_BINDING_INSPECTION 120 | inline virtual std::string getResolverLabel() const override { 121 | return "dynamic"; 122 | } 123 | #endif 124 | 125 | private: 126 | inversify::Factory factory_; 127 | }; 128 | 129 | 130 | template 131 | class AutoResolver; 132 | 133 | template < 134 | typename T, 135 | typename U, 136 | typename... SymbolTypes 137 | > 138 | class AutoResolverBase 139 | : public Resolver { 140 | public: 141 | #ifdef INVERSIFY_BINDING_INSPECTION 142 | inline virtual std::string getResolverLabel() const override { 143 | return "auto"; 144 | } 145 | 146 | inline virtual std::string getImplementationLabel() const override { 147 | return typeid(U).name(); 148 | } 149 | 150 | inline virtual inversify::DependencyTuple getDependencies() const { 151 | auto unroll = [](auto&&... deps) { 152 | DependencyTuple dep_tuple; 153 | 154 | auto enable_dep = [&dep_tuple](auto dep) { 155 | dep_tuple.template set(true); 156 | }; 157 | (enable_dep(deps), ...); 158 | 159 | return dep_tuple; 160 | }; 161 | 162 | return std::apply(unroll, typename inversify::Injectable::value{}); 163 | } 164 | #endif 165 | }; 166 | 167 | 168 | template < 169 | typename T, 170 | typename U, 171 | typename... SymbolTypes 172 | > 173 | class AutoResolver 174 | : public AutoResolverBase { 175 | public: 176 | inline virtual T resolve(const inversify::Context& context) override { 177 | return std::make_from_tuple( 178 | inversify::Injectable::template resolve(context) 179 | ); 180 | } 181 | }; 182 | 183 | 184 | // unique_ptr specialization 185 | template < 186 | typename T, 187 | typename U, 188 | typename... SymbolTypes 189 | > 190 | class AutoResolver, U, SymbolTypes...> 191 | : public AutoResolverBase, U, SymbolTypes...> { 192 | public: 193 | inline std::unique_ptr resolve(const inversify::Context& context) override { 194 | auto expansion = [&context](auto&& ... deps){ 195 | return std::make_unique(deps...); 196 | }; 197 | 198 | return std::apply( 199 | expansion, 200 | std::move( 201 | inversify::Injectable::template resolve(context) 202 | ) 203 | ); 204 | } 205 | }; 206 | 207 | 208 | // shared_ptr specialization 209 | template < 210 | typename T, 211 | typename U, 212 | typename... SymbolTypes 213 | > 214 | class AutoResolver, U, SymbolTypes...> 215 | : public AutoResolverBase, U, SymbolTypes...> { 216 | public: 217 | inline std::shared_ptr resolve(const inversify::Context& context) override { 218 | auto expansion = [&context](auto&& ... deps){ 219 | return std::make_shared(deps...); 220 | }; 221 | 222 | return std::apply( 223 | expansion, 224 | std::move( 225 | inversify::Injectable::template resolve(context) 226 | ) 227 | ); 228 | } 229 | }; 230 | 231 | 232 | template < 233 | typename T, 234 | typename... SymbolTypes 235 | > 236 | class CachedResolver 237 | : public Resolver { 238 | static_assert( 239 | std::is_copy_constructible_v, 240 | "inversify::CachedResolver requires a copy constructor. Are you caching a unique_ptr?" 241 | ); 242 | 243 | public: 244 | explicit CachedResolver(ResolverPtr parent) : parent_(parent) { } 245 | 246 | inline T resolve(const inversify::Context& context) override { 247 | if (!hasCached_) { 248 | hasCached_ = true; 249 | cached_ = parent_->resolve(context); 250 | } 251 | 252 | return cached_; 253 | } 254 | 255 | #ifdef INVERSIFY_BINDING_INSPECTION 256 | inline virtual std::string getResolverLabel() const override { 257 | return std::string("cached - ") + parent_->getResolverLabel(); 258 | } 259 | 260 | inline virtual std::string getImplementationLabel() const override { 261 | return parent_->getImplementationLabel(); 262 | } 263 | #endif 264 | 265 | private: 266 | T cached_; 267 | std::atomic hasCached_ { false }; 268 | ResolverPtr parent_; 269 | }; 270 | 271 | } 272 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # inversify-cpp 2 | 3 | [![Test Ubuntu](https://github.com/mosure/inversify-cpp/workflows/ubuntu/badge.svg)](https://github.com/Mosure/inversify-cpp/actions?query=workflow%3Aubuntu) 4 | [![Test MacOS](https://github.com/mosure/inversify-cpp/workflows/macos/badge.svg)](https://github.com/Mosure/inversify-cpp/actions?query=workflow%3Amacos) 5 | [![Test Windows](https://github.com/mosure/inversify-cpp/workflows/windows/badge.svg)](https://github.com/Mosure/inversify-cpp/actions?query=workflow%3Awindows) 6 | [![GitHub License](https://img.shields.io/github/license/mosure/inversify-cpp)](https://raw.githubusercontent.com/mosure/inversify-cpp/main/LICENSE) 7 | [![GitHub Last Commit](https://img.shields.io/github/last-commit/mosure/inversify-cpp)](https://github.com/mosure/inversify-cpp) 8 | [![GitHub Releases](https://img.shields.io/github/v/release/mosure/inversify-cpp?include_prereleases&sort=semver)](https://github.com/mosure/inversify-cpp/releases) 9 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/ed98bee84ee14c8eb6ad6a0f85b94ca1)](https://app.codacy.com/gh/Mosure/inversify-cpp/dashboard) 10 | [![GitHub Downloads](https://img.shields.io/github/downloads/mosure/inversify-cpp/total)](https://github.com/mosure/inversify-cpp/releases) 11 | [![GitHub Issues](https://img.shields.io/github/issues/mosure/inversify-cpp)](https://github.com/mosure/inversify-cpp/issues) 12 | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/mosure/inversify-cpp.svg)](http://isitmaintained.com/project/mosure/inversify-cpp "Average time to resolve an issue") 13 | 14 | C++17 inversion of control and dependency injection container library. 15 | 16 | ## Dependency Visualization 17 | 18 | See the [inversify-cpp-visualizer](https://github.com/mosure/inversify-cpp-visualizer) project to generate dependency graphs. 19 | 20 | ## Features 21 | * Constant, dynamic, and automatic resolvers 22 | * Singleton, resolution (TODO), and unique scopes 23 | 24 | ## Documentation 25 | 26 | ### Installation 27 | inversify-cpp supports conan. add `inversify-cpp/2.1.0` to your `conanfile.py`. 28 | 29 | view the library on [conan center](https://conan.io/center/recipes/inversify-cpp) 30 | 31 | ### Scope 32 | Scope manages the uniqueness of a dependency. 33 | 34 | Singleton scopes are cached after the first resolution and will be returned on subsequent `container.get...` calls. 35 | 36 | Resolution scopes are cached throughout the duration of a single `container.get...` call. A dependency tree with duplicate dependencies will resolve each to the same cached value. 37 | 38 | By default, the unique scope is used (except for constant values). The unique scope will resolve a unique dependency for each `container.get...` call. 39 | 40 | ## Integration 41 | 42 | ```cpp 43 | 44 | #include 45 | 46 | // for convenience 47 | namespace inversify = mosure::inversify; 48 | 49 | ``` 50 | 51 | ### Examples 52 | 53 | #### Declare Interfaces 54 | 55 | ```cpp 56 | 57 | struct IFizz { 58 | virtual ~IFizz() = default; 59 | 60 | virtual void buzz() = 0; 61 | }; 62 | 63 | using IFizzPtr = std::unique_ptr; 64 | 65 | ``` 66 | 67 | #### Declare Types 68 | 69 | ```cpp 70 | 71 | namespace symbols { 72 | using foo = inversify::Symbol; 73 | using bar = inversify::Symbol; 74 | using fizz = inversify::Symbol; 75 | using fizzFactory = inversify::Symbol>; 76 | 77 | using autoFizzUnique = inversify::Symbol>; 78 | using autoFizzShared = inversify::Symbol>; 79 | } 80 | 81 | ``` 82 | 83 | > Note: symbols which hold the same interface type may do so via structs which inherit inversify::Symbol 84 | 85 | #### Declare Classes and Dependencies 86 | 87 | ```cpp 88 | 89 | struct Fizz : IFizz { 90 | Fizz(int foo, double bar) 91 | : 92 | foo_(foo), 93 | bar_(bar) 94 | { } 95 | 96 | void buzz() override { 97 | std::cout << "Fizz::buzz() - foo: " << foo_ 98 | << " - bar: " << bar_ 99 | << " - counter: " << ++counter_ 100 | << std::endl; 101 | } 102 | 103 | int foo_; 104 | int bar_; 105 | 106 | int counter_ { 0 }; 107 | }; 108 | 109 | template <> 110 | struct inversify::Injectable 111 | : inversify::Inject< 112 | symbols::foo, 113 | symbols::bar 114 | > 115 | { }; 116 | 117 | ``` 118 | 119 | #### Configure Bindings 120 | 121 | ```cpp 122 | 123 | inversify::Container< 124 | symbols::foo, 125 | symbols::bar 126 | > container; 127 | 128 | ``` 129 | 130 | ##### Constant Values 131 | 132 | Constant bindings are always singletons. 133 | 134 | ```cpp 135 | 136 | container.bind().toConstantValue(10); 137 | container.bind().toConstantValue(1.618); 138 | 139 | ``` 140 | 141 | ##### Dynamic Bindings 142 | 143 | Dynamic bindings are resolved when calling `container.get...`. 144 | 145 | By default, dynamic bindings have resolution scope (e.g. each call to `container.get...` calls the factory). 146 | 147 | Singleton scope dynamic bindings cache the first resolution of the binding. 148 | 149 | ```cpp 150 | 151 | container.bind().toDynamicValue( 152 | [](auto& ctx) { 153 | auto foo = ctx.container.template get(); 154 | auto bar = ctx.container.template get(); 155 | 156 | auto fizz = std::make_shared(foo, bar); 157 | 158 | return fizz; 159 | } 160 | ).inSingletonScope(); 161 | 162 | ``` 163 | 164 | ##### Factory Bindings 165 | 166 | Dynamic bindings can be used to resolve factory functions. 167 | 168 | ```cpp 169 | 170 | container.bind().toDynamicValue( 171 | [](auto& ctx) { 172 | return [&]() { 173 | auto foo = ctx.container.template get(); 174 | auto bar = ctx.container.template get(); 175 | 176 | auto fizz = std::make_shared(foo, bar); 177 | 178 | return fizz; 179 | }; 180 | } 181 | ); 182 | 183 | ``` 184 | 185 | ##### Automatic Bindings 186 | 187 | Dependencies can be resolved automatically using an automatic binding. Injectables are a prerequisite to the type being constructed. 188 | 189 | Automatic bindings can generate instances, unique_ptr's, and shared_ptr's of a class. The returned type is determined by the binding interface. 190 | 191 | ```cpp 192 | 193 | container.bind().to(); 194 | container.bind().to().inSingletonScope(); 195 | 196 | ``` 197 | 198 | #### Resolving Dependencies 199 | 200 | ```cpp 201 | 202 | auto bar = container.get(); 203 | 204 | auto fizz = container.get(); 205 | fizz->buzz(); 206 | 207 | auto fizzFactory = container.get(); 208 | auto anotherFizz = fizzFactory(); 209 | anotherFizz->buzz(); 210 | 211 | 212 | auto autoFizzUnique = container.get(); 213 | autoFizzUnique->buzz(); 214 | 215 | auto autoFizzShared = container.get(); 216 | autoFizzShared->buzz(); 217 | 218 | ``` 219 | 220 | ### Running Tests 221 | 222 | Use the following to run tests: 223 | 224 | `bazel run test --enable_platform_specific_config` 225 | 226 | > Note: run the example app in a similar way: `bazel run example/simple --enable_platform_specific_config` 227 | 228 | ## TODOS 229 | * More compile-time checks 230 | * Resolution scope 231 | 232 | ### Profiling 233 | 234 | Run the following to generate a callgrind file: 235 | 236 | * `bazel run example/profiling --enable_platform_specific_config --compilation_mode=dbg -s` 237 | * `valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --collect-jumps=yes ./bazel-bin/example/profiling/profiling` 238 | 239 | ## Containerless Version 240 | 241 | See the [`containerless` branch](https://github.com/mosure/inversify-cpp/tree/containerless) for a static binding (containerless) version of the library. 242 | 243 | ## Generating `single_include` Variant 244 | 245 | Run `python ./third_party/amalgamate/amalgamate.py -c ./third_party/amalgamate/config.json -s ./` from the root of the repository. 246 | 247 | ## Thanks 248 | 249 | * [InversifyJS](https://inversify.io/) - API design and inspiration for the project. 250 | -------------------------------------------------------------------------------- /third_party/amalgamate/amalgamate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # amalgamate.py - Amalgamate C source and header files. 5 | # Copyright (c) 2012, Erik Edlund 6 | # 7 | # Redistribution and use in source and binary forms, with or without modification, 8 | # are permitted provided that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright notice, 11 | # this list of conditions and the following disclaimer. 12 | # 13 | # * Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation 15 | # and/or other materials provided with the distribution. 16 | # 17 | # * Neither the name of Erik Edlund, nor the names of its contributors may 18 | # be used to endorse or promote products derived from this software without 19 | # specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 25 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from __future__ import division 33 | from __future__ import print_function 34 | from __future__ import unicode_literals 35 | 36 | import argparse 37 | import datetime 38 | import json 39 | import os 40 | import re 41 | 42 | 43 | class Amalgamation(object): 44 | 45 | # Prepends self.source_path to file_path if needed. 46 | def actual_path(self, file_path): 47 | if not os.path.isabs(file_path): 48 | file_path = os.path.join(self.source_path, file_path) 49 | return file_path 50 | 51 | # Search included file_path in self.include_paths and 52 | # in source_dir if specified. 53 | def find_included_file(self, file_path, source_dir): 54 | search_dirs = self.include_paths[:] 55 | if source_dir: 56 | search_dirs.insert(0, source_dir) 57 | 58 | for search_dir in search_dirs: 59 | search_path = os.path.join(search_dir, file_path) 60 | if os.path.isfile(self.actual_path(search_path)): 61 | return search_path 62 | return None 63 | 64 | def __init__(self, args): 65 | with open(args.config, 'r') as f: 66 | config = json.loads(f.read()) 67 | for key in config: 68 | setattr(self, key, config[key]) 69 | 70 | self.verbose = args.verbose == "yes" 71 | self.prologue = args.prologue 72 | self.source_path = args.source_path 73 | self.included_files = [] 74 | 75 | # Generate the amalgamation and write it to the target file. 76 | def generate(self): 77 | amalgamation = "" 78 | 79 | if self.prologue: 80 | with open(self.prologue, 'r') as f: 81 | amalgamation += datetime.datetime.now().strftime(f.read()) 82 | 83 | if self.verbose: 84 | print("Config:") 85 | print(" target = {0}".format(self.target)) 86 | print(" working_dir = {0}".format(os.getcwd())) 87 | print(" include_paths = {0}".format(self.include_paths)) 88 | print("Creating amalgamation:") 89 | for file_path in self.sources: 90 | # Do not check the include paths while processing the source 91 | # list, all given source paths must be correct. 92 | # actual_path = self.actual_path(file_path) 93 | print(" - processing \"{0}\"".format(file_path)) 94 | t = TranslationUnit(file_path, self, True) 95 | amalgamation += t.content 96 | 97 | with open(self.target, 'w') as f: 98 | f.write(amalgamation) 99 | 100 | print("...done!\n") 101 | if self.verbose: 102 | print("Files processed: {0}".format(self.sources)) 103 | print("Files included: {0}".format(self.included_files)) 104 | print("") 105 | 106 | 107 | def _is_within(match, matches): 108 | for m in matches: 109 | if match.start() > m.start() and \ 110 | match.end() < m.end(): 111 | return True 112 | return False 113 | 114 | 115 | class TranslationUnit(object): 116 | # // C++ comment. 117 | cpp_comment_pattern = re.compile(r"//.*?\n") 118 | 119 | # /* C comment. */ 120 | c_comment_pattern = re.compile(r"/\*.*?\*/", re.S) 121 | 122 | # "complex \"stri\\\ng\" value". 123 | string_pattern = re.compile("[^']" r'".*?(?<=[^\\])"', re.S) 124 | 125 | # Handle simple include directives. Support for advanced 126 | # directives where macros and defines needs to expanded is 127 | # not a concern right now. 128 | include_pattern = re.compile( 129 | r'#\s*include\s+(<|")(?P.*?)("|>)', re.S) 130 | 131 | # #pragma once 132 | pragma_once_pattern = re.compile(r'#\s*pragma\s+once', re.S) 133 | 134 | # Search for pattern in self.content, add the match to 135 | # contexts if found and update the index accordingly. 136 | def _search_content(self, index, pattern, contexts): 137 | match = pattern.search(self.content, index) 138 | if match: 139 | contexts.append(match) 140 | return match.end() 141 | return index + 2 142 | 143 | # Return all the skippable contexts, i.e., comments and strings 144 | def _find_skippable_contexts(self): 145 | # Find contexts in the content in which a found include 146 | # directive should not be processed. 147 | skippable_contexts = [] 148 | 149 | # Walk through the content char by char, and try to grab 150 | # skippable contexts using regular expressions when found. 151 | i = 1 152 | content_len = len(self.content) 153 | while i < content_len: 154 | j = i - 1 155 | current = self.content[i] 156 | previous = self.content[j] 157 | 158 | if current == '"': 159 | # String value. 160 | i = self._search_content(j, self.string_pattern, 161 | skippable_contexts) 162 | elif current == '*' and previous == '/': 163 | # C style comment. 164 | i = self._search_content(j, self.c_comment_pattern, 165 | skippable_contexts) 166 | elif current == '/' and previous == '/': 167 | # C++ style comment. 168 | i = self._search_content(j, self.cpp_comment_pattern, 169 | skippable_contexts) 170 | else: 171 | # Skip to the next char. 172 | i += 1 173 | 174 | return skippable_contexts 175 | 176 | # Returns True if the match is within list of other matches 177 | 178 | # Removes pragma once from content 179 | def _process_pragma_once(self): 180 | content_len = len(self.content) 181 | if content_len < len("#include "): 182 | return 0 183 | 184 | # Find contexts in the content in which a found include 185 | # directive should not be processed. 186 | skippable_contexts = self._find_skippable_contexts() 187 | 188 | pragmas = [] 189 | pragma_once_match = self.pragma_once_pattern.search(self.content) 190 | while pragma_once_match: 191 | if not _is_within(pragma_once_match, skippable_contexts): 192 | pragmas.append(pragma_once_match) 193 | 194 | pragma_once_match = self.pragma_once_pattern.search(self.content, 195 | pragma_once_match.end()) 196 | 197 | # Handle all collected pragma once directives. 198 | prev_end = 0 199 | tmp_content = '' 200 | for pragma_match in pragmas: 201 | tmp_content += self.content[prev_end:pragma_match.start()] 202 | prev_end = pragma_match.end() 203 | tmp_content += self.content[prev_end:] 204 | self.content = tmp_content 205 | 206 | # Include all trivial #include directives into self.content. 207 | def _process_includes(self): 208 | content_len = len(self.content) 209 | if content_len < len("#include "): 210 | return 0 211 | 212 | # Find contexts in the content in which a found include 213 | # directive should not be processed. 214 | skippable_contexts = self._find_skippable_contexts() 215 | 216 | # Search for include directives in the content, collect those 217 | # which should be included into the content. 218 | includes = [] 219 | include_match = self.include_pattern.search(self.content) 220 | while include_match: 221 | if not _is_within(include_match, skippable_contexts): 222 | include_path = include_match.group("path") 223 | search_same_dir = include_match.group(1) == '"' 224 | found_included_path = self.amalgamation.find_included_file( 225 | include_path, self.file_dir if search_same_dir else None) 226 | if found_included_path: 227 | includes.append((include_match, found_included_path)) 228 | 229 | include_match = self.include_pattern.search(self.content, 230 | include_match.end()) 231 | 232 | # Handle all collected include directives. 233 | prev_end = 0 234 | tmp_content = '' 235 | for include in includes: 236 | include_match, found_included_path = include 237 | tmp_content += self.content[prev_end:include_match.start()] 238 | tmp_content += "// {0}\n".format(include_match.group(0)) 239 | if found_included_path not in self.amalgamation.included_files: 240 | t = TranslationUnit(found_included_path, self.amalgamation, False) 241 | tmp_content += t.content 242 | prev_end = include_match.end() 243 | tmp_content += self.content[prev_end:] 244 | self.content = tmp_content 245 | 246 | return len(includes) 247 | 248 | # Make all content processing 249 | def _process(self): 250 | if not self.is_root: 251 | self._process_pragma_once() 252 | self._process_includes() 253 | 254 | def __init__(self, file_path, amalgamation, is_root): 255 | self.file_path = file_path 256 | self.file_dir = os.path.dirname(file_path) 257 | self.amalgamation = amalgamation 258 | self.is_root = is_root 259 | 260 | self.amalgamation.included_files.append(self.file_path) 261 | 262 | actual_path = self.amalgamation.actual_path(file_path) 263 | if not os.path.isfile(actual_path): 264 | raise IOError("File not found: \"{0}\"".format(file_path)) 265 | with open(actual_path, 'r') as f: 266 | self.content = f.read() 267 | self._process() 268 | 269 | 270 | def main(): 271 | description = "Amalgamate C source and header files." 272 | usage = " ".join([ 273 | "amalgamate.py", 274 | "[-v]", 275 | "-c path/to/config.json", 276 | "-s path/to/source/dir", 277 | "[-p path/to/prologue.(c|h)]" 278 | ]) 279 | argsparser = argparse.ArgumentParser( 280 | description=description, usage=usage) 281 | 282 | argsparser.add_argument("-v", "--verbose", dest="verbose", 283 | choices=["yes", "no"], metavar="", help="be verbose") 284 | 285 | argsparser.add_argument("-c", "--config", dest="config", 286 | required=True, metavar="", help="path to a JSON config file") 287 | 288 | argsparser.add_argument("-s", "--source", dest="source_path", 289 | required=True, metavar="", help="source code path") 290 | 291 | argsparser.add_argument("-p", "--prologue", dest="prologue", 292 | required=False, metavar="", help="path to a C prologue file") 293 | 294 | amalgamation = Amalgamation(argsparser.parse_args()) 295 | amalgamation.generate() 296 | 297 | 298 | if __name__ == "__main__": 299 | main() 300 | -------------------------------------------------------------------------------- /single_include/mosure/inversify.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | IoC and Dependency Injection for Modern C++ 3 | Version 1.0.0 4 | https://github.com/mosure/inversifycpp 5 | 6 | MIT License 7 | 8 | Copyright (c) 2020 Mitchell Mosure 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | #pragma once 30 | 31 | #include // function 32 | #include // make_shared, make_unique, shared_ptr, unique_ptr 33 | #include // runtime_error 34 | #include // string 35 | #include // make_from_tuple, tuple 36 | #include // apply, conjunction_v, disjunction_v, false_type, is_copy_constructable, is_same, true_type 37 | 38 | // #include 39 | 40 | 41 | #include 42 | #include 43 | 44 | // #include 45 | 46 | 47 | // #include 48 | 49 | 50 | #include 51 | 52 | // #include 53 | 54 | 55 | #include 56 | #include 57 | 58 | 59 | namespace mosure::inversify { 60 | 61 | template 62 | struct Symbol { 63 | static_assert( 64 | !std::is_abstract(), 65 | "inversify::Container cannot bind/get abstract class value (use a smart pointer instead)." 66 | ); 67 | 68 | using value = Interface; 69 | }; 70 | 71 | } 72 | 73 | 74 | 75 | namespace mosure::inversify { 76 | 77 | template < 78 | typename T, 79 | typename... SymbolTypes 80 | > 81 | class BindingTo; 82 | 83 | template < 84 | template class Implementation, 85 | typename... SymbolTypes 86 | > 87 | class IContainer { 88 | public: 89 | template 90 | inversify::BindingTo& bind() { 91 | auto crtpImplementation = static_cast *>(this); 92 | 93 | return crtpImplementation->template bind(); 94 | } 95 | 96 | template 97 | typename T::value get() { 98 | auto crtpImplementation = static_cast *>(this); 99 | 100 | return crtpImplementation->template get(); 101 | } 102 | }; 103 | 104 | } 105 | 106 | 107 | 108 | namespace mosure::inversify { 109 | 110 | template 111 | class Container; 112 | 113 | template 114 | struct Context { 115 | inversify::IContainer& container; 116 | }; 117 | 118 | } 119 | 120 | // #include 121 | 122 | 123 | #include 124 | 125 | // #include 126 | 127 | 128 | 129 | namespace mosure::inversify { 130 | 131 | template 132 | using Factory = std::function&)>; 133 | 134 | } 135 | 136 | // #include 137 | 138 | 139 | #include 140 | #include 141 | #include 142 | #include 143 | 144 | #ifdef INVERSIFY_BINDING_INSPECTION 145 | #include 146 | #include 147 | #include 148 | #endif 149 | 150 | // #include 151 | 152 | // #include 153 | 154 | // #include 155 | 156 | 157 | #include 158 | #include 159 | 160 | // #include 161 | 162 | // #include 163 | 164 | 165 | #include 166 | #include 167 | #include 168 | 169 | // #include 170 | 171 | 172 | 173 | namespace mosure::inversify::meta { 174 | 175 | template< 176 | typename T, 177 | typename... Types 178 | > 179 | inline constexpr bool contains_v = std::disjunction_v< 180 | std::is_same... 181 | >; 182 | 183 | 184 | template 185 | struct is_empty : std::false_type { }; 186 | 187 | template<> 188 | struct is_empty<> : std::true_type { }; 189 | 190 | template 191 | inline constexpr bool is_empty_v = is_empty::value; 192 | 193 | 194 | template < 195 | typename T, 196 | template typename Template 197 | > 198 | struct is_specialization : std::false_type { }; 199 | 200 | template < 201 | template typename Template, 202 | typename... Args 203 | > 204 | struct is_specialization, Template> : std::true_type { }; 205 | 206 | 207 | template 208 | inline constexpr bool valid_symbol_types_v = std::conjunction_v< 209 | meta::is_specialization... 210 | > || std::conjunction_v< 211 | std::is_base_of, Types>... 212 | >; 213 | 214 | } 215 | 216 | 217 | 218 | namespace mosure::inversify { 219 | 220 | template 221 | struct Inject { 222 | static_assert( 223 | meta::valid_symbol_types_v, 224 | "inversify::Injectable dependencies must be of type inversify::Symbol" 225 | ); 226 | 227 | #ifdef INVERSIFY_BINDING_INSPECTION 228 | using value = std::tuple; 229 | #endif 230 | 231 | template 232 | inline static auto resolve(const inversify::Context& context) { 233 | return std::make_tuple(context.container.template get()...); 234 | } 235 | }; 236 | 237 | template > 238 | struct Injectable: Inject { }; 239 | 240 | } 241 | 242 | // #include 243 | 244 | 245 | #include 246 | #include 247 | 248 | 249 | namespace mosure::inversify::exceptions { 250 | 251 | struct ResolutionException : public std::runtime_error { 252 | explicit ResolutionException(const std::string& msg) : std::runtime_error(msg) { } 253 | }; 254 | 255 | } 256 | 257 | 258 | 259 | namespace mosure::inversify { 260 | 261 | #ifdef INVERSIFY_BINDING_INSPECTION 262 | template 263 | struct Dependency { 264 | using symbol = Symbol; 265 | 266 | operator bool() { return dependent; } 267 | 268 | bool dependent; 269 | }; 270 | 271 | template 272 | class DependencyTuple { 273 | public: 274 | template 275 | auto get() { 276 | return std::get>(values); 277 | } 278 | 279 | template 280 | void set(bool value) { 281 | std::get>(values).dependent = value; 282 | } 283 | 284 | std::tuple< 285 | inversify::Dependency< 286 | SymbolTypes 287 | >... 288 | > values; 289 | }; 290 | #endif 291 | 292 | 293 | template < 294 | typename T, 295 | typename... SymbolTypes 296 | > 297 | class Resolver { 298 | public: 299 | inline virtual T resolve(const inversify::Context&) = 0; 300 | 301 | #ifdef INVERSIFY_BINDING_INSPECTION 302 | inline virtual std::string getResolverLabel() const = 0; 303 | 304 | inline virtual std::string getImplementationLabel() const { 305 | return typeid(T).name(); 306 | }; 307 | 308 | inline virtual inversify::DependencyTuple getDependencies() const { 309 | return DependencyTuple{}; 310 | } 311 | #endif 312 | }; 313 | 314 | template < 315 | typename T, 316 | typename... SymbolTypes 317 | > 318 | using ResolverPtr = std::shared_ptr>; 319 | 320 | 321 | template < 322 | typename T, 323 | typename... SymbolTypes 324 | > 325 | class ConstantResolver 326 | : public Resolver { 327 | public: 328 | explicit ConstantResolver(T value) : value_(value) { } 329 | 330 | inline T resolve(const inversify::Context&) override { 331 | return value_; 332 | } 333 | 334 | #ifdef INVERSIFY_BINDING_INSPECTION 335 | inline virtual std::string getResolverLabel() const override { 336 | return "constant"; 337 | } 338 | #endif 339 | 340 | private: 341 | T value_; 342 | }; 343 | 344 | 345 | template < 346 | typename T, 347 | typename... SymbolTypes 348 | > 349 | class DynamicResolver 350 | : public Resolver { 351 | public: 352 | explicit DynamicResolver(inversify::Factory factory) : factory_(factory) { } 353 | 354 | inline T resolve(const inversify::Context& context) override { 355 | return factory_(context); 356 | } 357 | 358 | #ifdef INVERSIFY_BINDING_INSPECTION 359 | inline virtual std::string getResolverLabel() const override { 360 | return "dynamic"; 361 | } 362 | #endif 363 | 364 | private: 365 | inversify::Factory factory_; 366 | }; 367 | 368 | 369 | template 370 | class AutoResolver; 371 | 372 | template < 373 | typename T, 374 | typename U, 375 | typename... SymbolTypes 376 | > 377 | class AutoResolverBase 378 | : public Resolver { 379 | public: 380 | #ifdef INVERSIFY_BINDING_INSPECTION 381 | inline virtual std::string getResolverLabel() const override { 382 | return "auto"; 383 | } 384 | 385 | inline virtual std::string getImplementationLabel() const override { 386 | return typeid(U).name(); 387 | } 388 | 389 | inline virtual inversify::DependencyTuple getDependencies() const { 390 | auto unroll = [](auto&&... deps) { 391 | DependencyTuple dep_tuple; 392 | 393 | auto enable_dep = [&dep_tuple](auto dep) { 394 | dep_tuple.template set(true); 395 | }; 396 | (enable_dep(deps), ...); 397 | 398 | return dep_tuple; 399 | }; 400 | 401 | return std::apply(unroll, typename inversify::Injectable::value{}); 402 | } 403 | #endif 404 | }; 405 | 406 | 407 | template < 408 | typename T, 409 | typename U, 410 | typename... SymbolTypes 411 | > 412 | class AutoResolver 413 | : public AutoResolverBase { 414 | public: 415 | inline virtual T resolve(const inversify::Context& context) override { 416 | return std::make_from_tuple( 417 | inversify::Injectable::template resolve(context) 418 | ); 419 | } 420 | }; 421 | 422 | 423 | // unique_ptr specialization 424 | template < 425 | typename T, 426 | typename U, 427 | typename... SymbolTypes 428 | > 429 | class AutoResolver, U, SymbolTypes...> 430 | : public AutoResolverBase, U, SymbolTypes...> { 431 | public: 432 | inline std::unique_ptr resolve(const inversify::Context& context) override { 433 | auto expansion = [&context](auto&& ... deps){ 434 | return std::make_unique(deps...); 435 | }; 436 | 437 | return std::apply( 438 | expansion, 439 | std::move( 440 | inversify::Injectable::template resolve(context) 441 | ) 442 | ); 443 | } 444 | }; 445 | 446 | 447 | // shared_ptr specialization 448 | template < 449 | typename T, 450 | typename U, 451 | typename... SymbolTypes 452 | > 453 | class AutoResolver, U, SymbolTypes...> 454 | : public AutoResolverBase, U, SymbolTypes...> { 455 | public: 456 | inline std::shared_ptr resolve(const inversify::Context& context) override { 457 | auto expansion = [&context](auto&& ... deps){ 458 | return std::make_shared(deps...); 459 | }; 460 | 461 | return std::apply( 462 | expansion, 463 | std::move( 464 | inversify::Injectable::template resolve(context) 465 | ) 466 | ); 467 | } 468 | }; 469 | 470 | 471 | template < 472 | typename T, 473 | typename... SymbolTypes 474 | > 475 | class CachedResolver 476 | : public Resolver { 477 | static_assert( 478 | std::is_copy_constructible_v, 479 | "inversify::CachedResolver requires a copy constructor. Are you caching a unique_ptr?" 480 | ); 481 | 482 | public: 483 | explicit CachedResolver(ResolverPtr parent) : parent_(parent) { } 484 | 485 | inline T resolve(const inversify::Context& context) override { 486 | if (!hasCached_) { 487 | hasCached_ = true; 488 | cached_ = parent_->resolve(context); 489 | } 490 | 491 | return cached_; 492 | } 493 | 494 | #ifdef INVERSIFY_BINDING_INSPECTION 495 | inline virtual std::string getResolverLabel() const override { 496 | return std::string("cached - ") + parent_->getResolverLabel(); 497 | } 498 | 499 | inline virtual std::string getImplementationLabel() const override { 500 | return parent_->getImplementationLabel(); 501 | } 502 | #endif 503 | 504 | private: 505 | T cached_; 506 | std::atomic hasCached_ { false }; 507 | ResolverPtr parent_; 508 | }; 509 | 510 | } 511 | 512 | // #include 513 | 514 | 515 | 516 | namespace mosure::inversify { 517 | 518 | template 519 | class BindingScope { 520 | public: 521 | void inSingletonScope() { 522 | resolver_ = std::make_shared>(resolver_); 523 | } 524 | 525 | #ifdef INVERSIFY_BINDING_INSPECTION 526 | auto getResolver() const { 527 | return resolver_; 528 | } 529 | #endif 530 | 531 | protected: 532 | inversify::ResolverPtr resolver_; 533 | }; 534 | 535 | template 536 | class BindingTo 537 | : public BindingScope 538 | { 539 | public: 540 | void toConstantValue(T&& value) { 541 | this->resolver_ = std::make_shared>(value); 542 | } 543 | 544 | BindingScope& toDynamicValue(inversify::Factory&& factory) { 545 | this->resolver_ = std::make_shared>(factory); 546 | 547 | return *this; 548 | } 549 | 550 | template 551 | BindingScope& to() { 552 | this->resolver_ = std::make_shared>(); 553 | 554 | return *this; 555 | } 556 | }; 557 | 558 | template 559 | class Binding 560 | : public BindingTo 561 | { 562 | public: 563 | inline typename T::value resolve(const inversify::Context& context) { 564 | if (!this->resolver_) { 565 | throw inversify::exceptions::ResolutionException("inversify::Resolver not found. Malformed binding."); 566 | } 567 | 568 | return this->resolver_->resolve(context); 569 | } 570 | }; 571 | 572 | } 573 | 574 | // #include 575 | 576 | 577 | #include 578 | 579 | // #include 580 | 581 | // #include 582 | 583 | // #include 584 | 585 | // #include 586 | 587 | 588 | 589 | namespace mosure::inversify { 590 | 591 | template 592 | class Container 593 | : public inversify::IContainer 594 | { 595 | public: 596 | static_assert( 597 | meta::valid_symbol_types_v, 598 | "inversify::Container symbols must be of type inversify::Symbol" 599 | ); 600 | 601 | static_assert( 602 | !meta::is_empty_v, 603 | "inversify::Container must register at least one symbol" 604 | ); 605 | 606 | using BindingMap = std::tuple< 607 | inversify::Binding< 608 | SymbolTypes, 609 | SymbolTypes... 610 | >... 611 | >; 612 | 613 | template 614 | inline inversify::BindingTo& bind() { 615 | static_assert( 616 | meta::contains_v, 617 | "inversify::Container symbol not registered" 618 | ); 619 | 620 | return std::get< 621 | inversify::Binding 622 | >(bindings_); 623 | } 624 | 625 | template 626 | inline typename T::value get() { 627 | static_assert( 628 | meta::contains_v, 629 | "inversify::Container symbol not registered" 630 | ); 631 | 632 | return std::get< 633 | inversify::Binding 634 | >(bindings_).resolve(context_); 635 | } 636 | 637 | private: 638 | BindingMap bindings_ {}; 639 | inversify::Context context_ { *this }; 640 | }; 641 | 642 | } 643 | 644 | // #include 645 | 646 | // #include 647 | 648 | // #include 649 | 650 | // #include 651 | 652 | // #include 653 | 654 | // #include 655 | 656 | 657 | // #include 658 | 659 | 660 | 661 | /** 662 | @brief namespace for Mitchell Mosure - InversifyCpp 663 | @see https://github.com/mosure 664 | @since version 1.0.0 665 | */ 666 | namespace mosure::inversify { } 667 | --------------------------------------------------------------------------------