├── .lgtm.yml ├── examples ├── example1 │ ├── CMakeLists.txt │ └── example1.cpp ├── example2 │ ├── CMakeLists.txt │ └── example2.cpp ├── example3 │ ├── CMakeLists.txt │ └── example3.cpp ├── example4 │ ├── CMakeLists.txt │ └── example4.cpp ├── example5 │ ├── CMakeLists.txt │ └── example5.cpp ├── example6 │ ├── CMakeLists.txt │ └── example6.cpp ├── example7 │ ├── CMakeLists.txt │ └── example7.cpp ├── example8 │ ├── CMakeLists.txt │ └── example8.cpp ├── example9 │ ├── CMakeLists.txt │ └── example9.cpp ├── cars │ ├── CMakeLists.txt │ └── cars-example.cpp ├── readme_example │ ├── CMakeLists.txt │ └── readme_example.cpp ├── wandbox_example │ ├── CMakeLists.txt │ └── wandbox_example.cpp ├── abstract_factory │ ├── CMakeLists.txt │ └── abstract_factory.cpp └── CMakeLists.txt ├── vcpkg.json ├── .gitignore ├── .gitmodules ├── vcpkg-configuration.json ├── cmake └── config │ ├── kangaruConfig.cmake.in │ ├── config.hpp.in │ └── kangaruBuildConfig.cmake.in ├── test ├── src │ ├── noexcept_invoke.cpp │ ├── error.cpp │ ├── type_id_cxx14.cpp │ ├── noexcept.cpp │ ├── basic.cpp │ ├── single.cpp │ ├── generic_lambdas.cpp │ ├── dependency.cpp │ └── service_range.cpp └── CMakeLists.txt ├── include └── kangaru │ ├── kangaru.hpp │ ├── detail │ ├── void_t.hpp │ ├── undef.hpp │ ├── container_service.hpp │ ├── utils.hpp │ ├── override_range_service.hpp │ ├── operator_service_helper.hpp │ ├── lazy_base.hpp │ ├── hash.hpp │ ├── override_storage_service.hpp │ ├── exception.hpp │ ├── override_traits.hpp │ ├── seq.hpp │ ├── nostd_invoke.hpp │ ├── string_view.hpp │ ├── service_range.hpp │ ├── validity_check.hpp │ ├── single.hpp │ ├── define.hpp │ ├── service_storage.hpp │ ├── service_check.hpp │ ├── detection.hpp │ └── meta_list.hpp │ ├── predicate.hpp │ ├── debug.hpp │ ├── autocall.hpp │ ├── experimental │ └── autowiring.hpp │ ├── generic.hpp │ ├── compatibility.hpp │ ├── operator_service.hpp │ └── type_id.hpp ├── SECURITY.md ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── security-or-undefined-behavior-issue.md └── workflows │ └── codeql.yml ├── .appveyor.yml ├── benchmark └── CMakeLists.txt ├── LICENSE ├── CONTRIBUTING.md ├── doc ├── section11_debug.md ├── section07_autocall.md ├── section10_mapping.md ├── section04_container.md ├── intro.md ├── section02_invoke.md ├── section05_supplied.md └── section13_structure.md ├── CMakePresets.json ├── CMakeLists.txt └── CODE_OF_CONDUCT.md /.lgtm.yml: -------------------------------------------------------------------------------- 1 | extraction: 2 | cpp: 3 | configure: 4 | command: 5 | - cmake -DKANGARU_TEST=On . 6 | -------------------------------------------------------------------------------- /examples/example1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example1 example1.cpp) 2 | 3 | target_link_libraries(example1 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example2 example2.cpp) 2 | 3 | target_link_libraries(example2 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example3 example3.cpp) 2 | 3 | target_link_libraries(example3 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example4 example4.cpp) 2 | 3 | target_link_libraries(example4 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example5 example5.cpp) 2 | 3 | target_link_libraries(example5 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example6/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example6 example6.cpp) 2 | 3 | target_link_libraries(example6 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example7/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example7 example7.cpp) 2 | 3 | target_link_libraries(example7 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example8/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example8 example8.cpp) 2 | 3 | target_link_libraries(example8 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/example9/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(example9 example9.cpp) 2 | 3 | target_link_libraries(example9 PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/cars/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cars-example cars-example.cpp) 2 | 3 | target_link_libraries(cars-example PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kangaru", 3 | "version-string": "4.3.1", 4 | "dependencies": [ 5 | "catch2", 6 | "benchmark" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/readme_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(readme_example readme_example.cpp) 2 | 3 | target_link_libraries(readme_example PUBLIC kangaru) 4 | -------------------------------------------------------------------------------- /examples/wandbox_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(wandbox_example wandbox_example.cpp) 2 | 3 | target_link_libraries(wandbox_example PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /examples/abstract_factory/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(abstract_factory abstract_factory.cpp) 2 | 3 | target_link_libraries(abstract_factory PUBLIC kangaru-example) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | build 3 | build-* 4 | *.kdev4 5 | !.gitignore 6 | !.gitmodules 7 | !.travis.yml 8 | !.appveyor.yml 9 | !.github 10 | CMakeSettings.json 11 | compile_commands.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/third_party/Catch2"] 2 | path = test/third_party/Catch2 3 | url = https://github.com/catchorg/Catch2.git 4 | [submodule "benchmark/third_party/benchmark"] 5 | path = benchmark/third_party/benchmark 6 | url = https://github.com/google/benchmark.git 7 | -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://github.com/microsoft/vcpkg-tool/blob/de85ec90b8b8c12878dae776811392415f746314/docs/vcpkg-configuration.schema.json", 3 | "default-registry": { 4 | "kind": "builtin", 5 | "baseline": "c1b768b0d6bc9b05d6272d604496d33a3faedd90" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /cmake/config/kangaruConfig.cmake.in: -------------------------------------------------------------------------------- 1 | set(KANGARU_VERSION "@KANGARU_VERSION@") 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/kangaruTargets.cmake") 4 | 5 | # Namespaced aliases 6 | add_library(kangaru::kangaru INTERFACE IMPORTED) 7 | set_property(TARGET kangaru::kangaru PROPERTY INTERFACE_LINK_LIBRARIES kangaru) 8 | -------------------------------------------------------------------------------- /cmake/config/config.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_GENERATED_CONFIG_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_GENERATED_CONFIG_HPP 3 | 4 | #cmakedefine KGR_KANGARU_REVERSE_DESTRUCTION 5 | #cmakedefine KGR_KANGARU_NOEXCEPTION 6 | #cmakedefine KGR_KANGARU_HASH_TYPE_ID 7 | 8 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_GENERATED_EXCEPTION_HPP 9 | -------------------------------------------------------------------------------- /cmake/config/kangaruBuildConfig.cmake.in: -------------------------------------------------------------------------------- 1 | set(KANGARU_VERSION "@KANGARU_VERSION@") 2 | set(KANGARU_INCLUDE_DIRS "@CMAKE_CURRENT_SOURCE_DIR@") 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/kangaruTargets.cmake") 5 | 6 | # Namespaced aliases 7 | add_library(kangaru::kangaru INTERFACE IMPORTED) 8 | set_property(TARGET kangaru::kangaru PROPERTY INTERFACE_LINK_LIBRARIES kangaru) 9 | -------------------------------------------------------------------------------- /test/src/noexcept_invoke.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace { 5 | bool called = false; 6 | 7 | void function() noexcept { 8 | called = true; 9 | } 10 | 11 | TEST_CASE("Container can invoke noexcept functions", "[noexcept_invoke]") { 12 | kgr::container{}.invoke(function); 13 | REQUIRE(called); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/src/error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TEST_ERROR(...) int toplevel = (void(__VA_ARGS__), 0) 4 | 5 | #if defined(kgr_error_test_not_service_no_forward) 6 | 7 | struct test {}; 8 | TEST_ERROR(kgr::debug::service()); 9 | 10 | #elif defined(kgr_error_test_not_service_has_forward) 11 | 12 | struct test { 13 | virtual ~test() = default; 14 | auto forward() -> int; 15 | }; 16 | TEST_ERROR(kgr::debug::service()); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/kangaru/kangaru.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_KANGARU_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_KANGARU_HPP 3 | 4 | #include "autocall.hpp" 5 | #include "autowire.hpp" 6 | #include "container.hpp" 7 | #include "generic.hpp" 8 | #include "operator.hpp" 9 | #include "operator_service.hpp" 10 | #include "optional.hpp" 11 | #include "predicate.hpp" 12 | #include "debug.hpp" 13 | #include "service.hpp" 14 | #include "type_id.hpp" 15 | 16 | #endif // KGR_KANGARU_INCLUDE_KANGARU_KANGARU_HPP 17 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Those are the current major versions that are still supported for security vulnerabilities 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | 4.x.y | :white_check_mark: | 10 | | 3.x.y | :x: | 11 | | 2.x.y | :x: | 12 | | 1.x.y | :x: | 13 | 14 | ## Reporting a Vulnerability 15 | 16 | To report a security vulnerability, open an issue with the security template. 17 | 18 | -------------------------------------------------------------------------------- /include/kangaru/detail/void_t.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VOID_T_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VOID_T_HPP 3 | 4 | namespace kgr { 5 | namespace detail { 6 | 7 | /* 8 | * This is the void_t implementation. 9 | * We chose this particular implementation because C++11 don't enforce sfinae otherwise. 10 | */ 11 | template 12 | struct voider { using type = void; }; 13 | 14 | template using void_t = typename voider::type; 15 | 16 | } // namespace detail 17 | } // namespace kgr 18 | 19 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VOID_T_HPP 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps and code to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Desktop (please complete the following information):** 20 | - OS: [e.g. Windows, Linux, MacOS] 21 | - Compiler [e.g MSVC, GCC, Clang] 22 | - Version [e.g. 19.1, 8.3, 7.0] 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /include/kangaru/detail/undef.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UNDEF 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UNDEF 3 | 4 | #undef KGR_KANGARU_CXX17_NOEXCEPT 5 | #undef KGR_KANGARU_CXX17_NOEXCEPT_FPTR 6 | #undef KGR_KANGARU_USE_ALTERNATE_MAP_PROBE 7 | #undef KGR_KANGARU_MSVC_DISABLE_VALIDATION_AUTOWIRE 8 | #undef KGR_KANGARU_THROW 9 | #undef KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD 10 | #undef KGR_KANGARU_MSVC_EXACT_DECLTYPE 11 | #undef KGR_KANGARU_EMPTY_BASES 12 | #undef KGR_KANGARU_FUNCTION_SIGNATURE 13 | #undef KGR_KANGARU_NONCONST_TYPEID 14 | #undef KGR_KANGARU_HASH_EXTENDED_CONSTEXPR 15 | 16 | // These two header are meant to be included 17 | // everytime they are needed since they cancel each other 18 | #undef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UNDEF 19 | #undef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEFINE 20 | 21 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UNDEF 22 | -------------------------------------------------------------------------------- /test/src/type_id_cxx14.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace kgr_test { 8 | 9 | struct test {}; 10 | class another_type; 11 | 12 | TEST_CASE("The hash based typeid give back reproducible values", "[type_id]") { 13 | REQUIRE(kgr::detail::type_name() == "kgr_test::test"); 14 | REQUIRE(kgr::type_id() == ((kgr::detail::hash_64_fnv1a("kgr_test::test") & kgr::detail::hash_mask))); 15 | 16 | REQUIRE(kgr::detail::type_name() == "kgr_test::another_type"); 17 | REQUIRE(kgr::type_id() == ((kgr::detail::hash_64_fnv1a("kgr_test::another_type") & kgr::detail::hash_mask))); 18 | 19 | REQUIRE(kgr::detail::type_name() == "int"); 20 | REQUIRE(kgr::type_id() == ((kgr::detail::hash_64_fnv1a("int") & kgr::detail::hash_mask))); 21 | } 22 | 23 | } // namespace kgr_test 24 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(kangaru-example INTERFACE) 2 | 3 | target_compile_options(kangaru-example INTERFACE 4 | $<$,$>:-Werror -Wall -Wextra -Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable> 5 | $<$:-Werror -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-variable -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-comment> 6 | ) 7 | 8 | target_link_libraries(kangaru-example INTERFACE kangaru) 9 | 10 | add_subdirectory(abstract_factory) 11 | add_subdirectory(cars) 12 | add_subdirectory(example1) 13 | add_subdirectory(example2) 14 | add_subdirectory(example3) 15 | add_subdirectory(example4) 16 | add_subdirectory(example5) 17 | add_subdirectory(example6) 18 | add_subdirectory(example7) 19 | add_subdirectory(example8) 20 | add_subdirectory(example9) 21 | add_subdirectory(readme_example) 22 | add_subdirectory(wandbox_example) 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/security-or-undefined-behavior-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Security or undefined behavior issue 3 | about: Report a security vulnerability, undefined behavior or memory leak here 4 | title: Security issue 5 | labels: security/UB 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Nature of the security issue** 11 | Select one: 12 | [ ] Undefined behavior 13 | [ ] Memory leak 14 | [ ] Other security issue (please describe) 15 | 16 | **Describe the bug** 17 | A clear and concise description of what the security is. 18 | 19 | **To Reproduce** 20 | Steps and code to reproduce the behavior: 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Desktop (please complete the following information):** 26 | - OS: [e.g. Windows, Linux, MacOS] 27 | - Compiler [e.g MSVC, GCC, Clang] 28 | - Version [e.g. 19.1, 8.3, 7.0] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | # This file is inspired by nlohmann 2 | # You can find the original file here: https://github.com/nlohmann/json/blob/develop/appveyor.yml 3 | 4 | version: '{build}' 5 | 6 | os: 7 | - Visual Studio 2015 8 | 9 | environment: 10 | matrix: 11 | - additional_flags: "/EHsc" 12 | - additional_flags: "/EHsc /std:c++14 /utf-8" 13 | - additional_flags: "/EHsc /permissive- /std:c++14 /utf-8" 14 | 15 | init: [] 16 | 17 | install: 18 | - git submodule update --init --recursive 19 | 20 | build_script: 21 | - IF "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" ( SET GEN="Visual Studio 14 2015") ELSE ( IF "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" ( SET GEN="Visual Studio 15 2017") ELSE ( SET GEN="Visual Studio 16 2019") ) 22 | - cmake . -G%GEN% -DKANGARU_BUILD_EXAMPLES=true -DKANGARU_TEST=true -DKANGARU_TEST_CXX14=true -DCMAKE_CXX_FLAGS="%additional_flags%" -DCMAKE_BUILD_TYPE=RelWithDebInfo 23 | - cmake --build . --config RelWithDebInfo 24 | 25 | test_script: 26 | - ctest . -C RelWithDebInfo --output-on-failure 27 | 28 | -------------------------------------------------------------------------------- /include/kangaru/detail/container_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_CONTAINER_SERVICE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_CONTAINER_SERVICE_HPP 3 | 4 | #include 5 | 6 | namespace kgr { 7 | 8 | struct container; 9 | 10 | /* 11 | * Special definition. 12 | * 13 | * This is the definition for the service kgr::container. 14 | * When the container is asked for this definition, il will proceed to inject himself in the constructor. 15 | */ 16 | struct container_service { 17 | explicit container_service(container& instance) : _instance{&instance} {} 18 | 19 | inline container& forward() { 20 | return *_instance; 21 | } 22 | 23 | private: 24 | container* _instance; 25 | }; 26 | 27 | namespace detail { 28 | 29 | /* 30 | * Trait that tells if a particular service is the container service. 31 | */ 32 | template 33 | using is_container_service = std::is_same; 34 | 35 | } // namespace detail 36 | } // namespace kgr 37 | 38 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_CONTAINER_SERVICE_HPP 39 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(benchmark QUIET) 2 | 3 | if (NOT benchmark_FOUND) 4 | set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "" FORCE) 5 | set(BENCHMARK_DOWNLOAD_DEPENDENCIES OFF CACHE INTERNAL "" FORCE) 6 | set(BENCHMARK_ENABLE_ASSEMBLY_TESTS OFF CACHE INTERNAL "" FORCE) 7 | set(BENCHMARK_ENABLE_EXCEPTIONS ON CACHE INTERNAL "" FORCE) 8 | set(BENCHMARK_ENABLE_INSTALL OFF CACHE INTERNAL "" FORCE) 9 | set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "" FORCE) 10 | set(BENCHMARK_BUILD_32_BITS OFF CACHE INTERNAL "" FORCE) 11 | set(BENCHMARK_ENABLE_LTO OFF CACHE INTERNAL "" FORCE) 12 | set(BENCHMARK_USE_LIBCXX OFF CACHE INTERNAL "" FORCE) 13 | add_subdirectory(third_party/benchmark) 14 | mark_as_advanced(benchmark_DIR) 15 | mark_as_advanced(LIBRT) 16 | mark_as_advanced(LLVM_FILECHECK_EXE) 17 | endif() 18 | 19 | add_executable(kangaru_benchmarks src/benchmarks.cpp) 20 | target_link_libraries(kangaru_benchmarks PRIVATE kangaru::kangaru) 21 | target_compile_features(kangaru_benchmarks PRIVATE cxx_std_11) 22 | target_link_libraries(kangaru_benchmarks PRIVATE benchmark) 23 | -------------------------------------------------------------------------------- /examples/example6/example6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * This example reflects snippets of code found in the documentation section 6: Autowire 6 | * It explains how to autowire services using the service map. 7 | */ 8 | 9 | struct Camera { 10 | int position; 11 | }; 12 | 13 | struct Scene { 14 | Scene(Camera& c, int w = 0, int h = 0) : 15 | camera(c), width{w}, height{h} {} 16 | 17 | Camera& camera; 18 | int width; 19 | int height; 20 | }; 21 | 22 | auto service_map(Camera const&) -> kgr::autowire_single; 23 | auto service_map(Scene const&) -> kgr::autowire; 24 | 25 | int main() 26 | { 27 | kgr::container container; 28 | 29 | container.invoke([](Camera& camera, Scene scene) { 30 | // Do stuff with the camera and the scene! 31 | (void) scene.camera.position; 32 | }); 33 | 34 | // Optional, but can be useful 35 | using SceneService = kgr::mapped_service_t; 36 | 37 | auto scene = container.service(1920, 1080); 38 | 39 | std::cout << "Scene created with size " << scene.width << "x" << scene.height << '\n'; 40 | } 41 | -------------------------------------------------------------------------------- /examples/readme_example/readme_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * This example only show basic usage of services and the container. 6 | */ 7 | 8 | // Normal classes with dependency between them 9 | struct Camera {}; 10 | 11 | struct Scene { 12 | Camera& camera; 13 | }; 14 | 15 | // This is the configuration of our classes. 16 | // Structure and dependency graph is defined here. 17 | 18 | // Camera is a single service so the service has a shared instance. 19 | struct CameraService : kgr::single_service {}; 20 | 21 | // Scene is not single, so the container return scenes by value. 22 | // Also, we depends on a camera to be constructed. 23 | struct SceneService : kgr::service> {}; 24 | 25 | int main() 26 | { 27 | kgr::container container; 28 | 29 | // The service function return instances of the normal classes. 30 | Scene scene = container.service(); 31 | Camera& camera = container.service(); 32 | 33 | std::cout 34 | << std::boolalpha 35 | << (&scene.camera == &camera) << std::endl; // outputs true 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Guillaume Racicot 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 | 23 | -------------------------------------------------------------------------------- /test/src/noexcept.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void abort_handler(int signal) { 6 | if (signal == SIGABRT) { 7 | SUCCEED(); 8 | std::_Exit(EXIT_SUCCESS); 9 | } 10 | } 11 | 12 | static void setup_handlers() { 13 | std::signal(SIGABRT, abort_handler); 14 | 15 | // On windows we must prevent the "Abort, Retry, Ignore" window from opening. 16 | #if defined(_WIN32) || defined(_WIN64) 17 | _set_abort_behavior(0, _WRITE_ABORT_MSG); 18 | #endif 19 | } 20 | 21 | TEST_CASE("Should abort when exceptions are disabled", "[noexcept]") { 22 | setup_handlers(); 23 | 24 | #if defined(KGR_KANGARU_TEST_ABSTRACT_ABORT) 25 | SECTION("Supplied service error") { 26 | struct S {}; 27 | struct Supplied : kgr::single_service, kgr::supplied {}; 28 | 29 | kgr::container{}.service(); 30 | } 31 | #elif defined(KGR_KANGARU_TEST_SUPPLIED_ABORT) 32 | SECTION("Abstract service error") { 33 | struct A {}; 34 | struct Abstract : kgr::abstract_service {}; 35 | 36 | kgr::container{}.service(); 37 | } 38 | #else 39 | #error "Specify a test to run" 40 | #endif 41 | } 42 | 43 | -------------------------------------------------------------------------------- /include/kangaru/detail/utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UTILS_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UTILS_HPP 3 | 4 | #include 5 | 6 | namespace kgr { 7 | namespace detail { 8 | 9 | /* 10 | * Dependent type false 11 | * Useful for static asserts 12 | */ 13 | template 14 | struct to_false { 15 | using type = std::false_type; 16 | }; 17 | 18 | template 19 | using false_t = typename to_false::type; 20 | 21 | /* 22 | * Missing utility taken from C++17 23 | */ 24 | template 25 | using bool_constant = std::integral_constant; 26 | 27 | /* 28 | * Simple alias to member type `value_type` 29 | * Used mostly for detection. 30 | */ 31 | template 32 | using value_type_t = typename T::value_type; 33 | 34 | /* 35 | * Variable sent as parameter for in_place_t. 36 | * 37 | * Used by the container to desambiguate between contructor that construct the service, and those that doesnt. 38 | * 39 | * We defined the variable before the type because the variable is private, but the type is public. 40 | */ 41 | struct in_place_t {} constexpr in_place{}; 42 | 43 | } // namespace detail 44 | } // namespace kgr 45 | 46 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UTILS_HPP 47 | -------------------------------------------------------------------------------- /include/kangaru/detail/override_range_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_RANGE_SERVICE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_RANGE_SERVICE_HPP 3 | 4 | #include "service_range.hpp" 5 | 6 | namespace kgr { 7 | namespace detail { 8 | 9 | struct override_range_service_tag {}; 10 | 11 | } // namespace detail 12 | 13 | template 14 | struct override_range_service : detail::override_range_service_tag { 15 | explicit override_range_service(override_range> range) noexcept : _range{range} {} 16 | 17 | auto forward() -> override_range> { 18 | return _range; 19 | } 20 | 21 | private: 22 | override_range> _range; 23 | }; 24 | 25 | namespace detail { 26 | 27 | template 28 | using is_override_range_service = std::is_base_of; 29 | 30 | template 31 | struct override_range_service_type {}; 32 | 33 | template 34 | struct override_range_service_type> { 35 | using type = T; 36 | }; 37 | 38 | template 39 | using override_range_service_type_t = typename override_range_service_type::type; 40 | 41 | } // namespace detail 42 | } // namespace kgr 43 | 44 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_RANGE_SERVICE_HPP 45 | -------------------------------------------------------------------------------- /test/src/basic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST_CASE("Container returns a service from a definition", "[service]") { 5 | struct Service {}; 6 | struct Definition { 7 | explicit Definition(kgr::in_place_t) {} 8 | 9 | Service forward() { 10 | return std::move(service); 11 | } 12 | 13 | static auto construct() -> decltype(kgr::inject()) { 14 | return kgr::inject(); 15 | } 16 | 17 | Service service; 18 | }; 19 | 20 | struct Result { 21 | static std::false_type test(...) { 22 | return {}; 23 | } 24 | 25 | static std::true_type test(Service&&) { 26 | return {}; 27 | } 28 | }; 29 | 30 | REQUIRE(Result::test(kgr::container{}.service())); 31 | } 32 | 33 | TEST_CASE("Container returns a service by moving it", "[service]") { 34 | struct Service { 35 | Service() = default; 36 | Service(Service&&) = default; 37 | Service& operator=(Service&&) = default; 38 | 39 | bool copied = false; 40 | 41 | Service(const Service&) { 42 | copied = true; 43 | } 44 | 45 | Service& operator=(const Service&) { 46 | copied = true; 47 | 48 | return *this; 49 | } 50 | }; 51 | 52 | struct Definition : kgr::service {}; 53 | 54 | kgr::container c; 55 | 56 | auto service = c.service(); 57 | 58 | REQUIRE_FALSE(c.service().copied); 59 | REQUIRE_FALSE(service.copied); 60 | } 61 | -------------------------------------------------------------------------------- /include/kangaru/detail/operator_service_helper.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OPERATOR_SERVICE_HELPER_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OPERATOR_SERVICE_HELPER_HPP 3 | 4 | #include 5 | 6 | namespace kgr { 7 | 8 | struct all; 9 | 10 | namespace detail { 11 | 12 | /* 13 | * Forward declaration of services defined in operator_service.hpp 14 | */ 15 | template 16 | struct forked_operator_service; 17 | 18 | template 19 | struct operator_service; 20 | 21 | struct operator_base; 22 | struct forked_operator_base; 23 | 24 | /* 25 | * Indirect map that generate a service definition for an operator service 26 | * 27 | * It will choose between operator_service and forked_operator_service dependening on the operator_base type 28 | */ 29 | template 30 | struct select_operator_service; 31 | 32 | template<> 33 | struct select_operator_service { 34 | template 35 | using mapped_service = operator_service::type>::type>; 36 | }; 37 | 38 | template<> 39 | struct select_operator_service { 40 | template 41 | using mapped_service = forked_operator_service::type>::type>; 42 | }; 43 | 44 | } // namespace detail 45 | } // namespace kgr 46 | 47 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OPERATOR_SERVICE_HELPER_HPP 48 | -------------------------------------------------------------------------------- /include/kangaru/detail/lazy_base.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_BASE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_BASE_HPP 3 | 4 | #include "operator_service_helper.hpp" 5 | #include "../optional.hpp" 6 | #include "../container.hpp" 7 | 8 | namespace kgr { 9 | namespace detail { 10 | 11 | /* 12 | * This is the base class of the Lazy and ForkedLazy class. 13 | * This implements all the common things a Lazy must implement. 14 | */ 15 | template 16 | struct lazy_base : protected Base { 17 | private: 18 | using ref = typename std::remove_reference>::type&; 19 | using rref = typename std::remove_reference>::type&&; 20 | using ptr = typename std::add_pointer>>::type; 21 | 22 | public: 23 | using Base::Base; 24 | 25 | ref get() { 26 | if (!_service) { 27 | kgr::container& c = this->container(); 28 | _service.construct(c.service()); 29 | } 30 | 31 | return _service.value(); 32 | } 33 | 34 | ref operator*() & { 35 | return get(); 36 | } 37 | 38 | ptr operator->() { 39 | return &get(); 40 | } 41 | 42 | rref operator*() && { 43 | return std::move(get()); 44 | } 45 | 46 | private: 47 | optional> _service; 48 | 49 | inline friend auto service_map(lazy_base const&) -> select_operator_service { return {}; } 50 | }; 51 | 52 | } // namespace detail 53 | } // namespace kgr 54 | 55 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_BASE_HPP 56 | -------------------------------------------------------------------------------- /include/kangaru/detail/hash.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_HASH_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_HASH_HPP 3 | 4 | #include "string_view.hpp" 5 | 6 | #include 7 | 8 | #include "define.hpp" 9 | 10 | namespace kgr { 11 | namespace detail { 12 | 13 | // C++11 compile type hash function for strings 14 | // Implementation taken from TheLartians/StaticTypeInfo 15 | // Link: https://github.com/TheLartians/StaticTypeInfo/blob/master/include/static_type_info/hash.h 16 | // License: MIT 17 | 18 | // Implementation also inspired from ruby0x1/hash_fnv1a.h 19 | // Link: https://gist.github.com/ruby0x1/81308642d0325fd386237cfa3b44785c 20 | // License: public domain 21 | 22 | 23 | constexpr auto val_64_const = std::uint64_t{0xcbf29ce484222325}; 24 | constexpr auto prime_64_const = std::uint64_t{0x100000001b3}; 25 | 26 | inline constexpr auto hash_64_fnv1a(string_view const str, std::uint64_t const value = val_64_const) noexcept -> std::uint64_t { 27 | #ifdef KGR_KANGARU_HASH_EXTENDED_CONSTEXPR 28 | auto hash = value; 29 | auto const len = str.size(); 30 | for(std::size_t i = 0; i < len; ++i) { 31 | uint8_t value = static_cast(str[i]); 32 | hash = hash ^ value; 33 | hash *= prime_64_const; 34 | } 35 | 36 | return hash; 37 | #else 38 | return (str.size() == 0) ? value : hash_64_fnv1a(str.substr(1), (value ^ static_cast(str[0])) * prime_64_const); 39 | #endif 40 | } 41 | 42 | } 43 | } 44 | 45 | #include "undef.hpp" 46 | 47 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_HASH_HPP 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Kangaru has benefitted a lot from patches and would benefit a lot from your help. Every commit counts! 5 | 6 | To submit a patch, fork the repo on your account and clone it: 7 | ```sh 8 | $ git clone git@github.com:your-username/kangaru.git && cd kangaru 9 | ``` 10 | 11 | Then, setup the project using CMake. As opposed to the default setup, we will enable tests and examples: 12 | ```sh 13 | mkdir build && cd build 14 | cmake .. -DKANGARU_BUILD_EXAMPLES=On -DKANGARU_TEST=On -DKANGARU_TEST_CXX14=On -DKANGARU_TEST_CXX17=On 15 | ``` 16 | 17 | You can build and test your project using your favourite IDE that supports CMake, or build using the command line: 18 | ```sh 19 | cmake --build . 20 | ctest 21 | ``` 22 | 23 | You're then ready to make your changes! To ensure your changes work as expected, simply build and test again. 24 | 25 | Finally, push your patch to your fork and [submit a pull request](https://github.com/gracicot/kangaru/compare/). 26 | 27 | To finish the process, we will review and comment your pull request. We will respond as fast as possible, but it might take a day or two. 28 | 29 | To increase the chances of a patch to be accepted, please be mindful of: 30 | 31 | * Writing a descriptive but consice commit message, 32 | * Follow the coding style of the edited file, 33 | * Write tests if applicable. 34 | 35 | ### Testing 36 | 37 | When a patch influence the public API or has any obverable property in user code, a test is required. Any major functionality should have a dedicated unit test. 38 | -------------------------------------------------------------------------------- /examples/example5/example5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * This example reflects snippets of code found in the documentation section 5: Supplied Services 6 | * It explains how to make single services not constructible implicitly by the container. 7 | */ 8 | 9 | struct Camera { 10 | int position; 11 | }; 12 | 13 | struct Scene { 14 | Scene(Camera c, int w, int h) : 15 | camera{c.position}, width{w}, height{h} {} 16 | 17 | private: 18 | Camera camera; 19 | int width; 20 | int height; 21 | }; 22 | 23 | struct CameraService : kgr::service {}; 24 | struct SceneService : kgr::single_service>, kgr::supplied {}; 25 | 26 | int main() 27 | { 28 | { 29 | kgr::container container; 30 | bool inserted; 31 | 32 | inserted = container.emplace(1920, 1080); // construct a scene in the container. 33 | std::cout << std::boolalpha << "Is inserted? " << inserted << '\n'; 34 | 35 | inserted = container.emplace(1024, 768); // construct a scene in the container. 36 | std::cout << std::boolalpha << "Is inserted a second time? " << inserted << '\n'; 37 | 38 | Scene& scene = container.service(); // works, won't try to construct it. 39 | 40 | (void) scene; 41 | } 42 | 43 | try { 44 | kgr::container container; 45 | // The container cannot construct a scene and don't contain one. 46 | // Throws a supplied_not_found error. 47 | container.service(); 48 | } catch(kgr::supplied_not_found const& error) { 49 | std::cout << "supplied_not_found thrown, what(): " << error.what() << std::endl; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /include/kangaru/predicate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_PREDICATE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_PREDICATE_HPP 3 | 4 | #include "type_id.hpp" 5 | 6 | namespace kgr { 7 | namespace detail { 8 | 9 | template 10 | struct find_id_predicate { 11 | protected: 12 | constexpr bool find(type_id_t id) const noexcept { 13 | return find(id); 14 | } 15 | 16 | template 17 | constexpr bool find(type_id_t id) const noexcept { 18 | return id == type_id() && find(id); 19 | } 20 | 21 | template 22 | constexpr bool find(type_id_t id) const noexcept { 23 | return id == type_id(); 24 | } 25 | }; 26 | 27 | } // namespace detail 28 | 29 | /* 30 | * This predicate always returns true. 31 | * 32 | * This is the default predicate used by the container. 33 | */ 34 | struct all { 35 | constexpr inline bool operator()(type_id_t) const noexcept { 36 | return true; 37 | } 38 | }; 39 | 40 | /* 41 | * This predicate returns true for all services except those specified. 42 | */ 43 | template 44 | struct except : private detail::find_id_predicate { 45 | constexpr bool operator()(type_id_t id) const noexcept { 46 | return !this->find(id); 47 | } 48 | }; 49 | 50 | /* 51 | * Predicate that returns false for all services, except those passed as argument. 52 | */ 53 | template 54 | struct only : private detail::find_id_predicate { 55 | constexpr bool operator()(type_id_t id) const noexcept { 56 | return this->find(id); 57 | } 58 | }; 59 | 60 | } // namespace kgr 61 | 62 | #endif // KGR_KANGARU_INCLUDE_KANGARU_PREDICATE_HPP 63 | -------------------------------------------------------------------------------- /examples/example1/example1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * This example reflects snippets of code found in the documentation section 1: Services 6 | * It explains how to branch containers and operate between them. 7 | */ 8 | 9 | 10 | // Camera is a user class. 11 | struct Camera { 12 | int position; 13 | }; 14 | 15 | // Scene too. User class. 16 | struct Scene { 17 | Scene(Camera c, int w = 800, int h = 600) : 18 | camera{c.position}, width{w}, height{h} {} 19 | 20 | private: 21 | Camera camera; 22 | int width; 23 | int height; 24 | }; 25 | 26 | struct Screen { 27 | Scene& scene; 28 | Camera camera; 29 | }; 30 | 31 | // This is our service definitions 32 | struct CameraService : kgr::service {}; 33 | 34 | // SceneService is a single service of Scene, that depends on a camera 35 | struct SceneService : kgr::single_service> {}; 36 | 37 | // ScreenService is a single service of Screen, that depends on a scene and camera 38 | struct ScreenService : kgr::service> {}; 39 | 40 | int main() 41 | { 42 | kgr::container container; 43 | 44 | // We create two cameras. 45 | Camera camera = container.service(); 46 | Camera furtherCamera = container.service(14); 47 | 48 | // prints 0 49 | std::cout << "Camera Position: " << camera.position << '\n'; 50 | 51 | // prints 14 52 | std::cout << "Further Camera Position: " << furtherCamera.position << '\n'; 53 | 54 | // A Screen has a Scene and a Camera injected in it. 55 | Screen screen1 = container.service(); 56 | Screen screen2 = container.service(); 57 | 58 | // Spoiler: yes they are the same 59 | std::cout << "Is both scene the same? " << (&screen1.scene == &screen2.scene ? "yes" : "no") << '\n'; 60 | } 61 | -------------------------------------------------------------------------------- /include/kangaru/debug.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEBUG_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEBUG_HPP 3 | 4 | #include "detail/error.hpp" 5 | 6 | namespace kgr { 7 | namespace debug { 8 | 9 | /* 10 | * Debug function for services that takes argument. 11 | * 12 | * This overload takes a ill formed service and static_assert the detected error. 13 | */ 14 | template 15 | auto service(U&& u, Args&&...) -> detail::enable_if_t, U>::value> { 16 | (void) detail::service_error {std::forward(u)}; 17 | } 18 | 19 | /* 20 | * This overload is called when no error is found in a service that takes arguments. 21 | * 22 | * Output as a static assert that no error is detected. 23 | */ 24 | template 25 | auto service(U&&, Args&&...) -> detail::enable_if_t, U>::value> { 26 | static_assert(detail::false_t::value, "No known error detected."); 27 | } 28 | 29 | /* 30 | * Debug function for a service without arguments. 31 | * 32 | * Outputs the detected error as a static_assert 33 | */ 34 | template 35 | auto service() -> detail::enable_if_t>::value> { 36 | (void) detail::service_error {}; 37 | } 38 | 39 | /* 40 | * This overload is choosen when no error is detected. 41 | * 42 | * Output as a static assert that no error is detected. 43 | */ 44 | template 45 | auto service() -> detail::enable_if_t>::value> { 46 | static_assert(detail::false_t::value, "No known error detected."); 47 | } 48 | 49 | } // namespace debug 50 | } // namespace kgr 51 | 52 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEBUG_HPP 53 | -------------------------------------------------------------------------------- /include/kangaru/detail/override_storage_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_STORAGE_SERVICE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_STORAGE_SERVICE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "single.hpp" 9 | #include "injected.hpp" 10 | #include "service_storage.hpp" 11 | 12 | #include "../type_id.hpp" 13 | 14 | namespace kgr { 15 | namespace detail { 16 | 17 | struct override_storage { 18 | using overrides_t = std::vector>>; 19 | 20 | template 21 | auto filter(P predicate) const -> overrides_t { 22 | overrides_t fork; 23 | fork.reserve(overrides.capacity()); 24 | 25 | std::transform( 26 | overrides.begin(), 27 | overrides.end(), 28 | std::back_inserter(fork), 29 | [&predicate](overrides_t::const_reference overrides) -> overrides_t::value_type { 30 | overrides_t::value_type filtered; 31 | filtered.reserve(overrides.capacity()); 32 | std::copy_if( 33 | overrides.begin(), 34 | overrides.end(), 35 | std::back_inserter(filtered), 36 | [&predicate](overrides_t::value_type::const_reference service) -> bool { 37 | return predicate(service.first); 38 | } 39 | ); 40 | 41 | return filtered; 42 | } 43 | ); 44 | 45 | return fork; 46 | } 47 | 48 | overrides_t overrides; 49 | }; 50 | 51 | struct override_storage_service : single { 52 | override_storage service; 53 | 54 | inline static auto construct() -> kgr::inject_result<> { 55 | return kgr::inject(); 56 | } 57 | 58 | inline auto forward() -> override_storage& { 59 | return service; 60 | } 61 | 62 | inline auto forward() const -> override_storage const& { 63 | return service; 64 | } 65 | }; 66 | 67 | template 68 | struct index_storage; 69 | 70 | } // namespace detail 71 | } // namespace kgr 72 | 73 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_STORAGE_SERVICE_HPP 74 | -------------------------------------------------------------------------------- /examples/example7/example7.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | /** 7 | * This example reflects snippets of code found in the documentation section 7: Autocall 8 | * It explains how to call member functions of a service upon construction and injection by setters. 9 | */ 10 | 11 | // This is a utility macro to workaround the lack of type inference for non-type template parameter 12 | // Will not be needed once this library upgrade to C++17 13 | #define METHOD(...) ::kgr::method 14 | 15 | struct Scene {}; 16 | struct Camera {}; 17 | struct Window { 18 | int get_framerate() { 19 | return 60; 20 | } 21 | }; 22 | 23 | struct MessageBus { 24 | MessageBus() = default; 25 | 26 | void init(Window& window, Camera& camera) { 27 | max_delay = 3 * window.get_framerate(); 28 | std::cout << "max_delay set to: " << max_delay << '\n'; 29 | } 30 | 31 | void set_scene(Scene& scene) { 32 | this->scene = &scene; 33 | std::cout << "Setting scene" << '\n'; 34 | } 35 | 36 | private: 37 | Scene* scene = nullptr; 38 | int max_delay = 0; 39 | }; 40 | 41 | struct SceneService : kgr::single_service {}; 42 | struct CameraService : kgr::single_service {}; 43 | struct WindowService : kgr::single_service {}; 44 | struct MessageBusService : kgr::single_service, kgr::autocall< 45 | METHOD(&MessageBus::init), 46 | METHOD(&MessageBus::set_scene) 47 | > {}; 48 | 49 | struct MessageBusServiceInvoke : kgr::single_service, kgr::autocall< 50 | kgr::invoke, 51 | kgr::invoke 52 | > {}; 53 | 54 | auto service_map(Scene const&) -> SceneService; 55 | auto service_map(Camera const&) -> CameraService; 56 | auto service_map(Window const&) -> WindowService; 57 | 58 | int main() 59 | { 60 | kgr::container container; 61 | 62 | container.emplace(); 63 | std::cout << '\n'; 64 | container.emplace(); 65 | } 66 | -------------------------------------------------------------------------------- /include/kangaru/detail/exception.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_EXCEPTION_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_EXCEPTION_HPP 3 | 4 | #include "kangaru/detail/config.hpp" 5 | #include "define.hpp" 6 | 7 | #ifndef KGR_KANGARU_NOEXCEPTION 8 | 9 | #include 10 | 11 | namespace kgr { 12 | 13 | /* 14 | * This exception is the base exception type for all kind of exception caused by a service not found. 15 | */ 16 | struct service_not_found : std::exception {}; 17 | 18 | /* 19 | * This exception is thrown when an abstract service is not found, yet asked for it's definition. 20 | */ 21 | struct abstract_not_found : service_not_found { 22 | inline char const* what() const noexcept override { 23 | return "No instance found for the requested abstract service"; 24 | } 25 | }; 26 | 27 | /* 28 | * This exception is thrown when a supplied service is not found, yet asked for it's definition. 29 | */ 30 | struct supplied_not_found : service_not_found { 31 | inline char const* what() const noexcept override { 32 | return "No instance found for the requested supplied service"; 33 | } 34 | }; 35 | 36 | } // namespace kgr 37 | 38 | #else // defined KGR_KANGARU_NOEXCEPTION 39 | 40 | #include 41 | 42 | namespace kgr { 43 | 44 | /* 45 | * This exception is the base exception type for all kind of exception caused by a service not found. 46 | */ 47 | struct service_not_found {}; 48 | 49 | /* 50 | * This exception is thrown when an abstract service is not found, yet asked for it's definition. 51 | */ 52 | struct abstract_not_found : service_not_found { 53 | constexpr static char const* what() noexcept { 54 | return "No instance found for the requested abstract service"; 55 | } 56 | }; 57 | 58 | /* 59 | * This exception is thrown when a supplied service is not found, yet asked for it's definition. 60 | */ 61 | struct supplied_not_found : service_not_found { 62 | constexpr static char const* what() noexcept { 63 | return "No instance found for the requested supplied service"; 64 | } 65 | }; 66 | 67 | } // namespace kgr 68 | 69 | #endif // KGR_KANGARU_NOEXCEPTION 70 | 71 | #include "undef.hpp" 72 | 73 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_EXCEPTION_HPP 74 | -------------------------------------------------------------------------------- /include/kangaru/detail/override_traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_TRAITS_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_TRAITS_HPP 3 | 4 | #include "traits.hpp" 5 | #include "meta_list.hpp" 6 | #include "detection.hpp" 7 | 8 | namespace kgr { 9 | namespace detail { 10 | 11 | /* 12 | * Trait that check if all types specified in overrides are services 13 | */ 14 | template 15 | using is_override_services = all_of_traits, is_service>; 16 | 17 | /* 18 | * Trait that check if the service type returned by 19 | * one override's service definitions can be converted to the service type of the service T definition. 20 | */ 21 | template 22 | using is_one_override_convertible = is_explicitly_convertible, detected_t>; 23 | 24 | /* 25 | * Trait that check if the service type returned by 26 | * all overriden service definitions can be converted to the service type of the service T definition. 27 | */ 28 | template 29 | using is_override_convertible = all_of_traits, is_one_override_convertible, T>; 30 | 31 | /* 32 | * Trait that check if all overriden services are polymorphic 33 | */ 34 | template 35 | using is_override_polymorphic = all_of_traits, is_polymorphic>; 36 | 37 | /* 38 | * Trait that check if no overriden services are final 39 | */ 40 | template 41 | using is_override_not_final = all_of_traits, is_not_final_service>; 42 | 43 | /* 44 | * Trait that check if the default service of an abstract service overrides that abstract service 45 | */ 46 | template 47 | using is_default_overrides_abstract = bool_constant< 48 | !has_default::value || is_overriden_by>::value 49 | >; 50 | 51 | /* 52 | * Trait that check if the default service type is convertible to the abstract service type. 53 | */ 54 | template 55 | using is_default_convertible = bool_constant< 56 | !has_default::value || is_explicitly_convertible< 57 | detected_t>, 58 | detected_t 59 | >::value 60 | >; 61 | 62 | } // namespace detail 63 | } // namespace kgr 64 | 65 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_OVERRIDE_TRAITS_HPP 66 | -------------------------------------------------------------------------------- /include/kangaru/detail/seq.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SEQ_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SEQ_HPP 3 | 4 | #include "meta_list.hpp" 5 | 6 | #include 7 | 8 | namespace kgr { 9 | namespace detail { 10 | 11 | template struct seq_concat; 12 | 13 | template 14 | struct seq { using type = seq; }; 15 | 16 | template 17 | struct seq_concat, seq> : seq{}; 18 | 19 | template 20 | using seq_concat_t = typename seq_concat::type; 21 | 22 | template 23 | struct seq_gen : seq_concat_t::type, typename seq_gen::type> {}; 24 | 25 | template<> 26 | struct seq_gen<0> : seq<> {}; 27 | 28 | template<> 29 | struct seq_gen<1> : seq<0> {}; 30 | 31 | template<> 32 | struct seq_gen<2> : seq<0, 1> {}; 33 | 34 | template<> 35 | struct seq_gen<3> : seq<0, 1, 2> {}; 36 | 37 | template<> 38 | struct seq_gen<4> : seq<0, 1, 2, 3> {}; 39 | 40 | template<> 41 | struct seq_gen<5> : seq<0, 1, 2, 3, 4> {}; 42 | 43 | template<> 44 | struct seq_gen<6> : seq<0, 1, 2, 3, 4, 5> {}; 45 | 46 | template<> 47 | struct seq_gen<7> : seq<0, 1, 2, 3, 4, 5, 6> {}; 48 | 49 | template<> 50 | struct seq_gen<8> : seq<0, 1, 2, 3, 4, 5, 6, 7> {}; 51 | 52 | template 53 | struct tuple_seq_gen; 54 | 55 | template 56 | struct tuple_seq_gen> : seq_gen {}; 57 | 58 | template 59 | struct tuple_seq_gen> : seq_gen {}; 60 | 61 | template 62 | using tuple_seq = typename tuple_seq_gen::type; 63 | 64 | template 65 | struct seq_drop_first; 66 | 67 | template 68 | struct seq_drop_first> { 69 | using type = seq; 70 | }; 71 | 72 | inline constexpr auto safe_minus(std::size_t lhs, std::size_t rhs) -> std::size_t { 73 | return lhs - (lhs > rhs ? rhs : lhs); 74 | } 75 | 76 | template 77 | using seq_drop_first_t = typename seq_drop_first::type; 78 | 79 | template 80 | using tuple_seq_minus = typename detail::seq_gen::value, n)>::type; 81 | 82 | } // namespace detail 83 | } // namespace kgr 84 | 85 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SEQ_HPP 86 | -------------------------------------------------------------------------------- /include/kangaru/autocall.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_AUTOCALL_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_AUTOCALL_HPP 3 | 4 | #include "detail/meta_list.hpp" 5 | #include "detail/utils.hpp" 6 | #include "detail/traits.hpp" 7 | #include "detail/injected.hpp" 8 | #include "detail/container_service.hpp" 9 | #include "container.hpp" 10 | 11 | namespace kgr { 12 | namespace detail { 13 | 14 | template 15 | struct autocall_function_helper; 16 | 17 | /* 18 | * This class implements all autocall functions that a definition must implement for autocall. 19 | * 20 | * All autocall specialization extends this one. 21 | * 22 | * It extending autocall is only way to enable autocall in a service. 23 | */ 24 | template 25 | struct autocall_base { 26 | using autocall_functions = meta_list; 27 | using map = Map; 28 | 29 | private: 30 | template friend struct autocall_function_helper; 31 | 32 | // TODO: Remove this function since it doesn't belong here anymore. 33 | template 34 | static void autocall_helper(seq, inject_t cs, T& service) { 35 | cs.forward().invoke([&](function_argument_t... args) { 36 | service.call(F::value, std::forward>(args)...); 37 | }); 38 | } 39 | }; 40 | 41 | } // namespace detail 42 | 43 | /* 44 | * This alias simply to transform a member function address to a type. 45 | */ 46 | template::type t> 47 | using method = std::integral_constant::type, t>; 48 | 49 | /* 50 | * This class wraps a method and tell explicitly which services are needed. 51 | */ 52 | template 53 | struct invoke : M { 54 | using parameters = detail::meta_list; 55 | }; 56 | 57 | /* 58 | * The class that defines autocall with the default map. 59 | */ 60 | template 61 | struct autocall : detail::autocall_base, Ts...> {}; 62 | 63 | /* 64 | * Specialization of autocall when a map is sent as first parameter. 65 | */ 66 | template 67 | struct autocall, Ts...> : detail::autocall_base, Ts...> {}; 68 | 69 | } // namespace kgr 70 | 71 | #endif // KGR_KANGARU_INCLUDE_KANGARU_AUTOCALL_HPP 72 | -------------------------------------------------------------------------------- /include/kangaru/experimental/autowiring.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_EXPERIMENTAL_AUTOWIRING_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_EXPERIMENTAL_AUTOWIRING_HPP 3 | 4 | #include "../detail/autowire_traits.hpp" 5 | 6 | namespace kgr { 7 | namespace experimental { 8 | namespace autowiring { 9 | 10 | /* 11 | * A class used for indirect mapping and tranmitting information about autowiring. 12 | */ 13 | template class ServiceType, template class GetService, typename Map, std::size_t max_dependencies> 14 | using autowire_map = detail::autowire_map; 15 | 16 | using default_inject_function_t = detail::default_inject_function_t; 17 | constexpr default_inject_function_t default_inject_function{}; 18 | 19 | /* 20 | * A construct function usable by many service definition implementation. 21 | * Will send as many deducers as there are numbers in S 22 | */ 23 | template 24 | inline auto deduce_construct(detail::seq s, I inject, inject_t cont, Args&&... args) -> detail::call_result_t..., Args...> { 25 | return detail::deduce_construct(s, std::move(inject), std::move(cont), std::forward(args)...); 26 | } 27 | 28 | /* 29 | * A shortcut for deduce_construct with the default injection function. 30 | */ 31 | template 32 | inline auto deduce_construct_default(detail::seq s, inject_t cont, Args&&... args) -> detail::call_result_t..., Args...> { 33 | return detail::deduce_construct_default(s, std::move(cont), std::forward(args)...); 34 | } 35 | 36 | /* 37 | * Alias to amount_of_deductible_service_helper to ease usage 38 | */ 39 | template 40 | using amount_of_deductible_service = detail::amount_of_deductible_service; 41 | 42 | /* 43 | * Tag that replaces dependencies in a service defintion. Carries all information on how to autowire. 44 | */ 45 | template, std::size_t max_dependencies = detail::default_max_dependency> 46 | using autowire_tag = detail::autowire_tag; 47 | 48 | } // namespace autowiring 49 | } // namespace experimental 50 | } // namespace kgr 51 | 52 | #endif // KGR_KANGARU_INCLUDE_KANGARU_EXPERIMENTAL_AUTOWIRING_HPP 53 | -------------------------------------------------------------------------------- /examples/wandbox_example/wandbox_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // This macro is used as a shortcut to use kgr::Method. Won't be needed in C++17 5 | #define METHOD(...) ::kgr::method 6 | 7 | // The following classes are user classes. 8 | // As you can see, this library is not intrusive and don't require modifications 9 | struct Credential {}; 10 | 11 | struct Connection { 12 | // The connect needs some credential 13 | void connect(Credential const&) { 14 | std::cout << "connection established" << std::endl; 15 | } 16 | }; 17 | 18 | struct Database { 19 | // A database needs a connection 20 | explicit Database(Connection const&) { 21 | std::cout << "database created" << std::endl; 22 | } 23 | 24 | // For the sake of having a method to call 25 | void commit() { 26 | std::cout << "database commited" << std::endl; 27 | } 28 | }; 29 | 30 | 31 | // Service definitions. 32 | // We define all dependencies between classes, 33 | // and tell the container how to map function parameter to those definitions. 34 | 35 | // Simple injectable service by value 36 | struct CredentialService : kgr::service {}; 37 | 38 | // Connection service is single, 39 | // and need the connect function to be called on creation 40 | struct ConnectionService : kgr::single_service, 41 | kgr::autocall {}; 42 | 43 | 44 | // Database is also a single, and has a connection as dependency 45 | struct DatabaseService : kgr::single_service> {}; 46 | 47 | // The service map maps a function parameter type to a service definition 48 | // We also want to map a Database argument type for the example. 49 | auto service_map(Database const&) -> DatabaseService; 50 | auto service_map(Credential const&) -> CredentialService; 51 | 52 | int main() { 53 | kgr::container container; 54 | 55 | // Get the database. 56 | // The database has a connection injected, 57 | // and the connection had the connect function called before injection. 58 | auto&& database = container.service(); 59 | 60 | // Commit the database 61 | database.commit(); 62 | 63 | // Let `function` be a callable object that takes mapped services. 64 | auto function = [](Credential c, Database& db) { 65 | // Do stuff with credential and database 66 | }; 67 | 68 | // The function is called with it's parameter injected automatically. 69 | container.invoke(function); 70 | 71 | // The programs outputs: 72 | // connection established 73 | // database created 74 | // database commited 75 | } 76 | -------------------------------------------------------------------------------- /include/kangaru/detail/nostd_invoke.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_NOSTD_INVOKE 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_NOSTD_INVOKE 3 | 4 | namespace kgr { 5 | namespace detail { 6 | namespace nostd { 7 | // Implementation of std::invoke 8 | // Inspired by https://stackoverflow.com/questions/33462729/how-does-stdinvokec1z-work 9 | template 10 | inline auto invoke(F&& f, Args&&... args) 11 | noexcept(noexcept(std::forward(f)(std::forward(args)...))) 12 | -> decltype(std::forward(f)(std::forward(args)...)) 13 | { 14 | return std::forward(f)(std::forward(args)...); 15 | } 16 | 17 | template 18 | inline auto invoke(T Base::*pmd, Derived&& ref) noexcept -> 19 | decltype((std::forward(ref).*pmd)) 20 | { 21 | return std::forward(ref).*pmd; 22 | } 23 | 24 | template 25 | inline auto invoke(T Base::*pmd, std::reference_wrapper ref) noexcept -> 26 | decltype((ref.get().*pmd)) 27 | { 28 | return ref.get().*pmd; 29 | } 30 | 31 | template 32 | inline auto invoke(PMD pmd, Pointer&& ptr) noexcept -> 33 | decltype(((*std::forward(ptr)).*pmd)) 34 | { 35 | return (*std::forward(ptr)).*pmd; 36 | } 37 | 38 | template 39 | inline auto invoke(T Base::*pmf, Derived&& ref, Args&&... args) 40 | noexcept(noexcept((std::forward(ref).*pmf)(std::forward(args)...))) 41 | -> decltype((std::forward(ref).*pmf)(std::forward(args)...)) 42 | { 43 | return (std::forward(ref).*pmf)(std::forward(args)...); 44 | } 45 | 46 | template 47 | inline auto invoke(T Base::*pmf, std::reference_wrapper ref, Args&&... args) 48 | noexcept(noexcept((ref.get().*pmf)(std::forward(args)...))) 49 | -> decltype((ref.get().*pmf)(std::forward(args)...)) 50 | { 51 | return (ref.get().*pmf)(std::forward(args)...); 52 | } 53 | 54 | template 55 | inline auto invoke(PMF pmf, Pointer&& ptr, Args&&... args) 56 | noexcept(noexcept(((*std::forward(ptr)).*pmf)(std::forward(args)...))) 57 | -> decltype(((*std::forward(ptr)).*pmf)(std::forward(args)...)) 58 | { 59 | return ((*std::forward(ptr)).*pmf)(std::forward(args)...); 60 | } 61 | 62 | template 63 | using invoke_result_t = decltype(invoke(std::declval()...)); 64 | 65 | } // namespace nostd 66 | } // namespace detail 67 | } // namespace kgr 68 | 69 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_NOSTD_INVOKE 70 | -------------------------------------------------------------------------------- /examples/example2/example2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * This example reflects snippets of code found in the documentation section 2: Invoke 6 | * It explains how to do service injection through the parameters of a function. 7 | */ 8 | 9 | // Uncomment this to receive the scene in process_inputs_mod 10 | // #define HAS_SCENE_PARAMETER 11 | 12 | struct KeyboardState {}; 13 | struct MessageBus {}; 14 | struct Camera {}; 15 | 16 | // This is the definition for our classes 17 | struct KeyboardStateService : kgr::single_service {}; 18 | struct MessageBusService : kgr::single_service {}; 19 | struct CameraService : kgr::service {}; 20 | 21 | // This is the service map. We map a parameter type to a service definition. 22 | auto service_map(KeyboardState const&) -> KeyboardStateService; 23 | auto service_map(MessageBus const&) -> MessageBusService; 24 | auto service_map(Camera const&) -> CameraService; 25 | 26 | // These are functions we will call with invoke 27 | bool process_inputs(KeyboardState& ks, MessageBus& mb) { 28 | std::cout << "processing inputs...\n"; 29 | return true; 30 | } 31 | 32 | #ifndef HAS_SCENE_PARAMETER 33 | 34 | bool process_inputs_mod(KeyboardState& ks, MessageBus& mb, bool check_modifiers) { 35 | std::cout << "process inputs " << (check_modifiers ? "with" : "without") << " modifiers\n"; 36 | return !check_modifiers; 37 | } 38 | 39 | #else 40 | 41 | bool process_inputs_mod(KeyboardState& ks, MessageBus& mb, Camera scene, bool check_modifiers) { 42 | std::cout << "process inputs " << (check_modifiers ? "with" : "without") << " modifiers\n"; 43 | std::cout << "got the scene!\n"; 44 | return !check_modifiers; 45 | } 46 | 47 | #endif 48 | 49 | int main() 50 | { 51 | kgr::container container; 52 | 53 | 54 | // We invoke a function specifying services 55 | bool result1 = container.invoke(process_inputs); 56 | 57 | // The code above is equivalent to this: 58 | // 59 | // process_inputs( 60 | // container.service(), 61 | // container.service() 62 | // ); 63 | // 64 | 65 | // We can also let the container match parameters itself with the service map. 66 | bool result2 = container.invoke(process_inputs); 67 | bool result3 = container.invoke(process_inputs_mod, true); 68 | 69 | std::cout << '\n'; 70 | 71 | // 1: true, 2: true, 3: false 72 | std::cout << "Invoke results: \n 1: " << result1; 73 | std::cout << "\n 2: " << result2; 74 | std::cout << "\n 3: " << result3 << '\n'; 75 | 76 | // We call a lambda with injected parameter using invoke. 77 | container.invoke([](Camera camera) { 78 | std::cout << "Lambda called." << std::endl; 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /doc/section11_debug.md: -------------------------------------------------------------------------------- 1 | Debugging 2 | ========= 3 | 4 | kangaru is a heavily templated library. It's commonly known that library that rely a lot on templates are hard to diagnostic, 5 | and as soon as something goes wrong with user defined types, you'll get a huge monologue of errors from the compiler. 6 | 7 | With kangaru we decided to emulate concepts to provide better error messages the users of our library. 8 | 9 | Most of kangaru function are guarded by a constraints. It will recursively detect errors in constructors, dependencies, definitions, and others. 10 | For GCC 7 and older, we used a small trick with templated constructors and default values that allows us to call a static assertion when a deleted function is invoked. 11 | 12 | ## Explicit debugging 13 | 14 | If you don't have GCC or you explicitly want to know what's wrong with a particular service, you can call `kgr::debug::service(Args...)`. 15 | Simply call that function just like it would be to call `container.service()` then build your project. 16 | 17 | Let's trigger the same error as above with this code: 18 | 19 | kgr::debug::service(2); 20 | 21 | This is the compiler output (Clang): 22 | 23 | In file included from example1.cpp:5: 24 | In file included from ../../include/kangaru/kangaru.hpp:4: 25 | In file included from ../../include/kangaru/detail/../container.hpp:11: 26 | ../../include/kangaru/detail/error.hpp:50:3: error: static_assert failed "The service type is not constructible given passed arguments to kgr::container::service(...)." 27 | static_assert(false_t::value, "The service type is not constructible given passed arguments to kgr::Container::service(...)."); 28 | ^ ~~~~~~~~~~~~~~~~~~~ 29 | ../../include/kangaru/debug.hpp:11:35: note: in instantiation of function template specialization 'kgr::detail::service_error::service_error' requested here 30 | detail::service_error error{std::forward(u)}; 31 | ^ 32 | example1.cpp:50:14: note: in instantiation of function template specialization 'kgr::debug::service' requested here 33 | kgr::debug::service(2); 34 | ^ 35 | 1 error generated. 36 | 37 | As you can see, the compiler is effectively stating that the service is not constructible given passed arguments. 38 | 39 | If `kgr::debug::service(Args...)` Detects no error, it will output `"No known error detected."` as a static assert. 40 | If you have found a case when it does not compile and yet no error is detected, just let me know and report a bug in our issue tracker. 41 | 42 | [Next chapter: Generic Services](section12_generic.md) 43 | -------------------------------------------------------------------------------- /include/kangaru/detail/string_view.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_STRING_VIEW_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_STRING_VIEW_HPP 3 | 4 | #include 5 | 6 | namespace kgr { 7 | namespace detail { 8 | 9 | /** 10 | * A function that returns the size of a string at compile time 11 | */ 12 | constexpr auto strlen(char const* string) -> std::size_t { 13 | return *string == 0 ? std::size_t{0} : std::size_t{1} + strlen(string + 1); 14 | } 15 | 16 | /** 17 | * A function that returns if two strings are equal 18 | */ 19 | constexpr auto strncmp(char const* str1, char const* str2, std::size_t count) -> bool { 20 | return count == 0 || (*str1 == *str2 && strncmp(str1 + 1, str2 + 1, count - 1)); 21 | } 22 | 23 | /** 24 | * This class is similar to a C++17 string view. 25 | * 26 | * However, in C++11, you can't really have mutating member functions 27 | * This implementation skip such member functions. 28 | */ 29 | struct string_view { 30 | static constexpr auto npos = std::size_t(-1); 31 | 32 | constexpr string_view(char const* literal) noexcept : _data{literal}, _size{strlen(literal)} {} 33 | explicit constexpr string_view(char const* string, std::size_t size) noexcept : _data{string}, _size{size} {} 34 | 35 | constexpr auto operator==(string_view const& rhs) const noexcept -> bool { 36 | return _size == rhs._size && strncmp(_data, rhs._data, _size); 37 | } 38 | 39 | constexpr auto operator[](std::size_t index) const noexcept -> char const& { 40 | return _data[index]; 41 | } 42 | 43 | constexpr auto begin() const noexcept -> char const* { 44 | return _data; 45 | } 46 | 47 | constexpr auto end() const noexcept -> char const* { 48 | return _data + _size; 49 | } 50 | 51 | constexpr auto cbegin() const noexcept -> char const* { 52 | return _data; 53 | } 54 | 55 | constexpr auto cend() const noexcept -> char const* { 56 | return _data + _size; 57 | } 58 | 59 | constexpr auto data() const noexcept -> char const* { 60 | return _data; 61 | } 62 | 63 | constexpr auto size() const noexcept -> std::size_t { 64 | return _size; 65 | } 66 | 67 | constexpr auto substr(std::size_t pos, std::size_t count = npos) const noexcept -> string_view { 68 | return string_view{_data + pos, count == npos ? _size - pos : count}; 69 | } 70 | 71 | constexpr auto find(string_view str, std::size_t pos = 0) const noexcept -> std::size_t { 72 | return pos > _size || pos + str._size > _size ? npos : find_impl(_data + pos, str, _size - pos - str._size - 1); 73 | } 74 | 75 | constexpr auto starts_with(string_view str) const noexcept -> bool { 76 | return _size >= str._size && strncmp(_data, str._data, str._size); 77 | } 78 | 79 | private: 80 | static constexpr auto find_impl(char const* haystack, string_view needle, std::size_t max_iter) -> std::size_t { 81 | return max_iter == 0 ? 0 : ( 82 | strncmp(haystack, needle._data, needle._size) ? 0 : 1 + find_impl(haystack + 1, needle, max_iter - 1) 83 | ); 84 | } 85 | 86 | char const* _data; 87 | std::size_t _size; 88 | }; 89 | 90 | } // namespace detail 91 | } // namespace kgr 92 | 93 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_STRING_VIEW_HPP 94 | -------------------------------------------------------------------------------- /include/kangaru/detail/service_range.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_RANGE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_RANGE_HPP 3 | 4 | #include "service_storage.hpp" 5 | #include "override_storage_service.hpp" 6 | 7 | #include "../type_id.hpp" 8 | #include "../optional.hpp" 9 | 10 | #include 11 | #include 12 | 13 | namespace kgr { 14 | 15 | template 16 | struct override_range { 17 | using iterator = Iterator; 18 | using service = typename Iterator::service; 19 | using service_type = kgr::service_type; 20 | 21 | explicit override_range(iterator begin, iterator end) noexcept : 22 | _begin{begin}, _end{end} {} 23 | 24 | auto begin() const noexcept -> iterator { 25 | return _begin; 26 | } 27 | 28 | auto end() const noexcept -> iterator { 29 | return _end; 30 | } 31 | 32 | private: 33 | iterator _begin; 34 | iterator _end; 35 | }; 36 | 37 | namespace detail { 38 | 39 | template 40 | struct override_iterator { 41 | using storage = optional>; 42 | 43 | static_assert( 44 | is_trivially_copy_constructible::value && 45 | std::is_trivially_destructible::value, 46 | "override_iterator only support services that yield trivial types" 47 | ); 48 | 49 | public: 50 | using value_type = typename storage::value_type; 51 | using reference = typename storage::reference; 52 | using const_reference = typename storage::const_reference; 53 | using pointer = typename storage::pointer; 54 | using difference_type = std::ptrdiff_t; 55 | using iterator_category = std::input_iterator_tag; 56 | 57 | explicit override_iterator(std::vector>::iterator internal) noexcept : 58 | _internal{internal} {} 59 | 60 | friend auto operator!=(override_iterator const& lhs, override_iterator const& rhs) -> bool { 61 | return lhs._internal != rhs._internal; 62 | } 63 | 64 | friend auto operator==(override_iterator const& lhs, override_iterator const& rhs) -> bool { 65 | return lhs._internal == rhs._internal; 66 | } 67 | 68 | auto operator++() -> override_iterator& { 69 | ++_internal; 70 | _service = {}; 71 | return *this; 72 | } 73 | 74 | auto operator++(int) -> override_storage { 75 | auto prev = *this; 76 | ++*this; 77 | _service = {}; 78 | return prev; 79 | } 80 | 81 | auto operator*() -> typename storage::reference { 82 | return get(); 83 | } 84 | 85 | auto operator->() -> typename storage::pointer { 86 | return &get(); 87 | } 88 | 89 | private: 90 | auto get() -> typename storage::reference { 91 | if (!_service) { 92 | auto const& typed_storage = _internal->second.cast(); 93 | _service.construct(typed_storage.forward(typed_storage.service)); 94 | } 95 | 96 | return _service.value(); 97 | } 98 | 99 | using service = T; 100 | friend struct override_range>; 101 | std::vector>::iterator _internal; 102 | storage _service; 103 | }; 104 | 105 | } // namespace detail 106 | } // namespace kgr 107 | 108 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_RANGE_HPP 109 | -------------------------------------------------------------------------------- /include/kangaru/detail/validity_check.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VALIDITY_CHECK_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VALIDITY_CHECK_HPP 3 | 4 | #include "invoke_function.hpp" 5 | #include "single.hpp" 6 | #include "service_check.hpp" 7 | #include "autocall_traits.hpp" 8 | 9 | namespace kgr { 10 | namespace detail { 11 | 12 | /* 13 | * Check if a call for kgr::Container::service has no arguments in the case of a single service 14 | */ 15 | template 16 | using is_single_no_args = bool_constant< 17 | !is_single::value || meta_list_empty>::value 18 | >; 19 | 20 | /* 21 | * Complete validity check for a particlar service and all it's properties 22 | */ 23 | template 24 | using is_construction_valid = bool_constant< 25 | service_check::value && 26 | is_autocall_valid::value && 27 | dependency_trait::value 28 | >; 29 | 30 | /* 31 | * Complete validity check for a particlar service and all it's properties 32 | */ 33 | template 34 | struct is_service_valid : bool_constant< 35 | is_single_no_args::value && 36 | is_construction_valid::value 37 | > {}; 38 | 39 | /* 40 | * Complete validity check for a particular service, but also prohibit circular dependencies with a hard error. 41 | */ 42 | template 43 | struct is_service_valid_circular_hard_error { 44 | static_assert(is_detected>::value, "No circular dependencies are allowed in autowire"); 45 | template 46 | static constexpr bool is_valid() { return instantiate_if_or>::value, std::false_type, is_service_valid, U>::value; } 47 | }; 48 | 49 | /* 50 | * Complete validity check for a particlar service and all it's properties 51 | */ 52 | template 53 | struct is_emplace_valid : bool_constant< 54 | is_single::value && 55 | is_construction_valid::value && 56 | is_construct_function_callable::value && 57 | is_service_constructible::value 58 | > {}; 59 | 60 | /* 61 | * Metafunction that returns a partially applied trait. 62 | * Condition for is_invoke_service_valid. 63 | */ 64 | template 65 | using each_mapped_service_valid = all_of_traits...>, is_service_valid>; 66 | 67 | /* 68 | * Trait that check if a function is invocable, and all it's injected arguments are valid. 69 | */ 70 | template 71 | using is_invoke_service_valid = bool_constant< 72 | is_detected::value && 73 | expand_minus_n< 74 | sizeof...(Args), 75 | detected_or, invoke_function_arguments_t, Map, T, Args...>, 76 | each_mapped_service_valid, Map 77 | >::value 78 | >; 79 | 80 | /* 81 | * Validity check for a invoke expression 82 | */ 83 | template 84 | struct is_invoke_valid : bool_constant< 85 | is_invoke_service_valid::value && 86 | is_invokable::value 87 | > {}; 88 | 89 | } // namespace detail 90 | } // namespace kgr 91 | 92 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VALIDITY_CHECK_HPP 93 | -------------------------------------------------------------------------------- /include/kangaru/detail/single.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SINGLE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SINGLE_HPP 3 | 4 | #include "traits.hpp" 5 | #include "meta_list.hpp" 6 | #include "detection.hpp" 7 | 8 | namespace kgr { 9 | 10 | /* 11 | * Tag base class to identify a single service 12 | * 13 | * Also disable copy construction 14 | */ 15 | struct single { 16 | single() = default; 17 | ~single() = default; 18 | single(const single&) = delete; 19 | single& operator=(const single&) = delete; 20 | single(single&&) = default; 21 | single& operator=(single&&) = default; 22 | }; 23 | 24 | /* 25 | * Bunch of other tag types for various features 26 | */ 27 | struct polymorphic {}; 28 | struct final {}; 29 | struct supplied {}; 30 | struct abstract : polymorphic, single {}; 31 | 32 | /* 33 | * Mixin for abstract service to set the default implementation 34 | */ 35 | template 36 | struct defaults_to { 37 | using default_service = T; 38 | }; 39 | 40 | /* 41 | * Used to list all types a service should override when constructing 42 | */ 43 | template 44 | struct overrides { 45 | using parent_types = detail::meta_list; 46 | }; 47 | 48 | namespace detail { 49 | 50 | /* 51 | * Type trait to either get the specified overrides or an empty list 52 | */ 53 | template 54 | struct parent_type_helper { 55 | using parent_types = meta_list<>; 56 | }; 57 | 58 | template 59 | struct parent_type_helper> { 60 | using parent_types = typename T::parent_types; 61 | }; 62 | 63 | template 64 | using parent_types = typename parent_type_helper::parent_types; 65 | 66 | /* 67 | * Type trait to either get the default implementation type of an abstract serivce 68 | * 69 | * Also tell if there is a default or not 70 | */ 71 | template 72 | struct default_type_helper { 73 | using has_default = std::false_type; 74 | }; 75 | 76 | template 77 | struct default_type_helper> { 78 | using has_default = std::true_type; 79 | using service = typename T::default_service; 80 | }; 81 | 82 | template 83 | using default_type = typename default_type_helper::service; 84 | 85 | template 86 | using has_default = typename default_type_helper::has_default; 87 | 88 | template 89 | using is_abstract_service = std::is_base_of; 90 | 91 | template 92 | using is_single = std::is_base_of; 93 | 94 | template 95 | using is_supplied_service = std::is_base_of; 96 | 97 | template 98 | using is_final_service = std::is_base_of; 99 | 100 | template 101 | using is_not_final_service = negation>; 102 | 103 | template 104 | using is_polymorphic = std::integral_constant::value || !meta_list_empty>::value>; 105 | 106 | template 107 | using is_overriden_by = meta_list_contains>; 108 | 109 | } // namespace detail 110 | } // namespace kgr 111 | 112 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SINGLE_HPP 113 | -------------------------------------------------------------------------------- /examples/example3/example3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * This example reflects snippets of code found in the documentation section 3: Polymorphic Services 6 | * It explains how to override services and use them polymorphically. 7 | */ 8 | 9 | struct AbstractCamera { 10 | virtual void projection() = 0; 11 | }; 12 | 13 | struct Camera : AbstractCamera { 14 | void projection() override { 15 | std::cout << "default projection" << std::endl; 16 | } 17 | }; 18 | 19 | struct PerspectiveCamera : Camera { 20 | void projection() override { 21 | std::cout << "perspective projection" << std::endl; 22 | } 23 | }; 24 | 25 | struct OrthogonalCamera : Camera { 26 | void projection() override { 27 | std::cout << "orthogonal projection" << std::endl; 28 | } 29 | }; 30 | 31 | 32 | struct AbstractCameraService : 33 | kgr::abstract_service {}; 34 | 35 | struct AbstractCameraServiceDefault : 36 | kgr::abstract_service, 37 | kgr::defaults_to {}; 38 | 39 | struct CameraService : 40 | kgr::single_service, 41 | kgr::polymorphic {}; 42 | 43 | // Multiple overrides are supported 44 | struct PerspectiveCameraService : 45 | kgr::single_service, 46 | kgr::overrides {}; 47 | 48 | struct OrthogonalCameraService : 49 | kgr::single_service, 50 | kgr::overrides, 51 | kgr::final {}; 52 | 53 | int main() 54 | { 55 | { 56 | kgr::container container; 57 | 58 | // Camera is returned 59 | Camera& camera1 = container.service(); 60 | camera1.projection(); // prints `default projection` 61 | 62 | // PerspectiveCamera is registered as Camera 63 | container.emplace(); 64 | 65 | // PerspectiveCamera is returned 66 | Camera& camera2 = container.service(); 67 | camera2.projection(); // prints `perspective projection` 68 | } 69 | 70 | std::cout << '\n'; 71 | 72 | { 73 | kgr::container container; 74 | 75 | container.service(); 76 | container.service(); 77 | 78 | // instance of OrthogonalCamera returned 79 | Camera& camera = container.service(); 80 | camera.projection(); 81 | } 82 | 83 | std::cout << '\n'; 84 | 85 | { 86 | kgr::container container; 87 | 88 | container.service(); 89 | container.service(); 90 | 91 | // instance of PerspectiveCamera returned 92 | Camera& camera = container.service(); 93 | camera.projection(); 94 | } 95 | 96 | std::cout << '\n'; 97 | 98 | try { 99 | kgr::container container; 100 | 101 | container.service(); 102 | 103 | std::cout << "No exceptions thrown\n"; 104 | } catch(kgr::abstract_not_found const& e) { 105 | std::cout << "abstract_not_found thrown, what(): " << e.what() << std::endl; 106 | } 107 | 108 | try { 109 | kgr::container container; 110 | 111 | container.service(); 112 | 113 | std::cout << "No exceptions thrown\n"; 114 | } catch(kgr::abstract_not_found const& e) { 115 | std::cout << "abstract_not_found thrown, what(): " << e.what() << std::endl; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /include/kangaru/detail/define.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEFINE 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEFINE 3 | 4 | #ifndef KGR_KANGARU_CXX17_NOEXCEPT 5 | #if defined(__cpp_noexcept_function_type) || __cplusplus >= 201703L || _MSVC_LANG > 201402L 6 | #define KGR_KANGARU_CXX17_NOEXCEPT_FPTR 7 | #define KGR_KANGARU_CXX17_NOEXCEPT noexcept 8 | #else 9 | #define KGR_KANGARU_CXX17_NOEXCEPT 10 | #endif 11 | #endif 12 | 13 | #ifndef KGR_KANGARU_USE_ALTERNATE_MAP_PROBE 14 | #if ( \ 15 | !(defined(__clang__) && __clang_major__ < 7 && !defined(__APPLE__)) && \ 16 | !(defined(_MSC_VER) && !defined(__clang__)) && \ 17 | !(defined(__APPLE__) && defined(__clang__) && __clang_major__ == 10 && __clang_patchlevel__ < 1) && \ 18 | !(defined(__APPLE__) && defined(__clang__) && __clang_major__ < 10) \ 19 | ) 20 | #define KGR_KANGARU_USE_ALTERNATE_MAP_PROBE 21 | #endif 22 | #endif 23 | 24 | #ifndef KGR_KANGARU_MSVC_DISABLE_VALIDATION_AUTOWIRE 25 | #if defined(_MSC_VER) && _MSC_VER <= 1900 26 | // MSVC 2015 cannot properly validate autowired argument to services. 27 | // It will generate bad code and cause crashes 28 | #define KGR_KANGARU_MSVC_DISABLE_VALIDATION_AUTOWIRE 29 | #endif 30 | #endif 31 | 32 | // These preprocessor directives allow kangaru to work with exceptions disabled. 33 | #ifndef KGR_KANGARU_THROW 34 | #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(KGR_KANGARU_NOEXCEPTION) 35 | #define KGR_KANGARU_THROW(exception) throw exception 36 | #else 37 | #ifndef KGR_KANGARU_NOEXCEPTION 38 | #define KGR_KANGARU_NOEXCEPTION 39 | #endif 40 | #define KGR_KANGARU_THROW(exception) std::abort() 41 | #endif 42 | #endif 43 | 44 | #ifndef KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD 45 | #if defined(_MSC_VER) && _MSC_VER <= 1900 46 | #ifndef __clang__ 47 | // MSVC has a defect that makes the use of the template keyword an error in some corner cases. 48 | #define KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD 49 | #endif // !__clang__ 50 | #endif // _MSC_VER 51 | #endif // KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD 52 | 53 | #ifndef KGR_KANGARU_MSVC_EXACT_DECLTYPE 54 | #if _MSC_VER 55 | #ifndef __clang__ 56 | // MSVC has a defect that makes decltype with the address of a 57 | // generic lambda not possible unless sending the address to a function. 58 | #define KGR_KANGARU_MSVC_EXACT_DECLTYPE 59 | #endif // !__clang__ 60 | #endif // _MSC_VER 61 | #endif // KGR_KANGARU_MSVC_EXACT_DECLTYPE 62 | 63 | #ifndef KGR_KANGARU_EMPTY_BASES 64 | #ifdef _MSC_VER 65 | #define KGR_KANGARU_EMPTY_BASES __declspec(empty_bases) 66 | #else 67 | #define KGR_KANGARU_EMPTY_BASES 68 | #endif // _MSC_VER 69 | #endif // KGR_KANGARU_EMPTY_BASES 70 | 71 | #ifndef KGR_KANGARU_FUNCTION_SIGNATURE 72 | #if defined(_MSC_VER) 73 | #define KGR_KANGARU_FUNCTION_SIGNATURE __FUNCSIG__ 74 | #else 75 | #define KGR_KANGARU_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ 76 | #endif // _MSC_VER 77 | #endif // KGR_KANGARU_FUNCTION_SIGNATURE 78 | 79 | #ifndef KGR_KANGARU_NONCONST_TYPEID 80 | #ifdef _MSC_VER 81 | #ifndef __clang__ 82 | #define KGR_KANGARU_NONCONST_TYPEID 83 | #endif // !__clang__ 84 | #endif // _MSC_VER 85 | #endif // KGR_KANGARU_NONCONST_TYPEID 86 | 87 | #ifndef KGR_KANGARU_HASH_EXTENDED_CONSTEXPR 88 | #if (defined(_MSC_VER) && _MSC_VER >= 1910) || __cplusplus >= 201402L 89 | #define KGR_KANGARU_HASH_EXTENDED_CONSTEXPR 90 | #endif 91 | #endif // KGR_KANGARU_HASH_EXTENDED_CONSTEXPR 92 | 93 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEFINE 94 | -------------------------------------------------------------------------------- /test/src/single.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST_CASE("Container creates a single", "[single]") { 5 | kgr::container c; 6 | 7 | static int constructed; 8 | constructed = 0; 9 | 10 | struct Service { 11 | Service() { constructed++; } 12 | }; 13 | 14 | struct Definition : kgr::single_service {}; 15 | 16 | SECTION("Construct the single one time") { 17 | (void) c.service(); 18 | 19 | REQUIRE(constructed == 1); 20 | } 21 | 22 | SECTION("Construct the single one time with multiple invocation") { 23 | (void) c.service(); 24 | (void) c.service(); 25 | 26 | REQUIRE(constructed == 1); 27 | } 28 | 29 | SECTION("Returns the same instance") { 30 | REQUIRE(&c.service() == &c.service()); 31 | } 32 | } 33 | 34 | TEST_CASE("Container contains a single after contruction", "[single]") { 35 | kgr::container c; 36 | 37 | struct Service {}; 38 | struct Definition : kgr::single_service {}; 39 | 40 | (void) c.service(); 41 | 42 | REQUIRE(c.contains()); 43 | } 44 | 45 | TEST_CASE("Singles are never moved or copied", "[single]") { 46 | kgr::container c; 47 | 48 | static bool constructed = false; 49 | static bool displaced = false; 50 | 51 | struct Service { 52 | Service() noexcept { 53 | constructed = true; 54 | } 55 | 56 | Service(const Service&) noexcept { 57 | displaced = true; 58 | } 59 | 60 | Service(Service&&) noexcept { 61 | displaced = true; 62 | } 63 | 64 | Service& operator=(const Service&) noexcept { 65 | displaced = true; 66 | 67 | return *this; 68 | } 69 | 70 | Service& operator=(Service&&) noexcept { 71 | displaced = true; 72 | 73 | return *this; 74 | } 75 | }; 76 | 77 | struct Definition : kgr::single_service {}; 78 | 79 | SECTION("When created") { 80 | (void) c.service(); 81 | 82 | REQUIRE(constructed); 83 | REQUIRE_FALSE(displaced); 84 | } 85 | 86 | SECTION("When the container is moved") { 87 | (void) c.service(); 88 | 89 | auto c2 = std::move(c); 90 | 91 | REQUIRE(constructed); 92 | REQUIRE_FALSE(displaced); 93 | } 94 | } 95 | 96 | TEST_CASE("Singles are defined using tags", "[single]") { 97 | SECTION("By being abstract") { 98 | struct Definition : kgr::abstract {}; 99 | 100 | REQUIRE(kgr::detail::is_single{}); 101 | } 102 | 103 | SECTION("By extending single") { 104 | struct Definition : kgr::single {}; 105 | 106 | REQUIRE(kgr::detail::is_single{}); 107 | } 108 | } 109 | 110 | TEST_CASE("Singles are deleted when the container dies", "[single]") { 111 | static bool deleted = false; 112 | 113 | struct Service { ~Service() { deleted = true; } }; 114 | struct Definition : kgr::single_service {}; 115 | 116 | { 117 | kgr::container c; 118 | 119 | (void) c.service(); 120 | 121 | REQUIRE_FALSE(deleted); 122 | } 123 | 124 | REQUIRE(deleted); 125 | } 126 | 127 | TEST_CASE("Singles are not copiable", "[single]") { 128 | SECTION("By being abstract") { 129 | struct Definition : kgr::abstract {}; 130 | 131 | REQUIRE_FALSE(std::is_copy_constructible{}); 132 | } 133 | 134 | SECTION("By extending single") { 135 | struct Definition : kgr::single {}; 136 | 137 | REQUIRE_FALSE(std::is_copy_constructible{}); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /examples/example8/example8.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | /** 7 | * This example reflects snippets of code found in the documentation section 8: Operator Services 8 | * It explains how to use services bundled with kangaru that helps making specific operation on the container. 9 | */ 10 | 11 | // User classes 12 | struct Window {}; 13 | struct Type { 14 | kgr::container& container; 15 | }; 16 | 17 | struct MessageBus { 18 | void process_messages() { 19 | std::cout << "Messages processed\n"; 20 | } 21 | }; 22 | 23 | struct Scene { 24 | explicit Scene(char const* n = "") noexcept : name{n} { 25 | std::cout << "Scene created\n"; 26 | } 27 | 28 | char const* name; 29 | }; 30 | 31 | // Services declarations 32 | struct MessageBusService : kgr::single_service {}; 33 | struct WindowService : kgr::single_service {}; 34 | struct SceneService : kgr::service {}; 35 | struct TypeService : kgr::service> {}; 36 | 37 | // Service map 38 | auto service_map(Type const&) -> TypeService; 39 | auto service_map(Window const&) -> WindowService; 40 | auto service_map(MessageBus const&) -> MessageBusService; 41 | 42 | 43 | // A function to be invoked 44 | void send_message(MessageBus&, Window&, double timeout) { 45 | std::cout << "Message sent with a timeout of " << timeout << '\n'; 46 | } 47 | 48 | int main() { 49 | // Container 50 | { 51 | kgr::container container1; 52 | kgr::container& container2 = container1.service(); 53 | 54 | std::cout << std::boolalpha << "Both containers are the same? " << (&container1 == &container2) << '\n'; 55 | 56 | container1.invoke([&](kgr::container& container3, Type type) { 57 | std::cout << std::boolalpha << "Both container1 and container3 are the same? "; 58 | std::cout << (&container1 == &container3) << '\n'; 59 | 60 | std::cout << std::boolalpha << "Both container3 and type.container are the same? "; 61 | std::cout << (&container3 == &type.container) << '\n'; 62 | }); 63 | } 64 | 65 | std::cout << '\n'; 66 | 67 | // Fork 68 | { 69 | kgr::container container1; 70 | kgr::container container2 = container1.service(); 71 | 72 | std::cout << std::boolalpha << "Both containers are the same? " << (&container1 == &container2) << '\n'; 73 | 74 | container1.invoke([&](kgr::container container3) { 75 | std::cout << std::boolalpha << "Is container3 a fork? "; 76 | std::cout << (&container1 != &container3) << '\n'; 77 | }); 78 | } 79 | 80 | std::cout << '\n'; 81 | 82 | // Generator 83 | { 84 | kgr::container container; 85 | kgr::generator scene_generator = container.service>(); 86 | 87 | Scene scene1 = scene_generator(); 88 | Scene scene2 = scene_generator(); 89 | Scene scene3 = scene_generator("special parameter"); 90 | } 91 | 92 | std::cout << '\n'; 93 | 94 | // Invoker 95 | { 96 | kgr::container container; 97 | kgr::invoker invoker = container.service(); 98 | 99 | invoker(send_message, 10); // calls send_message with 10 as it's timeout 100 | } 101 | 102 | std::cout << '\n'; 103 | 104 | // Lazy 105 | { 106 | kgr::container container; 107 | kgr::lazy lazy_message_bus = container.service>(); 108 | 109 | // MessageBus constructed here at `->` usage 110 | lazy_message_bus->process_messages(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /include/kangaru/generic.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_GENERIC_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_GENERIC_HPP 3 | 4 | #include "container.hpp" 5 | #include "detail/injected.hpp" 6 | #include "detail/traits.hpp" 7 | 8 | namespace kgr { 9 | namespace detail { 10 | 11 | template 12 | struct generic_service_destruction { 13 | ~generic_service_destruction() { 14 | static_cast(*this).instance().~Type(); 15 | } 16 | }; 17 | 18 | template 19 | struct generic_service_destruction::value>> {}; 20 | 21 | } // namespace detail 22 | 23 | /* 24 | * A generic storage for services. It also provides the emplace function and constructors. 25 | */ 26 | template 27 | struct generic_service : detail::generic_service_destruction, Type> { 28 | generic_service() = default; 29 | 30 | generic_service(generic_service&& other) noexcept { 31 | emplace(std::move(other.instance())); 32 | } 33 | 34 | generic_service& operator=(generic_service&& other) noexcept { 35 | emplace(std::move(other.instance())); 36 | return *this; 37 | } 38 | 39 | generic_service(const generic_service& other) = delete; 40 | generic_service& operator=(const generic_service& other) = delete; 41 | 42 | template::value, int> = 0> 43 | generic_service(in_place_t, Args&&... args) { 44 | emplace(std::forward(args)...); 45 | } 46 | 47 | template::value, int> = 0> 48 | void emplace(Args&&... args) { 49 | new (&_instance) Type(std::forward(args)...); 50 | } 51 | 52 | template::value, int> = 0> 53 | void emplace(Args&&... args) { 54 | new (&_instance) Type{std::forward(args)...}; 55 | } 56 | 57 | protected: 58 | Type& instance() { 59 | return *static_cast(static_cast(&_instance)); 60 | } 61 | 62 | const Type& instance() const { 63 | return *static_cast(static_cast(&_instance)); 64 | } 65 | 66 | private: 67 | friend detail::generic_service_destruction, Type>; 68 | alignas(alignof(Type)) unsigned char _instance[sizeof(Type)]; 69 | }; 70 | 71 | /* 72 | * A generic storage for services. It also provides the emplace function and constructors. 73 | * 74 | * This specialisation is for lvalue reference types. Mainly used for external services. 75 | */ 76 | template 77 | struct generic_service { 78 | generic_service() = default; 79 | ~generic_service() = default; 80 | 81 | generic_service(generic_service&& other) = default; 82 | generic_service& operator=(generic_service&& other) = default; 83 | 84 | generic_service(const generic_service& other) = delete; 85 | generic_service& operator=(const generic_service& other) = delete; 86 | 87 | generic_service(in_place_t, Type& ref) { 88 | emplace(ref); 89 | } 90 | 91 | generic_service(in_place_t, Type&& ref) = delete; 92 | 93 | void emplace(Type& ref) { 94 | _instance = &ref; 95 | } 96 | 97 | protected: 98 | Type& instance() { 99 | return *_instance; 100 | } 101 | 102 | const Type& instance() const { 103 | return *_instance; 104 | } 105 | 106 | private: 107 | Type* _instance; 108 | }; 109 | 110 | } // namespace kgr 111 | 112 | #endif // KGR_KANGARU_INCLUDE_KANGARU_GENERIC_HPP 113 | -------------------------------------------------------------------------------- /include/kangaru/compatibility.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_COMPATIBILITY_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_COMPATIBILITY_HPP 3 | 4 | #include "kangaru.hpp" 5 | 6 | namespace kgr { 7 | 8 | using Abstract = abstract; 9 | using AbstractNotFound = abstract_not_found; 10 | 11 | template 12 | using AbstractService = abstract_service; 13 | 14 | template 15 | using AbstractSharedService = abstract_shared_service; 16 | 17 | using AdlMap = map<>; 18 | using All = all; 19 | 20 | template 21 | using AnyOf = only; 22 | 23 | template 24 | using AutoCall = autocall; 25 | 26 | template 27 | using AutoCallNoMap = autocall; 28 | 29 | using Container = container; 30 | using ContainerService = container_service; 31 | 32 | template 33 | using Default = defaults_to; 34 | 35 | using DefaultForkedInvoker = forked_invoker; 36 | using DefaultForkedInvokerService = forked_invoker_service; 37 | using DefaultInvoker = invoker; 38 | using DefaultInvokerService = invoker_service; 39 | 40 | template 41 | using Dependency = dependency; 42 | 43 | template 44 | using FilteredForkService = filtered_fork_service; 45 | 46 | using ForkService = fork_service; 47 | 48 | template 49 | using ForkedGenerator = forked_generator; 50 | 51 | template 52 | using ForkedGeneratorService = forked_generator_service; 53 | 54 | template 55 | using ForkedInvoker = forked_mapped_invoker; 56 | 57 | template 58 | using ForkedInvokerService = forked_mapped_invoker_service; 59 | 60 | template 61 | using ForkedLazy = forked_lazy; 62 | 63 | template 64 | using ForkedLazyService = forked_lazy_service; 65 | 66 | template 67 | using Generator = generator; 68 | 69 | template 70 | using GeneratorService = generator_service; 71 | 72 | template 73 | using GenericService = generic_service; 74 | 75 | template 76 | using Inject = inject_t; 77 | 78 | template 79 | using Invoke = invoke; 80 | 81 | template 82 | using Invoker = mapped_invoker; 83 | 84 | template 85 | using InvokerService = mapped_invoker_service; 86 | 87 | template 88 | using Lazy = lazy; 89 | 90 | template 91 | using LazyService = lazy_service; 92 | 93 | template 94 | using Map = map_t; 95 | 96 | template 97 | using Method = method; 98 | 99 | template 100 | using NoneOf = except; 101 | 102 | template 103 | using Overrides = overrides; 104 | 105 | template 106 | using ServiceType = service_type; 107 | 108 | template> 109 | using Service = service; 110 | 111 | template> 112 | using SharedService = shared_service; 113 | 114 | using Single = single; 115 | 116 | template> 117 | using SingleService = single_service; 118 | 119 | template> 120 | using UniqueService = unique_service; 121 | 122 | } 123 | 124 | #endif // KGR_KANGARU_INCLUDE_KANGARU_COMPATIBILITY_HPP 125 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 23, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "generator", 11 | "generator": "Ninja Multi-Config", 12 | "binaryDir": "${sourceDir}/build", 13 | "hidden": true, 14 | "cacheVariables": { 15 | "CMAKE_MAKE_PROGRAM": "ninja" 16 | } 17 | }, 18 | { 19 | "name": "ci", 20 | "displayName": "CI", 21 | "inherits": "generator", 22 | "cacheVariables": { 23 | "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", 24 | "KANGARU_BUILD_EXAMPLES": "ON", 25 | "KANGARU_INSTALL": "OFF", 26 | "KANGARU_EXPORT": "OFF", 27 | "KANGARU_REVERSE_DESTRUCTION": "ON", 28 | "KANGARU_NO_EXCEPTION": "OFF", 29 | "KANGARU_BENCHMARK": "OFF", 30 | "KANGARU_TEST": "ON" 31 | } 32 | }, 33 | { 34 | "name": "dev", 35 | "displayName": "Development", 36 | "inherits": "generator", 37 | "cacheVariables": { 38 | "KANGARU_BUILD_EXAMPLES": "ON", 39 | "KANGARU_INSTALL": "ON", 40 | "KANGARU_EXPORT": "ON", 41 | "KANGARU_REVERSE_DESTRUCTION": "ON", 42 | "KANGARU_HASH_TYPE_ID": "ON", 43 | "KANGARU_NO_EXCEPTION": "OFF", 44 | "KANGARU_BENCHMARK": "OFF", 45 | "KANGARU_TEST": "ON", 46 | "KANGARU_TEST_CXX14": "ON", 47 | "KANGARU_TEST_CXX17": "ON" 48 | } 49 | }, 50 | { 51 | "name": "export", 52 | "displayName": "Export", 53 | "inherits": "generator", 54 | "cacheVariables": { 55 | "KANGARU_BUILD_EXAMPLES": "OFF", 56 | "KANGARU_INSTALL": "ON", 57 | "KANGARU_EXPORT": "ON", 58 | "KANGARU_REVERSE_DESTRUCTION": "ON", 59 | "KANGARU_HASH_TYPE_ID": "ON", 60 | "KANGARU_NO_EXCEPTION": "OFF", 61 | "KANGARU_BENCHMARK": "OFF", 62 | "KANGARU_TEST": "OFF", 63 | "KANGARU_TEST_CXX14": "OFF", 64 | "KANGARU_TEST_CXX17": "OFF" 65 | } 66 | } 67 | ], 68 | "buildPresets": [ 69 | { 70 | "name": "debug", 71 | "displayName": "Debug", 72 | "description": "Compiles with debug informations", 73 | "configuration": "Debug", 74 | "configurePreset": "dev" 75 | }, 76 | { 77 | "name": "relwithdebinfo", 78 | "displayName": "RelWithDebInfo", 79 | "description": "Compiles with optimisations and debug informations", 80 | "configuration": "RelWithDebInfo", 81 | "configurePreset": "dev" 82 | }, 83 | { 84 | "name": "release", 85 | "displayName": "Release", 86 | "description": "Compiles with optimisation enabled", 87 | "configuration": "Release", 88 | "configurePreset": "dev" 89 | }, 90 | { 91 | "name": "export", 92 | "displayName": "Export", 93 | "description": "Simply installs kangaru", 94 | "configuration": "Release", 95 | "configurePreset": "export" 96 | }, 97 | { 98 | "name": "ci", 99 | "displayName": "CI", 100 | "description": "The build configuration for CI", 101 | "configuration": "RelWithDebInfo", 102 | "configurePreset": "ci" 103 | } 104 | ], 105 | "testPresets": [ 106 | { 107 | "name": "default-test", 108 | "hidden": true, 109 | "output": {"outputOnFailure": true}, 110 | "execution": {"noTestsAction": "error"} 111 | }, 112 | { 113 | "name": "debug", 114 | "inherits": "default-test", 115 | "configuration": "Debug", 116 | "configurePreset": "dev" 117 | }, 118 | { 119 | "name": "relwithdebinfo", 120 | "inherits": "default-test", 121 | "configuration": "RelWithDebInfo", 122 | "configurePreset": "dev" 123 | }, 124 | { 125 | "name": "release", 126 | "inherits": "default-test", 127 | "configuration": "Release", 128 | "configurePreset": "dev" 129 | }, 130 | { 131 | "name": "ci", 132 | "inherits": "default-test", 133 | "configuration": "RelWithDebInfo", 134 | "configurePreset": "ci" 135 | } 136 | ] 137 | } 138 | -------------------------------------------------------------------------------- /test/src/generic_lambdas.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace kgr_test { 8 | 9 | static std::mt19937 random{std::random_device{}()}; 10 | 11 | static std::uniform_int_distribution any_int_distribution{ 12 | std::numeric_limits::min(), 13 | std::numeric_limits::max() 14 | }; 15 | 16 | static std::uniform_real_distribution any_double_distribution{ 17 | std::numeric_limits::min(), 18 | std::numeric_limits::max() 19 | }; 20 | 21 | namespace testcase_generic_inject_mapped { 22 | struct Service1 {}; 23 | struct Definition1 : kgr::service {}; 24 | struct Service2 {}; 25 | struct Definition2 : kgr::single_service {}; 26 | 27 | auto service_map(const Service1&) -> Definition1; 28 | auto service_map(const Service2&) -> Definition2; 29 | 30 | TEST_CASE("The container inject service parameters using service map", "[generic_lambdas]") { 31 | kgr::container c; 32 | 33 | bool called = false; 34 | 35 | SECTION("Deduces one argument") { 36 | c.invoke([&](auto a) { 37 | REQUIRE((std::is_same, int>::value)); 38 | called = true; 39 | }, 1); 40 | REQUIRE(called); 41 | } 42 | 43 | SECTION("Forward deduced arguments after injection") { 44 | const auto arg1 = any_int_distribution(random); 45 | 46 | auto function = [&](Service1, auto&& a) { 47 | called = true; 48 | 49 | REQUIRE((std::is_same::value)); 50 | 51 | return a; 52 | }; 53 | 54 | CHECK(c.invoke(function, arg1) == arg1); 55 | 56 | REQUIRE(called); 57 | } 58 | 59 | SECTION("Forward many deduced arguments after injection") { 60 | const auto arg1 = any_int_distribution(random); 61 | const auto arg2 = any_double_distribution(random); 62 | 63 | auto function = [&](Service1, auto&& a, auto&& b) { 64 | called = true; 65 | 66 | REQUIRE((std::is_same::value)); 67 | REQUIRE((std::is_same::value)); 68 | 69 | return std::make_pair(a, b); 70 | }; 71 | 72 | auto pair = c.invoke(function, arg1, arg2); 73 | 74 | CHECK(pair.first == arg1); 75 | CHECK(pair.second == arg2); 76 | 77 | REQUIRE(called); 78 | } 79 | 80 | SECTION("Forward variadic deduced arguments after injection") { 81 | auto function = [&](Service1, Service2&, auto&&... a) { 82 | called = true; 83 | REQUIRE(sizeof...(a) == 3); 84 | }; 85 | 86 | c.invoke(function, "test", 2.9, std::tuple<>{}); 87 | 88 | REQUIRE(called); 89 | REQUIRE(c.contains()); 90 | } 91 | 92 | SECTION("Forward mix of deduced and regular parameter after injection") { 93 | const auto arg1 = any_int_distribution(random); 94 | const auto arg2 = any_double_distribution(random); 95 | 96 | auto function = [&](Service1, Service2&, int a, auto b) { 97 | called = true; 98 | REQUIRE((std::is_same, double>::value)); 99 | 100 | return std::make_pair(a, b); 101 | }; 102 | 103 | auto pair = c.invoke(function, arg1, arg2); 104 | 105 | CHECK(pair.first == arg1); 106 | CHECK(pair.second == arg2); 107 | 108 | REQUIRE(called); 109 | REQUIRE(c.contains()); 110 | } 111 | 112 | SECTION("Forward mix of variadic deduced and regular parameter after injection") { 113 | const auto arg1 = any_int_distribution(random); 114 | const auto arg2 = any_int_distribution(random); 115 | 116 | auto function = [&](Service1, Service2&, int a, int b, auto... c) { 117 | called = true; 118 | REQUIRE(sizeof...(c) == 4); 119 | return std::make_pair(a, b); 120 | }; 121 | 122 | CHECK(c.invoke(function, arg1, arg2, 9.3, "test", 3, std::tuple<>{}) == std::make_pair(arg1, arg2)); 123 | 124 | REQUIRE(called); 125 | REQUIRE(c.contains()); 126 | } 127 | } 128 | } 129 | 130 | } // namespace kgr_test 131 | -------------------------------------------------------------------------------- /examples/cars/cars-example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | struct Fuel { 8 | auto getPrice() const noexcept -> double { 9 | return price_; 10 | } 11 | 12 | auto setPrice(double price) -> void { 13 | if (price < 0.0) 14 | throw std::runtime_error{"Price cannot be negative"}; 15 | price_ = price; 16 | } 17 | protected: 18 | Fuel() = default; 19 | private: 20 | double price_; 21 | }; 22 | 23 | struct Car { 24 | auto refuel(size_t litres) const noexcept -> double { 25 | return litres * fuel_->getPrice(); 26 | } 27 | 28 | auto setFuel(Fuel* fuel) -> void { 29 | fuel_ = std::move(fuel); 30 | } 31 | protected: 32 | explicit Car(Fuel* fuel) : fuel_{std::move(fuel)} {} 33 | private: 34 | Fuel* fuel_; 35 | }; 36 | 37 | struct Petrol : Fuel { 38 | Petrol() = default; 39 | }; 40 | 41 | struct PremiumPetrol : Petrol { 42 | PremiumPetrol() = default; 43 | }; 44 | 45 | struct Diesel : Fuel { 46 | Diesel() = default; 47 | }; 48 | 49 | struct OpelAstra : Car { 50 | explicit OpelAstra(Fuel& fuel) : 51 | Car{&fuel} {} 52 | }; 53 | 54 | struct NissanQuashqai : Car { 55 | explicit NissanQuashqai(Fuel& fuel) : 56 | Car{&fuel} {} 57 | }; 58 | 59 | struct HondaHRV : Car { 60 | explicit HondaHRV(Fuel& fuel) : 61 | Car{&fuel} {} 62 | }; 63 | 64 | struct HondaHRVDiesel : HondaHRV { 65 | explicit HondaHRVDiesel(Fuel& fuel) : 66 | HondaHRV{fuel} {} 67 | }; 68 | 69 | struct FuelService : kgr::abstract_service {}; 70 | struct PetrolService : kgr::single_service, kgr::overrides {}; 71 | struct PremiumPetrolService : kgr::single_service {}; 72 | struct DieselService : kgr::single_service {}; 73 | 74 | struct OpelAstraService : kgr::service> {}; 75 | struct NissanQuashqaiService : kgr::service> {}; 76 | struct HondaHRVService : kgr::service> {}; 77 | struct HondaHRVDieselService : kgr::service> {}; 78 | 79 | kgr::container makeCarsContainer() { 80 | kgr::container container; 81 | 82 | container.service().setPrice(130.70); 83 | container.service().setPrice(144.50); 84 | container.service().setPrice(135.30); 85 | 86 | return container; 87 | } 88 | 89 | int main() { 90 | kgr::container garage = makeCarsContainer(); 91 | 92 | auto astra1 = garage.service(); 93 | auto astra2 = garage.service(); 94 | auto quashqai = garage.service(); 95 | auto hrv = garage.service(); 96 | auto hrv_diesel = garage.service(); 97 | auto& premium = garage.service(); 98 | 99 | astra2.setFuel(&premium); 100 | 101 | std::cout << std::boolalpha << std::fixed << std::setprecision(2); 102 | std::cout << "`astra1` and `astra2` is the same auto: " 103 | << (&astra1 == &astra2) 104 | << "\n`hrv` and `hrv_diesel` is the same auto: " 105 | << (&hrv == &hrv_diesel) 106 | << "\npremium petrol is single: " 107 | << (&premium == &garage.service()) 108 | << "\nPremium petrol costs " << premium.getPrice() 109 | << " per litre" 110 | << "\n\nRefuel costs:" 111 | << "\n\tOpel Astra(1), 50 litres: " << astra1.refuel(50) 112 | << "\n\tOpel Astra(2), 50 litres: " << astra2.refuel(50) 113 | << "\n\tNissan Quashqai, 30 litres: " << quashqai.refuel(30) 114 | << "\n\tHonda" 115 | << "\n\t HR-V, 30 litres: " << hrv.refuel(30) 116 | << "\n\t HR-V Diesel, 30 litres: " << hrv_diesel.refuel(30) 117 | << '\n'; 118 | 119 | premium.setPrice(147.20); 120 | 121 | std::cout << "Update premium petrol price to " << premium.getPrice() 122 | << "\nNow refuel of Honda HR-V (30 litres) costs " 123 | << hrv.refuel(30) 124 | << '\n'; 125 | } 126 | 127 | -------------------------------------------------------------------------------- /include/kangaru/operator_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_SERVICE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_SERVICE_HPP 3 | 4 | #include "container.hpp" 5 | #include "operator.hpp" 6 | 7 | namespace kgr { 8 | namespace detail { 9 | 10 | /* 11 | * Base class for all non-forking operator service definitions. 12 | * 13 | * Holds a non-owning reference to the container as a pointer. 14 | */ 15 | struct operator_service_base { 16 | inline explicit operator_service_base(in_place_t, kgr::container& container) noexcept : _container{&container} {} 17 | 18 | inline static auto construct(inject_t cs) -> decltype(inject(cs.forward())) { 19 | return inject(cs.forward()); 20 | } 21 | 22 | protected: 23 | kgr::container* _container; 24 | }; 25 | 26 | /* 27 | * Template base class for non-forking operator service definitions. 28 | * 29 | * Only exist to provide the forward function and not template the whole base class. 30 | */ 31 | template 32 | struct operator_service : operator_service_base { 33 | using operator_service_base::operator_service_base; 34 | 35 | auto forward() -> Type { 36 | return Type{*_container}; 37 | } 38 | }; 39 | 40 | /* 41 | * Base class for all forking operator service definitions. 42 | * 43 | * Holds a non-owning reference to the container as a pointer. 44 | */ 45 | template 46 | struct forked_operator_service { 47 | explicit forked_operator_service(in_place_t, kgr::container& container) noexcept : _container{container.fork()} {} 48 | 49 | static auto construct(inject_t cs) -> decltype(inject(cs.forward())) { 50 | return inject(cs.forward()); 51 | } 52 | 53 | auto forward() -> Type { 54 | return Type{std::move(_container)}; 55 | } 56 | 57 | protected: 58 | kgr::container _container; 59 | }; 60 | 61 | } // namespace detail 62 | 63 | /* 64 | * Service defintion for a forking container. 65 | */ 66 | template 67 | struct filtered_fork_service : detail::forked_operator_service { 68 | using detail::forked_operator_service::forked_operator_service; 69 | 70 | auto forward() -> container { 71 | return std::move(this->_container); 72 | } 73 | }; 74 | 75 | /* 76 | * Service defintion for the mapped_invoker. 77 | */ 78 | template 79 | using mapped_invoker_service = detail::operator_service>; 80 | 81 | /* 82 | * Service defintion for the forked_mapped_invoker. 83 | */ 84 | template 85 | using forked_mapped_invoker_service = detail::forked_operator_service>; 86 | 87 | /* 88 | * Service defintion for the generator. 89 | */ 90 | template 91 | using generator_service = detail::operator_service>; 92 | 93 | /* 94 | * Service defintion for the forked_generator. 95 | */ 96 | template 97 | using forked_generator_service = detail::forked_operator_service>; 98 | 99 | /* 100 | * Service defintion for the lazy. 101 | */ 102 | template 103 | using lazy_service = detail::operator_service>; 104 | 105 | /* 106 | * Service defintion for the forked_lazy. 107 | */ 108 | template 109 | using forked_lazy_service = detail::forked_operator_service>; 110 | 111 | /* 112 | * Alias to the filtered_fork_service with the default predicate. 113 | */ 114 | using fork_service = filtered_fork_service; 115 | 116 | /* 117 | * Alias to the invoker service with the default map. 118 | */ 119 | using invoker_service = mapped_invoker_service>; 120 | 121 | /* 122 | * Alias to the forked invoker service with the default map. 123 | * 124 | * TODO: add a way to use a custom predicate 125 | */ 126 | using forked_invoker_service = forked_mapped_invoker_service>; 127 | 128 | 129 | } // namespace kgr 130 | 131 | #endif // KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_SERVICE_HPP 132 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | set(KANGARU_VERSION_MAJOR "4") 4 | set(KANGARU_VERSION_MINOR "3") 5 | set(KANGARU_VERSION_PATCH "2") 6 | 7 | set(KANGARU_VERSION "${KANGARU_VERSION_MAJOR}.${KANGARU_VERSION_MINOR}.${KANGARU_VERSION_PATCH}") 8 | 9 | project(kangaru VERSION ${KANGARU_VERSION} LANGUAGES CXX) 10 | 11 | option(KANGARU_BUILD_EXAMPLES "Build kangaru examples" false) 12 | option(KANGARU_INSTALL "Generate kangaru installation target" true) 13 | option(KANGARU_EXPORT "Export kangaru source tree as package" true) 14 | option(KANGARU_REVERSE_DESTRUCTION "Reverse destruction order in the container mimicking a stack" false) 15 | option(KANGARU_HASH_TYPE_ID "Generate the type IDs using the hashed type names" false) 16 | option(KANGARU_NO_EXCEPTION "Disable exceptions by replacing throws by asserts" false) 17 | option(KANGARU_BENCHMARK "Build benchmark binaries" false) 18 | option(KANGARU_TEST "Build test binaries" false) 19 | option(KANGARU_TEST_CXX14 "Build C++14 test binaries" false) 20 | option(KANGARU_TEST_CXX17 "Build C++17 test binaries" false) 21 | 22 | set(KGR_KANGARU_NOEXCEPTION ${KANGARU_NO_EXCEPTION}) 23 | set(KGR_KANGARU_REVERSE_DESTRUCTION ${KANGARU_REVERSE_DESTRUCTION}) 24 | set(KGR_KANGARU_HASH_TYPE_ID ${KANGARU_HASH_TYPE_ID}) 25 | 26 | if(NOT KGR_KANGARU_REVERSE_DESTRUCTION) 27 | message(WARNING 28 | "The recommended behavior is to enable reverse destruction. This option will the the " 29 | "default behavior for kangaru 5.x.y. Not enabling this option is considered deprecated." 30 | ) 31 | endif() 32 | 33 | configure_file( 34 | cmake/config/config.hpp.in 35 | ${CMAKE_CURRENT_BINARY_DIR}/generated/include/kangaru/detail/config.hpp 36 | @ONLY 37 | ) 38 | 39 | if(KANGARU_TEST_CXX14 OR KANGARU_TEST_CXX17 OR KANGARU_TEST) 40 | set(KANGARU_ENABLE_TESTS true) 41 | endif() 42 | 43 | set(KANGARU_INSTALL_INCLUDE_DIR "include") 44 | 45 | add_library(kangaru INTERFACE) 46 | add_library(kangaru::kangaru ALIAS kangaru) 47 | 48 | target_compile_features(kangaru INTERFACE cxx_std_11) 49 | target_include_directories(kangaru INTERFACE 50 | $ 51 | $ 52 | $ 53 | ) 54 | 55 | if(KANGARU_BUILD_EXAMPLES) 56 | add_subdirectory(examples) 57 | endif() 58 | 59 | if(KANGARU_ENABLE_TESTS) 60 | enable_testing() 61 | add_subdirectory(test) 62 | endif() 63 | 64 | if(KANGARU_BENCHMARK) 65 | add_subdirectory(benchmark) 66 | endif() 67 | 68 | if(KANGARU_EXPORT) 69 | export(PACKAGE kangaru) 70 | endif() 71 | 72 | if(KANGARU_INSTALL) 73 | set(KANGARU_CONFIG_PATH "lib/cmake/kangaru") 74 | 75 | include(CMakePackageConfigHelpers) 76 | 77 | write_basic_package_version_file( 78 | "${CMAKE_CURRENT_BINARY_DIR}/kangaruConfigVersion.cmake" 79 | VERSION ${KANGARU_VERSION} 80 | COMPATIBILITY SameMajorVersion 81 | ) 82 | 83 | # build tree package config 84 | configure_file( 85 | cmake/config/kangaruBuildConfig.cmake.in 86 | ${CMAKE_CURRENT_BINARY_DIR}/kangaruConfig.cmake 87 | @ONLY 88 | ) 89 | 90 | # install tree package config 91 | configure_package_config_file( 92 | cmake/config/kangaruConfig.cmake.in 93 | ${KANGARU_CONFIG_PATH}/kangaruConfig.cmake 94 | INSTALL_DESTINATION ${KANGARU_CONFIG_PATH} 95 | NO_CHECK_REQUIRED_COMPONENTS_MACRO 96 | ) 97 | 98 | install( 99 | DIRECTORY include/kangaru ${CMAKE_CURRENT_BINARY_DIR}/generated/include/kangaru 100 | DESTINATION ${KANGARU_INSTALL_INCLUDE_DIR} FILES_MATCHING PATTERN "*.hpp" 101 | ) 102 | 103 | install( 104 | FILES 105 | "${CMAKE_CURRENT_BINARY_DIR}/${KANGARU_CONFIG_PATH}/kangaruConfig.cmake" 106 | "${CMAKE_CURRENT_BINARY_DIR}/kangaruConfigVersion.cmake" 107 | DESTINATION ${KANGARU_CONFIG_PATH} 108 | ) 109 | 110 | install(TARGETS kangaru EXPORT kangaruTargets) 111 | 112 | export( 113 | EXPORT kangaruTargets 114 | FILE "${CMAKE_CURRENT_BINARY_DIR}/kangaruTargets.cmake" 115 | ) 116 | 117 | install( 118 | EXPORT kangaruTargets FILE kangaruTargets.cmake 119 | DESTINATION ${KANGARU_CONFIG_PATH} 120 | ) 121 | endif() 122 | -------------------------------------------------------------------------------- /include/kangaru/detail/service_storage.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_STORAGE_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_STORAGE_HPP 3 | 4 | #include "traits.hpp" 5 | #include "utils.hpp" 6 | 7 | namespace kgr { 8 | namespace detail { 9 | 10 | /* 11 | * This defines the pointer to a forward function. 12 | * This is simply a shortcut for not writing the function pointer type everywhere. 13 | */ 14 | template 15 | using forward_ptr = service_type(*)(void*); 16 | 17 | template 18 | struct forward_storage { 19 | forward_ptr forward; 20 | }; 21 | 22 | /* 23 | * Pair of a pointer to a service and it's forward function 24 | * Used to passe around a type erase service that yield a particular type 25 | */ 26 | template 27 | struct typed_service_storage { 28 | void* service; 29 | forward_ptr forward; 30 | }; 31 | 32 | /* 33 | * Tag type to tell service_storage that its supposed to contain an index of an override 34 | */ 35 | struct override_index_t {} constexpr override_index{}; 36 | 37 | /* 38 | * Type erased storage for any service type or an override index 39 | */ 40 | struct service_storage { 41 | private: 42 | using function_pointer = void*(*)(void*); 43 | 44 | public: 45 | template 46 | service_storage(typed_service_storage const& storage) noexcept : _service{storage.service} { 47 | static_assert(sizeof(function_pointer) >= sizeof(forward_storage), "The forward storage size exceed the size of a function pointer"); 48 | static_assert(alignof(function_pointer) >= alignof(forward_storage), "The forward storage alignement exceed the alignement of a function pointer"); 49 | 50 | new (&forward_function) forward_storage{storage.forward}; 51 | } 52 | 53 | inline explicit service_storage(override_index_t, std::size_t index) noexcept : _service{nullptr} { 54 | static_assert( 55 | sizeof(function_pointer) >= sizeof(std::size_t) && 56 | alignof(function_pointer) >= alignof(std::size_t), 57 | "The size and alignement of std::size_t exceed the size and alignement of a function pointer" 58 | ); 59 | 60 | new (&forward_function) std::size_t{index}; 61 | } 62 | 63 | inline explicit service_storage(override_index_t, void* collection) noexcept : _service{collection}, forward_function{} {} 64 | 65 | template 66 | auto service() noexcept -> T& { 67 | return *static_cast(_service); 68 | } 69 | 70 | template 71 | auto service() const noexcept -> T const& { 72 | return *static_cast(_service); 73 | } 74 | 75 | template 76 | auto cast() const noexcept -> typed_service_storage { 77 | return typed_service_storage{ 78 | _service, 79 | static_cast const*>(static_cast(&forward_function))->forward 80 | }; 81 | } 82 | 83 | inline auto index() const noexcept -> std::size_t { 84 | return *static_cast(static_cast(&forward_function)); 85 | } 86 | 87 | private: 88 | void* _service; 89 | alignas(alignof(function_pointer)) unsigned char forward_function[sizeof(function_pointer)]; 90 | }; 91 | 92 | /* 93 | * A non moveable and non copyable type wrapper for a service 94 | * 95 | * Also conveniently choose the right constructor 96 | */ 97 | template 98 | struct memory_block { 99 | memory_block(memory_block const&) = delete; 100 | memory_block(memory_block &&) = delete; 101 | memory_block& operator=(memory_block const&) = delete; 102 | memory_block& operator=(memory_block &&) = delete; 103 | 104 | template::value, int> = 0> 105 | explicit memory_block(Args&&... args) noexcept(std::is_nothrow_constructible::value) : 106 | service(std::forward(args)...) {} 107 | 108 | template::value, int> = 0> 109 | explicit memory_block(Args&&... args) noexcept(noexcept(T{std::forward(args)...})) : 110 | service{std::forward(args)...} {} 111 | 112 | T service; 113 | }; 114 | 115 | } // namespace detail 116 | } // namespace kgr 117 | 118 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_STORAGE_HPP 119 | -------------------------------------------------------------------------------- /examples/example4/example4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * This example reflects snippets of code found in the documentation section 4: Managing Containers 6 | * It explains how to branch containers and operate between them. 7 | */ 8 | 9 | struct Service {}; 10 | 11 | struct SingleService1 : kgr::single_service {}; 12 | struct SingleService2 : kgr::single_service {}; 13 | struct SingleService3 : kgr::single_service {}; 14 | 15 | int main() 16 | { 17 | { 18 | kgr::container container1; 19 | 20 | container1.emplace(); 21 | container1.emplace(); 22 | 23 | auto container2 = container1.fork(); 24 | 25 | std::cout << "container2 has SingleService1, SingleService2? "; 26 | std::cout << std::boolalpha << container2.contains() << ", "; 27 | std::cout << std::boolalpha << container2.contains() << '\n'; 28 | 29 | container1.emplace(); 30 | container2.emplace(); 31 | 32 | bool are_same = &container1.service() == &container2.service(); 33 | 34 | std::cout << "container1 and container2 has the same SingleService3? "; 35 | std::cout << std::boolalpha << are_same << '\n'; 36 | 37 | // container1 ---o---o---*---o 38 | // \ 39 | // container2 ---o 40 | } 41 | 42 | std::cout << '\n'; 43 | 44 | { 45 | kgr::container container1; 46 | 47 | container1.emplace(); 48 | 49 | { 50 | auto container2 = container1.fork(); 51 | 52 | container1.emplace(); 53 | container2.emplace(); 54 | container2.emplace(); 55 | 56 | bool are_same2 = &container1.service() == &container2.service(); 57 | 58 | std::cout << "container1 and container2 has the same SingleService2? "; 59 | std::cout << std::boolalpha << are_same2 << '\n'; 60 | 61 | // At that point, both containers have their own SingleService2 instance. 62 | // Only container2 has SingleService3 63 | // container1 is owner of SingleService1, and container2 observes it. 64 | 65 | container1.merge(container2); 66 | 67 | // container2 is still valid, but is no longer owner of any services. 68 | // We can still use the container and will still observe every service it was owner before. 69 | are_same2 = &container1.service() != &container2.service(); 70 | 71 | std::cout << "has container1 kept his own instance of SingleService2 after merge? "; 72 | std::cout << std::boolalpha << are_same2 << '\n'; 73 | 74 | std::cout << "do container1 contains container2's SingleService3 instance? "; 75 | std::cout << std::boolalpha << container1.contains() << '\n'; 76 | } 77 | 78 | // container1 ---o---*---o----------*--- 79 | // \ / 80 | // container2 ---o---o--- 81 | } 82 | 83 | std::cout << '\n'; 84 | 85 | { 86 | kgr::container container1; 87 | 88 | container1.emplace(); 89 | 90 | auto container2 = container1.fork(); 91 | 92 | container2.contains(); // true 93 | container2.contains(); // false 94 | 95 | std::cout << "do container2 has instances of SingleService1, SingleService2? "; 96 | std::cout << std::boolalpha << container2.contains() << ", "; 97 | std::cout << std::boolalpha << container2.contains() << '\n'; 98 | 99 | std::cout << "Creating SingleService2 in container1...\n"; 100 | container1.emplace(); 101 | 102 | std::cout << "Rebasing container2 from container1...\n"; 103 | container2.rebase(container1); // rebase from container1 104 | 105 | std::cout << "do container2 has instances of SingleService1, SingleService2? "; 106 | std::cout << std::boolalpha << container2.contains() << ", "; 107 | std::cout << std::boolalpha << container2.contains() << '\n'; 108 | 109 | // container1 ---o---*---o---*--- 110 | // \ \ 111 | // container2 --------*--- 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL Advanced" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | branches: [ "master" ] 19 | schedule: 20 | - cron: '32 1 * * 6' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | permissions: 32 | # required for all workflows 33 | security-events: write 34 | 35 | # required to fetch internal or private CodeQL packs 36 | packages: read 37 | 38 | # only required for workflows in private repositories 39 | actions: read 40 | contents: read 41 | 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | include: 46 | - language: c-cpp 47 | build-mode: manual 48 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 49 | # Use `c-cpp` to analyze code written in C, C++ or both 50 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 51 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 52 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 53 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 54 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 55 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 56 | steps: 57 | - name: Checkout repository 58 | uses: actions/checkout@v4 59 | with: 60 | submodules: true 61 | 62 | - name: Install ninja-build tool 63 | uses: seanmiddleditch/gha-setup-ninja@v5 64 | 65 | # Initializes the CodeQL tools for scanning. 66 | - name: Initialize CodeQL 67 | uses: github/codeql-action/init@v3 68 | with: 69 | languages: ${{ matrix.language }} 70 | build-mode: ${{ matrix.build-mode }} 71 | # If you wish to specify custom queries, you can do so here or in a config file. 72 | # By default, queries listed here will override any specified in a config file. 73 | # Prefix the list here with "+" to use these queries and those in the config file. 74 | 75 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 76 | # queries: security-extended,security-and-quality 77 | 78 | # If the analyze step fails for one of the languages you are analyzing with 79 | # "We were unable to automatically build your code", modify the matrix above 80 | # to set the build mode to "manual" for that language. Then modify this step 81 | # to build your code. 82 | # ℹ️ Command-line programs to run using the OS shell. 83 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 84 | - if: matrix.build-mode == 'manual' 85 | shell: bash 86 | run: | 87 | cmake --preset dev 88 | cmake --build --preset debug 89 | 90 | - name: Perform CodeQL Analysis 91 | uses: github/codeql-action/analyze@v3 92 | with: 93 | category: "/language:${{matrix.language}}" 94 | -------------------------------------------------------------------------------- /examples/abstract_factory/abstract_factory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // Shape classes 9 | // These are the classes we want to create with our factory. 10 | struct Shape { 11 | virtual int area() = 0; 12 | virtual ~Shape() = default; 13 | }; 14 | 15 | struct Square : Shape { 16 | explicit Square(int l) noexcept : length{l} {} 17 | int length; 18 | 19 | int area() override { 20 | std::cout << "Square!\n"; 21 | return length * length; 22 | } 23 | }; 24 | 25 | struct Rectangle : Shape { 26 | explicit Rectangle(int h, int w) noexcept : height{h}, width{w} {} 27 | int height, width; 28 | 29 | int area() override { 30 | std::cout << "Rectangle!\n"; 31 | return height * width; 32 | } 33 | }; 34 | 35 | struct Circle : Shape { 36 | explicit Circle(int r) noexcept : radius{r} {} 37 | int radius; 38 | 39 | int area() override { 40 | std::cout << "Circle!\n"; 41 | return int(3.14 * radius * radius); 42 | } 43 | }; 44 | 45 | // Other classes. Contains information we need to instantiate shape classes. 46 | struct Window { 47 | int height, width; 48 | }; 49 | 50 | struct CursorState { 51 | int dragging_distance; 52 | }; 53 | 54 | // This is our factory. 55 | // You can add a type associated with a lambda that constructs it. 56 | // Since that lambda is called through the invoker, we can receive any number of services. 57 | struct Factory { 58 | explicit Factory(kgr::invoker i) noexcept : invoker{i} {} 59 | 60 | template 61 | void add(std::string const& type, T maker) { 62 | makers[type] = [maker](kgr::invoker invoker) { 63 | return std::unique_ptr{invoker(maker)}; 64 | }; 65 | } 66 | 67 | std::unique_ptr make(std::string type) { 68 | return makers[type](invoker); 69 | } 70 | 71 | private: 72 | // We have the invoker to call functions in the map. 73 | kgr::invoker invoker; 74 | 75 | // A map of string as key and functions that takes an invoker as value. 76 | std::unordered_map(kgr::invoker)>> makers; 77 | }; 78 | 79 | // This is our services. Factory depends on the invoker. 80 | struct WindowService : kgr::single_service {}; 81 | struct CursorStateService : kgr::single_service {}; 82 | struct FactoryService : kgr::service> {}; 83 | 84 | // This is the mapping. We need it to make the lambda callable with injection. 85 | auto service_map(Window const&) -> WindowService; 86 | auto service_map(CursorState const&) -> CursorStateService; 87 | auto service_map(Factory const&) -> FactoryService; 88 | 89 | Factory setup_factory(Factory factory) { 90 | // To make a rectangle, we need the window only. 91 | factory.add("Rectangle", [](Window& window) { 92 | return new Rectangle{window.width / 10, window.height / 10}; 93 | }); 94 | 95 | // To make a square, we need the window and the cursor state. 96 | factory.add("Square", [](Window& window, CursorState& cursor_state) { 97 | return new Square{(window.height / 10 ) - cursor_state.dragging_distance}; 98 | }); 99 | 100 | // To make a circle, we only need the cursor state. 101 | factory.add("Circle", [](CursorState& cursor_state) { 102 | return new Circle{cursor_state.dragging_distance}; 103 | }); 104 | 105 | return factory; 106 | } 107 | 108 | int main() { 109 | kgr::container container; 110 | 111 | // We setup the factory. 112 | // Since FactoryService, we can simply ask the container to invoke it. 113 | Factory factory = container.invoke(setup_factory); 114 | 115 | // We instantiate the other services with parameters. 116 | container.emplace(640, 480); 117 | container.emplace(20); 118 | 119 | // area to display. 120 | int area; 121 | 122 | // Here we make many shapes. 123 | // Even though creating a specific state require 124 | // many specific parameters, we only need to give the string of the type. 125 | auto circle_shape = factory.make("Circle"); 126 | area = circle_shape->area(); 127 | std::cout << "Area of circle_shape is: " << area << " pixels squared.\n\n"; 128 | 129 | auto square_shape = factory.make("Square"); 130 | area = square_shape->area(); 131 | std::cout << "Area of square_shape is: " << area << " pixels squared.\n\n"; 132 | 133 | auto rectangle_shape = factory.make("Rectangle"); 134 | area = rectangle_shape->area(); 135 | std::cout << "Area of rectangle_shape is: " << area << " pixels squared.\n"; 136 | } 137 | -------------------------------------------------------------------------------- /include/kangaru/detail/service_check.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_CHECK_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_CHECK_HPP 3 | 4 | #include "traits.hpp" 5 | #include "function_traits.hpp" 6 | #include "construct_function.hpp" 7 | #include "container_service.hpp" 8 | #include "override_range_service.hpp" 9 | #include "override_traits.hpp" 10 | 11 | namespace kgr { 12 | namespace detail { 13 | 14 | template 15 | using injected_forwarded_type = meta_list_transform_t; 16 | 17 | template 18 | using curry_is_constructible_from_construct = expand_all< 19 | injected_forwarded_type, function_result_t, detected_t>>>, 20 | is_service_instantiable, T 21 | >; 22 | 23 | /* 24 | * Trait that check if the service construct function can be called and returns arguments that construct the service. 25 | */ 26 | template 27 | using is_constructible_from_construct = instantiate_if_or< 28 | is_tuple>>::value, 29 | std::false_type, curry_is_constructible_from_construct, T, Args... 30 | >; 31 | 32 | /* 33 | * Trait that check if the service definition can be constructed given the return type of it's construct function. 34 | */ 35 | template 36 | using is_service_constructible = instantiate_if_or< 37 | !is_abstract_service::value && !is_container_service::value && !is_override_range_service::value, std::true_type, 38 | is_constructible_from_construct, T, Args... 39 | >; 40 | 41 | template 42 | using is_service_constructible_if_required = bool_constant< 43 | is_service_constructible::value || is_supplied_service::value 44 | >; 45 | 46 | /* 47 | * Meta trait that applies a trait recursively for each dependencies and thier dependencies. 48 | */ 49 | template class Trait, typename T, typename... Args> 50 | struct dependency_trait { 51 | // This subtrait recursively call dependency_trait for each dependencies 52 | template 53 | struct service_check_dependencies { 54 | static constexpr bool value = conjunction< 55 | Trait>..., 56 | dependency_trait>... 57 | >::value; 58 | }; 59 | 60 | static constexpr bool value = 61 | is_supplied_service::value || 62 | expand_minus_n< 63 | sizeof...(Args), 64 | detected_or, function_arguments_t, detected_t>, 65 | service_check_dependencies 66 | >::value; 67 | }; 68 | 69 | /* 70 | * Validity check for default services 71 | */ 72 | template 73 | using is_default_service_valid = std::integral_constant::value || !has_default::value) && 75 | is_default_overrides_abstract::value && 76 | is_default_convertible::value 77 | >; 78 | 79 | template 80 | using is_abstract_not_final = std::integral_constant::value || !is_final_service::value 82 | >; 83 | 84 | template 85 | using polymorphic_service_check = bool_constant< 86 | is_default_service_valid::value && 87 | is_override_convertible::value && 88 | is_override_polymorphic::value && 89 | is_override_services::value && 90 | is_override_not_final::value && 91 | is_abstract_not_final::value 92 | >; 93 | 94 | /* 95 | * Validity check for a service, without it's dependencies 96 | */ 97 | template 98 | using shallow_service_check = std::integral_constant::value && 100 | (is_service_constructible::value || is_supplied_service::value) && 101 | (is_construct_function_callable_if_needed::value || is_supplied_service::value) && 102 | instantiate_if_or::value, std::true_type, polymorphic_service_check, T>::value 103 | >; 104 | 105 | /* 106 | * Validity check for dependencies of a service 107 | */ 108 | template 109 | using dependency_check = dependency_trait; 110 | 111 | template 112 | struct service_check : bool_constant< 113 | shallow_service_check::value && dependency_check::value 114 | > {}; 115 | 116 | } // namespace detail 117 | } // namespace kgr 118 | 119 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_CHECK_HPP 120 | -------------------------------------------------------------------------------- /test/src/dependency.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST_CASE("Injected singles are the same returned by the container", "[dependency]") { 5 | struct Service1 {}; 6 | 7 | struct Service2 { 8 | Service2() = default; 9 | explicit Service2(Service1& s) : s1{&s} {} 10 | 11 | Service1* s1 = nullptr; 12 | }; 13 | 14 | struct Definition1 : kgr::single_service {}; 15 | struct Definition2 : kgr::service> {}; 16 | 17 | kgr::container c; 18 | 19 | REQUIRE(c.service().s1 == &c.service()); 20 | } 21 | 22 | TEST_CASE("Injected arguments are sent correctly to the constructor", "[dependency]") { 23 | struct Service1 {}; 24 | 25 | struct Service2 { 26 | Service2() = default; 27 | explicit Service2(Service1& s) : s1{&s} {} 28 | 29 | Service1* s1 = nullptr; 30 | }; 31 | 32 | struct Definition1 : kgr::single_service {}; 33 | 34 | struct Definition2 { 35 | explicit Definition2(kgr::in_place_t) {} 36 | Definition2(kgr::in_place_t, Service1& dep) : service{dep} {} 37 | 38 | Service2 forward() { 39 | return std::move(service); 40 | } 41 | 42 | static auto construct(kgr::inject_t d1) -> decltype(kgr::inject(d1.forward())) { 43 | return kgr::inject(d1.forward()); 44 | } 45 | 46 | Service2 service; 47 | }; 48 | 49 | kgr::container c; 50 | 51 | REQUIRE(c.service().s1 == &c.service()); 52 | } 53 | 54 | TEST_CASE("Injected arguments can be single and non single", "[dependency]") { 55 | static bool constructor_called = false; 56 | 57 | struct Service1 {}; 58 | struct Service2 {}; 59 | 60 | struct Service3 { 61 | Service3() = default; 62 | 63 | Service3(Service1& s, Service2) : s1{&s} { 64 | constructor_called = true; 65 | } 66 | 67 | Service1* s1 = nullptr; 68 | }; 69 | 70 | struct Definition1 : kgr::single_service {}; 71 | struct Definition2 : kgr::service {}; 72 | 73 | struct Definition3 { 74 | Definition3(kgr::in_place_t, Service1& dep1, Service2 dep2) : service{dep1, std::move(dep2)} {} 75 | 76 | Service3 forward() { 77 | return std::move(service); 78 | } 79 | 80 | static auto construct(kgr::inject_t d1, kgr::inject_t d2) -> decltype(kgr::inject(d1.forward(), d2.forward())) { 81 | return kgr::inject(d1.forward(), d2.forward()); 82 | } 83 | 84 | Service3 service; 85 | }; 86 | 87 | (void) kgr::container{}.service(); 88 | 89 | REQUIRE(constructor_called); 90 | } 91 | 92 | TEST_CASE("Container injects arguments recursively", "[dependency]") { 93 | static bool service1_constructed; 94 | static bool service2_constructed; 95 | static bool service3_one_constructed; 96 | static bool service3_two_constructed; 97 | static bool service4_constructed; 98 | 99 | service1_constructed = false; 100 | service2_constructed = false; 101 | service3_one_constructed = false; 102 | service3_two_constructed = false; 103 | service4_constructed = false; 104 | 105 | struct Service1 { 106 | Service1() { service1_constructed = true; } 107 | }; 108 | 109 | struct Service2 { 110 | Service2() = default; 111 | explicit Service2(Service1) { service2_constructed = true; } 112 | }; 113 | 114 | struct Service3 { 115 | Service3() = default; 116 | Service3(Service1, Service2) { service3_two_constructed = true; } 117 | explicit Service3(Service2) { service3_one_constructed = true; } 118 | }; 119 | 120 | struct Service4 { 121 | Service4() = default; 122 | explicit Service4(Service3) { service4_constructed = true; } 123 | }; 124 | 125 | struct Definition1 : kgr::service {}; 126 | struct Definition2 : kgr::service> {}; 127 | struct Definition3One : kgr::service> {}; 128 | struct Definition3Two : kgr::service> {}; 129 | struct Definition4 : kgr::service> {}; 130 | 131 | SECTION("Injected service can have dependencies") { 132 | (void) kgr::container{}.service(); 133 | 134 | CHECK(service1_constructed); 135 | CHECK(service2_constructed); 136 | CHECK(!service3_two_constructed); 137 | CHECK(!service4_constructed); 138 | REQUIRE(service3_one_constructed); 139 | } 140 | 141 | SECTION("Injected service can have multiple dependencies") { 142 | (void) kgr::container{}.service(); 143 | 144 | CHECK(service1_constructed); 145 | CHECK(service2_constructed); 146 | CHECK(!service3_one_constructed); 147 | CHECK(service3_two_constructed); 148 | REQUIRE(service4_constructed); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /test/src/service_range.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace service_range_test { 8 | 9 | template 10 | void test_iterator_values(It b, It e, std::initializer_list values) { 11 | for (auto value : values) { 12 | auto has_value = std::any_of(b, e, [&](decltype((*e)) elem) { 13 | return elem.type == value; 14 | }); 15 | 16 | CHECK(has_value); 17 | } 18 | } 19 | 20 | template 21 | void test_iterator_not_values(It b, It e, std::initializer_list values) { 22 | for (auto value : values) { 23 | auto exclude_value = std::all_of(b, e, [&](decltype((*e)) elem) { 24 | return elem.type != value; 25 | }); 26 | 27 | CHECK(exclude_value); 28 | } 29 | } 30 | 31 | enum struct Type { BaseT, Derived1T, Derived2T }; 32 | 33 | struct Base { 34 | Base(Type t = Type::BaseT) : type{ t } {} 35 | Type type; 36 | }; 37 | 38 | struct BaseService : kgr::single_service, kgr::polymorphic {}; 39 | 40 | struct Derived1Service : kgr::single_service, kgr::overrides { 41 | static auto construct() noexcept -> kgr::inject_result { 42 | return kgr::inject(Type::Derived1T); 43 | } 44 | }; 45 | 46 | struct Derived2Service : kgr::single_service, kgr::overrides { 47 | static auto construct() noexcept -> kgr::inject_result { 48 | return kgr::inject(Type::Derived2T); 49 | } 50 | }; 51 | 52 | struct AbstractService : kgr::abstract_service {}; 53 | struct Concrete1Service : kgr::single_service, kgr::overrides {}; 54 | struct Concrete2Service : kgr::single_service, kgr::overrides {}; 55 | 56 | } 57 | 58 | TEST_CASE("The container holds a list of overriders", "[service_range, virtual]") { 59 | using namespace service_range_test; 60 | kgr::container container; 61 | 62 | SECTION("Can retrieve") { 63 | container.emplace(); 64 | REQUIRE(container.service().type == Type::BaseT); 65 | 66 | container.emplace(); 67 | 68 | REQUIRE(container.service().type == Type::Derived1T); 69 | 70 | SECTION("All service of type") { 71 | container.emplace(); 72 | REQUIRE(container.service().type == Type::Derived2T); 73 | 74 | auto range = container.service>(); 75 | 76 | test_iterator_values( 77 | range.begin(), range.end(), 78 | {Type::BaseT, Type::Derived1T, Type::Derived2T} 79 | ); 80 | 81 | CHECK(std::distance(range.begin(), range.end()) == 3); 82 | } 83 | } 84 | 85 | SECTION("Only service in the fork") { 86 | kgr::container fork; 87 | 88 | SECTION("filtering except") { 89 | container.emplace(); 90 | container.emplace(); 91 | fork = container.fork(kgr::except{}); 92 | fork.emplace(); 93 | } 94 | 95 | SECTION("filtering only") { 96 | container.emplace(); 97 | container.emplace(); 98 | fork = container.fork(kgr::only{}); 99 | fork.emplace(); 100 | } 101 | 102 | SECTION("emplace in original") { 103 | container.emplace(); 104 | fork = container.fork(); 105 | container.emplace(); 106 | fork.emplace(); 107 | } 108 | 109 | auto const range_original = container.service>(); 110 | auto const range_fork = fork.service>(); 111 | 112 | test_iterator_values( 113 | range_fork.begin(), range_fork.end(), 114 | {Type::BaseT, Type::Derived2T} 115 | ); 116 | 117 | test_iterator_values( 118 | range_original.begin(), range_original.end(), 119 | {Type::BaseT, Type::Derived1T} 120 | ); 121 | 122 | test_iterator_not_values( 123 | range_fork.begin(), range_fork.end(), 124 | {Type::Derived1T} 125 | ); 126 | 127 | test_iterator_not_values( 128 | range_original.begin(), range_original.end(), 129 | {Type::Derived2T} 130 | ); 131 | } 132 | 133 | SECTION("Of abstract services") { 134 | container.emplace(Type::Derived1T); 135 | container.emplace(Type::Derived2T); 136 | 137 | auto const range = container.service>(); 138 | 139 | test_iterator_values( 140 | range.begin(), range.end(), 141 | {Type::Derived1T, Type::Derived2T} 142 | ); 143 | 144 | test_iterator_not_values( 145 | range.begin(), range.end(), 146 | {Type::BaseT} 147 | ); 148 | 149 | CHECK(std::distance(range.begin(), range.end()) == 2); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Catch2 QUIET) 2 | 3 | add_library(kangaru_test_common INTERFACE) 4 | 5 | target_link_libraries(kangaru_test_common INTERFACE Catch2::Catch2) 6 | target_compile_features(kangaru_test_common INTERFACE cxx_std_11) 7 | target_compile_definitions(kangaru_test_common INTERFACE CATCH_CONFIG_MAIN) 8 | 9 | target_include_directories(kangaru_test_common INTERFACE $) 10 | 11 | target_compile_options(kangaru_test_common INTERFACE 12 | $<$,$,$>:-Wall -Wextra> 13 | $<$:/W3> 14 | ) 15 | 16 | function(add_kgr_error_test testname error_message) 17 | add_executable(error_${testname}_test src/error.cpp) 18 | target_link_libraries(error_${testname}_test PRIVATE kangaru_test_common) 19 | target_compile_definitions(error_${testname}_test PRIVATE kgr_error_test_${testname}) 20 | 21 | set_target_properties(error_${testname}_test 22 | PROPERTIES EXCLUDE_FROM_ALL true 23 | EXCLUDE_FROM_DEFAULT_BUILD true 24 | ) 25 | 26 | add_test(NAME error_${testname} COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}" --target error_${testname}_test --config $) 27 | set_tests_properties(error_${testname} PROPERTIES PASS_REGULAR_EXPRESSION "${error_message}") 28 | endfunction() 29 | 30 | if (NOT Catch2_FOUND) 31 | add_subdirectory(third_party/Catch2) 32 | mark_as_advanced(Catch2_DIR) 33 | mark_as_advanced(CATCH_USE_VALGRIND) 34 | mark_as_advanced(CATCH_BUILD_TESTING) 35 | mark_as_advanced(CATCH_BUILD_EXAMPLES) 36 | mark_as_advanced(CATCH_BUILD_EXTRA_TESTS) 37 | mark_as_advanced(CATCH_ENABLE_COVERAGE) 38 | mark_as_advanced(CATCH_ENABLE_WERROR) 39 | mark_as_advanced(CATCH_INSTALL_DOCS) 40 | mark_as_advanced(CATCH_INSTALL_HELPERS) 41 | mark_as_advanced(BUILD_TESTING) 42 | endif() 43 | 44 | function(add_kgr_test TEST_NAME) 45 | if(${ARGC} GREATER_EQUAL 2) 46 | set(TEST_FILE "${ARGV1}") 47 | else() 48 | set(TEST_FILE "${TEST_NAME}.cpp") 49 | endif() 50 | 51 | add_executable("${TEST_NAME}_test" "src/${TEST_FILE}") 52 | target_link_libraries("${TEST_NAME}_test" PRIVATE kangaru_test_common) 53 | add_test(${TEST_NAME} "${TEST_NAME}_test") 54 | endfunction() 55 | 56 | if(KANGARU_TEST) 57 | add_kgr_test(autocall) 58 | add_kgr_test(autowire) 59 | add_kgr_test(basic) 60 | add_kgr_test(container) 61 | add_kgr_test(default_services) 62 | add_kgr_test(definition) 63 | add_kgr_test(dependency) 64 | add_kgr_test(invoke) 65 | add_kgr_test(noexcept_compiler_disabled_supplied noexcept.cpp) 66 | add_kgr_test(noexcept_compiler_disabled_abstract noexcept.cpp) 67 | add_kgr_test(noexcept_macro_disabled_supplied noexcept.cpp) 68 | add_kgr_test(noexcept_macro_disabled_abstract noexcept.cpp) 69 | add_kgr_test(operator) 70 | add_kgr_test(service_map) 71 | add_kgr_test(service_range) 72 | add_kgr_test(single) 73 | add_kgr_test(virtual) 74 | add_kgr_error_test(not_service_no_forward "The type sent to kgr::container::service\\(\\.\\.\\.\\) is not a service\\.") 75 | add_kgr_error_test(not_service_has_forward "The service type must not contain any virtual functions or virtual inheritance\\.") 76 | endif() 77 | 78 | target_compile_options(noexcept_compiler_disabled_supplied_test PRIVATE 79 | $<$,$,$>:-fno-exceptions> 80 | $<$:/EHs-c-> 81 | ) 82 | 83 | target_compile_options(noexcept_compiler_disabled_abstract_test PRIVATE 84 | $<$,$,$>:-fno-exceptions> 85 | $<$:/EHs-c-> 86 | ) 87 | 88 | target_compile_definitions(noexcept_macro_disabled_supplied_test PRIVATE KGR_KANGARU_NOEXCEPTION KGR_KANGARU_TEST_SUPPLIED_ABORT) 89 | target_compile_definitions(noexcept_macro_disabled_abstract_test PRIVATE KGR_KANGARU_NOEXCEPTION KGR_KANGARU_TEST_ABSTRACT_ABORT) 90 | target_compile_definitions(noexcept_compiler_disabled_supplied_test PRIVATE KGR_KANGARU_TEST_SUPPLIED_ABORT) 91 | target_compile_definitions(noexcept_compiler_disabled_abstract_test PRIVATE KGR_KANGARU_TEST_ABSTRACT_ABORT) 92 | 93 | if(KANGARU_TEST_CXX14) 94 | add_kgr_test(generic_lambdas) 95 | set_property(TARGET generic_lambdas_test PROPERTY CXX_STANDARD 14) 96 | target_compile_features(generic_lambdas_test PRIVATE cxx_std_14) 97 | endif() 98 | 99 | if(KANGARU_TEST_CXX14 AND KANGARU_HASH_TYPE_ID) 100 | add_kgr_test(type_id_cxx14) 101 | set_property(TARGET type_id_cxx14_test PROPERTY CXX_STANDARD 14) 102 | target_compile_features(type_id_cxx14_test PRIVATE cxx_std_14) 103 | endif() 104 | 105 | if(KANGARU_TEST_CXX17) 106 | add_kgr_test(noexcept_invoke) 107 | set_property(TARGET noexcept_invoke_test PROPERTY CXX_STANDARD 17) 108 | target_compile_features(noexcept_invoke_test PRIVATE cxx_std_17) 109 | endif() 110 | -------------------------------------------------------------------------------- /examples/example9/example9.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | /** 7 | * This example reflects snippets of code found in the documentation section 9: Custom Definitions 8 | * It explains how to make your own service definition without using generic ones from kangaru. 9 | */ 10 | 11 | // User classes. 12 | struct Window {}; 13 | struct Camera {}; 14 | struct BasicFileManager { 15 | BasicFileManager() { 16 | std::cout << "BasicFileManager constructed\n"; 17 | } 18 | }; 19 | struct FileManager { 20 | FileManager(Window&, Camera, int parameter = 0) { 21 | std::cout << "Parameter: " << parameter << '\n'; 22 | } 23 | }; 24 | 25 | // Service definitions 26 | struct WindowService : kgr::single_service {}; 27 | struct CameraService : kgr::service {}; 28 | 29 | 30 | // Custom defintion. We kind of reimplement what `kgr::service` is doing 31 | struct BasicFileManagerService { 32 | explicit BasicFileManagerService(kgr::in_place_t) : instance{} { 33 | std::cout << "BasicFileManagerService::BasicFileManagerService called\n"; 34 | } 35 | 36 | static auto construct() -> kgr::inject_result<> { 37 | std::cout << "BasicFileManagerService::construct called\n"; 38 | return kgr::inject(); 39 | } 40 | 41 | // return as move, invalidating the service definition is okay since it's not single and won't be reused. 42 | BasicFileManager forward() { 43 | return std::move(instance); 44 | } 45 | 46 | private: 47 | BasicFileManager instance; 48 | }; 49 | 50 | // Custom defintion. We kind of reimplement what `kgr::single_service` is doing, 51 | // and we defined injected parameter in construct. 52 | // Parameter returned from construct are sent to the constructor 53 | struct FileManagerService : kgr::single { 54 | FileManagerService(kgr::in_place_t, Window& w, Camera c) : instance{w, std::move(c)} {} 55 | 56 | static auto construct(kgr::inject_t ws, kgr::inject_t cs) 57 | -> kgr::inject_result, kgr::service_type> 58 | { 59 | std::cout << "FileManagerService::construct called\n"; 60 | return kgr::inject(ws.forward(), cs.forward()); 61 | } 62 | 63 | FileManager forward() { 64 | return std::move(instance); 65 | } 66 | 67 | private: 68 | FileManager instance; 69 | }; 70 | 71 | // and we defined injected parameter in construct. 72 | // Parameter returned from construct are sent to the constructor 73 | // We are receiving a int parameter, which must be sent from the caller. 74 | struct FileManagerParamService { 75 | FileManagerParamService(kgr::in_place_t, Window& w, Camera c, int p) : instance{w, std::move(c), p} {} 76 | 77 | static auto construct(kgr::inject_t ws, kgr::inject_t cs, int p) 78 | -> kgr::inject_result, kgr::service_type, int> 79 | { 80 | std::cout << "FileManagerParamService::construct called\n"; 81 | return kgr::inject(ws.forward(), cs.forward(), p); 82 | } 83 | 84 | FileManager forward() { 85 | return std::move(instance); 86 | } 87 | 88 | private: 89 | FileManager instance; 90 | }; 91 | 92 | // and we defined injected parameter in construct. 93 | // Parameter returned from construct are sent to the constructor 94 | // We are receiving a parameter pack, which can be empty or parameters. 95 | struct FileManagerParamTemplService { 96 | template 97 | FileManagerParamTemplService(kgr::in_place_t, Args&&... args) : instance{std::forward(args)...} {} 98 | 99 | template 100 | static auto construct(kgr::inject_t ws, kgr::inject_t cs, Args&&... args) 101 | -> kgr::inject_result, kgr::service_type, Args...> 102 | { 103 | std::cout << "FileManagerParamTemplService::construct called\n"; 104 | return kgr::inject(ws.forward(), cs.forward(), std::forward(args)...); 105 | } 106 | 107 | FileManager forward() { 108 | return std::move(instance); 109 | } 110 | 111 | private: 112 | FileManager instance; 113 | }; 114 | 115 | int main() { 116 | kgr::container container; 117 | 118 | // BasicFileManagerService::construct called, then the FileManager constructor 119 | BasicFileManager fm1 = container.service(); 120 | 121 | std::cout << '\n'; 122 | 123 | // BasicFileManagerService::construct called, then the FileManager constructor 124 | FileManager fm2 = container.service(); 125 | 126 | std::cout << '\n'; 127 | 128 | // BasicFileManagerService::construct called, then the FileManager constructor 129 | FileManager fm3 = container.service(2); 130 | 131 | std::cout << '\n'; 132 | 133 | // BasicFileManagerService::construct called, then the FileManager constructor 134 | FileManager fm4 = container.service(2); // Args is int 135 | FileManager fm5 = container.service(); // Args is empty 136 | } 137 | -------------------------------------------------------------------------------- /doc/section07_autocall.md: -------------------------------------------------------------------------------- 1 | Autocall 2 | ======== 3 | 4 | Sometime only constructing a service is not enough. Some classes needs configuration, or some function to be called initially, or even call some setters. 5 | 6 | The autocall feature is doing exactly that. It's a list of function to call upon the construction of a service. 7 | The container will invoke all function in that list. You can use this feature to perform injection through setters. 8 | 9 | 10 | First of all, we recommend defining a shortcut for using `kgr::invoke`. Of you have C++17 enabled, you can define that shortcut like that: 11 | ```c++ 12 | template 13 | using method = kgr::method; 14 | ``` 15 | 16 | Alternatively, if you only have C++11 or C++14 on hand, you can use a macro: 17 | ```c++ 18 | #define METHOD(...) ::kgr::method 19 | ``` 20 | 21 | Of course, you are free to name them as you want. 22 | Once your shortcut is declared, let's get started! 23 | 24 | ## Enabling Autocall 25 | 26 | Any service can extends the class `kgr::autocall`. This class enable the needed metadata for the container to call the methods you want to be called. 27 | 28 | The `kgr::autocall` type is a list of method to call in your class. More specifically, a list of `kgr::method`. 29 | 30 | Let's see an example of it's usage. So we have this class: 31 | 32 | ```c++ 33 | struct MessageBus { 34 | void init() { 35 | max_delay = 42; 36 | } 37 | 38 | private: 39 | int max_delay; 40 | }; 41 | ``` 42 | 43 | If we want `init()` to be called at the service's construction, we need our definition to extends `kgr::autocall`: 44 | 45 | ```c++ 46 | struct MessageBusService : kgr::service, kgr::autocall {}; 47 | ``` 48 | 49 | Great! Now creating the service will call that function: 50 | 51 | ```c++ 52 | // MessageBus::init is called before returning 53 | MessageBus md = container.service(); 54 | ``` 55 | 56 | ## Parameters 57 | 58 | Here comes the fancy thing. Functions listed in `kgr::autocall` can receive other services. 59 | In fact, the container will call these functions using the `invoke` function. So you can receive any other services. 60 | 61 | For example, we need `max_delay` to be calculated with values that comes from other service. 62 | So here's our class according to the new need: 63 | 64 | ```c++ 65 | struct MessageBus { 66 | void init(Window& window) { 67 | max_delay = 3 * window.get_framerate(); 68 | } 69 | 70 | private: 71 | int max_delay; 72 | }; 73 | ``` 74 | 75 | That's it! You can add any number of parameter as you wish, the definition will stay the same and the method will receive what you ask for. 76 | 77 | ## Multiple Methods 78 | 79 | As said before, `kgr::autocall` is a list of method to call. You can have as many method to call as you wish 80 | ```c++ 81 | struct MessageBus { 82 | void init(Window& window, Camera& camera) { 83 | max_delay = 3 * window.get_framerate(); 84 | } 85 | 86 | void set_scene(Scene& scene) { 87 | this->scene = &scene; 88 | } 89 | 90 | private: 91 | Scene* scene; 92 | int max_delay; 93 | }; 94 | 95 | struct MessageBusService : kgr::service, kgr::autocall< 96 | METHOD(&MessageBus::init), 97 | METHOD(&MessageBus::set_scene) 98 | > {}; 99 | ``` 100 | 101 | The functions are called in the order that are listed in `kgr::autocall`. 102 | 103 | ## Non-Member Functions 104 | 105 | Non-member function are also supported. Here's the same code as above, using non-member functions: 106 | 107 | ```c++ 108 | struct MessageBus { 109 | Scene* scene; 110 | int max_delay; 111 | }; 112 | 113 | void init(MessageBus& self, Window& window, Camera& camera) { 114 | self.max_delay = 3 * window.get_framerate(); 115 | } 116 | 117 | void set_scene(MessageBus& self, Scene& scene) { 118 | self.scene = &scene; 119 | } 120 | 121 | struct MessageBusService : kgr::service, kgr::autocall< 122 | METHOD(init), 123 | METHOD(set_scene) 124 | > {}; 125 | ``` 126 | 127 | 128 | ## Specifying The Service Map 129 | 130 | In previous examples, we used the default service map. If you deal with advanced mapping, you might want to specify which maps to use. 131 | You can set the default map to use in the first parameter of autocall: 132 | 133 | ```c++ 134 | struct MyMap1 {}; 135 | struct MyMap2 {}; 136 | 137 | struct MessageBusService : kgr::service, kgr::autocall< 138 | kgr::map, 139 | METHOD(&MessageBus::init), 140 | METHOD(&MessageBus::set_scene) 141 | > {}; 142 | ``` 143 | 144 | ## Specifying Services 145 | 146 | Alternatively, you can list needed services for every methods. Parameters are grouped within the `kgr::invoke` class: 147 | 148 | ```c++ 149 | struct MessageBusService : kgr::service, kgr::autocall< 150 | kgr::invoke, 151 | METHOD(&MessageBus::set_scene) 152 | > {}; 153 | ``` 154 | 155 | To see those snippets in action, go check [example7](../examples/example7/example7.cpp). 156 | 157 | [Next chapter: Operator Services](section08_operator.md) 158 | -------------------------------------------------------------------------------- /doc/section10_mapping.md: -------------------------------------------------------------------------------- 1 | Advanced Mapping 2 | ================ 3 | 4 | Until now, we only used basic mapping in other examples. The service map was a simple parameter to service mapping. 5 | But there's a mechanism to make multiple maps, prioritize them, and use many of them. 6 | 7 | ## The Map Parameter 8 | 9 | In a mapping expression, you can actually put a second parameter. That parameter is `kgr::map_t<>`. 10 | 11 | So here's an example of a service map with that parameter: 12 | 13 | ```c++ 14 | auto service_map(Service, kgr::map_t<>) -> Definition; 15 | ``` 16 | 17 | What is it doing? What that parameter for? Well, except for disambiguation for that declaration, not a lot. 18 | It will simply make the container prefer this one. Consider this: 19 | 20 | ```c++ 21 | auto service_map(Service) -> Definition1; 22 | auto service_map(Service, kgr::map_t<>) -> Definition2; 23 | ``` 24 | 25 | When using `kgr::mapped_service_t`, that will yield `Definition2`. Because it has higher priority for the service map. 26 | 27 | ## Named Map 28 | 29 | The additional parameter can be configured with a name. This is how to make a map with a particular name. 30 | 31 | A named map in not gonna be used unless to tell the container to use that map. 32 | 33 | Every function or metafunction that deal with the service map can have a named map specified. Here are some examples of their usage: 34 | 35 | ```c++ 36 | struct MyMap; 37 | 38 | auto service_map(Service) -> Definition1; 39 | auto service_map(Service, kgr::map_t) -> Definition2; 40 | 41 | // Without named parameter, yields Definition1 42 | using UsedDefinition1 = kgr::mapped_service_t; 43 | 44 | // With the named parameter, yields Definition2 45 | using UsedDefinition2 = kgr::mapped_service_t>; 46 | 47 | kgr::container container; 48 | 49 | auto function = [](Service) {}; 50 | 51 | container.invoke(function); // Definition1 used 52 | container.invoke>(function); // Definition2 used 53 | ``` 54 | 55 | Also, there's an alternative syntax to send which map is sent to the invoke function: 56 | 57 | ```c++ 58 | container.invoke(kgr::map{}, function); // Equivalent 59 | ``` 60 | 61 | ## Multiple Maps 62 | 63 | The map name parameter we used with other functionalities, the `kgr::map<...>`, can in fact receive many named map to pick from. 64 | The leftmost names will have higher priority. If a map with that name cannot be found, the container will fallback to the next name. 65 | If it has no named map left to try, it will try the default map. 66 | 67 | Here's an example of multiple maps: 68 | 69 | ```c++ 70 | struct MyMap1; 71 | struct MyMap2; 72 | 73 | auto service_map(Service1, kgr::map_t) -> Service1_Definition1; 74 | auto service_map(Service1, kgr::map_t) -> Service1_Definition2; 75 | 76 | auto service_map(Service2, kgr::map_t) -> Service1_Definition1; 77 | auto service_map(Service2) -> Service1_Definition2; 78 | 79 | auto function = [](Service1, Service2) {}; 80 | 81 | container.invoke>(function); // Service1_Definition1 and Service1_Definition2 used 82 | container.invoke>(function); // Service1_Definition2 and Service1_Definition2 used 83 | container.invoke>(function); // Service1_Definition1 and Service1_Definition1 used 84 | ``` 85 | 86 | # Indirect Maps 87 | 88 | Since kangaru 4.1.0, the service map allows you to define an indirect mapping. 89 | Instead of having the service definition type at the right of the arrow, you can put a class type that has a member template named `mapped_service`. 90 | 91 | It can be used to generate the service definition type automatically by the service map with a decent syntax. 92 | 93 | Here's how to create a indirect map: 94 | 95 | ```c++ 96 | struct indirect_service { 97 | template 98 | using mapped_service = kgr::service> {}; 99 | }; 100 | 101 | auto service_map(Camera const&) -> indirect_service; 102 | ``` 103 | 104 | if the container found the mapping for camera, instead of trying to use `indirect_service` as the service type, 105 | it will use `indirect_service::mapped_service`. This in turn will result in `kgr::service`. 106 | 107 | It's primary use was for autowiring services, but it can serves many other purposes. 108 | 109 | It's also nice to note that when using indirect mapping, the container will try to be stricter in order to avoid generating definitions for unwanted types. 110 | 111 | For example, the container won't allow subclasses to be mapped: 112 | 113 | ```c++ 114 | struct ServiceBase {}; 115 | struct ServiceDerived : ServiceBase {}; 116 | 117 | struct indirect_service { 118 | template 119 | using mapped_service = kgr::service; 120 | }; 121 | 122 | auto service_map(ServiceBase const&) -> misleading_map; 123 | ``` 124 | 125 | Even though `ServiceDerived` is convertible to `ServiceBase const&` and the generated service definition yield the right type, 126 | this particular mapping won't be picked, since the mapping was intended for `ServiceBase`. 127 | 128 | [Next chapter: Debugging](section11_debug.md) 129 | -------------------------------------------------------------------------------- /doc/section04_container.md: -------------------------------------------------------------------------------- 1 | Managing Containers 2 | =================== 3 | 4 | The container can be seen as a repository of services. 5 | It's possible to manage many container and operate between them with several operation. 6 | 7 | We will use these operation to scope the lifetime of single services, and extend it. 8 | 9 | ## Fork 10 | 11 | This operation creates a new container from a container. That new container will observe all instances contained in the source one. 12 | 13 | ```c++ 14 | kgr::container container1; 15 | 16 | container1.emplace(); 17 | container1.emplace(); 18 | 19 | auto container2 = container1.fork(); 20 | 21 | container1.emplace(); 22 | container2.emplace(); 23 | ``` 24 | 25 | In this example, `container1` first create and saves `Single1` and `Single2`. Then, we fork `container1` to create `container2`. 26 | At that point both container 1 and 2 references the same services. 27 | 28 | Then, in both container, we creates `Single3` in both containers. Since we forked the container, these two containers now have separated instances of `Single3`. 29 | 30 | Here's a graph to represent that: 31 | 32 | container1 ---1---2---*---3 33 | \ 34 | container2 ---3' 35 | 36 | Note that the lifetime of the `container2` must not extend the lifetime of `container1`. 37 | This is because the first container is owner of `Single1` and `Single2`. 38 | A container forked or rebased from another is considered invalidated if the source container dies. 39 | 40 | In this example, while `container2` is still owner of `Single3`, that service and all it's references are also considered invalidated since `Service3` may depend on `Single1` and `Single2` from `container1` that died. 41 | 42 | ## Merge 43 | 44 | The merge operation is the contrary of the fork. It will take all instances of one container and move them all into another container. 45 | This is useful when you forked the container, created some services in the new container, and want to bring those services into the original before dropping the fork. 46 | In this operation, the merged container will no longer be owner of any instances. You can drop that empty container without having any invalidated services. 47 | 48 | In case of conflict, the original container will prefer it's own services over the merged container services. 49 | 50 | ```c++ 51 | kgr::container container1; 52 | 53 | container1.emplace(); 54 | 55 | { 56 | auto container2 = container1.fork(); 57 | 58 | container1.emplace(); 59 | container2.emplace(); 60 | container2.emplace(); 61 | 62 | // At that point, both containers have their own SingleService2 instance. 63 | // Only container2 has SingleService3 64 | // container1 is owner of SingleService1, and container2 observes it. 65 | 66 | container1.merge(container2); 67 | 68 | // container2 is still valid, but is no longer owner of any services. 69 | // We can still use container2 and it still observe every service it owned before. 70 | } 71 | 72 | // At that point, no services are deleted yet. 73 | // container1 is owner of all instances created so far. 74 | // Since both containers had their own version of SingleService2, container1 kept his own instance. 75 | // Now container1 has a SingleService3 that came from the merging of container2 into it. 76 | ``` 77 | 78 | A graph of this example look like this: 79 | 80 | container1 ---1---*---2-----------*--- 81 | \ / 82 | container2 ---2'---3--- 83 | 84 | ## Rebase 85 | 86 | Containers can also be rebased from another one. In fact, a fork is simply the creation of a new container rebased on the original. 87 | This operation makes the forked container observe any new singles added in the original container since the fork. 88 | 89 | ```c++ 90 | kgr::container container1; 91 | 92 | container1.emplace(); 93 | 94 | auto container2 = container1.fork(); 95 | 96 | container2.contains(); // true 97 | container2.contains(); // false 98 | 99 | container1.emplace(); 100 | container2.rebase(container1); // rebase from container1 101 | 102 | container2.contains(); // true 103 | container2.contains(); // true 104 | ``` 105 | 106 | Here's the graph for it: 107 | 108 | container1 ---1---*---2---*--- 109 | \ \ 110 | container2 --------*--- 111 | 112 | Note that you can also rebase unrelated containers: 113 | 114 | ```c++ 115 | kgr::container container1; 116 | kgr::container container2; 117 | 118 | container1.emplace(); 119 | 120 | container2.contains(); // false 121 | container2.rebase(container1); 122 | container2.contains(); // true 123 | ``` 124 | 125 | ## Conclusion 126 | 127 | As we can see, containers are not just a class that contains every instance for all your classes. Single services are not just plain singletons. You can manage multiple instances of those and operate on them, have local containers and more. 128 | 129 | To see this in action, check out [example4](../examples/example4/example4.cpp), which shows more usage related to the snippets found on this page. 130 | 131 | [Next chapter: Supplied Services](section05_supplied.md) 132 | -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | Intro 2 | ===== 3 | 4 | Welcome to our documentation! 5 | 6 | #### First, what is kangaru? 7 | 8 | Kangaru is an inversion of control container for C++11 and later. We support features like operation between containers, 9 | injection via function parameter, automatic call of member function on instance creation and much more! 10 | 11 | Our goal is to create a container capable of automatic, recursive dependency injection that do most diagnostics at compile time, 12 | while keeping the simplest interface possible, and all that without being intrusive into user/library code. 13 | 14 | Kangaru is a header only library because of it's extensive use of templates. 15 | The name kangaru comes from the container's feature to inject itself into a service as a dependency, and because kangaroos are awesome. 16 | 17 | #### Inversion of control that puts you back in control 18 | 19 | Indeed, the container does not impose a way to construct, contain or inject your classes. You are in full control of everything related to the classes of your project. 20 | 21 | Control over memory allocation in containers is not yet supported, but is planned: #41. We do use `std::unordered_map` and `std::vector` with the default allocators. 22 | 23 | Getting Started 24 | --------------- 25 | 26 | To make kangaru available on your machine, you must clone the repository and create a build directory: 27 | 28 | $ git clone https://github.com/gracicot/kangaru.git && cd kangaru 29 | $ mkdir build && cd build 30 | 31 | Then use cmake to generate the makefile and export the package information: 32 | 33 | $ cmake .. 34 | 35 | You can then use cmake to find the package and add include paths: 36 | 37 | find_package(kangaru REQUIRED) 38 | target_link_libraries( PUBLIC kangaru) 39 | 40 | Then, you can simply include the library: 41 | 42 | #include 43 | 44 | Take note that you will need to add the library to your include paths. 45 | 46 | All declarations are made in the namespace `kgr`. Additionally, the namespace `kgr` contains the namespace `detail`, which itself contains implementation details. 47 | Note that the `detail` namespace is not considered as a part of the API and its content might be subject to changes. 48 | 49 | ### Services, Definitions, Oh My! 50 | 51 | In this documentation, many classes will be referred as services or service definitions. 52 | 53 | _Services_ are classes that are either injected by the container or have other classes as dependencies. 54 | 55 | _Service Definitions_ are classes that contain a service and tell the container how this particular service should behave within the container. 56 | 57 | ### About Macros 58 | 59 | This library make use of macros to prevent multiple inclusion. 60 | Every macros that starts with `KGR_KANGARU_` is considered reserved for implementation. 61 | Macros defined by the library are not part of it's interface. 62 | 63 | Note that [some features](section07_autocall.md) of this library may be easier to use with macros, especially before C++17. We recommend you to define some for your own usage if you feel the need. 64 | 65 | ### Compiler Requirement 66 | 67 | - MSVC: 2015 update 3 or better 68 | - GCC: 4.8.5 or better 69 | - Clang: 3.6 or better 70 | 71 | ### Backward Compatibility 72 | 73 | In between minor versions, we make our best to remain source compatible and not break any code using kangaru. Our continuous integration tests is our primary mean to guarantee this statement. If a breaking change is introduced in a non-breaking release, please submit an issue and we'll discuss how we can solve this. 74 | 75 | Note that we don't recommend forward declaring types from the `kgr` namespace, as we reserve the right to change a type to an alias in minor versions. Since forward declaring an alias is not a thing in C++, this may create some incompatibility. Some details in the build system might also change between minor version, but we will not break documented usage of the kangaru cmake package or it's generation. 76 | 77 | In between major versions, we still try our best to make the transition to future version as smooth as possible. To do this, we provide a migration guide to help developers and we make our best to not break basic usage of the library. 78 | 79 | Breaking versions might bump compiler requirements and might also bump language version requirement. Note that when this happen, we are willing to offer support for older versions if there's a demand for it. 80 | 81 | ### Migration Guide 82 | 83 | Since many breaking changes has been introduced in `v4.0.0`, we decided to write a [migration guide from the `3.x.y` series to `v4.0.0`](migration_guide_3xy.md). 84 | We also added a compatibility header to make the transition smoother between the two versions. 85 | 86 | Index 87 | ----- 88 | * [Services](section01_services.md) 89 | * [Invoke](section02_invoke.md) 90 | * [Polymorphic Services](section03_polymorphic.md) 91 | * [Managing Containers](section04_container.md) 92 | * [Supplied services](section05_supplied.md) 93 | * [Autowire](section06_autowire.md) 94 | * [Autocall](section07_autocall.md) 95 | * [Operator services](section08_operator.md) 96 | * [Custom service definitions](section09_definitions.md) 97 | * [Advanced Mapping](section10_mapping.md) 98 | * [Debugging](section11_debug.md) 99 | * [Generic Services](section12_generic.md) 100 | * [Structuring projects](section13_structure.md) 101 | * [API Reference](section14_api_reference.md) 102 | -------------------------------------------------------------------------------- /include/kangaru/detail/detection.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DETECTION_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DETECTION_HPP 3 | 4 | #include "meta_list.hpp" 5 | #include "void_t.hpp" 6 | #include "utils.hpp" 7 | #include "seq.hpp" 8 | 9 | namespace kgr { 10 | namespace detail { 11 | 12 | /* 13 | * The following namespace contains the detection idiom implementation 14 | */ 15 | namespace detail_detection { 16 | 17 | template class Op, typename... Args> 18 | struct detector { 19 | using value_t = std::false_type; 20 | using type = Default; 21 | }; 22 | 23 | template class Op, typename... Args> 24 | struct detector>, Op, Args...> { 25 | using value_t = std::true_type; 26 | using type = Op; 27 | }; 28 | 29 | /* 30 | * This higher order metafunction only instantiate a template using `detector` if a condition is satisfied. 31 | * Useful when the instantiation of a template is known to trigger a hard error. 32 | */ 33 | template class, typename...> 34 | struct instantiate_if { 35 | using type = Default; 36 | }; 37 | 38 | template class Template, typename... Args> 39 | struct instantiate_if { 40 | using type = typename detector::type; 41 | }; 42 | 43 | } // namespace detail_detection 44 | 45 | /* 46 | * Represent nothing. Used as a non-void nothing type. 47 | */ 48 | struct nonesuch { 49 | nonesuch() = delete; 50 | ~nonesuch() = delete; 51 | nonesuch(nonesuch const&) = delete; 52 | void operator=(nonesuch const&) = delete; 53 | }; 54 | 55 | /* 56 | * Alias to the detectors. 57 | */ 58 | template class Op, typename... Args> 59 | using is_detected = typename detail_detection::detector::value_t; 60 | 61 | template class Op, typename... Args> 62 | using detected_t = typename detail_detection::detector::type; 63 | 64 | template class Op, typename... Args> 65 | using detected_or = typename detail_detection::detector::type; 66 | 67 | template class Template, typename... Args> 68 | using instantiate_if_t = typename detail_detection::instantiate_if::type; 69 | 70 | template class Template, typename... Args> 71 | using instantiate_if_or = typename detail_detection::instantiate_if::type; 72 | 73 | /* 74 | * Simple meta operators missing from C++11 and C++14. 75 | */ 76 | template 77 | struct negation : bool_constant {}; 78 | 79 | template struct conjunction : std::true_type {}; 80 | template struct conjunction : B1 {}; 81 | template 82 | struct conjunction : std::conditional, B1>::type {}; 83 | 84 | template 85 | using such = negation>; 86 | 87 | /* 88 | * all_of_traits is a higher order type trait that execute a particular type trait over a list of types. 89 | * Work much like the STL algorithm `all_of` but for type traits. 90 | */ 91 | template class, typename...> 92 | struct all_of_traits : std::false_type { 93 | static_assert(false_t::value, "Incorrect usage of all_of_traits. The first parameter must be a meta_list, a tuple or nonesuch"); 94 | }; 95 | 96 | template class Trait, typename... Args> 97 | struct all_of_traits : std::false_type {}; 98 | 99 | template class Trait, typename... Args> 100 | struct all_of_traits, Trait, Args...> : conjunction...> {}; 101 | 102 | template class Trait, typename... Args> 103 | struct all_of_traits, Trait, Args...> : conjunction...> {}; 104 | 105 | /* 106 | * This higher order metafunction will expand `n` elements of a meta_list into a template. 107 | */ 108 | template class, typename... Args> 109 | struct expand_n_helper; 110 | 111 | template class Trait, typename... Args> 112 | struct expand_n_helper, List, Trait, Args...> { 113 | // Cannot use the alias here or msvc will try to send `I` as reference. 114 | using type = Trait::type...>; 115 | }; 116 | 117 | /* 118 | * Shortcut to expand_n that expand all the list minus `N` elements into the template. 119 | */ 120 | template class Trait, typename... Args> 121 | using expand_minus_n = typename expand_n_helper, List, Trait, Args...>::type; 122 | 123 | /* 124 | * Shortcut to expand_n that always expand all the list into the template. 125 | */ 126 | template class Trait, typename... Args> 127 | using expand_all = typename expand_n_helper, List, Trait, Args...>::type; 128 | 129 | } // namespace detail 130 | } // namespace kgr 131 | 132 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DETECTION_HPP 133 | -------------------------------------------------------------------------------- /doc/section02_invoke.md: -------------------------------------------------------------------------------- 1 | Invoke 2 | ====== 3 | In the previous tutorial, we were doing injection via class constructors. Kangaru offers another way to inject services, that is through function calls. 4 | 5 | For example, let's say you have this kind of code: 6 | 7 | ```c++ 8 | bool result = process_inputs( 9 | container.service(), 10 | container.service() 11 | ); 12 | ``` 13 | 14 | We want to make that kind of code less painful. Imagine doing this instead, and let the container figure out how to call your function: 15 | 16 | ```c++ 17 | bool result = container.invoke(process_inputs); 18 | ``` 19 | 20 | The idea behind this is you give the container a function to call, and the container will match 21 | parameters to services as automatically as possible, giving a nice shortcut for this kind of code. 22 | This is what `kgr::container::invoke` is all about. It receives a function to call, and it call it with injected services. 23 | 24 | ## Specifying Definitions 25 | 26 | Injection of parameters can be done manually by specifying every definition the function needs. 27 | 28 | Let's say we have this little function and services: 29 | ```c++ 30 | struct KeyboardStateService : kgr::single_service {}; 31 | struct MessageBusService : kgr::single_service {}; 32 | 33 | bool process_inputs(KeyboardState& ks, MessageBus& mb); 34 | ``` 35 | 36 | Then you can use the container to call the function, specifying each needed services: 37 | 38 | ```c++ 39 | bool result = container.invoke(process_inputs); 40 | ``` 41 | 42 | Of course, all additional parameters sent to `invoke` are forwarded to the invoked function. We can change our function to send a `bool` parameter. 43 | Just like with constructors, additional parameters are forwarded only after injected parameters: 44 | 45 | ```c++ 46 | bool process_inputs_mod(KeyboardState& ks, MessageBus& mb, bool check_modifiers); 47 | 48 | bool result = container.invoke(process_inputs_mod, false); 49 | ``` 50 | 51 | ## Mapped Service 52 | 53 | While the utilities shown above is a great shortcut for calling `container.service` for each parameter, we can do even better. 54 | If we can tell the service how to match each parameter to a corresponding service, we could omit listing every needed definitions. 55 | 56 | We will associate a parameter to a specific service definition using the service map. 57 | The form of a mapping look like this: 58 | 59 | ``` 60 | auto service_map() -> ; 61 | ``` 62 | So for `KeyboardState` and `MessageBus` the mapping is done as follow: 63 | 64 | ```c++ 65 | auto service_map(KeyboardState const&) -> KeyboardStateService; 66 | auto service_map(MessageBus const&) -> MessageBusService; 67 | ``` 68 | 69 | The service map is a function that takes a parameter to be mapped, and has the associated service definition as return type. 70 | Each entry must be in the same namespace as the argument it receives. The container will search the right `service_map` declaration through [ADL](http://en.cppreference.com/w/cpp/language/adl). 71 | 72 | Now calling our `process_inputs` will look like this: 73 | ```c++ 74 | bool result1 = container.invoke(process_inputs); 75 | bool result2 = container.invoke(process_inputs_mod, true); 76 | ``` 77 | 78 | Neat! Now the container will match every injected parameters automatically. 79 | The great thing about this is that if one day our function signature change to take another injected parameter, 80 | the calling site won't need to get refactored. So for example, we change the function signature to this: 81 | 82 | ```c++ 83 | bool process_inputs(KeyboardState& ks, MessageBus& mb, Scene newScene); 84 | ``` 85 | 86 | Just like changing the dependencies of a service constructor, the calling site stays the same: 87 | 88 | ```c++ 89 | bool result = container.invoke(process_inputs); 90 | ``` 91 | 92 | As long as every service definition are written and their service map, the container will be able to resolve injected parameters automatically. 93 | 94 | ## Callable Objects 95 | 96 | Just like function pointer, callable objects like lambdas are supported too: 97 | 98 | ```c++ 99 | auto function = [](Window& window, MessageBus& bus, int data) { 100 | // Do stuff with window and bus 101 | return 10 + data; 102 | }; 103 | 104 | int quantity = container.invoke(function, 32); // quantity == 42 105 | ``` 106 | 107 | C++14 generic lambda are also supported. The only restriction is that all `auto` must be at the end. Just like this: 108 | 109 | ```c++ 110 | auto function = [](Window& window, MessageBus& bus, int a, auto b) { // b to be deduced 111 | return 10 + a + b; 112 | }; 113 | 114 | double quantity = container.invoke(function, 30, 2.1); // quantity == 42.1, b deduced as double 115 | ``` 116 | 117 | > WARNING: To inspect the parameter types, the container must instantiate the template function by using forwarded parameters, which maintain their reference type. If you're calling `container.invoke([](auto){}, std::move(some_integer))`, it will first instantiate the lambda with `int&&` as `auto` to inspect parameters, and then call the function as usual. This may make generic lambda function to instantiate two times. If you want to avoid this behavior, consider using `auto&&` for deduced parameters. That way, generic lambdas are only instantiated one time with the right types. This is expected to be solved with the version 5. 118 | 119 | To see more about invoke, I welcome you to check [example2](../examples/example2/example2.cpp) and 120 | [abstract factory example](../examples/abstract_factory/abstract_factory.cpp). 121 | 122 | [Next chapter: Polymorphic Services](section03_polymorphic.md) 123 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | https://gitter.im/gracicot. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /doc/section05_supplied.md: -------------------------------------------------------------------------------- 1 | Supplied Services 2 | ================= 3 | 4 | Indeed, when a service is single, `service()` won't accept argument to forward to the constructor. 5 | This is because only the first call to `service()` creates an instance. Consider this code (bad example): 6 | 7 | ```c++ 8 | // Bad :( 9 | Scene& scene1 = c.service(1024, 768); 10 | Scene& scene2 = c.service(1920, 1080); // Oops! Constructor not called! The instance is reused. 11 | 12 | // The scene still have a resolution of 1024x768 13 | ``` 14 | 15 | As a matter of fact, the `service` function has no mean to tell if the service has actually been created here. 16 | To resolve this, you can instead request the construction of a service with the `emplace(...)` function. 17 | That function will perform injection like `service()`, but serves only to save a single into the container. 18 | Just like `std::map`, emplace only perform initialization if the container don't contain and instance yet. 19 | It returns `true` if the service is created, and `false` if there's already an instance in the container: 20 | 21 | ```c++ 22 | bool inserted = c.emplace(1920, 1080); // (1) 23 | assert(inserted); // Passes. 24 | 25 | inserted = c.emplace(1024, 768); 26 | assert(inserted); // Fires, not created two times. 27 | 28 | Scene& scene = container.service(); // Returns the instance created at (1) 29 | ``` 30 | 31 | As we can see, `emplace` only constructs if the element is not found. 32 | But as opposed to `service`, it can reports if it has already been inserted before. 33 | This gives us the opportunity to handle those case correctly. 34 | 35 | ## Supplied Services 36 | 37 | As `container.service()` requires the `SceneService` to be constructible using only it's dependencies, the following `Scene` declaration won't work: 38 | 39 | ```c++ 40 | struct Scene { 41 | Scene(Camera c, int w, int h) : 42 | camera{c}, width{w}, height{h} {} 43 | 44 | private: 45 | Camera camera; 46 | int width; 47 | int height; 48 | }; 49 | 50 | Scene& scene = container.service(); // fails! 51 | ``` 52 | 53 | It's constructor must receive two additional integers after it's dependencies. 54 | Since `container.service()` must create a scene and does not forward any arguments to single services, 55 | there are no way to obtain a scene from the container! 56 | 57 | In these cases, we must tell the container that it's normal it cannot construct it without arguments, 58 | and must be provided to the container before usage. 59 | 60 | ```c++ 61 | struct SceneService : kgr::single_service>, kgr::supplied {}; 62 | ``` 63 | 64 | Now, we can use the service like this: 65 | 66 | ```c++ 67 | container.emplace(1920, 1080); // construct a scene in the container. 68 | 69 | Scene& scene = container.service(); // works, won't try to construct it. 70 | ``` 71 | 72 | Note that if the instance is not found, the container won't be able to construct it and will throw a `kgr::supplied_not_found` instead. 73 | 74 | ## External services 75 | 76 | The kangaru library also provide two supplied services specialized for cases where instances are created from an external system. 77 | 78 | The first, `kgr::extern_service` holds a reference to an instance of `T`. It behave just as a single service, but the instance must be provided to the container manually. 79 | 80 | Here's an example of extern service: 81 | 82 | ```c++ 83 | struct Scene {}; 84 | 85 | struct SceneService : kgr::extern_service {}; 86 | 87 | int main() { 88 | Scene scene; 89 | kgr::container container; 90 | 91 | // Add the scene 92 | container.emplace(scene); 93 | 94 | // Passes, the container returns the instance we sent it. 95 | assert(&scene == &container.service()); 96 | } 97 | ``` 98 | 99 | The other external service is `kgr::extern_shared_service`, which is analogous to the `kgr::extern_service` but inject and contains the service by shared pointers. 100 | 101 | Here's the same example as above, but with teh shared external service: 102 | 103 | ```c++ 104 | struct Scene {}; 105 | 106 | struct SceneService : kgr::extern_shared_service {}; 107 | 108 | int main() { 109 | auto scene = std::make_shared(); 110 | kgr::container container; 111 | 112 | // Add the scene 113 | container.emplace(scene); 114 | 115 | // Passes, the container returns the same shared pointer we sent it. 116 | assert(scene == container.service()); 117 | } 118 | ``` 119 | 120 | ## Replace Services 121 | 122 | The `emplace` function will only construct a service is it's not in the container yet. But what if you wanted to replace an existing service? 123 | 124 | The container give you a way to explicitly replace a single service to it would be used in future injection. It's usage is similar to emplace: 125 | 126 | ```c++ 127 | kgr::container container; 128 | 129 | container.emplace(640, 480); 130 | Scene& scene1 = container.service(); 131 | container.replace(1920, 1080); 132 | Scene& scene2 = container.service(); 133 | 134 | assert(&scene1 != &scene2); // passes, these are different instances of scenes 135 | ``` 136 | 137 | Note that when replacing, the container will not destroy the old service. In our example, `scene1` is still valid after `replace` has been called. 138 | The lifetime of the first `SceneService` is not affected, and will be destroyed at the same time as the container. 139 | 140 | To see more about supplied services, please see [example5](../examples/example5/example5.cpp). 141 | 142 | [Next chapter: Autowire](section06_autowire.md) 143 | -------------------------------------------------------------------------------- /include/kangaru/type_id.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "kangaru/detail/config.hpp" 8 | 9 | #ifdef KGR_KANGARU_HASH_TYPE_ID 10 | #include "detail/string_view.hpp" 11 | #include "detail/hash.hpp" 12 | #endif 13 | 14 | #include "detail/define.hpp" 15 | 16 | namespace kgr { 17 | namespace detail { 18 | 19 | /* 20 | * Declaration of built-in private service types. 21 | */ 22 | template 23 | struct index_storage; 24 | 25 | struct override_storage_service; 26 | 27 | /* 28 | * Trait that identify if some type is an index_storage 29 | */ 30 | template 31 | struct is_index_storage : std::false_type {}; 32 | 33 | template 34 | struct is_index_storage> : std::true_type {}; 35 | 36 | /* 37 | * We need to have a small amount of static data in order to 38 | * get it's pointer. We reuse that space to store meta information. 39 | */ 40 | enum struct service_kind_t : std::uint8_t { normal, override_storage, index_storage }; 41 | 42 | /** 43 | * Returns which kind of service a given type is 44 | */ 45 | template 46 | inline constexpr auto kind_for() noexcept -> service_kind_t { 47 | return std::is_same::value 48 | ? service_kind_t::override_storage 49 | : is_index_storage::value ? service_kind_t::index_storage : service_kind_t::normal; 50 | } 51 | 52 | #ifdef KGR_KANGARU_HASH_TYPE_ID 53 | 54 | /** 55 | * The type of a generated type id. 56 | */ 57 | using type_id_t = std::uint64_t; 58 | 59 | /** 60 | * We need two bits to encode all kind of services 61 | */ 62 | constexpr auto kind_significant_bits = std::uint64_t{2}; 63 | 64 | /** 65 | * Returns the kind of service from a type id 66 | */ 67 | inline constexpr auto type_id_kind(type_id_t const id) -> service_kind_t { 68 | return static_cast((id >> (64 - kind_significant_bits)) & 0x2); 69 | } 70 | 71 | // Extraction of type names from a function signature 72 | // Implementation taken from TheLartians/StaticTypeInfo 73 | // Link: https://github.com/TheLartians/StaticTypeInfo/blob/master/include/static_type_info/type_name.h 74 | // License: MIT 75 | 76 | template 77 | inline constexpr auto typed_signature() -> string_view { 78 | return KGR_KANGARU_FUNCTION_SIGNATURE; 79 | } 80 | 81 | constexpr auto signature_prefix_length = std::size_t{typed_signature().find("int")}; 82 | constexpr auto signature_postfix_length = std::size_t{typed_signature().size() - signature_prefix_length - string_view{"int"}.size()}; 83 | 84 | static_assert(signature_prefix_length != string_view::npos, "Cannot find the type name in the function signature"); 85 | 86 | template 87 | inline constexpr auto type_name_prefix_length() -> std::size_t { 88 | return typed_signature().substr(signature_prefix_length).starts_with("class") 89 | ? signature_prefix_length + 6 90 | : typed_signature().substr(signature_prefix_length).starts_with("struct") 91 | ? signature_prefix_length + 7 92 | : signature_prefix_length; 93 | } 94 | 95 | template 96 | inline constexpr auto type_name() -> string_view { 97 | return typed_signature().substr( 98 | type_name_prefix_length(), 99 | typed_signature().size() - type_name_prefix_length() - signature_postfix_length 100 | ); 101 | } 102 | 103 | /** 104 | * We need to drop two if the upper bits to encode our metadata about the services 105 | * This mask will clear the two uppermost bits of a 64 bit number when used with '&' 106 | */ 107 | constexpr auto hash_mask = std::uint64_t{0x4FFFFFFFFFFFFFFF}; 108 | 109 | template 110 | inline constexpr auto type_id_impl_helper() -> type_id_t { 111 | return ( 112 | (hash_64_fnv1a(type_name()) & hash_mask) | 113 | (static_cast(kind_for()) << (64 - kind_significant_bits)) 114 | ); 115 | } 116 | 117 | // Here we force type_id_impl_helper to be evaluated at compile time 118 | template()> 119 | inline constexpr auto type_id_impl() -> type_id_t { 120 | return hash; 121 | } 122 | 123 | #else 124 | 125 | using type_id_t = void const*; 126 | 127 | struct type_id_data { 128 | service_kind_t const kind; 129 | }; 130 | 131 | template 132 | struct type_id_ptr { 133 | #ifdef KGR_KANGARU_NONCONST_TYPEID 134 | // On visual studio, we must have the id as non const since it may nonetheless be optimised away 135 | // It is strongly recommended to use hash based type id on Visual Studio compiler 136 | static type_id_data id; 137 | #else 138 | // Having a static data member will ensure us that it has only one address for the whole program. 139 | static constexpr type_id_data id = type_id_data{kind_for()}; 140 | #endif 141 | }; 142 | 143 | #ifdef KGR_KANGARU_NONCONST_TYPEID 144 | template 145 | type_id_data type_id_ptr::id = type_id_data{kind_for()}; 146 | #else 147 | template 148 | constexpr type_id_data const type_id_ptr::id; 149 | #endif 150 | 151 | inline constexpr auto type_id_kind(void const* id) -> service_kind_t { 152 | return static_cast(id)->kind; 153 | } 154 | 155 | template 156 | constexpr auto type_id_impl() -> type_id_t { 157 | return &detail::type_id_ptr::id; 158 | } 159 | 160 | #endif // KGR_KANGARU_HASH_TYPE_ID 161 | 162 | } // namespace detail 163 | 164 | using detail::type_id_t; 165 | 166 | /* 167 | * The function that returns the type id. 168 | * 169 | * It uses the pointer to the static data member of a class template to achieve this. 170 | * Altough the value is not predictible, it's stable. 171 | */ 172 | template 173 | inline constexpr auto type_id() -> type_id_t { 174 | return detail::type_id_impl(); 175 | } 176 | 177 | } // namespace kgr 178 | 179 | #include "detail/undef.hpp" 180 | 181 | #endif // KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP 182 | -------------------------------------------------------------------------------- /doc/section13_structure.md: -------------------------------------------------------------------------------- 1 | Structuring projects 2 | ==================== 3 | 4 | Have a large codebase? Want to structure your code correctly and scale better? 5 | Here's some suggestions to keep usage of kangaru in your projects well organized! 6 | 7 | ## Definition completeness 8 | 9 | When you use a service definition, it must be complete. For a service definition to be valid, it's service must be complete too! 10 | 11 | *How can we keep a fast compilation and minimize dependencies?* 12 | 13 | For services in your own project, keeping one definition in each header will make including only services you need will be easier. 14 | Multiple unrelated definitions per header file will prevent you from including only what you need. 15 | Also, in a service definition, other definitions used should be complete. Incomplete service may result in errors when using that service. 16 | 17 | Headers of your classes should not be significantly changed when integrating kangaru to your project. 18 | The big change is in the cpp file where you actually use the container. 19 | If you need to deal with the container, you should include the definition instead of the class you want to use. 20 | 21 | Let's say we have a project that has the classes `ClassA` and `ClassB`. We want to add `ClassC` that will use kangaru. 22 | Here's a include graph of this project: 23 | 24 | // ClassAService.h and ClassBService.h includes kangaru.hpp 25 | 26 | ------------ -------------- 27 | | ClassA.h |<----| ClassA.cpp | 28 | ------------ -------------- 29 | ^ 30 | | 31 | | ------------------- 32 | |-----------| ClassAService.h |<------ 33 | | ------------------- | 34 | | | 35 | |__________________ | 36 | | | 37 | | | 38 | ------------ -------------- | 39 | | ClassB.h |<----| ClassB.cpp | | 40 | ------------ -------------- | 41 | ^ | 42 | | | 43 | | ------------------- | 44 | ------------| ClassBService.h |------- 45 | ------------------- 46 | ^ 47 | | 48 | --------------- | 49 | | kangaru.hpp |<------- | 50 | --------------- | | 51 | | | 52 | ------------ -------------- 53 | | ClassC.h |<----| ClassC.cpp | 54 | ------------ -------------- 55 | 56 | As we can see, the files of `ClassA` and `ClassB` are unchanged and are not dependent of kangaru. The only thing changed is the addition of `ClassAService.h` and `ClassBService.h`. 57 | This has the effect of reducing coupling considerably: only code that uses kangaru will depend on it. 58 | If we were to remove kangaru from this project, the only changed thing would be to reimplement the desired logic in `ClassC`. 59 | 60 | ## Fork the container 61 | 62 | If you have services that are tied together in a logical unit, and you only need to access some of those elsewhere, 63 | don't be afraid to fork the container! 64 | 65 | Having only one huge for everything is considered harmful to some extent. 66 | Having a container with thousands of services in it may slow your application. 67 | If you want your application to scale, prefer separating containers to contain only services that are useful in their context. 68 | 69 | ## Minimize coupling to the container 70 | 71 | It may sound funny, but if you can, try not using the container directly. 72 | Having a lot of classes that uses the container will make coupling less controllable. 73 | Kangaru offers operator services that are meant to express your intent about how you plan to use the container. 74 | If you can drop usage of the container, then do it! This library is a great tool to minimize coupling, 75 | but coupling with this library is still coupling. 76 | 77 | If on the other hand using the container (or operator services) in a particular place 78 | reduces unwanted coupling with other thing, then don't be afraid and use the container. 79 | 80 | ## Limit the scope of the service map 81 | 82 | The service map is an integral part of this library. It enables invoking functions and autowiring constructors. 83 | 84 | However, having too many overload in the same scope can slightly slow down compilation. 85 | 86 | In a project with about a hundred of autowired services, declaring service map functions as friends decreased from 1 minute 5 seconds to 59 seconds. 87 | 88 | It may seem small but it's important to be aware of some way to make compilation faster, especially if you're a big template user. 89 | 90 | ## Including kangaru 91 | 92 | We would recommend to not include `kangaru.hpp` directly, and use a proxy header file instead. Why? Because right now `kgr::AutoCall` is easier to use with a macro, and you can also include your own generic services that you want to use throughout your application. 93 | A "include kangaru" header file should look like this: 94 | 95 | ```c++ 96 | #pragma once // or a header guard. 97 | 98 | #include // include kangaru 99 | 100 | // Here you can put your generic service. 101 | // Example: 102 | // #include "sharedservice.h" 103 | // #include "uniqueservice.h" 104 | 105 | // You can optionally include `compatibility.hpp` 106 | // #include 107 | 108 | // declare some recommended shortcut macros 109 | #define METHOD(...) ::kgr::Method 110 | 111 | // Or if you have C++17 available: 112 | // template 113 | // using invoke = kgr::invoke; 114 | ``` 115 | 116 | This will add a common point between your project and kangaru. 117 | Feel free to copy this header into your project. Just replace the macro name to fit your needs and you're ready to hack! 118 | 119 | [Next chapter: API Reference](section14_api_reference.md) 120 | -------------------------------------------------------------------------------- /include/kangaru/detail/meta_list.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_META_LIST_HPP 2 | #define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_META_LIST_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace kgr { 8 | namespace detail { 9 | 10 | /* 11 | * This class is a meta list of types. 12 | * It is used instead of tuples as a lighter alternative to simply hold a list of types. 13 | */ 14 | template 15 | struct meta_list {}; 16 | 17 | /* 18 | * This trait simply return the size of the list. 19 | */ 20 | template 21 | struct meta_list_size; 22 | 23 | template 24 | struct meta_list_size> { 25 | static constexpr std::size_t value = sizeof...(Types); 26 | }; 27 | 28 | /* 29 | * This class is a trait that returns the nth element of a type. 30 | */ 31 | template 32 | struct meta_list_element; 33 | 34 | /* 35 | * This class is the specialization case where the meta list os empty, and I is at 0. 36 | * We don't define any alias in that case. 37 | */ 38 | template 39 | struct meta_list_element> {}; 40 | 41 | /* 42 | * This is the case where I is at 0. We find Head to be the nth element and define an alias to it. 43 | */ 44 | template 45 | struct meta_list_element<0, meta_list> { 46 | using type = Head; 47 | }; 48 | 49 | /* 50 | * This is the case where I is larger than 0, yet types are still in the list. 51 | * In that case, we call itself with I - 1 and the Head removed from the list. 52 | */ 53 | template 54 | struct meta_list_element> : meta_list_element> {}; 55 | 56 | /* 57 | * This is an alias for the typedef in meta_list_element_t. 58 | */ 59 | template 60 | using meta_list_element_t = typename meta_list_element::type; 61 | 62 | /* 63 | * This class is a trait that tells if a particular type is contained in the list. 64 | */ 65 | template 66 | struct meta_list_find; 67 | 68 | /* 69 | * This is the case where the type isn't found. 70 | * We extends false_type to return the result. 71 | */ 72 | template 73 | struct meta_list_find, nth> : std::integral_constant {}; 74 | 75 | /* 76 | * This is the case where T is different from Head. 77 | * In this case, we continue to iterate by removeing the Head and comparing the Head again. 78 | */ 79 | template 80 | struct meta_list_find, nth> : meta_list_find, nth + 1> {}; 81 | 82 | /* 83 | * This is the case where T is the same as the head. In that case, return true by extending std::true_type. 84 | */ 85 | template 86 | struct meta_list_find, nth> : std::integral_constant {}; 87 | 88 | template 89 | using meta_list_contains = std::integral_constant::value < meta_list_size::value)>; 90 | 91 | /* 92 | * This trait return if a type is in the list at most one time. 93 | * If the type isn't in the list, the returned value is false. 94 | * If the type is there multiple times, the return value is false too. 95 | */ 96 | template 97 | struct meta_list_unique; 98 | 99 | /* 100 | * This trait is used to convert a std::tuple into a meta_list 101 | */ 102 | template 103 | struct to_meta_list; 104 | 105 | template 106 | struct to_meta_list> { 107 | using type = meta_list; 108 | }; 109 | 110 | template 111 | using to_meta_list_t = typename to_meta_list::type; 112 | 113 | /* 114 | * Case where we didn't find the type in the list. 115 | */ 116 | template 117 | struct meta_list_unique> : std::false_type {}; 118 | 119 | /* 120 | * Case where we are still searching the type in the list. 121 | * We iterate by removing the head and do the comparison again. 122 | */ 123 | template 124 | struct meta_list_unique> : meta_list_unique> {}; 125 | 126 | /* 127 | * This is the case where we found the type T for the first time. 128 | * In that case, we extend the inverse value returned by meta_list_contains for the rest of the list. 129 | */ 130 | template 131 | struct meta_list_unique> : std::integral_constant>::value> {}; 132 | 133 | /* 134 | * This trait remove the first element of the list. 135 | */ 136 | template 137 | struct meta_list_pop_front; 138 | 139 | template 140 | struct meta_list_pop_front> { 141 | using type = meta_list; 142 | }; 143 | 144 | template 145 | using meta_list_pop_front_t = typename meta_list_pop_front::type; 146 | 147 | /* 148 | * This trait apply a metafunction on each element in the list, and return the transformed list. 149 | */ 150 | template class> 151 | struct meta_list_transform; 152 | 153 | template class F> 154 | struct meta_list_transform, F> { 155 | using type = meta_list...>; 156 | }; 157 | 158 | /* 159 | * This is an alias for the transformed list made by meta_list_transform. 160 | */ 161 | template class F> 162 | using meta_list_transform_t = typename meta_list_transform::type; 163 | 164 | /* 165 | * This trait returns if the list is empty. 166 | */ 167 | template 168 | struct meta_list_empty; 169 | 170 | template 171 | struct meta_list_empty> : std::integral_constant {}; 172 | 173 | 174 | } // namespace detail 175 | } // namespace kgr 176 | 177 | #endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_META_LIST_HPP 178 | --------------------------------------------------------------------------------