aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Murashkin <iam@google.com>2018-07-26 14:14:44 -0700
committerIgor Murashkin <iam@google.com>2018-08-10 11:59:21 -0700
commit7dfecaa23cfb56b360d683a301e6dd0144b15214 (patch)
tree2bf5660768e2e48adc2c105778f9d9508a6748e5
parent67a7fe692ecc0da7ea6e10b10bf8b1ca5662eeab (diff)
parent4f1aacf4aaeec2565b2945bba311d2e0efbbb579 (diff)
downloadgoogle-fruit-7dfecaa23cfb56b360d683a301e6dd0144b15214.tar.gz
android: Import fruit from upstream
Add required metadata files for AOSP usage. Merge remote-tracking branch 'aosp/upstream-master' into master Change-Id: I7d36b446f210c6d1a71810b5954881a988825455
-rw-r--r--.clang-format6
-rw-r--r--.gitignore17
-rw-r--r--.travis.yml219
-rw-r--r--BUILD23
-rw-r--r--CMakeLists.txt125
-rw-r--r--CONTRIBUTING.md240
-rw-r--r--COPYING202
-rw-r--r--LICENSE202
-rw-r--r--METADATA23
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--NOTICE202
-rw-r--r--README.md11
-rw-r--r--appveyor.yml62
-rw-r--r--conanfile.py37
-rw-r--r--configuration/CMakeLists.txt221
-rw-r--r--configuration/bazel/fruit/impl/fruit-config-base.h70
-rw-r--r--configuration/fruit-config-base.h.in39
-rw-r--r--examples/CMakeLists.txt12
-rw-r--r--examples/annotated_injection/BUILD11
-rw-r--r--examples/annotated_injection/CMakeLists.txt10
-rw-r--r--examples/annotated_injection/brake.h26
-rw-r--r--examples/annotated_injection/car.cpp45
-rw-r--r--examples/annotated_injection/car.h29
-rw-r--r--examples/annotated_injection/emergency_brake.cpp30
-rw-r--r--examples/annotated_injection/emergency_brake.h30
-rw-r--r--examples/annotated_injection/main.cpp28
-rw-r--r--examples/annotated_injection/main_brake.cpp30
-rw-r--r--examples/annotated_injection/main_brake.h30
-rw-r--r--examples/hello_world/BUILD8
-rw-r--r--examples/hello_world/CMakeLists.txt7
-rw-r--r--examples/hello_world/main.cpp55
-rw-r--r--examples/multibindings/BUILD8
-rw-r--r--examples/multibindings/CMakeLists.txt7
-rw-r--r--examples/multibindings/main.cpp83
-rw-r--r--examples/scaling_doubles/BUILD11
-rw-r--r--examples/scaling_doubles/CMakeLists.txt9
-rw-r--r--examples/scaling_doubles/main.cpp31
-rw-r--r--examples/scaling_doubles/multiplier.cpp28
-rw-r--r--examples/scaling_doubles/multiplier.h30
-rw-r--r--examples/scaling_doubles/scaler.cpp39
-rw-r--r--examples/scaling_doubles/scaler.h33
-rw-r--r--examples/server/BUILD12
-rw-r--r--examples/server/CMakeLists.txt13
-rw-r--r--examples/server/bar_handler.cpp41
-rw-r--r--examples/server/bar_handler.h34
-rw-r--r--examples/server/foo_handler.cpp41
-rw-r--r--examples/server/foo_handler.h34
-rw-r--r--examples/server/main.cpp31
-rw-r--r--examples/server/request.h26
-rw-r--r--examples/server/request_dispatcher.cpp63
-rw-r--r--examples/server/request_dispatcher.h34
-rw-r--r--examples/server/server.cpp99
-rw-r--r--examples/server/server.h35
-rw-r--r--examples/server/server_context.h28
-rw-r--r--examples/simple_injection/BUILD27
-rw-r--r--examples/simple_injection/CMakeLists.txt14
-rw-r--r--examples/simple_injection/adder.h26
-rw-r--r--examples/simple_injection/checked_adder.cpp75
-rw-r--r--examples/simple_injection/checked_adder.h25
-rw-r--r--examples/simple_injection/checked_incrementer.cpp24
-rw-r--r--examples/simple_injection/checked_incrementer.h25
-rw-r--r--examples/simple_injection/incrementer.h26
-rw-r--r--examples/simple_injection/incrementer_component.cpp27
-rw-r--r--examples/simple_injection/incrementer_component.h26
-rw-r--r--examples/simple_injection/incrementer_impl.cpp33
-rw-r--r--examples/simple_injection/incrementer_impl.h26
-rw-r--r--examples/simple_injection/main.cpp42
-rw-r--r--examples/simple_injection/main_v1.cpp36
-rw-r--r--examples/simple_injection/simple_adder.cpp30
-rw-r--r--examples/simple_injection/simple_adder.h25
-rw-r--r--examples/simple_injection/simple_incrementer.cpp24
-rw-r--r--examples/simple_injection/simple_incrementer.h25
-rw-r--r--extras/CMakeLists.txt4
-rw-r--r--extras/bazel_root/WORKSPACE0
l---------extras/bazel_root/third_party/fruit/BUILD1
l---------extras/bazel_root/third_party/fruit/build1
l---------extras/bazel_root/third_party/fruit/configuration1
l---------extras/bazel_root/third_party/fruit/examples1
l---------extras/bazel_root/third_party/fruit/include1
l---------extras/bazel_root/third_party/fruit/src1
l---------extras/bazel_root/third_party/fruit/tests1
-rw-r--r--extras/benchmark/CMakeLists.txt8
-rw-r--r--extras/benchmark/README.md109
-rwxr-xr-xextras/benchmark/analyze_symbol_size.sh46
-rw-r--r--extras/benchmark/boost_di_source_generator.py91
-rw-r--r--extras/benchmark/compile_time_benchmark.cpp131
-rwxr-xr-xextras/benchmark/format_bench_results.py353
-rw-r--r--extras/benchmark/fruit_source_generator.py206
-rwxr-xr-xextras/benchmark/generate_benchmark.py202
-rw-r--r--extras/benchmark/makefile_generator.py51
-rw-r--r--extras/benchmark/new_delete_benchmark.cpp98
-rwxr-xr-xextras/benchmark/run_benchmarks.py633
-rw-r--r--extras/benchmark/suites/boost_di.yml38
-rw-r--r--extras/benchmark/suites/fruit_debug.yml97
-rw-r--r--extras/benchmark/suites/fruit_full.yml66
-rw-r--r--extras/benchmark/suites/fruit_full_old_style.yml66
-rw-r--r--extras/benchmark/suites/fruit_mostly_full.yml51
-rw-r--r--extras/benchmark/suites/fruit_quick.yml49
-rw-r--r--extras/benchmark/suites/fruit_quick_old_style.yml50
-rw-r--r--extras/benchmark/suites/fruit_single.yml36
-rw-r--r--extras/benchmark/suites/fruit_single_old_style.yml36
-rw-r--r--extras/benchmark/tables/fruit_internal.yml192
-rw-r--r--extras/benchmark/tables/fruit_internal_old_vs_new_style.yml108
-rw-r--r--extras/benchmark/tables/fruit_wiki.yml282
-rw-r--r--extras/benchmark/tables/fruit_wiki_old_style.yml270
-rw-r--r--extras/doc/CMakeLists.txt94
-rw-r--r--extras/doc/README2
-rw-r--r--extras/doc/bar_handler.tex10
-rw-r--r--extras/doc/bind.tex14
-rw-r--r--extras/doc/bind_instance.tex9
-rw-r--r--extras/doc/car_component.tex17
-rw-r--r--extras/doc/checked_adder.tex9
-rw-r--r--extras/doc/checked_incrementer.tex15
-rw-r--r--extras/doc/common-footer.tex4
-rw-r--r--extras/doc/common-header.tex36
-rw-r--r--extras/doc/component_composition.tex35
-rw-r--r--extras/doc/component_dep_loop.tex11
-rwxr-xr-xextras/doc/extract_dependencies.sh13
-rw-r--r--extras/doc/foo_handler.tex10
-rwxr-xr-xextras/doc/generate_snippets.sh9
-rw-r--r--extras/doc/greeter.tex20
-rw-r--r--extras/doc/incrementer.tex10
-rw-r--r--extras/doc/incrementer_component.tex16
-rw-r--r--extras/doc/inject_macro.tex13
-rw-r--r--extras/doc/inject_macro_no_args.tex11
-rw-r--r--extras/doc/inject_macro_template.tex15
-rw-r--r--extras/doc/inject_typedef_greeter.tex13
-rw-r--r--extras/doc/inject_typedef_templated_constructor.tex13
-rw-r--r--extras/doc/inject_typedef_writer.tex11
-rw-r--r--extras/doc/inject_typedef_writer2.tex11
-rw-r--r--extras/doc/multiplier.tex9
-rw-r--r--extras/doc/parametrized_component.tex20
-rw-r--r--extras/doc/provider.tex10
-rw-r--r--extras/doc/provider_functor.tex12
-rw-r--r--extras/doc/register_constructor.tex13
-rw-r--r--extras/doc/register_constructor_component.tex18
-rw-r--r--extras/doc/register_factory.tex10
-rw-r--r--extras/doc/register_factory_macro.tex10
-rw-r--r--extras/doc/register_factory_typedef.tex10
-rw-r--r--extras/doc/register_factory_use.tex10
-rw-r--r--extras/doc/request_dispatcher.tex22
-rw-r--r--extras/doc/request_injector.tex17
-rw-r--r--extras/doc/scaler.tex15
-rw-r--r--extras/doc/server.tex9
-rw-r--r--extras/doc/simple_adder.tex9
-rw-r--r--extras/doc/simple_greeter.tex15
-rw-r--r--extras/doc/simple_incrementer.tex15
-rw-r--r--extras/doc/templated_component.tex16
-rw-r--r--extras/dockerfiles/Dockerfile.ubuntu-14.049
-rw-r--r--extras/dockerfiles/Dockerfile.ubuntu-16.049
-rw-r--r--extras/dockerfiles/Dockerfile.ubuntu-17.049
-rw-r--r--extras/dockerfiles/Dockerfile.ubuntu-17.109
-rw-r--r--extras/dockerfiles/common_cleanup.sh20
-rw-r--r--extras/dockerfiles/common_install.sh36
-rw-r--r--extras/dockerfiles/ubuntu-14.04_custom.list10
-rw-r--r--extras/dockerfiles/ubuntu-14.04_install.sh9
-rw-r--r--extras/dockerfiles/ubuntu-16.04_custom.list11
-rw-r--r--extras/dockerfiles/ubuntu-16.04_install.sh22
-rw-r--r--extras/dockerfiles/ubuntu-17.04_custom.list8
-rw-r--r--extras/dockerfiles/ubuntu-17.04_install.sh14
-rw-r--r--extras/dockerfiles/ubuntu-17.10_custom.list8
-rw-r--r--extras/dockerfiles/ubuntu-17.10_install.sh14
-rw-r--r--extras/fruit-2.0.0.ebuild28
-rwxr-xr-xextras/git_hooks/pre-commit12
-rw-r--r--extras/packaging/CMakeLists.txt24
-rw-r--r--extras/packaging/PKGBUILD30
-rw-r--r--extras/packaging/debian.compat1
-rw-r--r--extras/packaging/debian.control23
-rw-r--r--extras/packaging/debian.rules74
-rw-r--r--extras/packaging/libfruit.dsc10
-rw-r--r--extras/packaging/libfruit.install1
-rw-r--r--extras/packaging/libfruit.spec64
-rwxr-xr-xextras/scripts/analyze_template_instantiations_clang_diagnostics.py380
-rwxr-xr-xextras/scripts/filter_gcc_error.sh130
-rwxr-xr-xextras/scripts/find_untested_types.sh15
-rwxr-xr-xextras/scripts/find_unused_types.sh30
-rw-r--r--extras/scripts/parser.out897
-rw-r--r--extras/scripts/parsetab.py45
-rwxr-xr-xextras/scripts/postsubmit-helper.sh154
-rw-r--r--extras/scripts/postsubmit.bat49
-rwxr-xr-xextras/scripts/postsubmit.sh42
-rwxr-xr-xextras/scripts/run_benchs.sh107
-rwxr-xr-xextras/scripts/test_coverage.sh45
-rwxr-xr-xextras/scripts/travis_ci_install_linux.sh4
-rwxr-xr-xextras/scripts/travis_ci_install_osx.sh48
-rwxr-xr-xextras/scripts/travis_yml_generator.py225
-rw-r--r--include/fruit/component.h1050
-rw-r--r--include/fruit/fruit.h34
-rw-r--r--include/fruit/fruit_forward_decls.h65
-rw-r--r--include/fruit/impl/bindings.h142
-rw-r--r--include/fruit/impl/component.defn.h315
-rw-r--r--include/fruit/impl/component_functors.defn.h1262
-rw-r--r--include/fruit/impl/component_storage/binding_deps.defn.h55
-rw-r--r--include/fruit/impl/component_storage/binding_deps.h41
-rw-r--r--include/fruit/impl/component_storage/component_storage.defn.h80
-rw-r--r--include/fruit/impl/component_storage/component_storage.h65
-rw-r--r--include/fruit/impl/component_storage/component_storage_entry.defn.h232
-rw-r--r--include/fruit/impl/component_storage/component_storage_entry.h337
-rw-r--r--include/fruit/impl/component_storage/partial_component_storage.defn.h472
-rw-r--r--include/fruit/impl/component_storage/partial_component_storage.h49
-rw-r--r--include/fruit/impl/data_structures/arena_allocator.defn.h55
-rw-r--r--include/fruit/impl/data_structures/arena_allocator.h76
-rw-r--r--include/fruit/impl/data_structures/fixed_size_allocator.defn.h138
-rw-r--r--include/fruit/impl/data_structures/fixed_size_allocator.h120
-rw-r--r--include/fruit/impl/data_structures/fixed_size_vector.defn.h139
-rw-r--r--include/fruit/impl/data_structures/fixed_size_vector.h88
-rw-r--r--include/fruit/impl/data_structures/fixed_size_vector.templates.h56
-rw-r--r--include/fruit/impl/data_structures/memory_pool.defn.h97
-rw-r--r--include/fruit/impl/data_structures/memory_pool.h65
-rw-r--r--include/fruit/impl/data_structures/packed_pointer_and_bool.defn.h78
-rw-r--r--include/fruit/impl/data_structures/packed_pointer_and_bool.h64
-rw-r--r--include/fruit/impl/data_structures/semistatic_graph.defn.h198
-rw-r--r--include/fruit/impl/data_structures/semistatic_graph.h243
-rw-r--r--include/fruit/impl/data_structures/semistatic_graph.templates.h228
-rw-r--r--include/fruit/impl/data_structures/semistatic_map.defn.h41
-rw-r--r--include/fruit/impl/data_structures/semistatic_map.h130
-rw-r--r--include/fruit/impl/data_structures/semistatic_map.templates.h200
-rw-r--r--include/fruit/impl/fruit-config.h90
-rw-r--r--include/fruit/impl/fruit_assert.h37
-rw-r--r--include/fruit/impl/fruit_internal_forward_decls.h48
-rw-r--r--include/fruit/impl/injection_debug_errors.h110
-rw-r--r--include/fruit/impl/injection_errors.h577
-rw-r--r--include/fruit/impl/injector.defn.h155
-rw-r--r--include/fruit/impl/injector/injector_accessor_for_tests.defn.h36
-rw-r--r--include/fruit/impl/injector/injector_accessor_for_tests.h57
-rw-r--r--include/fruit/impl/injector/injector_storage.defn.h850
-rw-r--r--include/fruit/impl/injector/injector_storage.h285
-rw-r--r--include/fruit/impl/meta/algos.h74
-rw-r--r--include/fruit/impl/meta/basics.h241
-rw-r--r--include/fruit/impl/meta/component.h1012
-rw-r--r--include/fruit/impl/meta/errors.h67
-rw-r--r--include/fruit/impl/meta/eval.h259
-rw-r--r--include/fruit/impl/meta/fold.h124
-rw-r--r--include/fruit/impl/meta/graph.h110
-rw-r--r--include/fruit/impl/meta/immutable_map.h91
-rw-r--r--include/fruit/impl/meta/immutable_set.h64
-rw-r--r--include/fruit/impl/meta/list.h57
-rw-r--r--include/fruit/impl/meta/logical_operations.h39
-rw-r--r--include/fruit/impl/meta/map.h104
-rw-r--r--include/fruit/impl/meta/metaprogramming.h183
-rw-r--r--include/fruit/impl/meta/numeric_operations.h78
-rw-r--r--include/fruit/impl/meta/pair.h57
-rw-r--r--include/fruit/impl/meta/proof_tree_comparison.h125
-rw-r--r--include/fruit/impl/meta/proof_trees.h84
-rw-r--r--include/fruit/impl/meta/set.h181
-rw-r--r--include/fruit/impl/meta/signatures.h74
-rw-r--r--include/fruit/impl/meta/triplet.h51
-rw-r--r--include/fruit/impl/meta/vector.h274
-rw-r--r--include/fruit/impl/meta/wrappers.h178
-rw-r--r--include/fruit/impl/normalized_component.defn.h47
-rw-r--r--include/fruit/impl/normalized_component_storage/binding_normalization.h371
-rw-r--r--include/fruit/impl/normalized_component_storage/binding_normalization.templates.h725
-rw-r--r--include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h54
-rw-r--r--include/fruit/impl/normalized_component_storage/normalized_bindings.h85
-rw-r--r--include/fruit/impl/normalized_component_storage/normalized_component_storage.defn.h56
-rw-r--r--include/fruit/impl/normalized_component_storage/normalized_component_storage.h168
-rw-r--r--include/fruit/impl/normalized_component_storage/normalized_component_storage_holder.h71
-rw-r--r--include/fruit/impl/provider.defn.h71
-rw-r--r--include/fruit/impl/util/call_with_tuple.h46
-rw-r--r--include/fruit/impl/util/demangle_type_name.h24
-rw-r--r--include/fruit/impl/util/hash_codes.defn.h70
-rw-r--r--include/fruit/impl/util/hash_codes.h35
-rw-r--r--include/fruit/impl/util/hash_helpers.defn.h77
-rw-r--r--include/fruit/impl/util/hash_helpers.h102
-rw-r--r--include/fruit/impl/util/lambda_invoker.h56
-rw-r--r--include/fruit/impl/util/type_info.defn.h178
-rw-r--r--include/fruit/impl/util/type_info.h113
-rw-r--r--include/fruit/injector.h260
-rw-r--r--include/fruit/macro.h114
-rw-r--r--include/fruit/normalized_component.h143
-rw-r--r--include/fruit/provider.h156
-rw-r--r--src/CMakeLists.txt26
-rw-r--r--src/binding_normalization.cpp520
-rw-r--r--src/component.cpp48
-rw-r--r--src/demangle_type_name.cpp46
-rw-r--r--src/fixed_size_allocator.cpp38
-rw-r--r--src/injector_storage.cpp131
-rw-r--r--src/memory_pool.cpp27
-rw-r--r--src/normalized_component_storage.cpp129
-rw-r--r--src/normalized_component_storage_holder.cpp37
-rw-r--r--src/semistatic_graph.cpp34
-rw-r--r--src/semistatic_map.cpp34
-rw-r--r--tests/BUILD98
-rw-r--r--tests/CMakeLists.txt109
-rw-r--r--tests/build_defs.bzl22
-rw-r--r--tests/class_construction_tracker.h48
-rw-r--r--tests/data_structures/BUILD8
-rw-r--r--tests/data_structures/test_fixed_size_allocator.py193
-rw-r--r--tests/data_structures/test_fixed_size_vector.py217
-rw-r--r--tests/data_structures/test_semistatic_graph.py375
-rw-r--r--tests/data_structures/test_semistatic_map.py214
-rw-r--r--tests/fruit_test_common.py580
-rw-r--r--tests/meta/BUILD15
-rw-r--r--tests/meta/common.h102
-rw-r--r--tests/meta/test_algos.py41
-rw-r--r--tests/meta/test_basics.py88
-rw-r--r--tests/meta/test_graph.py63
-rw-r--r--tests/meta/test_list.py47
-rw-r--r--tests/meta/test_map.py78
-rw-r--r--tests/meta/test_meta_component.py85
-rw-r--r--tests/meta/test_metaprogramming.py46
-rw-r--r--tests/meta/test_proof_trees.py74
-rw-r--r--tests/meta/test_set.py273
-rw-r--r--tests/meta/test_vector.py119
-rwxr-xr-xtests/test_bind_interface.py325
-rwxr-xr-xtests/test_binding_clash.py522
-rwxr-xr-xtests/test_binding_compression.py161
-rw-r--r--tests/test_class_destruction.py173
-rw-r--r--tests/test_common.h30
-rwxr-xr-xtests/test_component.py172
-rwxr-xr-xtests/test_component_and_injector_params.py430
-rwxr-xr-xtests/test_component_replacement.py920
-rwxr-xr-xtests/test_defn_h_includes.py72
-rwxr-xr-xtests/test_dependency_loop.py146
-rw-r--r--tests/test_eager_injection.py119
-rw-r--r--tests/test_include_test.py42
-rwxr-xr-xtests/test_injected_provider.py292
-rwxr-xr-xtests/test_injector.py303
-rwxr-xr-xtests/test_injector_unsafe_get.py75
-rwxr-xr-xtests/test_install.py1067
-rw-r--r--tests/test_macros.h36
-rw-r--r--tests/test_misc.py169
-rwxr-xr-xtests/test_multibindings_bind_instance.py199
-rwxr-xr-xtests/test_multibindings_bind_interface.py208
-rwxr-xr-xtests/test_multibindings_bind_provider.py569
-rwxr-xr-xtests/test_multibindings_misc.py577
-rwxr-xr-xtests/test_normalized_component.py206
-rwxr-xr-xtests/test_register_constructor.py590
-rwxr-xr-xtests/test_register_factory.py2320
-rwxr-xr-xtests/test_register_instance.py496
-rwxr-xr-xtests/test_register_provider.py517
-rwxr-xr-xtests/test_required_types.py303
-rw-r--r--tests/test_semistatic_map_hash_selection.py58
-rw-r--r--tests/test_type_alignment.py60
-rw-r--r--tests/test_type_alignment_with_annotation.py69
-rw-r--r--tests/tested_features.md182
-rw-r--r--tests/util/BUILD9
-rw-r--r--tests/util/test_lambda_invoker.py66
-rw-r--r--tests/util/test_type_info.py94
-rw-r--r--tests/valgrind_suppressions.supp48
340 files changed, 40522 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..ad65d48
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,6 @@
+BasedOnStyle: LLVM
+Language: Cpp
+ColumnLimit: 120
+AllowShortFunctionsOnASingleLine: Empty
+AlwaysBreakTemplateDeclarations: true
+PointerAlignment: Left
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5da26d1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+/*.kdev4
+/.kdev_include_paths
+/coverage
+bench_results
+/extras/bazel_root/bazel-*
+.idea
+*.pyc
+/build*
+/cmake-build-debug
+/cmake-build-release
+/.vs
+/Debug
+/*.sln
+/*.vcxproj
+/*.vcxproj.filters
+/CMakeSettings.json
+/*.vcxproj.user
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6560579
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,219 @@
+#
+# This file was auto-generated from extras/scripts/travis_yml_generator.py, DO NOT EDIT
+#
+branches:
+ only:
+ - master
+dist: trusty
+language: cpp
+matrix:
+ fast_finish: true
+ include:
+ - compiler: gcc
+ env: COMPILER=gcc-7 UBUNTU=17.10 TEST=ReleasePlain
+ install: export OS=linux; export COMPILER='gcc-7'; export UBUNTU='17.10'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='gcc-7'; export UBUNTU='17.10'; extras/scripts/postsubmit.sh
+ ReleasePlain
+ - compiler: gcc
+ env: COMPILER=gcc-7 UBUNTU=17.10 TEST=DebugPlain
+ install: export OS=linux; export COMPILER='gcc-7'; export UBUNTU='17.10'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='gcc-7'; export UBUNTU='17.10'; extras/scripts/postsubmit.sh
+ DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-4.0 STL=libstdc++ UBUNTU=17.10 TEST=ReleasePlain
+ install: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+ export UBUNTU='17.10'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+ export UBUNTU='17.10'; extras/scripts/postsubmit.sh ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-4.0 STL=libstdc++ UBUNTU=17.10 TEST=DebugAsanUbsan
+ install: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+ export UBUNTU='17.10'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+ export UBUNTU='17.10'; extras/scripts/postsubmit.sh DebugAsanUbsan
+ - compiler: clang
+ env: COMPILER=clang-4.0 STL=libstdc++ UBUNTU=17.10 TEST=DebugPlain
+ install: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+ export UBUNTU='17.10'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+ export UBUNTU='17.10'; extras/scripts/postsubmit.sh DebugPlain
+ - compiler: gcc
+ env: COMPILER=bazel UBUNTU=16.04
+ install: export OS=linux; export COMPILER='bazel'; export UBUNTU='16.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='bazel'; export UBUNTU='16.04'; extras/scripts/postsubmit.sh
+ DebugPlain
+ - compiler: gcc
+ env: COMPILER=gcc-6 TEST=DebugPlain
+ install: export OS=osx; export COMPILER='gcc-6'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8
+ script: export OS=osx; export COMPILER='gcc-6'; extras/scripts/postsubmit.sh DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-4.0 STL=libc++ TEST=DebugPlain
+ install: export OS=osx; export COMPILER='clang-4.0'; export STL='libc++'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8
+ script: export OS=osx; export COMPILER='clang-4.0'; export STL='libc++'; extras/scripts/postsubmit.sh
+ DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-default STL=libc++ TEST=DebugPlain
+ install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
+ extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8.2
+ script: export OS=osx; export COMPILER='clang-default'; export STL='libc++'; extras/scripts/postsubmit.sh
+ DebugPlain
+ - compiler: gcc
+ env: COMPILER=gcc-5 UBUNTU=14.04 TEST=ReleasePlain
+ install: export OS=linux; export COMPILER='gcc-5'; export UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='gcc-5'; export UBUNTU='14.04'; extras/scripts/postsubmit.sh
+ ReleasePlain
+ - compiler: gcc
+ env: COMPILER=gcc-5 UBUNTU=14.04 TEST=DebugPlain
+ install: export OS=linux; export COMPILER='gcc-5'; export UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='gcc-5'; export UBUNTU='14.04'; extras/scripts/postsubmit.sh
+ DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-3.5 STL=libstdc++ UBUNTU=14.04 TEST=ReleasePlain
+ install: export OS=linux; export COMPILER='clang-3.5'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.5'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/postsubmit.sh ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-3.5 STL=libstdc++ UBUNTU=14.04 TEST=DebugPlain
+ install: export OS=linux; export COMPILER='clang-3.5'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.5'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/postsubmit.sh DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-3.9 STL=libstdc++ UBUNTU=14.04 TEST=ReleasePlain
+ install: export OS=linux; export COMPILER='clang-3.9'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.9'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/postsubmit.sh ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-3.9 STL=libstdc++ UBUNTU=14.04 TEST=DebugPlain
+ install: export OS=linux; export COMPILER='clang-3.9'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.9'; export STL='libstdc++';
+ export UBUNTU='14.04'; extras/scripts/postsubmit.sh DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-3.5 STL=libc++ UBUNTU=14.04 TEST=ReleasePlain
+ install: export OS=linux; export COMPILER='clang-3.5'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.5'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/postsubmit.sh ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-3.5 STL=libc++ UBUNTU=14.04 TEST=DebugPlain
+ install: export OS=linux; export COMPILER='clang-3.5'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.5'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/postsubmit.sh DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-3.9 STL=libc++ UBUNTU=14.04 TEST=ReleasePlain
+ install: export OS=linux; export COMPILER='clang-3.9'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.9'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/postsubmit.sh ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-3.9 STL=libc++ UBUNTU=14.04 TEST=DebugPlain
+ install: export OS=linux; export COMPILER='clang-3.9'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/travis_ci_install_linux.sh
+ os: linux
+ script: export OS=linux; export COMPILER='clang-3.9'; export STL='libc++'; export
+ UBUNTU='14.04'; extras/scripts/postsubmit.sh DebugPlain
+ - compiler: gcc
+ env: COMPILER=gcc-5 TEST=ReleasePlain
+ install: export OS=osx; export COMPILER='gcc-5'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8
+ script: export OS=osx; export COMPILER='gcc-5'; extras/scripts/postsubmit.sh ReleasePlain
+ - compiler: gcc
+ env: COMPILER=gcc-5 TEST=DebugPlain
+ install: export OS=osx; export COMPILER='gcc-5'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8
+ script: export OS=osx; export COMPILER='gcc-5'; extras/scripts/postsubmit.sh DebugPlain
+ - compiler: gcc
+ env: COMPILER=gcc-6 TEST=ReleasePlain
+ install: export OS=osx; export COMPILER='gcc-6'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8
+ script: export OS=osx; export COMPILER='gcc-6'; extras/scripts/postsubmit.sh ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-3.7 STL=libc++ TEST=ReleasePlain
+ install: export OS=osx; export COMPILER='clang-3.7'; export STL='libc++'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ script: export OS=osx; export COMPILER='clang-3.7'; export STL='libc++'; extras/scripts/postsubmit.sh
+ ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-3.7 STL=libc++ TEST=DebugPlain
+ install: export OS=osx; export COMPILER='clang-3.7'; export STL='libc++'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ script: export OS=osx; export COMPILER='clang-3.7'; export STL='libc++'; extras/scripts/postsubmit.sh
+ DebugPlain
+ - compiler: clang
+ env: COMPILER=clang-4.0 STL=libc++ TEST=ReleasePlain
+ install: export OS=osx; export COMPILER='clang-4.0'; export STL='libc++'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8
+ script: export OS=osx; export COMPILER='clang-4.0'; export STL='libc++'; extras/scripts/postsubmit.sh
+ ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-4.0 STL=libc++ TEST=DebugAsanUbsan
+ install: export OS=osx; export COMPILER='clang-4.0'; export STL='libc++'; extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8
+ script: export OS=osx; export COMPILER='clang-4.0'; export STL='libc++'; extras/scripts/postsubmit.sh
+ DebugAsanUbsan
+ - compiler: clang
+ env: COMPILER=clang-default STL=libc++ TEST=ReleasePlain
+ install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
+ extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode7.3
+ script: export OS=osx; export COMPILER='clang-default'; export STL='libc++'; extras/scripts/postsubmit.sh
+ ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-default STL=libc++ TEST=DebugAsan
+ install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
+ extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode7.3
+ script: export OS=osx; export COMPILER='clang-default'; export STL='libc++'; extras/scripts/postsubmit.sh
+ DebugAsan
+ - compiler: clang
+ env: COMPILER=clang-default STL=libc++ TEST=ReleasePlain
+ install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
+ extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8.2
+ script: export OS=osx; export COMPILER='clang-default'; export STL='libc++'; extras/scripts/postsubmit.sh
+ ReleasePlain
+ - compiler: clang
+ env: COMPILER=clang-default STL=libc++ TEST=DebugAsan
+ install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
+ extras/scripts/travis_ci_install_osx.sh
+ os: osx
+ osx_image: xcode8.2
+ script: export OS=osx; export COMPILER='clang-default'; export STL='libc++'; extras/scripts/postsubmit.sh
+ DebugAsan
+services:
+- docker
+sudo: required
+
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..463ddc9
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,23 @@
+
+package(default_visibility = ["//visibility:public"])
+licenses(["notice"])
+
+filegroup(
+ name = "fruit_headers",
+ srcs = glob([
+ "include/**/*.h",
+ "configuration/bazel/**/*.h",
+ ]),
+)
+
+cc_library(
+ name = "fruit",
+ srcs = glob([
+ "src/*.cpp",
+ "include/fruit/impl/**/*.h",
+ "configuration/bazel/**/*.h"]),
+ hdrs = glob(["include/fruit/*.h"]),
+ includes = ["include", "configuration/bazel"],
+ deps = [],
+ linkopts = ["-lm"],
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9ac08da
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,125 @@
+project(Fruit)
+cmake_minimum_required(VERSION 2.8)
+
+if (POLICY CMP0054)
+ cmake_policy(SET CMP0054 NEW)
+endif()
+
+# CMake on OSX likes to see this set explicitly, otherwise it outputs a warning.
+set(CMAKE_MACOSX_RPATH 1)
+
+if(NOT "${CMAKE_BUILD_TYPE}" MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)$")
+ message(FATAL_ERROR "Please re-run CMake, specifying -DCMAKE_BUILD_TYPE=Debug , -DCMAKE_BUILD_TYPE=Release , -DCMAKE_BUILD_TYPE=RelWithDebInfo or -DCMAKE_BUILD_TYPE=MinSizeRel .")
+endif()
+
+option(BUILD_SHARED_LIBS "Build shared library" ON)
+
+# The Visual Studio CMake generators default to multiple configurations, but Fruit doesn't support multi-configuration build directories.
+set(CMAKE_CONFIGURATION_TYPES "${CMAKE_BUILD_TYPE}")
+
+if(NOT "${RUNTIME_OUTPUT_DIRECTORY}")
+ set(RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+endif()
+
+set(FRUIT_ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX compiler flags." FORCE)
+
+set(FRUIT_ADDITIONAL_COMPILE_FLAGS "${FRUIT_ADDITIONAL_CXX_FLAGS}")
+
+if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Clang|AppleClang|MSVC)$")
+ message(WARNING "Compiler not officially supported: ${CMAKE_CXX_COMPILER_ID}")
+ # Full list of possible values at https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html .
+ # Major compilers not currently supported:
+ # * "Intel": not supported ATM due to compiler bugs:
+ # - https://software.intel.com/en-us/forums/intel-c-compiler/topic/606048
+ # - https://software.intel.com/en-us/forums/intel-c-compiler/topic/606049
+endif()
+
+if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Clang|Intel|AppleClang)$")
+ set(FRUIT_ADDITIONAL_COMPILE_FLAGS "${FRUIT_ADDITIONAL_COMPILE_FLAGS} -std=c++11 -W -Wall -Wno-unknown-warning-option -Wno-missing-braces")
+elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(MSVC)$")
+ # TODO: we currently disable the warning C4709 because MSVC emits it even when there is no reason to. Re-enable it when possible.
+ # TODO: the warning C4141 is disabled, because MSVC emits it ("'inline': used more than once") when a function/method is marked with both __forceinline and inline.
+ # TODO: the warning C4714 is disabled, MSVC emits it when it decides not to inline a __forceinline function/method.
+ # The warning C4577 is disabled because we don't need a termination guarantee on exceptions for functions marked with
+ # 'noexcept'.
+ # The warning C4530 is disabled because it's triggered by MSVC's STL.
+ set(FRUIT_ADDITIONAL_COMPILE_FLAGS "${FRUIT_ADDITIONAL_COMPILE_FLAGS} /nologo /FS /W4 /wd4324 /wd4709 /wd4459 /wd4141 /wd4714 /wd4577 /wd4530 /D_SCL_SECURE_NO_WARNINGS")
+endif()
+
+option(FRUIT_ENABLE_COVERAGE "Enable collection of test coverage. This is meant to be used by Fruit developers. It's only supported when using GCC on Linux." OFF)
+if("${FRUIT_ENABLE_COVERAGE}")
+ # We also disable exceptions because otherwise GCC considers every function/method call that could throw as an
+ # additional branch.
+ set(FRUIT_ADDITIONAL_COMPILE_FLAGS "${FRUIT_ADDITIONAL_COMPILE_FLAGS} -fprofile-arcs -ftest-coverage -fno-exceptions -fno-inline -O0")
+ set(FRUIT_ADDITIONAL_LINKER_FLAGS "${FRUIT_ADDITIONAL_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -fno-exceptions -fno-inline -O0")
+endif()
+
+set(FRUIT_USES_BOOST TRUE CACHE BOOL
+ "Whether to use Boost (specifically, boost::unordered_set and boost::unordered_map).
+ If this is false, Fruit will use std::unordered_set and std::unordered_map instead (however this causes injection to be a bit slower).")
+
+if("${WIN32}" AND "${FRUIT_USES_BOOST}")
+ set(BOOST_DIR "" CACHE PATH "The directory where the boost library is installed, e.g. C:\\boost\\boost_1_62_0.")
+ if("${BOOST_DIR}" STREQUAL "")
+ message(FATAL_ERROR "Please re-run CMake, specifying the boost library path as BOOST_DIR, e.g. -DBOOST_DIR=C:\\boost\\boost_1_62_0.")
+ endif()
+ include_directories("${BOOST_DIR}")
+endif()
+
+set(RUN_TESTS_UNDER_VALGRIND FALSE CACHE BOOL "Whether to run Fruit tests under valgrind")
+if ("${RUN_TESTS_UNDER_VALGRIND}")
+ set(RUN_TESTS_UNDER_VALGRIND_FLAG "1")
+endif()
+
+# Unsafe, only for debugging/benchmarking.
+#set(FRUIT_ADDITIONAL_COMPILE_FLAGS "${FRUIT_ADDITIONAL_COMPILE_FLAGS} -DFRUIT_NO_LOOP_CHECK")
+
+add_definitions(${FRUIT_ADDITIONAL_COMPILE_FLAGS})
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FRUIT_ADDITIONAL_LINKER_FLAGS}")
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FRUIT_ADDITIONAL_LINKER_FLAGS}")
+set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FRUIT_ADDITIONAL_LINKER_FLAGS}")
+
+if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
+ set(FRUIT_COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} ${FRUIT_ADDITIONAL_COMPILE_FLAGS}")
+else()
+ set(FRUIT_COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG} ${FRUIT_ADDITIONAL_COMPILE_FLAGS}")
+endif()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
+
+# (debug-only) compile switch to get deep template instantiation stacktraces for errors (instead
+# of the user-friendly default that hides Fruit internals).
+#add_definitions(-DFRUIT_DEEP_TEMPLATE_INSTANTIATION_STACKTRACES_FOR_ERRORS)
+
+set(INSTALL_INCLUDE_DIR include/fruit CACHE PATH "Installation directory for headers")
+set(INSTALL_LIBRARY_DIR lib CACHE PATH "Installation directory for libraries")
+
+set(FRUIT_VERSION "3.2.0")
+
+add_subdirectory(configuration)
+add_subdirectory(src)
+
+if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
+ # Do not exclude these from "make all" in debug mode, they must build.
+ add_subdirectory(examples)
+ add_subdirectory(tests)
+else()
+ add_subdirectory(examples EXCLUDE_FROM_ALL)
+ add_subdirectory(tests)
+endif()
+
+add_subdirectory(extras EXCLUDE_FROM_ALL)
+
+install(DIRECTORY include/fruit/
+ DESTINATION "${INSTALL_INCLUDE_DIR}"
+ FILES_MATCHING PATTERN "*.h")
+
+set(CPACK_PACKAGE_NAME "Fruit")
+set(CPACK_PACKAGE_VENDOR "Marco Poletti")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Fruit - Dependency Injection Framework For C++")
+string(REGEX REPLACE "([^.]*)\\.([^.]*)\\.([^.]*)" "\\1" CPACK_PACKAGE_VERSION_MAJOR "${FRUIT_VERSION}")
+string(REGEX REPLACE "([^.]*)\\.([^.]*)\\.([^.]*)" "\\2" CPACK_PACKAGE_VERSION_MINOR "${FRUIT_VERSION}")
+string(REGEX REPLACE "([^.]*)\\.([^.]*)\\.([^.]*)" "\\3" CPACK_PACKAGE_VERSION_PATCH "${FRUIT_VERSION}")
+set(CPACK_PACKAGE_VERSION "${FRUIT_VERSION}")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY "Fruit")
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..bf461be
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,240 @@
+
+# Contributing to Fruit
+
+This file contains various information and documentation for Fruit contributors.
+If you only want to use Fruit, see the [wiki](https://github.com/google/fruit/wiki);
+you can find instructions for building Fruit manually
+[here](https://github.com/google/fruit/wiki/install#building-fruit-manually).
+
+If you actually want to change Fruit itself, that's great! Read on.
+
+### Basics
+
+#### Build systems
+
+Fruit supports two build systems: CMake (configured in `CMakeLists.txt` files) and
+[Bazel](https://www.bazel.io) (configured in `BUILD` files).
+
+This means that when you build/test Fruit code you have a choice of what build system you want to use,
+but also that for larger changes (typically, if you add new files) you might need changes in both
+`CMakeLists.txt` and `BUILD` files, to make sure that Fruit keeps building (and passing its tests) under both build
+systems.
+Both build systems are tested in Travis CI (see below).
+
+Example commands to build a development version of Fruit using CMake (with all assertions enabled) and run the tests:
+
+```bash
+cd $PATH_TO_FRUIT
+mkdir build-debug
+cd build-debug
+cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-Werror -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG"
+make -j 16
+cd tests
+py.test-3 -n auto
+```
+
+### Continuous Integration (CI)
+
+Fruit uses Travis CI for continuous integration. You can see the latest CI runs in Travis CI
+[here](https://travis-ci.org/google/fruit/builds). The CI configuration is defined in
+`extras/scripts/travis_yml_generator.py`, that generates a `.travis.yml` file (which must also be checked in, due to the
+way Travis CI is configured).
+
+When editing the `travis_yml_generator.py` script you should also update the `.travis.yml` file (in the same commit)
+by running:
+
+```bash
+cd $PATH_TO_FRUIT
+extras/scripts/travis_yml_generator.py >.travis.yml
+```
+
+Fruit tests run in Travis CI in various configurations/environments, notably:
+
+* In Linux or OS X
+* In various Ubuntu versions
+* Using GCC or Clang
+* Optionally running under Valgrind
+* Optionally running with ASan/UBSan
+* Using CMake or Bazel
+
+These tests run after every commit in master and for every pull request (as soon as the pull request is sent).
+
+Linux tests run in Docker, using a set of images built for this purpose
+([list of images](https://hub.docker.com/r/polettimarco/fruit-basesystem/tags/)).
+
+If a test fails in Travis CI in some configuration, look at the beginning of the Travis CI Job log for a line such as:
+
+```bash
+export OS=linux; export COMPILER='clang-3.9'; export STL='libstdc++'; export UBUNTU='16.04'; extras/scripts/postsubmit.sh DebugValgrind
+```
+
+You can then run the same command locally (from your fruit directory) to reproduce the issue. Running this
+`postsubmit.sh` script will run the tests under Docker to ensure repeatability of the results.
+
+For example, even if the failure only happens with an old Ubuntu/GCC version you don't have installed, it will download
+a Docker image containing that old Ubuntu/GCC and then run the tests inside a VM started from that image.
+
+Once `postsubmit.sh` completes, if you want you can attach to the stopped VM used to run the tests by running:
+
+```bash
+docker attach fruit
+```
+
+This is often very useful to e.g. re-run a compilation manually with additional debug flags.
+
+When running `postsubmit.sh` manually in this way, it will run using the latest changes in your fruit directory, even if
+they aren't staged/committed yet. This allows to do a quicker edit/test cycle.
+
+To speed up the execution of `postsubmit.sh` you can also set the `NJOBS` variable, e.g.:
+
+```bash
+export NJOBS=16; export OS=linux; export COMPILER='clang-3.9'; export STL='libstdc++'; export UBUNTU='16.04'; extras/scripts/postsubmit.sh DebugValgrind
+```
+
+The default number of jobs (used in Travis CI) is 2.
+
+### How to run Fruit tests on Windows
+
+You can import Fruit in Visual Studio (2017 and later) as a CMake project. You need to set the relevant CMake flags in
+the `CMakeSettings.json` file that Visual Studio will create.
+For example, if you installed Boost in `C:\boost\boost_1_62_0`, you can put this configuration in your
+`CMakeSettings.json`:
+
+ {
+ // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
+ "configurations": [
+ {
+ "name": "x86-Debug",
+ "generator": "Visual Studio 15 2017",
+ "configurationType": "Debug",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "-DBOOST_DIR=C:\\boost\\boost_1_62_0 -DCMAKE_BUILD_TYPE=Debug -DFRUIT_ADDITIONAL_CXX_FLAGS=/Z7",
+ "buildCommandArgs": "-m -v:minimal"
+ },
+ {
+ "name": "x86-Release",
+ "generator": "Visual Studio 15 2017",
+ "configurationType": "Release",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "-DBOOST_DIR=C:\\boost\\boost_1_62_0 -DCMAKE_BUILD_TYPE=Release",
+ "buildCommandArgs": "-m -v:minimal"
+ },
+ {
+ "name": "x64-Debug",
+ "generator": "Visual Studio 15 2017 Win64",
+ "configurationType": "Debug",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "-DBOOST_DIR=C:\\boost\\boost_1_62_0 -DCMAKE_BUILD_TYPE=Debug -DFRUIT_ADDITIONAL_CXX_FLAGS=/Z7",
+ "buildCommandArgs": "-m -v:minimal"
+ },
+ {
+ "name": "x64-Release",
+ "generator": "Visual Studio 15 2017 Win64",
+ "configurationType": "Release",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "-DBOOST_DIR=C:\\boost\\boost_1_62_0 -DCMAKE_BUILD_TYPE=Release",
+ "buildCommandArgs": "-m -v:minimal"
+ }
+ ]
+ }
+
+The `/Z7` flag instructs Visual Studio to use the C7 format for debugging information, which allows Fruit's tests to run in parallel without interfering with each other.
+
+You can now run CMake within Visual Studio (from the menu: CMake -> Cache -> Generate -> CMakeLists.txt) and build Fruit (from the menu: CMake -> Build All).
+
+You can also run tests, but *only* from the command-line (after building Fruit from Visual Studio), running tests from Visual Studio doesn't work.
+To do so:
+
+* Open the Start menu
+* From there, open the "Native Tools Command Prompt for VS 2017" shell for the chosen architecture. For example, "x64 Native Tools Command Prompt for VS 2017".
+* In Visual Studio, open the Output view (from the menu: View -> Output) and select "CMake" in the "Show output from:" dropdown menu.
+* Scroll to the beginning of that view. You should see two lines starting with "Command line" and "Working directory" respectively.
+* Cd to that working directory in the shell. For example, if the path in the "Working directory" line is `C:\Users\Marco\AppData\Local\CMakeBuild\fa17dda0-4eec-6438-a358-e1253b7e86ff\build\x64-Debug`, you can run `cd "C:\Users\Marco\AppData\Local\CMakeBuild\fa17dda0-4eec-6438-a358-e1253b7e86ff\build\x64-Debug"`.
+* Cd to the "tests" subdirectory ("cd tests").
+* Then run pytest, e.g. `py.test -n auto`.
+
+### Sending pull requests
+
+If you send a pull request, you should make sure that these CI tests are passing. They will run automatically on your
+pull request as soon as you send it.
+
+As an exception, if the current master also failed the last CI run feel free to send the pull request anyway (you can go
+[here](https://travis-ci.org/google/fruit) to check if that's the case).
+
+If a test fails, see the CI section above for informations on how to reproduce.
+
+You should also make sure that your code:
+
+* Is formatted correctly ([more details here](#code-style))
+* Has appropriate tests (if your change is user-visible, or if you're introducing new branches that should be tested)
+
+### What to install in order to develop Fruit code
+
+In addition to
+[the compiler you need to install to build Fruit](https://github.com/google/fruit/wiki/install#dependencies),
+when developing Fruit code you might need some of the following software. Note that depending on your change you may or
+may not need all of these; you might want to go ahead without these and then only install additional things if you get
+an error about a missing tool.
+
+* CMake
+* Bazel ([installation instructions](https://www.bazel.io/docs/install.html))
+* Valgrind
+* Docker
+
+## Useful command for fast edit/rebuild/retest cycles
+
+This command uses Bazel to run the tests (so you need to have it installed in order to use this).
+Bazel has a much more fine-grained picture of what tests depend on what source files, so it will often avoid running
+tests that have passed before when it knows that they will pass (unlike py.test that runs the entire test suite every
+time). This is especially relevant for incremental builds when only test sources have changed (e.g. after adjusting an
+expectation in a test or fixing a bug in the test); there is little difference when changing `src/` or `include/`
+because all tests will be re-run anyway.
+
+```bash
+cd $PATH_TO_FRUIT/extras/bazel_root
+bazel test --python_path=/usr/bin/python3 \
+ --test_output=errors \
+ --test_summary=terse \
+ //third_party/fruit/...
+```
+
+## Checking test coverage
+
+Fruit's test suite supports collecting test coverage (only when building with GCC on Linux using CMake).
+Example commands:
+
+```bash
+cd $PATH_TO_FRUIT
+mkdir build-coverage
+cd build-coverage
+CXX=g++-6 cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DFRUIT_ENABLE_COVERAGE=ON
+make -j 10
+(cd tests; py.test-3 -n auto)
+lcov --rc lcov_branch_coverage=1 --capture --directory . --output-file coverage.info
+lcov --rc lcov_branch_coverage=1 --remove coverage.info '/usr/include/*' '/tmp/*' -o coverage-filtered.info
+genhtml --no-function-coverage --rc lcov_branch_coverage=1 --rc genhtml_hi_limit=100 coverage-filtered.info --output-directory html
+google-chrome html/index.html
+```
+
+The important figures for each file are:
+* Percentage of lines covered
+* Percentage of branches covered
+
+Ideally, they should both be 100%. The `LCOV_EXCL_LINE` and `LCOV_EXCL_BR_LINE` markers can be used to mark lines and
+branches (respectively) that can't be covered and therefore should be excluded.
+
+Note that the "percentage of **functions** covered" metric is not meaningful for Fruit, since it considers each
+instantiation of a template function/method as separate (even if they share the same source lines).
+
+## Code style
+
+C++ code in Fruit should be indented using clang-format (a `.clang-format` file is provided in the Fruit root
+directory). You can re-indent all code using this command:
+
+```bash
+$ clang-format -i $(git ls-files | egrep '\.cpp|\.h' )
+```
+
+## Reporting vulnerabilities
+
+In case of a security vulnerability in Fruit, please contact [poletti.marco@gmail.com](mailto:poletti.marco@gmail.com) directly instead of using the public issue tracker.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..80d612b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..084579a
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,23 @@
+name: "google-fruit"
+description:
+ "Fruit is a dependency injection framework for C++, loosely inspired by the "
+ "Guice framework for Java. It uses C++ metaprogramming together with some "
+ "new C++11 features to detect most injection problems at compile-time. It "
+ "allows to split the implementation code in \"components\" (aka modules) that "
+ "can be assembled to form other components. From a component with no "
+ "requirements it's then possible to create an injector, that provides an "
+ "instance of the interfaces exposed by the component."
+
+third_party {
+ url {
+ type: HOMEPAGE
+ value: "https://github.com/google/fruit"
+ }
+ url {
+ type: GIT
+ value: "https://github.com/google/fruit.git"
+ }
+ version: "4f1aacf4aaeec2565b2945bba311d2e0efbbb579"
+ last_upgrade_date { year: 2018 month: 7 day: 26 }
+ license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..80d612b
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b87cae3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+
+[![Build Status](https://img.shields.io/travis/google/fruit/master.svg?label=Linux/OSX%20build/tests)](https://travis-ci.org/google/fruit)
+[![Build status](https://img.shields.io/appveyor/ci/poletti-marco/fruit/master.svg?label=Windows%20build/tests)](https://ci.appveyor.com/project/poletti-marco/fruit)
+[![Coverity Scan Status](https://img.shields.io/coverity/scan/8486.svg?label=Coverity%20scan)](https://scan.coverity.com/projects/google-fruit)
+[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1040/badge)](https://bestpractices.coreinfrastructure.org/projects/1040)
+
+Fruit is a [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection) framework for C++, loosely inspired by the Guice framework for Java. It uses C++ metaprogramming together with some new C++11 features to detect most injection problems at compile-time.
+It allows to split the implementation code in "components" (aka modules) that can be assembled to form other components.
+From a component with no requirements it's then possible to create an injector, that provides an instance of the interfaces exposed by the component.
+
+See the [wiki](https://github.com/google/fruit/wiki) for more information, including installation instructions, tutorials and reference documentation.
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..1c3a847
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,62 @@
+version: 1.0.{build}
+clone_folder: C:\Fruit
+environment:
+ PYTHON3_PATH: C:\Python36
+ matrix:
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ CMAKE_GENERATOR: 'Visual Studio 15 2017 Win64'
+ VCVARSALL_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build'
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DCMAKE_CXX_FLAGS="/WX /DFRUIT_DEBUG /DFRUIT_EXTRA_DEBUG /D_ITERATOR_DEBUG_LEVEL=2" -T host=x64'
+ CONFIGURATION: Debug
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ CMAKE_GENERATOR: 'Visual Studio 14 2015 Win64'
+ VCVARSALL_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC'
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DCMAKE_CXX_FLAGS="/WX /DFRUIT_DEBUG /DFRUIT_EXTRA_DEBUG /D_ITERATOR_DEBUG_LEVEL=2" -T host=x64'
+ CONFIGURATION: Debug
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ MINGW_PATH: 'C:\MinGW\bin'
+ CMAKE_GENERATOR: 'MinGW Makefiles'
+ VCVARSALL_DIR: ''
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DCMAKE_CXX_FLAGS="-Werror -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG"'
+ CONFIGURATION: Debug
+
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ MINGW_PATH: 'C:\MinGW\bin'
+ CMAKE_GENERATOR: 'MinGW Makefiles'
+ VCVARSALL_DIR: ''
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DCMAKE_CXX_FLAGS="-Werror"'
+ CONFIGURATION: Release
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ MINGW_PATH: 'C:\MinGW\bin'
+ CMAKE_GENERATOR: 'MinGW Makefiles'
+ VCVARSALL_DIR: ''
+ ADDITIONAL_CMAKE_ARGS: '-DBOOST_DIR=C:\Libraries\boost_1_63_0 -DCMAKE_CXX_FLAGS="-Werror"'
+ CONFIGURATION: Release
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ CMAKE_GENERATOR: 'Visual Studio 15 2017 Win64'
+ VCVARSALL_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build'
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DCMAKE_CXX_FLAGS="/WX" -T host=x64'
+ CONFIGURATION: Release
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ CMAKE_GENERATOR: 'Visual Studio 14 2015 Win64'
+ VCVARSALL_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC'
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DCMAKE_CXX_FLAGS="/WX" -T host=x64'
+ CONFIGURATION: Release
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ MINGW_PATH: 'C:\MinGW\bin'
+ CMAKE_GENERATOR: 'MinGW Makefiles'
+ VCVARSALL_DIR: ''
+ ADDITIONAL_CMAKE_ARGS: '-DBOOST_DIR=C:\Libraries\boost_1_63_0 -DBUILD_SHARED_LIBS=False -DCMAKE_CXX_FLAGS="-Werror"'
+ CONFIGURATION: Release
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ CMAKE_GENERATOR: 'Visual Studio 15 2017 Win64'
+ VCVARSALL_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build'
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DBUILD_SHARED_LIBS=False -DCMAKE_CXX_FLAGS="/WX" -T host=x64'
+ CONFIGURATION: Release
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ CMAKE_GENERATOR: 'Visual Studio 14 2015 Win64'
+ VCVARSALL_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC'
+ ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DBUILD_SHARED_LIBS=False -DCMAKE_CXX_FLAGS="/WX" -T host=x64'
+ CONFIGURATION: Release
+build_script:
+- cmd: cmd /c C:\Fruit\extras\scripts\postsubmit.bat
diff --git a/conanfile.py b/conanfile.py
new file mode 100644
index 0000000..02e208e
--- /dev/null
+++ b/conanfile.py
@@ -0,0 +1,37 @@
+from conans import ConanFile, CMake, tools
+
+
+class FruitConan(ConanFile):
+ name = "fruit"
+ version = "3.2.0"
+ license = "Apache"
+ url = "https://github.com/google/fruit"
+ description = "C++ dependency injection framework"
+ settings = "os", "compiler", "build_type", "arch"
+ options = {"shared": [True, False]}
+ default_options = "shared=False"
+ generators = "cmake"
+
+ def source(self):
+ self.run("git clone https://github.com/google/fruit")
+ self.run("cd fruit && git checkout v3.2.0")
+
+ def build(self):
+ cmake = CMake(self)
+ if self.options.shared:
+ cmake.definitions["BUILD_SHARED_LIBS"] = "YES"
+ else:
+ cmake.definitions["BUILD_SHARED_LIBS"] = "NO"
+ cmake.definitions["FRUIT_USES_BOOST"] = "NO"
+ cmake.definitions["RUN_TESTS_UNDER_VALGRIND"] = "NO"
+ cmake.definitions["FRUIT_TESTS_USE_PRECOMPILED_HEADERS"] = "NO"
+ cmake.definitions["FRUIT_ENABLE_COVERAGE"] = "NO"
+ cmake.configure(source_folder="fruit")
+ cmake.build()
+ cmake.install()
+
+ def package(self):
+ pass
+
+ def package_info(self):
+ self.cpp_info.libs = ["fruit"]
diff --git a/configuration/CMakeLists.txt b/configuration/CMakeLists.txt
new file mode 100644
index 0000000..b18a463
--- /dev/null
+++ b/configuration/CMakeLists.txt
@@ -0,0 +1,221 @@
+
+include(CheckCXXSourceCompiles)
+
+set(CMAKE_REQUIRED_FLAGS "${FRUIT_COMPILE_FLAGS}")
+
+CHECK_CXX_SOURCE_COMPILES("
+int main() {}
+"
+FRUIT_TRIVIAL_SOURCE_COMPILES)
+
+if (NOT "${FRUIT_TRIVIAL_SOURCE_COMPILES}")
+ message(FATAL_ERROR "A trivial program with an empty main doesn't compile, something is wrong with your compiler and/or with your compiler flags.")
+endif()
+
+CHECK_CXX_SOURCE_COMPILES("
+template <typename T, typename U>
+struct Pair {};
+
+struct Map : public Pair<int, float>, Pair<int, char> {};
+
+template <typename Value>
+Value f(Pair<int, Value>*) { return Value(); }
+
+int main() {
+ f((Map*)0);
+}
+"
+FRUIT_HAS_CLANG_ARBITRARY_OVERLOAD_RESOLUTION_BUG)
+
+CHECK_CXX_SOURCE_COMPILES("
+int main() {
+ bool b = __has_trivial_copy(int);
+ (void) b;
+ return 0;
+}
+"
+FRUIT_HAS_HAS_TRIVIAL_COPY)
+
+CHECK_CXX_SOURCE_COMPILES("
+int main() {
+ bool b = __is_trivially_copyable(int);
+ (void) b;
+ return 0;
+}
+"
+FRUIT_HAS_IS_TRIVIALLY_COPYABLE)
+
+CHECK_CXX_SOURCE_COMPILES("
+#include <cstddef>
+using X = max_align_t;
+int main() {
+ return 0;
+}
+"
+FRUIT_HAS_MAX_ALIGN_T)
+
+CHECK_CXX_SOURCE_COMPILES("
+#include <type_traits>
+int main() {
+ bool b = std::is_trivially_copyable<int>::value;
+ (void) b;
+ return 0;
+}
+"
+FRUIT_HAS_STD_IS_TRIVIALLY_COPYABLE)
+
+CHECK_CXX_SOURCE_COMPILES("
+#include <type_traits>
+int main() {
+ bool b = std::is_trivially_copy_constructible<int>::value;
+ (void) b;
+ return 0;
+}
+"
+FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE)
+
+CHECK_CXX_SOURCE_COMPILES("
+#include <cstddef>
+using X = std::max_align_t;
+int main() {
+ return 0;
+}
+"
+FRUIT_HAS_STD_MAX_ALIGN_T)
+
+CHECK_CXX_SOURCE_COMPILES("
+#include <typeinfo>
+int main() {
+ (void) typeid(int);
+ return 0;
+}
+"
+FRUIT_HAS_TYPEID)
+
+CHECK_CXX_SOURCE_COMPILES("
+#include <typeinfo>
+int main() {
+ constexpr static const std::type_info& x = typeid(int);
+ (void) x;
+ return 0;
+}
+"
+FRUIT_HAS_CONSTEXPR_TYPEID)
+
+CHECK_CXX_SOURCE_COMPILES("
+#include <cxxabi.h>
+int main() {
+ auto* p = abi::__cxa_demangle;
+ (void) p;
+ return 0;
+}
+"
+FRUIT_HAS_CXA_DEMANGLE)
+
+if("${FRUIT_ENABLE_COVERAGE}")
+ set(FRUIT_HAS_ALWAYS_INLINE_ATTRIBUTE OFF)
+ set(FRUIT_HAS_FORCEINLINE OFF)
+else()
+CHECK_CXX_SOURCE_COMPILES("
+__attribute__((always_inline))
+void f() {
+}
+
+int main() {
+ return 0;
+}
+"
+FRUIT_HAS_ALWAYS_INLINE_ATTRIBUTE)
+
+CHECK_CXX_SOURCE_COMPILES("
+__forceinline
+void f() {
+}
+
+int main() {
+ return 0;
+}
+"
+FRUIT_HAS_FORCEINLINE)
+
+endif()
+
+CHECK_CXX_SOURCE_COMPILES("
+[[deprecated]] void f();
+
+int main() {
+ return 0;
+}
+"
+FRUIT_HAS_ATTRIBUTE_DEPRECATED)
+
+CHECK_CXX_SOURCE_COMPILES("
+void f() __attribute__((deprecated));
+
+int main() {
+ return 0;
+}
+"
+FRUIT_HAS_GCC_ATTRIBUTE_DEPRECATED)
+
+CHECK_CXX_SOURCE_COMPILES("
+__declspec(deprecated) void f();
+
+int main() {
+ return 0;
+}
+"
+FRUIT_HAS_DECLSPEC_DEPRECATED)
+
+CHECK_CXX_SOURCE_COMPILES("
+int f() {
+ static int x = 1;
+ if (x == 1) {
+ return 0;
+ } else {
+ __assume(0);
+ // Note: the lack of return here is intentional
+ }
+}
+
+int main() {
+ return f();
+}
+"
+FRUIT_HAS_MSVC_ASSUME)
+
+CHECK_CXX_SOURCE_COMPILES("
+int f() {
+ static int x = 1;
+ if (x == 1) {
+ return 0;
+ } else {
+ __builtin_unreachable();
+ // Note: the lack of return here is intentional
+ }
+}
+
+int main() {
+ return f();
+}
+"
+FRUIT_HAS_BUILTIN_UNREACHABLE)
+
+
+if (NOT "${FRUIT_HAS_STD_MAX_ALIGN_T}" AND NOT "${FRUIT_HAS_MAX_ALIGN_T}")
+ message(WARNING "The current C++ standard library doesn't support std::max_align_t nor ::max_align_t. Attempting to use std::max_align_t anyway, but it most likely won't work.")
+endif()
+
+if(NOT "${FRUIT_HAS_STD_IS_TRIVIALLY_COPYABLE}" AND NOT "${FRUIT_HAS_IS_TRIVIALLY_COPYABLE}"
+ AND NOT "${FRUIT_HAS_HAS_TRIVIAL_COPY}")
+ message(WARNING "The current standard library doesn't support std::is_trivially_copyable<T>, and the current compiler doesn't support __is_trivially_copyable(T) nor __has_trivial_copy(T). Attemping to use std::is_trivially_copyable<T> anyway, but it most likely won't work.")
+endif()
+
+if (NOT "${FRUIT_HAS_ATTRIBUTE_DEPRECATED}" AND NOT "${FRUIT_HAS_GCC_ATTRIBUTE_DEPRECATED}" AND NOT "${FRUIT_HAS_DECLSPEC_DEPRECATED}")
+ message(WARNING "No supported way to mark functions as deprecated was found. Continuing anyway, without the 'deprecated' markers.")
+endif()
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fruit-config-base.h.in ${CMAKE_CURRENT_BINARY_DIR}/../include/fruit/impl/fruit-config-base.h)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/../include/fruit/impl/fruit-config-base.h
+ DESTINATION ${INSTALL_INCLUDE_DIR}/impl)
diff --git a/configuration/bazel/fruit/impl/fruit-config-base.h b/configuration/bazel/fruit/impl/fruit-config-base.h
new file mode 100644
index 0000000..8e7af1d
--- /dev/null
+++ b/configuration/bazel/fruit/impl/fruit-config-base.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_CONFIG_BASE_H
+#define FRUIT_CONFIG_BASE_H
+
+// Needed for all Clang versions (as of January 2016), not needed for GCC.
+// This can also be defined for GCC, but it slightly slows down compile time of code using Fruit.
+#define FRUIT_HAS_CLANG_ARBITRARY_OVERLOAD_RESOLUTION_BUG 1
+
+// Whether the compiler defines std::max_align_t.
+#define FRUIT_HAS_STD_MAX_ALIGN_T 1
+
+// Whether the compiler defines ::max_align_t.
+// Ignored if FRUIT_HAS_STD_MAX_ALIGN_T is set.
+#define FRUIT_HAS_MAX_ALIGN_T 1
+
+// Whether the compiler defines std::is_trivially_copyable.
+#define FRUIT_HAS_STD_IS_TRIVIALLY_COPYABLE 1
+
+// Whether the compiler defines __has_trivial_copy.
+// Ignored if FRUIT_HAS_STD_IS_TRIVIALLY_COPYABLE is set.
+#define FRUIT_HAS_HAS_TRIVIAL_COPY 1
+
+// Whether the compiler defines __is_trivially_copyable.
+// Ignored if FRUIT_HAS_STD_IS_TRIVIALLY_COPYABLE is set.
+#define FRUIT_HAS_IS_TRIVIALLY_COPYABLE 1
+
+// Whether the compiler defines std::is_trivially_copy_constructible.
+#define FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE 1
+
+// Whether typeid() is available. Typically, it is unless RTTI is disabled.
+#define FRUIT_HAS_TYPEID 1
+
+// Whether typeid() is constexpr. Typically, it is except in MSVC.
+#define FRUIT_HAS_CONSTEXPR_TYPEID 1
+
+// Whether abi::__cxa_demangle() is available after including cxxabi.h.
+#define FRUIT_HAS_CXA_DEMANGLE 1
+
+#define FRUIT_USES_BOOST 1
+
+#define FRUIT_HAS_ALWAYS_INLINE_ATTRIBUTE 1
+
+#define FRUIT_HAS_FORCEINLINE 0
+
+#define FRUIT_HAS_ATTRIBUTE_DEPRECATED 0
+
+#define FRUIT_HAS_GCC_ATTRIBUTE_DEPRECATED 1
+
+#define FRUIT_HAS_DECLSPEC_DEPRECATED 0
+
+#define FRUIT_HAS_MSVC_ASSUME 0
+
+#define FRUIT_HAS_BUILTIN_UNREACHABLE 1
+
+#endif // FRUIT_CONFIG_BASE_H
diff --git a/configuration/fruit-config-base.h.in b/configuration/fruit-config-base.h.in
new file mode 100644
index 0000000..5554eea
--- /dev/null
+++ b/configuration/fruit-config-base.h.in
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_CONFIG_BASE_H
+#define FRUIT_CONFIG_BASE_H
+
+#cmakedefine FRUIT_HAS_CLANG_ARBITRARY_OVERLOAD_RESOLUTION_BUG 1
+#cmakedefine FRUIT_HAS_HAS_TRIVIAL_COPY 1
+#cmakedefine FRUIT_HAS_IS_TRIVIALLY_COPYABLE 1
+#cmakedefine FRUIT_HAS_MAX_ALIGN_T 1
+#cmakedefine FRUIT_HAS_STD_IS_TRIVIALLY_COPYABLE 1
+#cmakedefine FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE 1
+#cmakedefine FRUIT_HAS_STD_MAX_ALIGN_T 1
+#cmakedefine FRUIT_HAS_TYPEID 1
+#cmakedefine FRUIT_HAS_CONSTEXPR_TYPEID 1
+#cmakedefine FRUIT_HAS_CXA_DEMANGLE 1
+#cmakedefine FRUIT_USES_BOOST 1
+#cmakedefine FRUIT_HAS_ALWAYS_INLINE_ATTRIBUTE 1
+#cmakedefine FRUIT_HAS_FORCEINLINE 1
+#cmakedefine FRUIT_HAS_ATTRIBUTE_DEPRECATED 1
+#cmakedefine FRUIT_HAS_GCC_ATTRIBUTE_DEPRECATED 1
+#cmakedefine FRUIT_HAS_DECLSPEC_DEPRECATED 1
+#cmakedefine FRUIT_HAS_MSVC_ASSUME 1
+#cmakedefine FRUIT_HAS_BUILTIN_UNREACHABLE 1
+
+#endif // FRUIT_CONFIG_BASE_H
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..1e33fad
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,12 @@
+
+add_subdirectory(simple_injection)
+add_subdirectory(hello_world)
+
+# This uses threads and doesn't work on Windows (at least when using MinGW's GCC).
+if(NOT "${WIN32}")
+ add_subdirectory(server)
+endif()
+
+add_subdirectory(multibindings)
+add_subdirectory(scaling_doubles)
+add_subdirectory(annotated_injection)
diff --git a/examples/annotated_injection/BUILD b/examples/annotated_injection/BUILD
new file mode 100644
index 0000000..a659487
--- /dev/null
+++ b/examples/annotated_injection/BUILD
@@ -0,0 +1,11 @@
+
+licenses(["notice"])
+
+cc_binary(
+ name = "annotated_injection",
+ srcs = glob([
+ "*.cpp",
+ "*.h",
+ ]),
+ deps = ["//third_party/fruit"],
+)
diff --git a/examples/annotated_injection/CMakeLists.txt b/examples/annotated_injection/CMakeLists.txt
new file mode 100644
index 0000000..5fbf754
--- /dev/null
+++ b/examples/annotated_injection/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+set(ANNOTATED_INJECTION_SOURCES
+main.cpp
+car.cpp
+main_brake.cpp
+emergency_brake.cpp
+)
+
+add_executable(annotated_injection ${ANNOTATED_INJECTION_SOURCES})
+target_link_libraries(annotated_injection fruit)
diff --git a/examples/annotated_injection/brake.h b/examples/annotated_injection/brake.h
new file mode 100644
index 0000000..3cfaf52
--- /dev/null
+++ b/examples/annotated_injection/brake.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BRAKE_H
+#define BRAKE_H
+
+class Brake {
+public:
+ // Activates the brake. Throws an exception if braking failed.
+ virtual void activate() = 0;
+};
+
+#endif // BRAKE_H
diff --git a/examples/annotated_injection/car.cpp b/examples/annotated_injection/car.cpp
new file mode 100644
index 0000000..605a4de
--- /dev/null
+++ b/examples/annotated_injection/car.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "car.h"
+#include "emergency_brake.h"
+#include "main_brake.h"
+
+class CarImpl : public Car {
+private:
+ Brake* mainBrake;
+ Brake* emergencyBrake;
+
+public:
+ INJECT(CarImpl(ANNOTATED(MainBrake, Brake*) mainBrake, ANNOTATED(EmergencyBrake, Brake*) emergencyBrake))
+ : mainBrake(mainBrake), emergencyBrake(emergencyBrake) {}
+
+ void brake() override {
+ try {
+ mainBrake->activate();
+ } catch (...) {
+ // The main brake failed!
+ emergencyBrake->activate();
+ }
+ }
+};
+
+fruit::Component<Car> getCarComponent() {
+ return fruit::createComponent()
+ .bind<Car, CarImpl>()
+ .install(getMainBrakeComponent)
+ .install(getEmergencyBrakeComponent);
+}
diff --git a/examples/annotated_injection/car.h b/examples/annotated_injection/car.h
new file mode 100644
index 0000000..4b25043
--- /dev/null
+++ b/examples/annotated_injection/car.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAR_H
+#define CAR_H
+
+#include <fruit/fruit.h>
+
+class Car {
+public:
+ virtual void brake() = 0;
+};
+
+fruit::Component<Car> getCarComponent();
+
+#endif // CAR_H
diff --git a/examples/annotated_injection/emergency_brake.cpp b/examples/annotated_injection/emergency_brake.cpp
new file mode 100644
index 0000000..f78a67f
--- /dev/null
+++ b/examples/annotated_injection/emergency_brake.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "emergency_brake.h"
+
+class EmergencyBrakeImpl : public Brake {
+public:
+ INJECT(EmergencyBrakeImpl()) = default;
+
+ void activate() override {
+ // ...
+ }
+};
+
+fruit::Component<fruit::Annotated<EmergencyBrake, Brake>> getEmergencyBrakeComponent() {
+ return fruit::createComponent().bind<fruit::Annotated<EmergencyBrake, Brake>, EmergencyBrakeImpl>();
+}
diff --git a/examples/annotated_injection/emergency_brake.h b/examples/annotated_injection/emergency_brake.h
new file mode 100644
index 0000000..c5650b4
--- /dev/null
+++ b/examples/annotated_injection/emergency_brake.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EMERGENCY_BRAKE_H
+#define EMERGENCY_BRAKE_H
+
+#include "brake.h"
+
+#include <fruit/fruit.h>
+
+// This type is not meaningful by itself, it's only used for annotated injection.
+// This marks a the Brake instance that represents the main brake.
+struct EmergencyBrake {};
+
+fruit::Component<fruit::Annotated<EmergencyBrake, Brake>> getEmergencyBrakeComponent();
+
+#endif // EMERGENCY_BRAKE_H
diff --git a/examples/annotated_injection/main.cpp b/examples/annotated_injection/main.cpp
new file mode 100644
index 0000000..f054717
--- /dev/null
+++ b/examples/annotated_injection/main.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "car.h"
+
+using fruit::Injector;
+
+int main() {
+ Injector<Car> injector(getCarComponent);
+ Car* car(injector);
+
+ car->brake();
+
+ return 0;
+}
diff --git a/examples/annotated_injection/main_brake.cpp b/examples/annotated_injection/main_brake.cpp
new file mode 100644
index 0000000..722368c
--- /dev/null
+++ b/examples/annotated_injection/main_brake.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "main_brake.h"
+
+class MainBrakeImpl : public Brake {
+public:
+ INJECT(MainBrakeImpl()) = default;
+
+ void activate() override {
+ // ...
+ }
+};
+
+fruit::Component<fruit::Annotated<MainBrake, Brake>> getMainBrakeComponent() {
+ return fruit::createComponent().bind<fruit::Annotated<MainBrake, Brake>, MainBrakeImpl>();
+}
diff --git a/examples/annotated_injection/main_brake.h b/examples/annotated_injection/main_brake.h
new file mode 100644
index 0000000..34730e9
--- /dev/null
+++ b/examples/annotated_injection/main_brake.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MAIN_BRAKE_H
+#define MAIN_BRAKE_H
+
+#include "brake.h"
+
+#include <fruit/fruit.h>
+
+// This type is not meaningful by itself, it's only used for annotated injection.
+// This marks a the Brake instance that represents the main brake.
+struct MainBrake {};
+
+fruit::Component<fruit::Annotated<MainBrake, Brake>> getMainBrakeComponent();
+
+#endif // MAIN_BRAKE_H
diff --git a/examples/hello_world/BUILD b/examples/hello_world/BUILD
new file mode 100644
index 0000000..c11a74d
--- /dev/null
+++ b/examples/hello_world/BUILD
@@ -0,0 +1,8 @@
+
+licenses(["notice"])
+
+cc_binary(
+ name = "hello_world",
+ srcs = ["main.cpp"],
+ deps = ["//third_party/fruit"],
+)
diff --git a/examples/hello_world/CMakeLists.txt b/examples/hello_world/CMakeLists.txt
new file mode 100644
index 0000000..5a154ad
--- /dev/null
+++ b/examples/hello_world/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+set(HELLO_WORLD_SOURCES
+main.cpp
+)
+
+add_executable(hello ${HELLO_WORLD_SOURCES})
+target_link_libraries(hello fruit)
diff --git a/examples/hello_world/main.cpp b/examples/hello_world/main.cpp
new file mode 100644
index 0000000..4364993
--- /dev/null
+++ b/examples/hello_world/main.cpp
@@ -0,0 +1,55 @@
+
+#include <fruit/fruit.h>
+#include <iostream>
+
+using fruit::Component;
+using fruit::Injector;
+
+class Writer {
+public:
+ virtual void write(std::string s) = 0;
+};
+
+class StdoutWriter : public Writer {
+public:
+ // Like "StdoutWriter() = default;" but also marks this constructor as the
+ // one to use for injection.
+ INJECT(StdoutWriter()) = default;
+
+ virtual void write(std::string s) override {
+ std::cout << s;
+ }
+};
+
+class Greeter {
+public:
+ virtual void greet() = 0;
+};
+
+class GreeterImpl : public Greeter {
+private:
+ Writer* writer;
+
+public:
+ // Like "GreeterImpl(Writer* writer) {...}" but also marks this constructor
+ // as the one to use for injection.
+ INJECT(GreeterImpl(Writer* writer)) : writer(writer) {}
+
+ virtual void greet() override {
+ writer->write("Hello world!\n");
+ }
+};
+
+Component<Greeter> getGreeterComponent() {
+ return fruit::createComponent().bind<Writer, StdoutWriter>().bind<Greeter, GreeterImpl>();
+}
+
+int main() {
+
+ Injector<Greeter> injector(getGreeterComponent);
+ Greeter* greeter = injector.get<Greeter*>();
+
+ greeter->greet();
+
+ return 0;
+}
diff --git a/examples/multibindings/BUILD b/examples/multibindings/BUILD
new file mode 100644
index 0000000..1750e1d
--- /dev/null
+++ b/examples/multibindings/BUILD
@@ -0,0 +1,8 @@
+
+licenses(["notice"])
+
+cc_binary(
+ name = "multibindings",
+ srcs = ["main.cpp"],
+ deps = ["//third_party/fruit"],
+)
diff --git a/examples/multibindings/CMakeLists.txt b/examples/multibindings/CMakeLists.txt
new file mode 100644
index 0000000..fd0c06d
--- /dev/null
+++ b/examples/multibindings/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+set(MULTIBINDINGS_SOURCES
+main.cpp
+)
+
+add_executable(multibindings ${MULTIBINDINGS_SOURCES})
+target_link_libraries(multibindings fruit)
diff --git a/examples/multibindings/main.cpp b/examples/multibindings/main.cpp
new file mode 100644
index 0000000..bc34240
--- /dev/null
+++ b/examples/multibindings/main.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fruit/fruit.h>
+#include <iostream>
+
+using fruit::Component;
+using fruit::Injector;
+
+class Listener {
+public:
+ virtual void notify() = 0;
+};
+
+class Listener1 : public Listener {
+public:
+ INJECT(Listener1()) = default;
+
+ void notify() override {
+ std::cout << "Listener 1 notified" << std::endl;
+ }
+};
+
+class Writer {
+public:
+ virtual void write(std::string s) = 0;
+};
+
+// To show that we can inject parameters of multibindings
+class StdoutWriter : public Writer {
+public:
+ INJECT(StdoutWriter()) = default;
+
+ void write(std::string s) override {
+ std::cout << s << std::endl;
+ }
+};
+
+class Listener2 : public Listener {
+private:
+ Writer* writer;
+
+public:
+ INJECT(Listener2(Writer* writer)) : writer(writer) {}
+
+ void notify() override {
+ writer->write("Listener 2 notified");
+ }
+};
+
+Component<> getListenersComponent() {
+ // Here they are in the same component to keep it simple, but Fruit collects all multibindings in installed
+ // components.
+ return fruit::createComponent()
+ .bind<Writer, StdoutWriter>()
+ .addMultibinding<Listener, Listener1>()
+ .addMultibinding<Listener, Listener2>();
+}
+
+int main() {
+ Injector<> injector(getListenersComponent);
+ std::vector<Listener*> listeners = injector.getMultibindings<Listener>();
+
+ // The order of the returned listeners is unspecified, so the lines in output may have any order.
+ for (Listener* listener : listeners) {
+ listener->notify();
+ }
+
+ return 0;
+}
diff --git a/examples/scaling_doubles/BUILD b/examples/scaling_doubles/BUILD
new file mode 100644
index 0000000..a9d6e85
--- /dev/null
+++ b/examples/scaling_doubles/BUILD
@@ -0,0 +1,11 @@
+
+licenses(["notice"])
+
+cc_binary(
+ name = "scaling_doubles",
+ srcs = glob([
+ "*.cpp",
+ "*.h",
+ ]),
+ deps = ["//third_party/fruit"],
+)
diff --git a/examples/scaling_doubles/CMakeLists.txt b/examples/scaling_doubles/CMakeLists.txt
new file mode 100644
index 0000000..4a0472e
--- /dev/null
+++ b/examples/scaling_doubles/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+set(SCALING_SOURCES
+main.cpp
+scaler.cpp
+multiplier.cpp
+)
+
+add_executable(scaling ${SCALING_SOURCES})
+target_link_libraries(scaling fruit)
diff --git a/examples/scaling_doubles/main.cpp b/examples/scaling_doubles/main.cpp
new file mode 100644
index 0000000..d4d733a
--- /dev/null
+++ b/examples/scaling_doubles/main.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "scaler.h"
+
+using fruit::Injector;
+
+int main() {
+ Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+
+ return 0;
+}
diff --git a/examples/scaling_doubles/multiplier.cpp b/examples/scaling_doubles/multiplier.cpp
new file mode 100644
index 0000000..be0d349
--- /dev/null
+++ b/examples/scaling_doubles/multiplier.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "multiplier.h"
+
+class MultiplierImpl : public Multiplier {
+public:
+ double multiply(double x, double y) override {
+ return x * y;
+ }
+};
+
+fruit::Component<Multiplier> getMultiplierComponent() {
+ return fruit::createComponent().bind<Multiplier, MultiplierImpl>().registerConstructor<MultiplierImpl()>();
+}
diff --git a/examples/scaling_doubles/multiplier.h b/examples/scaling_doubles/multiplier.h
new file mode 100644
index 0000000..ae22c3d
--- /dev/null
+++ b/examples/scaling_doubles/multiplier.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MULTIPLIER_H
+#define MULTIPLIER_H
+
+#include <fruit/fruit.h>
+
+class Multiplier {
+public:
+ // Returns the product of x and y.
+ virtual double multiply(double x, double y) = 0;
+};
+
+fruit::Component<Multiplier> getMultiplierComponent();
+
+#endif // MULTIPLIER_H
diff --git a/examples/scaling_doubles/scaler.cpp b/examples/scaling_doubles/scaler.cpp
new file mode 100644
index 0000000..32838bd
--- /dev/null
+++ b/examples/scaling_doubles/scaler.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "scaler.h"
+#include "multiplier.h"
+
+using fruit::Component;
+using fruit::Injector;
+using fruit::createComponent;
+
+class ScalerImpl : public Scaler {
+private:
+ Multiplier* multiplier;
+ double factor;
+
+public:
+ INJECT(ScalerImpl(ASSISTED(double) factor, Multiplier* multiplier)) : multiplier(multiplier), factor(factor) {}
+
+ double scale(double x) override {
+ return multiplier->multiply(x, factor);
+ }
+};
+
+Component<ScalerFactory> getScalerComponent() {
+ return createComponent().bind<Scaler, ScalerImpl>().install(getMultiplierComponent);
+}
diff --git a/examples/scaling_doubles/scaler.h b/examples/scaling_doubles/scaler.h
new file mode 100644
index 0000000..9011e81
--- /dev/null
+++ b/examples/scaling_doubles/scaler.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCALER_H
+#define SCALER_H
+
+#include <fruit/fruit.h>
+
+class Scaler {
+public:
+ virtual double scale(double x) = 0;
+
+ virtual ~Scaler() = default;
+};
+
+using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+fruit::Component<ScalerFactory> getScalerComponent();
+
+#endif // SCALER_H
diff --git a/examples/server/BUILD b/examples/server/BUILD
new file mode 100644
index 0000000..1888614
--- /dev/null
+++ b/examples/server/BUILD
@@ -0,0 +1,12 @@
+
+licenses(["notice"])
+
+cc_binary(
+ name = "server",
+ srcs = glob([
+ "*.cpp",
+ "*.h",
+ ]),
+ linkopts = ["-pthread"],
+ deps = ["//third_party/fruit"],
+)
diff --git a/examples/server/CMakeLists.txt b/examples/server/CMakeLists.txt
new file mode 100644
index 0000000..9e61837
--- /dev/null
+++ b/examples/server/CMakeLists.txt
@@ -0,0 +1,13 @@
+
+set(SERVER_SOURCES
+main.cpp
+foo_handler.cpp
+bar_handler.cpp
+request_dispatcher.cpp
+server.cpp
+)
+
+add_definitions("-pthread")
+
+add_executable(server ${SERVER_SOURCES})
+target_link_libraries(server fruit pthread)
diff --git a/examples/server/bar_handler.cpp b/examples/server/bar_handler.cpp
new file mode 100644
index 0000000..c70b9cc
--- /dev/null
+++ b/examples/server/bar_handler.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bar_handler.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace fruit;
+
+class BarHandlerImpl : public BarHandler {
+private:
+ const Request& request;
+ const ServerContext& serverContext;
+
+public:
+ INJECT(BarHandlerImpl(const Request& request, const ServerContext& serverContext))
+ : request(request), serverContext(serverContext) {}
+
+ void handleRequest() override {
+ cout << "BarHandler handling request on server started at " << serverContext.startupTime
+ << " for path: " << request.path << endl;
+ }
+};
+
+Component<Required<Request, ServerContext>, BarHandler> getBarHandlerComponent() {
+ return fruit::createComponent().bind<BarHandler, BarHandlerImpl>();
+}
diff --git a/examples/server/bar_handler.h b/examples/server/bar_handler.h
new file mode 100644
index 0000000..475d8f5
--- /dev/null
+++ b/examples/server/bar_handler.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BAR_HANDLER_H
+#define BAR_HANDLER_H
+
+#include "request.h"
+#include "server_context.h"
+
+#include <fruit/fruit.h>
+
+class BarHandler {
+public:
+ // Handles a request for a subpath of "/bar/".
+ // The request is injected, no need to pass it directly here.
+ virtual void handleRequest() = 0;
+};
+
+fruit::Component<fruit::Required<Request, ServerContext>, BarHandler> getBarHandlerComponent();
+
+#endif // BAR_HANDLER_H
diff --git a/examples/server/foo_handler.cpp b/examples/server/foo_handler.cpp
new file mode 100644
index 0000000..db1261c
--- /dev/null
+++ b/examples/server/foo_handler.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "foo_handler.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace fruit;
+
+class FooHandlerImpl : public FooHandler {
+private:
+ const Request& request;
+ const ServerContext& serverContext;
+
+public:
+ INJECT(FooHandlerImpl(const Request& request, const ServerContext& serverContext))
+ : request(request), serverContext(serverContext) {}
+
+ void handleRequest() override {
+ cout << "FooHandler handling request on server started at " << serverContext.startupTime
+ << " for path: " << request.path << endl;
+ }
+};
+
+Component<Required<Request, ServerContext>, FooHandler> getFooHandlerComponent() {
+ return fruit::createComponent().bind<FooHandler, FooHandlerImpl>();
+}
diff --git a/examples/server/foo_handler.h b/examples/server/foo_handler.h
new file mode 100644
index 0000000..02d21ee
--- /dev/null
+++ b/examples/server/foo_handler.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FOO_HANDLER_H
+#define FOO_HANDLER_H
+
+#include "request.h"
+#include "server_context.h"
+
+#include <fruit/fruit.h>
+
+class FooHandler {
+public:
+ // Handles a request for a subpath of "/foo/".
+ // The request is injected, no need to pass it directly here.
+ virtual void handleRequest() = 0;
+};
+
+fruit::Component<fruit::Required<Request, ServerContext>, FooHandler> getFooHandlerComponent();
+
+#endif // FOO_HANDLER_H
diff --git a/examples/server/main.cpp b/examples/server/main.cpp
new file mode 100644
index 0000000..a1804e6
--- /dev/null
+++ b/examples/server/main.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fruit/fruit.h>
+
+#include "request_dispatcher.h"
+#include "server.h"
+
+using namespace fruit;
+
+int main() {
+ Injector<Server> injector(getServerComponent);
+
+ Server* server(injector);
+ server->run(getRequestDispatcherComponent);
+
+ return 0;
+}
diff --git a/examples/server/request.h b/examples/server/request.h
new file mode 100644
index 0000000..b09cf93
--- /dev/null
+++ b/examples/server/request.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef REQUEST_H
+#define REQUEST_H
+
+#include <string>
+
+struct Request {
+ std::string path;
+};
+
+#endif // REQUEST_H
diff --git a/examples/server/request_dispatcher.cpp b/examples/server/request_dispatcher.cpp
new file mode 100644
index 0000000..f63ca0b
--- /dev/null
+++ b/examples/server/request_dispatcher.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "request_dispatcher.h"
+
+#include "bar_handler.h"
+#include "foo_handler.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace fruit;
+
+class RequestDispatcherImpl : public RequestDispatcher {
+private:
+ const Request& request;
+ // We hold providers here for lazy injection; we only want to inject the handler that is actually used for the
+ // request.
+ // In a large system, there will be many handlers, and many will have lots of dependencies that also have to be
+ // injected.
+ Provider<FooHandler> fooHandler;
+ Provider<BarHandler> barHandler;
+
+public:
+ INJECT(RequestDispatcherImpl(const Request& request, Provider<FooHandler> fooHandler,
+ Provider<BarHandler> barHandler))
+ : request(request), fooHandler(fooHandler), barHandler(barHandler) {}
+
+ void handleRequest() override {
+ if (stringStartsWith(request.path, "/foo/")) {
+ fooHandler.get()->handleRequest();
+ } else if (stringStartsWith(request.path, "/bar/")) {
+ barHandler.get()->handleRequest();
+ } else {
+ cerr << "Error: no handler found for request path: '" << request.path << "' , ignoring request." << endl;
+ }
+ }
+
+private:
+ static bool stringStartsWith(const string& s, const string& candidatePrefix) {
+ return s.compare(0, candidatePrefix.size(), candidatePrefix) == 0;
+ }
+};
+
+Component<Required<Request, ServerContext>, RequestDispatcher> getRequestDispatcherComponent() {
+ return createComponent()
+ .bind<RequestDispatcher, RequestDispatcherImpl>()
+ .install(getFooHandlerComponent)
+ .install(getBarHandlerComponent);
+}
diff --git a/examples/server/request_dispatcher.h b/examples/server/request_dispatcher.h
new file mode 100644
index 0000000..5580ce1
--- /dev/null
+++ b/examples/server/request_dispatcher.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef REQUEST_DISPATCHER_H
+#define REQUEST_DISPATCHER_H
+
+#include "request.h"
+#include "server_context.h"
+
+#include <fruit/fruit.h>
+
+class RequestDispatcher {
+public:
+ // Handles the current request.
+ // The request is injected, no need to pass it directly here.
+ virtual void handleRequest() = 0;
+};
+
+fruit::Component<fruit::Required<Request, ServerContext>, RequestDispatcher> getRequestDispatcherComponent();
+
+#endif // REQUEST_DISPATCHER_H
diff --git a/examples/server/server.cpp b/examples/server/server.cpp
new file mode 100644
index 0000000..60e6853
--- /dev/null
+++ b/examples/server/server.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "server.h"
+
+#include <ctime>
+#include <iostream>
+#include <thread>
+
+using namespace std;
+using namespace fruit;
+
+class ServerImpl : public Server {
+private:
+ std::vector<std::thread> threads;
+
+public:
+ INJECT(ServerImpl()) {}
+
+ ~ServerImpl() {
+ for (std::thread& t : threads) {
+ t.join();
+ }
+ }
+
+ void run(Component<Required<Request, ServerContext>, RequestDispatcher> (*getRequestDispatcherComponent)()) override {
+ ServerContext serverContext;
+ serverContext.startupTime = getTime();
+
+ const NormalizedComponent<Required<Request>, RequestDispatcher> requestDispatcherNormalizedComponent(
+ getRequestDispatcherComponentWithContext, getRequestDispatcherComponent, &serverContext);
+
+ cerr << "Server started." << endl;
+
+ while (1) {
+ cerr << endl;
+ cerr << "Enter the request (absolute path starting with \"/foo/\" or \"/bar/\"), or an empty line to exit."
+ << endl;
+ Request request;
+ getline(cin, request.path);
+ cerr << "Server received request: " + request.path << endl;
+ if (request.path.empty()) {
+ cerr << "Server received empty line, shutting down." << endl;
+ break;
+ }
+
+ // In production code we would use a thread pool.
+ // Here we spawn a new thread each time to keep it simple.
+ threads.push_back(std::thread(worker_thread_main, std::ref(requestDispatcherNormalizedComponent), request));
+ }
+ }
+
+private:
+ static void worker_thread_main(
+ const NormalizedComponent<Required<Request>, RequestDispatcher>& requestDispatcherNormalizedComponent,
+ Request request) {
+ Injector<RequestDispatcher> injector(requestDispatcherNormalizedComponent, getRequestComponent, &request);
+
+ RequestDispatcher* requestDispatcher(injector);
+ requestDispatcher->handleRequest();
+ }
+
+ static string getTime() {
+ time_t now = time(nullptr);
+ tm* localTime = localtime(&now);
+ string result = asctime(localTime);
+ if (result.size() != 0 && result.back() == '\n') {
+ result.pop_back();
+ }
+ return result;
+ }
+
+ static Component<Request> getRequestComponent(Request* request) {
+ return createComponent().bindInstance(*request);
+ }
+
+ static Component<Required<Request>, RequestDispatcher> getRequestDispatcherComponentWithContext(
+ Component<Required<Request, ServerContext>, RequestDispatcher> (*getRequestDispatcherComponent)(),
+ ServerContext* serverContext) {
+ return createComponent().install(getRequestDispatcherComponent).bindInstance(*serverContext);
+ }
+};
+
+fruit::Component<Server> getServerComponent() {
+ return fruit::createComponent().bind<Server, ServerImpl>();
+}
diff --git a/examples/server/server.h b/examples/server/server.h
new file mode 100644
index 0000000..b97c55c
--- /dev/null
+++ b/examples/server/server.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SERVER_H
+#define SERVER_H
+
+#include "request.h"
+#include "request_dispatcher.h"
+#include "server_context.h"
+
+#include <fruit/fruit.h>
+#include <string>
+
+class Server {
+public:
+ virtual void run(fruit::Component<fruit::Required<Request, ServerContext>, RequestDispatcher> (
+ *getrequestDispatcherComponent)()) = 0;
+};
+
+fruit::Component<Server> getServerComponent();
+
+#endif // SERVER_H
diff --git a/examples/server/server_context.h b/examples/server/server_context.h
new file mode 100644
index 0000000..3da38dc
--- /dev/null
+++ b/examples/server/server_context.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SERVER_CONTEXT_H
+#define SERVER_CONTEXT_H
+
+#include <fruit/fruit.h>
+#include <string>
+
+struct ServerContext {
+ // This is to show that the requests can get non-request-specific information.
+ std::string startupTime;
+};
+
+#endif // SERVER_CONTEXT_H
diff --git a/examples/simple_injection/BUILD b/examples/simple_injection/BUILD
new file mode 100644
index 0000000..4c2cf42
--- /dev/null
+++ b/examples/simple_injection/BUILD
@@ -0,0 +1,27 @@
+
+licenses(["notice"])
+
+cc_library(
+ name = "simple_injection_lib",
+ srcs = glob(["*.cpp"], exclude = ["main*.cpp"]),
+ hdrs = glob(["*.h"]),
+ deps = ["//third_party/fruit"],
+)
+
+cc_binary(
+ name = "simple_injection",
+ srcs = ["main.cpp"],
+ deps = [
+ ":simple_injection_lib",
+ "//third_party/fruit",
+ ],
+)
+
+cc_binary(
+ name = "simple_injection_v1",
+ srcs = ["main_v1.cpp"],
+ deps = [
+ ":simple_injection_lib",
+ "//third_party/fruit",
+ ],
+)
diff --git a/examples/simple_injection/CMakeLists.txt b/examples/simple_injection/CMakeLists.txt
new file mode 100644
index 0000000..738dd3b
--- /dev/null
+++ b/examples/simple_injection/CMakeLists.txt
@@ -0,0 +1,14 @@
+
+set(INCREMENTER_SOURCES
+checked_adder.cpp
+checked_incrementer.cpp
+incrementer_impl.cpp
+incrementer_component.cpp
+simple_adder.cpp
+simple_incrementer.cpp
+)
+
+add_executable(incrementer_v1 ${INCREMENTER_SOURCES} main_v1.cpp)
+add_executable(incrementer ${INCREMENTER_SOURCES} main.cpp)
+target_link_libraries(incrementer_v1 fruit)
+target_link_libraries(incrementer fruit)
diff --git a/examples/simple_injection/adder.h b/examples/simple_injection/adder.h
new file mode 100644
index 0000000..111c5da
--- /dev/null
+++ b/examples/simple_injection/adder.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ADDER_H
+#define ADDER_H
+
+class Adder {
+public:
+ // Returns the sum of x and y.
+ virtual int add(int x, int y) = 0;
+};
+
+#endif // ADDER_H
diff --git a/examples/simple_injection/checked_adder.cpp b/examples/simple_injection/checked_adder.cpp
new file mode 100644
index 0000000..7ca6754
--- /dev/null
+++ b/examples/simple_injection/checked_adder.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "checked_adder.h"
+
+#include <climits>
+#include <iostream>
+
+class CheckedAdder : public Adder {
+private:
+ bool add_overflows(int x, int y) {
+ if (y > x)
+ std::swap(x, y);
+ // Now y <= x.
+ const int half_max = INT_MAX / 2;
+ const int half_min = INT_MIN / 2;
+ if (x > half_max) {
+ // We can't have negative overflow, but might have positive overflow.
+ if (y > half_max)
+ return true;
+ if (y <= 0)
+ return false;
+ // x <= INT_MAX && y <= half_max,
+ // so: x + y <= INT_MAX + half_max
+ // so: x - half_max + y <= INT_MAX
+ // so: (x - half_max + y) doesn't overflow.
+ // (x + y) > INT_MAX iff (x - half_max + y) > (INT_MAX - half_max)
+ return (x - half_max + y) > (INT_MAX - half_max);
+ }
+ // y <= x <= half_max, can't have positive overflow.
+ if (y < half_min) {
+ // We can't have positive overflow, but might have negative overflow.
+ if (x < half_min)
+ return true;
+ if (x >= 0)
+ return false;
+ // y >= INT_MIN && x >= half_min,
+ // so: y + x >= INT_MIN + half_min
+ // so: y - half_min + x >= INT_MAX
+ // so: (y - half_min + x) doesn't overflow.
+ // (y + x) < INT_MIN iff (y - half_min + x) < (INT_MIN - half_min)
+ return (y - half_min + x) < (INT_MIN - half_min);
+ }
+ // Neither negative nor positive overflow.
+ return false;
+ }
+
+public:
+ INJECT(CheckedAdder()) = default;
+
+ virtual int add(int x, int y) override {
+ if (add_overflows(x, y)) {
+ std::cerr << "CheckedAdder: detected overflow during addition of " << x << " and " << y << std::endl;
+ abort();
+ }
+ return x + y;
+ }
+};
+
+fruit::Component<Adder> getCheckedAdderComponent() {
+ return fruit::createComponent().bind<Adder, CheckedAdder>();
+}
diff --git a/examples/simple_injection/checked_adder.h b/examples/simple_injection/checked_adder.h
new file mode 100644
index 0000000..e3d9cc3
--- /dev/null
+++ b/examples/simple_injection/checked_adder.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHECKED_ADDER_H
+#define CHECKED_ADDER_H
+
+#include "adder.h"
+#include <fruit/fruit.h>
+
+fruit::Component<Adder> getCheckedAdderComponent();
+
+#endif // CHECKED_ADDER_H
diff --git a/examples/simple_injection/checked_incrementer.cpp b/examples/simple_injection/checked_incrementer.cpp
new file mode 100644
index 0000000..2ba166f
--- /dev/null
+++ b/examples/simple_injection/checked_incrementer.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "checked_incrementer.h"
+
+#include "checked_adder.h"
+#include "incrementer_impl.h"
+
+fruit::Component<Incrementer> getCheckedIncrementerComponent() {
+ return fruit::createComponent().install(getIncrementerImplComponent).install(getCheckedAdderComponent);
+}
diff --git a/examples/simple_injection/checked_incrementer.h b/examples/simple_injection/checked_incrementer.h
new file mode 100644
index 0000000..0394a1f
--- /dev/null
+++ b/examples/simple_injection/checked_incrementer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHECKED_INCREMENTER_H
+#define CHECKED_INCREMENTER_H
+
+#include "incrementer.h"
+#include <fruit/fruit.h>
+
+fruit::Component<Incrementer> getCheckedIncrementerComponent();
+
+#endif // CHECKED_INCREMENTER_H
diff --git a/examples/simple_injection/incrementer.h b/examples/simple_injection/incrementer.h
new file mode 100644
index 0000000..373d28c
--- /dev/null
+++ b/examples/simple_injection/incrementer.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCREMENTER_H
+#define INCREMENTER_H
+
+class Incrementer {
+public:
+ // Returns x + 1.
+ virtual int increment(int x) = 0;
+};
+
+#endif // INCREMENTER_H
diff --git a/examples/simple_injection/incrementer_component.cpp b/examples/simple_injection/incrementer_component.cpp
new file mode 100644
index 0000000..534ee81
--- /dev/null
+++ b/examples/simple_injection/incrementer_component.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "incrementer_component.h"
+
+#include "checked_incrementer.h"
+#include "simple_incrementer.h"
+
+fruit::Component<Incrementer> getIncrementerComponent(bool checked) {
+ if (checked)
+ return getCheckedIncrementerComponent();
+ else
+ return getSimpleIncrementerComponent();
+}
diff --git a/examples/simple_injection/incrementer_component.h b/examples/simple_injection/incrementer_component.h
new file mode 100644
index 0000000..8d25212
--- /dev/null
+++ b/examples/simple_injection/incrementer_component.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCREMENTER_COMPONENT_H
+#define INCREMENTER_COMPONENT_H
+
+#include "incrementer.h"
+
+#include <fruit/fruit.h>
+
+fruit::Component<Incrementer> getIncrementerComponent(bool checked);
+
+#endif // INCREMENTER_COMPONENT_H
diff --git a/examples/simple_injection/incrementer_impl.cpp b/examples/simple_injection/incrementer_impl.cpp
new file mode 100644
index 0000000..fb2bf2b
--- /dev/null
+++ b/examples/simple_injection/incrementer_impl.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "incrementer_impl.h"
+
+class IncrementerImpl : public Incrementer {
+private:
+ Adder* adder;
+
+public:
+ INJECT(IncrementerImpl(Adder* adder)) : adder(adder) {}
+
+ virtual int increment(int x) override {
+ return adder->add(x, 1);
+ }
+};
+
+fruit::Component<fruit::Required<Adder>, Incrementer> getIncrementerImplComponent() {
+ return fruit::createComponent().bind<Incrementer, IncrementerImpl>();
+}
diff --git a/examples/simple_injection/incrementer_impl.h b/examples/simple_injection/incrementer_impl.h
new file mode 100644
index 0000000..1e4df49
--- /dev/null
+++ b/examples/simple_injection/incrementer_impl.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCREMENTER_IMPL_H
+#define INCREMENTER_IMPL_H
+
+#include "adder.h"
+#include "incrementer.h"
+#include <fruit/fruit.h>
+
+fruit::Component<fruit::Required<Adder>, Incrementer> getIncrementerImplComponent();
+
+#endif // INCREMENTER_IMPL_H
diff --git a/examples/simple_injection/main.cpp b/examples/simple_injection/main.cpp
new file mode 100644
index 0000000..f97793e
--- /dev/null
+++ b/examples/simple_injection/main.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "incrementer_component.h"
+#include <iostream>
+
+using fruit::Component;
+using fruit::Injector;
+
+// Try e.g.:
+// echo 5 | ./incrementer
+// echo 2147483647 | ./incrementer
+// echo 2147483647 | ./incrementer --checked
+int main(int argc, const char* argv[]) {
+
+ bool checked = false;
+
+ if (argc == 2 && std::string(argv[1]) == "--checked")
+ checked = true;
+
+ Injector<Incrementer> injector(getIncrementerComponent, checked);
+ Incrementer* incrementer(injector);
+
+ int x;
+ std::cin >> x;
+ std::cout << incrementer->increment(x) << std::endl;
+
+ return 0;
+}
diff --git a/examples/simple_injection/main_v1.cpp b/examples/simple_injection/main_v1.cpp
new file mode 100644
index 0000000..ddcd5a0
--- /dev/null
+++ b/examples/simple_injection/main_v1.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "simple_incrementer.h"
+#include <iostream>
+
+using fruit::Component;
+using fruit::Injector;
+
+// Try e.g.:
+// echo 5 | ./incrementer
+// echo 2147483647 | ./incrementer
+int main() {
+
+ Injector<Incrementer> injector(getSimpleIncrementerComponent);
+ Incrementer* incrementer = injector.get<Incrementer*>();
+
+ int x;
+ std::cin >> x;
+ std::cout << incrementer->increment(x) << std::endl;
+
+ return 0;
+}
diff --git a/examples/simple_injection/simple_adder.cpp b/examples/simple_injection/simple_adder.cpp
new file mode 100644
index 0000000..8277d23
--- /dev/null
+++ b/examples/simple_injection/simple_adder.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "simple_adder.h"
+
+class SimpleAdder : public Adder {
+public:
+ INJECT(SimpleAdder()) = default;
+
+ virtual int add(int x, int y) override {
+ return x + y;
+ }
+};
+
+fruit::Component<Adder> getSimpleAdderComponent() {
+ return fruit::createComponent().bind<Adder, SimpleAdder>();
+}
diff --git a/examples/simple_injection/simple_adder.h b/examples/simple_injection/simple_adder.h
new file mode 100644
index 0000000..9b75a79
--- /dev/null
+++ b/examples/simple_injection/simple_adder.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_ADDER_H
+#define SIMPLE_ADDER_H
+
+#include "adder.h"
+#include <fruit/fruit.h>
+
+fruit::Component<Adder> getSimpleAdderComponent();
+
+#endif // SIMPLE_ADDER_H
diff --git a/examples/simple_injection/simple_incrementer.cpp b/examples/simple_injection/simple_incrementer.cpp
new file mode 100644
index 0000000..2ecf4a2
--- /dev/null
+++ b/examples/simple_injection/simple_incrementer.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "simple_incrementer.h"
+
+#include "incrementer_impl.h"
+#include "simple_adder.h"
+
+fruit::Component<Incrementer> getSimpleIncrementerComponent() {
+ return fruit::createComponent().install(getIncrementerImplComponent).install(getSimpleAdderComponent);
+}
diff --git a/examples/simple_injection/simple_incrementer.h b/examples/simple_injection/simple_incrementer.h
new file mode 100644
index 0000000..ac6e3a7
--- /dev/null
+++ b/examples/simple_injection/simple_incrementer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_INCREMENTER_H
+#define SIMPLE_INCREMENTER_H
+
+#include "incrementer.h"
+#include <fruit/fruit.h>
+
+fruit::Component<Incrementer> getSimpleIncrementerComponent();
+
+#endif // SIMPLE_INCREMENTER_H
diff --git a/extras/CMakeLists.txt b/extras/CMakeLists.txt
new file mode 100644
index 0000000..49af9ab
--- /dev/null
+++ b/extras/CMakeLists.txt
@@ -0,0 +1,4 @@
+
+add_subdirectory(doc EXCLUDE_FROM_ALL)
+add_subdirectory(packaging EXCLUDE_FROM_ALL)
+add_subdirectory(benchmark)
diff --git a/extras/bazel_root/WORKSPACE b/extras/bazel_root/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extras/bazel_root/WORKSPACE
diff --git a/extras/bazel_root/third_party/fruit/BUILD b/extras/bazel_root/third_party/fruit/BUILD
new file mode 120000
index 0000000..388dc92
--- /dev/null
+++ b/extras/bazel_root/third_party/fruit/BUILD
@@ -0,0 +1 @@
+../../../../BUILD \ No newline at end of file
diff --git a/extras/bazel_root/third_party/fruit/build b/extras/bazel_root/third_party/fruit/build
new file mode 120000
index 0000000..388dc92
--- /dev/null
+++ b/extras/bazel_root/third_party/fruit/build
@@ -0,0 +1 @@
+../../../../BUILD \ No newline at end of file
diff --git a/extras/bazel_root/third_party/fruit/configuration b/extras/bazel_root/third_party/fruit/configuration
new file mode 120000
index 0000000..2e678a5
--- /dev/null
+++ b/extras/bazel_root/third_party/fruit/configuration
@@ -0,0 +1 @@
+../../../../configuration \ No newline at end of file
diff --git a/extras/bazel_root/third_party/fruit/examples b/extras/bazel_root/third_party/fruit/examples
new file mode 120000
index 0000000..1929330
--- /dev/null
+++ b/extras/bazel_root/third_party/fruit/examples
@@ -0,0 +1 @@
+../../../../examples \ No newline at end of file
diff --git a/extras/bazel_root/third_party/fruit/include b/extras/bazel_root/third_party/fruit/include
new file mode 120000
index 0000000..0f0436a
--- /dev/null
+++ b/extras/bazel_root/third_party/fruit/include
@@ -0,0 +1 @@
+../../../../include \ No newline at end of file
diff --git a/extras/bazel_root/third_party/fruit/src b/extras/bazel_root/third_party/fruit/src
new file mode 120000
index 0000000..b3e266f
--- /dev/null
+++ b/extras/bazel_root/third_party/fruit/src
@@ -0,0 +1 @@
+../../../../src \ No newline at end of file
diff --git a/extras/bazel_root/third_party/fruit/tests b/extras/bazel_root/third_party/fruit/tests
new file mode 120000
index 0000000..70ef7f3
--- /dev/null
+++ b/extras/bazel_root/third_party/fruit/tests
@@ -0,0 +1 @@
+../../../../tests \ No newline at end of file
diff --git a/extras/benchmark/CMakeLists.txt b/extras/benchmark/CMakeLists.txt
new file mode 100644
index 0000000..fba9f91
--- /dev/null
+++ b/extras/benchmark/CMakeLists.txt
@@ -0,0 +1,8 @@
+
+# This is just to help IDEs (e.g. CLion) figure out how new_delete_benchmark.cpp is supposed to be built.
+add_executable(new_delete_benchmark-dummy-exec EXCLUDE_FROM_ALL new_delete_benchmark.cpp)
+target_link_libraries(new_delete_benchmark-dummy-exec fruit)
+
+# This is just to help IDEs (e.g. CLion) figure out how compile_time_benchmark.cpp is supposed to be built.
+add_executable(compile_time_benchmark_executable EXCLUDE_FROM_ALL compile_time_benchmark.cpp)
+target_link_libraries(compile_time_benchmark_executable fruit)
diff --git a/extras/benchmark/README.md b/extras/benchmark/README.md
new file mode 100644
index 0000000..775ba21
--- /dev/null
+++ b/extras/benchmark/README.md
@@ -0,0 +1,109 @@
+
+## Fruit benchmarks
+
+Fruit contains some code to benchmark various metrics (e.g. performance, compile time, executable size) in an automated
+way.
+
+### Benchmark suites
+
+The `suites` folder contains suites of Fruit (or Fruit-related) benchmarks that can be run using `run_benchmarks.py`.
+For example:
+
+```bash
+$ ~/projects/fruit/extras/benchmark/run_benchmarks.py \
+ --continue-benchmark=true \
+ --benchmark-definition ~/projects/fruit/extras/benchmark/suites/fruit_full.yml
+ --output-file ~/fruit_bench_results.txt \
+ --fruit-sources-dir ~/projects/fruit \
+ --fruit-benchmark-sources-dir ~/projects/fruit \
+ --boost-di-sources-dir ~/projects/boost-di
+```
+
+Once the benchmark run completes, you can format the results using some pre-defined tables, see the section below.
+
+The following benchmark suites are defined:
+
+* `fruit_full.yml`: full set of Fruit benchmarks (using the Fruit 3.x API).
+* `fruit_mostly_full.yml`: a subset of the tests in `fruit_full.yml`.
+* `fruit_quick.yml`: this is an even smaller subset, and the number of runs is capped at 10 so
+ the confidence intervals might be wider. It's useful as a quicker (around 10-15min) way to get a rough idea of the
+ performance (e.g. to evaluate the performance impact of a commit, during development).
+* `fruit_single.yml`: runs the Fruit runtime benchs under a single compiler and with just 1 combination of flags. This
+ also caps the number of runs at 8, so the resulting confidence intervals might be wider than they would be with
+ `fruit_full.yml`. This is a quick benchmark that can used during development of performance optimizations.
+* `fruit_debug.yml`: a suite used to debug Fruit's benchmarking code. This is very quick, but the actual results are
+ not meaningful. Run this after changing any benchmarking code, to check that it still works.
+* `fruit_full_old_style.yml`: a variant of `fruit_full.yml` that uses the Fruit 2.x API.
+* `fruit_quick_old_style.yml`: a variant of `fruit_quick.yml` that uses the Fruit 2.x API.
+* `fruit_single_old_style.yml`: a variant of `fruit_single.yml` that uses the Fruit 2.x API.
+* `boost_di`: unlike the others, this benchmark suite exercises the Boost.DI library (still in boost-experimental at the
+ time of writing) instead of Fruit.
+
+### Benchmark tables
+
+The `tables` folder contains some table definitions that can be used to get a human-readable representations of
+benchmark results generated using `run_benchmarks.py`.
+
+Note that there *isn't* a 1:1 mapping between benchmark suites and benchmark tables; the same table definition can be
+used with multiple benchmark suites (for example, a full suite and a quick variant that only has a subset of the
+dimensions) and multiple table definitions might be appropriate to display the results of a single suite (for example,
+there could be a table definition that displays only metrics meaningful to Fruit users and one that also displays
+more fine-grained metrics that are only meaningful to Fruit developers).
+
+Example usage of `format_bench_results.py` with one of these table definitions:
+
+```bash
+$ ~/projects/fruit/extras/benchmark/format_bench_results.py \
+ --benchmark-results ~/fruit_bench_results.txt \
+ --benchmark-tables-definition ~/projects/fruit/extras/benchmark/tables/fruit_wiki.yml
+```
+
+It's also possible to compare two benchmark results (for example, when running the same benchmarks before and after
+a Fruit commit):
+
+```bash
+$ ~/projects/fruit/extras/benchmark/format_bench_results.py \
+ --benchmark-results ~/fruit_bench_results_after.txt \
+ --benchmark-tables-definition ~/projects/fruit/extras/benchmark/tables/fruit_wiki.yml \
+ --baseline-benchmark-results ~/fruit_bench_results_before.txt
+```
+
+The following tables are defined:
+
+* `fruit_wiki.yml`: the "main" table definition, with the tables that are in Fruit's wiki.
+* `fruit_wiki_old_style.yml`: a variant of `fruit_wiki.yml` that uses the Fruit 2.x APIs, whereas `fruit_wiki.yml` uses
+ the new one. This is useful to visualize benchmarks of old Fruit versions that don't support the 3.x new-style API.
+* `fruit_internal.yml`: a more detailed version of `fruit_wiki.yml`, also displaying metrics that are only meaningful
+ to Fruit developers (e.g. splitting the setup time into component creation time and normalization time).
+* `fruit_internal_old_vs_new_style.yml`: used to compare the performance of the Fruit 3.x and 2.x APIs.
+
+### Manual benchmarks
+
+In some cases, you might want to run the benchmarks manually (e.g. if you want to use `callgrind` to profile the
+benchmark run). This is how you can do that:
+
+```bash
+$ cd ~/projects/fruit
+$ mkdir build
+$ cd build
+$ CXX=g++-6 cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
+$ make -j 10
+$ cd ..
+$ mkdir generated-benchs
+$ extras/benchmark/generate_benchmark.py \
+ --compiler g++-6 \
+ --fruit-sources-dir ~/projects/fruit \
+ --fruit-build-dir ~/projects/fruit/build \
+ --num-components-with-no-deps 10 \
+ --num-components-with-deps 90 \
+ --num-deps 10 \
+ --output-dir generated-benchs \
+ --generate-debuginfo=true
+$ cd generated-benchs
+$ make -j 10
+$ valgrind \
+ --tool=callgrind \
+ --simulate-cache=yes \
+ --dump-instr=yes \
+ ./main 10000
+```
diff --git a/extras/benchmark/analyze_symbol_size.sh b/extras/benchmark/analyze_symbol_size.sh
new file mode 100755
index 0000000..5ca2ea5
--- /dev/null
+++ b/extras/benchmark/analyze_symbol_size.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a quick-and-dirty script to analyze the ELF symbol sizes in a binary generated by generate_benchmark.py.
+# It can be used to decide which functions should no longer be inlined, to save space.
+
+if [ "$#" != "1" ]
+then
+ echo "Usage: $0 <executable_file>"
+ exit 1
+fi
+
+FILE1=$(mktemp)
+nm --print-size --size-sort --radix=d "$1" | sort -k 2 | c++filt \
+| sed 's/[^ ]* //;s/fruit::impl:://g;s/InvokeConstructorWithInjectedArgVector<.*>::operator()/InvokeConstructorWithInjectedArgVector<...>::operator()/' \
+| sed 's/getComponent[0-9]\+/getComponent$N/' \
+| sed 's/X[0-9]\+/X$N/g' \
+| sed 's/Interface[0-9]\+/Interface$N/g' \
+| sed 's/GetBindingDepsHelper<.*>::operator()()/GetBindingDepsHelper<...>::operator()()/' \
+| sed 's/\(std::shared_ptr<Interface$N>, \)\+std::shared_ptr<Interface$N>/.../' \
+>"$FILE1"
+
+FILE2=$(mktemp)
+python3 -c "
+lines = open('$FILE1', 'r').readlines()
+total_size = {}
+for line in lines:
+ splits = line.split(' ', maxsplit=1)
+ total_size[splits[1]] = total_size.get(splits[1], 0) + int(splits[0])
+for key, value in total_size.items():
+ print('%s %s' % (value, key))
+" >"$FILE2"
+
+sort -n "$FILE2"
diff --git a/extras/benchmark/boost_di_source_generator.py b/extras/benchmark/boost_di_source_generator.py
new file mode 100644
index 0000000..0f96015
--- /dev/null
+++ b/extras/benchmark/boost_di_source_generator.py
@@ -0,0 +1,91 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class BoostDiSourceGenerator:
+ def generate_component_header(self, component_index):
+ template = """
+#ifndef COMPONENT{component_index}_H
+#define COMPONENT{component_index}_H
+
+#include <boost/di.hpp>
+#include <memory>
+
+namespace di = boost::di;
+
+struct Interface{component_index} {{
+ virtual ~Interface{component_index}() = default;
+}};
+di::injector<std::shared_ptr<Interface{component_index}>> getComponent{component_index}();
+
+#endif // COMPONENT{component_index}_H
+"""
+ return template.format(**locals())
+
+ def generate_component_source(self, component_index, deps):
+ include_directives = ''.join(['#include "component%s.h"\n' % index for index in deps + [component_index]])
+
+ component_deps = ''.join([', std::shared_ptr<Interface%s>' % dep for dep in deps])
+
+ make_injector_params = ','.join(['\n getComponent%s()' % dep for dep in deps]
+ + ['\n di::bind<Interface%s>().in(di::singleton).to<X%s>()' % (component_index, component_index)])
+
+ template = """
+{include_directives}
+
+struct X{component_index} : public Interface{component_index} {{
+ BOOST_DI_INJECT(X{component_index}{component_deps}) {{}}
+
+ virtual ~X{component_index}() = default;
+}};
+
+di::injector<std::shared_ptr<Interface{component_index}>> getComponent{component_index}() {{
+ return di::make_injector({make_injector_params});
+}}
+"""
+ return template.format(**locals())
+
+ def generate_main(self, toplevel_component):
+ template = """
+#include "component{toplevel_component}.h"
+#include <ctime>
+#include <iostream>
+#include <cstdlib>
+#include <iomanip>
+#include <chrono>
+
+using namespace std;
+
+int main(int argc, char* argv[]) {{
+ if (argc != 2) {{
+ std::cout << "Need to specify num_loops as argument." << std::endl;
+ exit(1);
+ }}
+ size_t num_loops = std::atoi(argv[1]);
+ double perRequestTime = 0;
+ std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < num_loops; i++) {{
+ di::injector<std::shared_ptr<Interface{toplevel_component}>> injector(getComponent{toplevel_component}());
+ injector.create<std::shared_ptr<Interface{toplevel_component}>>();
+ }}
+ perRequestTime += std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+ std::cout << std::fixed;
+ std::cout << std::setprecision(15);
+ std::cout << "Total for setup = " << 0 << std::endl;
+ std::cout << "Full injection time = " << perRequestTime / num_loops << std::endl;
+ std::cout << "Total per request = " << perRequestTime / num_loops << std::endl;
+ return 0;
+}}
+"""
+ return template.format(**locals())
diff --git a/extras/benchmark/compile_time_benchmark.cpp b/extras/benchmark/compile_time_benchmark.cpp
new file mode 100644
index 0000000..ddc6fd3
--- /dev/null
+++ b/extras/benchmark/compile_time_benchmark.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fruit/fruit.h>
+
+#if MULTIPLIER == 1
+#define REPEAT(X) REPEAT_1(X, _)
+
+#elif MULTIPLIER == 2
+#define REPEAT(X) REPEAT_2(X, _)
+
+#elif MULTIPLIER == 4
+#define REPEAT(X) REPEAT_4(X, _)
+
+#elif MULTIPLIER == 8
+#define REPEAT(X) REPEAT_8(X, _)
+
+#elif MULTIPLIER == 16
+#define REPEAT(X) REPEAT_16(X, _)
+
+#elif MULTIPLIER == 32
+#define REPEAT(X) REPEAT_32(X, _)
+
+#elif MULTIPLIER == 64
+#define REPEAT(X) REPEAT_64(X, _)
+
+#elif MULTIPLIER == 128
+#define REPEAT(X) REPEAT_128(X, _)
+
+#elif MULTIPLIER == 256
+#define REPEAT(X) REPEAT_256(X, _)
+
+#elif MULTIPLIER == 512
+#define REPEAT(X) REPEAT_512(X, _)
+
+#elif MULTIPLIER == 1024
+#define REPEAT(X) REPEAT_1024(X, _)
+
+#else
+#error Multiplier not supported.
+#endif
+
+#define PLACEHOLDER
+
+#define EVAL0(...) __VA_ARGS__
+#define EVAL1(...) EVAL0(EVAL0(EVAL0(EVAL0(__VA_ARGS__))))
+#define EVAL2(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__))))
+#define EVAL(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__))))
+
+#define META_REPEAT_2(R, X, I) R PLACEHOLDER(X, I##0) R PLACEHOLDER(X, I##1)
+
+#define REPEAT_1(X, I) X(I)
+
+#define REPEAT_2(X, I) META_REPEAT_2(REPEAT_1, X, I)
+
+#define REPEAT_4(X, I) META_REPEAT_2(REPEAT_2, X, I)
+
+#define REPEAT_8(X, I) META_REPEAT_2(REPEAT_4, X, I)
+
+#define REPEAT_16(X, I) META_REPEAT_2(REPEAT_8, X, I)
+
+#define REPEAT_32(X, I) META_REPEAT_2(REPEAT_16, X, I)
+
+#define REPEAT_64(X, I) META_REPEAT_2(REPEAT_32, X, I)
+
+#define REPEAT_128(X, I) META_REPEAT_2(REPEAT_64, X, I)
+
+#define REPEAT_256(X, I) META_REPEAT_2(REPEAT_128, X, I)
+
+#define REPEAT_512(X, I) META_REPEAT_2(REPEAT_256, X, I)
+
+#define REPEAT_1024(X, I) META_REPEAT_2(REPEAT_512, X, I)
+
+using namespace fruit;
+
+#define DEFINITIONS(N) \
+ struct A##N { \
+ INJECT(A##N()) = default; \
+ }; \
+ \
+ struct B##N {}; \
+ \
+ struct C##N {}; \
+ \
+ struct I##N { \
+ virtual void f() = 0; \
+ }; \
+ \
+ struct X##N : public I##N { \
+ INJECT(X##N(A##N, B##N*, const C##N&)){}; \
+ \
+ virtual void f(); \
+ }; \
+ \
+ struct Y##N {}; \
+ \
+ struct Z##N {}; \
+ \
+ Component<Required<Y##N>, Z##N> getZ##N##Component();
+
+#define REQUIREMENTS(N) C##N,
+
+#define PARAMETERS(N) B##N &b##N,
+
+#ifdef USE_FRUIT_2_X_SYNTAX
+#define BINDINGS(N) \
+ .bind<I##N, X##N>().bindInstance(b##N).install(getZ##N##Component()).registerProvider([]() { return Y##N(); })
+#else
+
+#define BINDINGS(N) \
+ .bind<I##N, X##N>().bindInstance(b##N).install(getZ##N##Component).registerProvider([]() { return Y##N(); })
+#endif
+
+EVAL(REPEAT(DEFINITIONS))
+
+Component<Required<EVAL(REPEAT(REQUIREMENTS)) int>> getComponent(EVAL(REPEAT(PARAMETERS)) int) {
+ return createComponent() EVAL(REPEAT(BINDINGS));
+}
diff --git a/extras/benchmark/format_bench_results.py b/extras/benchmark/format_bench_results.py
new file mode 100755
index 0000000..f95a483
--- /dev/null
+++ b/extras/benchmark/format_bench_results.py
@@ -0,0 +1,353 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import json
+import yaml
+from collections import defaultdict
+
+def extract_results(bench_results, fixed_benchmark_params, column_dimension, row_dimension, result_dimension):
+ table_data = defaultdict(lambda: dict())
+ remaining_dimensions_by_row_column = dict()
+ for bench_result in bench_results:
+ try:
+ params = {dimension_name: make_immutable(dimension_value)
+ for dimension_name, dimension_value in bench_result['benchmark'].items()}
+ results = bench_result['results']
+ for param_name, param_value in fixed_benchmark_params.items():
+ if params.get(param_name) != param_value:
+ # fixed_benchmark_params not satisfied by this result, skip
+ break
+ if result_dimension not in results:
+ # result_dimension not found in this result, skip
+ break
+ params.pop(param_name)
+ else:
+ # fixed_benchmark_params were satisfied by these params (and were removed)
+ assert row_dimension in params.keys(), '%s not in %s' % (row_dimension, params.keys())
+ assert column_dimension in params.keys(), '%s not in %s' % (column_dimension, params.keys())
+ assert result_dimension in results, '%s not in %s' % (result_dimension, results)
+ row_value = params[row_dimension]
+ column_value = params[column_dimension]
+ remaining_dimensions = params.copy()
+ remaining_dimensions.pop(row_dimension)
+ remaining_dimensions.pop(column_dimension)
+ if column_value in table_data[row_value]:
+ previous_remaining_dimensions = remaining_dimensions_by_row_column[(row_value, column_value)]
+ raise Exception(
+ 'Found multiple benchmark results with the same fixed benchmark params, benchmark param for row and benchmark param for column, so a result can\'t be uniquely determined. '
+ + 'Consider adding additional values in fixed_benchmark_params. Remaining dimensions: %s vs %s' % (
+ remaining_dimensions, previous_remaining_dimensions))
+ table_data[row_value][column_value] = results[result_dimension]
+ remaining_dimensions_by_row_column[(row_value, column_value)] = remaining_dimensions
+ except Exception as e:
+ raise Exception('While processing %s' % bench_result) from e
+ return table_data
+
+
+def identity(x):
+ return x
+
+
+# Takes a 2-dimensional array (list of lists) and prints a markdown table with that content.
+def print_markdown_table(table_data):
+ max_content_length_by_column = [max([len(str(row[column_index])) for row in table_data])
+ for column_index in range(len(table_data[0]))]
+ for row_index in range(len(table_data)):
+ row = table_data[row_index]
+ cell_strings = []
+ for column_index in range(len(row)):
+ value = str(row[column_index])
+ # E.g. if max_content_length_by_column=20, table_cell_format='%20s'
+ table_cell_format = '%%%ss' % max_content_length_by_column[column_index]
+ cell_strings += [table_cell_format % value]
+ print('| ' + ' | '.join(cell_strings) + ' |')
+ if row_index == 0:
+ # Print the separator line, e.g. |---|-----|---|
+ print('|-'
+ + '-|-'.join(['-' * max_content_length_by_column[column_index]
+ for column_index in range(len(row))])
+ + '-|')
+
+def compute_min_max(table_data, row_headers, column_headers):
+ values_by_row = {row_header: [table_data[row_header][column_header]
+ for column_header in column_headers
+ if column_header in table_data[row_header]]
+ for row_header in row_headers}
+ # We compute min and max and pass it to the value pretty-printer, so that it can determine a unit that works well for all values in the table.
+ min_in_table = min([min([min(interval[0][0], interval[1][0]) for interval in values_by_row[row_header]])
+ for row_header in row_headers])
+ max_in_table = max([max([max(interval[0][1], interval[1][1]) for interval in values_by_row[row_header]])
+ for row_header in row_headers])
+ return (min_in_table, max_in_table)
+
+
+def pretty_print_percentage_difference(baseline_value, current_value):
+ baseline_min = baseline_value[0]
+ baseline_max = baseline_value[1]
+ current_min = current_value[0]
+ current_max = current_value[1]
+ percentage_min = (current_min / baseline_max - 1) * 100
+ percentage_max = (current_max / baseline_min - 1) * 100
+ percentage_min_s = "%+.1f%%" % percentage_min
+ percentage_max_s = "%+.1f%%" % percentage_max
+ if percentage_min_s == percentage_max_s:
+ return percentage_min_s
+ else:
+ return "%s - %s" % (percentage_min_s, percentage_max_s)
+
+
+# Takes a table as a dict of dicts (where each table_data[row_key][column_key] is a confidence interval) and prints it as a markdown table using
+# the specified pretty print functions for column keys, row keys and values respectively.
+# column_header_pretty_printer and row_header_pretty_printer must be functions taking a single value and returning the pretty-printed version.
+# value_pretty_printer must be a function taking (value_confidence_interval, min_in_table, max_in_table).
+# baseline_table_data is an optional table (similar to table_data) that contains the "before" state. If present, the values in two tables will be compared.
+def print_confidence_intervals_table(table_name,
+ table_data,
+ baseline_table_data,
+ column_header_pretty_printer=identity,
+ row_header_pretty_printer=identity,
+ value_pretty_printer=identity):
+ if table_data == {}:
+ print('%s: (no data)' % table_name)
+ return
+
+ row_headers = sorted(list(table_data.keys()))
+ # We need to compute the union of the headers of all rows; some rows might be missing values for certain columns.
+ column_headers = sorted(set().union(*[list(row_values.keys()) for row_values in table_data.values()]))
+ if baseline_table_data:
+ baseline_row_headers = sorted(list(baseline_table_data.keys()))
+ baseline_column_headers = sorted(set().union(*[list(row_values.keys()) for row_values in baseline_table_data.values()]))
+ unmached_baseline_column_headers = set(baseline_row_headers) - set(row_headers)
+ if unmached_baseline_column_headers:
+ print('Found baseline column headers with no match in new results (they will be ignored): ', unmached_baseline_column_headers)
+ unmached_baseline_row_headers = set(baseline_row_headers) - set(row_headers)
+ if unmached_baseline_row_headers:
+ print('Found baseline row headers with no match in new results (they will be ignored): ', unmached_baseline_row_headers)
+
+ min_in_table, max_in_table = compute_min_max(table_data, row_headers, column_headers)
+ if baseline_table_data:
+ min_in_baseline_table, max_in_baseline_table = compute_min_max(table_data, row_headers, column_headers)
+ min_in_table = min(min_in_table, min_in_baseline_table)
+ max_in_table = max(max_in_table, max_in_baseline_table)
+
+ table_content = []
+ table_content.append([table_name] + [column_header_pretty_printer(column_header) for column_header in column_headers])
+ for row_header in row_headers:
+ row_content = [row_header_pretty_printer(row_header)]
+ for column_header in column_headers:
+ if column_header in table_data[row_header]:
+ value = table_data[row_header][column_header]
+ raw_confidence_interval, rounded_confidence_interval = value
+ pretty_printed_value = value_pretty_printer(rounded_confidence_interval, min_in_table, max_in_table)
+ if baseline_table_data and row_header in baseline_table_data and column_header in baseline_table_data[row_header]:
+ baseline_value = baseline_table_data[row_header][column_header]
+ raw_baseline_confidence_interval, rounded_baseline_confidence_interval = baseline_value
+ pretty_printed_baseline_value = value_pretty_printer(rounded_baseline_confidence_interval, min_in_table, max_in_table)
+ pretty_printed_percentage_difference = pretty_print_percentage_difference(raw_baseline_confidence_interval, raw_confidence_interval)
+ row_content.append("%s -> %s (%s)" % (pretty_printed_baseline_value, pretty_printed_value, pretty_printed_percentage_difference))
+ else:
+ row_content.append(pretty_printed_value)
+ else:
+ row_content.append("N/A")
+ table_content.append(row_content)
+ print_markdown_table(table_content)
+
+
+def format_string_pretty_printer(format_string):
+ def pretty_print(s):
+ return format_string % s
+
+ return pretty_print
+
+
+def interval_pretty_printer(interval, unit, multiplier):
+ interval = interval.copy()
+ interval[0] *= multiplier
+ interval[1] *= multiplier
+
+ # This prevents the format strings below from printing '.0' for numbers that already have 2 digits:
+ # 23.0 -> 23
+ # 2.0 -> 2.0 (here we don't remove the '.0' because printing just '2' might suggest a lower precision)
+ if int(interval[0]) == interval[0] and interval[0] >= 10:
+ interval[0] = int(interval[0])
+ else:
+ interval[0] = '%.3g' % interval[0]
+ if int(interval[1]) == interval[1] and interval[1] >= 10:
+ interval[1] = int(interval[1])
+ else:
+ interval[1] = '%.3g' % interval[1]
+
+ if interval[0] == interval[1]:
+ return '%s %s' % (interval[0], unit)
+ else:
+ return '%s-%s %s' % (interval[0], interval[1], unit)
+
+
+# Finds the best unit to represent values in the range [min_value, max_value].
+# The units must be specified as an ordered list [multiplier1, ..., multiplierN]
+def find_best_unit(units, min_value, max_value):
+ assert min_value <= max_value
+ if max_value <= units[0]:
+ return units[0]
+ for i in range(len(units) - 1):
+ if min_value > units[i] and max_value < units[i + 1]:
+ return units[i]
+ if min_value > units[-1]:
+ return units[-1]
+ # There is no unit that works very well for all values, first let's try relaxing the min constraint
+ for i in range(len(units) - 1):
+ if min_value > units[i] * 0.2 and max_value < units[i + 1]:
+ return units[i]
+ if min_value > units[-1] * 0.2:
+ return units[-1]
+ # That didn't work either, just use a unit that works well for the min values then
+ for i in reversed(range(len(units))):
+ if min_value > units[i]:
+ return units[i]
+ assert min_value <= min(units)
+ # Pick the smallest unit
+ return units[0]
+
+
+def time_interval_pretty_printer(time_interval, min_in_table, max_in_table):
+ sec = 1
+ milli = 0.001
+ micro = milli * milli
+ units = [micro, milli, sec]
+ unit_name_by_unit = {micro: 'μs', milli: 'ms', sec: 's'}
+
+ unit = find_best_unit(units, min_in_table, max_in_table)
+ unit_name = unit_name_by_unit[unit]
+
+ return interval_pretty_printer(time_interval, unit=unit_name, multiplier=1 / unit)
+
+
+def file_size_interval_pretty_printer(file_size_interval, min_in_table, max_in_table):
+ byte = 1
+ kb = 1024
+ mb = kb * kb
+ units = [byte, kb, mb]
+ unit_name_by_unit = {byte: 'bytes', kb: 'KB', mb: 'MB'}
+
+ unit = find_best_unit(units, min_in_table, max_in_table)
+ unit_name = unit_name_by_unit[unit]
+
+ return interval_pretty_printer(file_size_interval, unit=unit_name, multiplier=1 / unit)
+
+
+def make_immutable(x):
+ if isinstance(x, list):
+ return tuple(make_immutable(elem) for elem in x)
+ return x
+
+
+def dict_pretty_printer(dict_data):
+ if isinstance(dict_data, list):
+ dict_data = {make_immutable(mapping['from']): mapping['to'] for mapping in dict_data}
+ def pretty_print(s):
+ if s in dict_data:
+ return dict_data[s]
+ else:
+ raise Exception('dict_pretty_printer(%s) can\'t handle the value %s' % (dict_data, s))
+
+ return pretty_print
+
+
+def determine_column_pretty_printer(pretty_printer_definition):
+ if 'format_string' in pretty_printer_definition:
+ return format_string_pretty_printer(pretty_printer_definition['format_string'])
+
+ if 'fixed_map' in pretty_printer_definition:
+ return dict_pretty_printer(pretty_printer_definition['fixed_map'])
+
+ raise Exception("Unrecognized pretty printer description: %s" % pretty_printer_definition)
+
+
+def determine_row_pretty_printer(pretty_printer_definition):
+ return determine_column_pretty_printer(pretty_printer_definition)
+
+
+def determine_value_pretty_printer(unit):
+ if unit == "seconds":
+ return time_interval_pretty_printer
+ if unit == "bytes":
+ return file_size_interval_pretty_printer
+ raise Exception("Unrecognized unit: %s" % unit)
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Runs all the benchmarks whose results are on the Fruit website.')
+ parser.add_argument('--benchmark-results',
+ help='The input file where benchmark results will be read from (1 per line, with each line in JSON format). You can use the run_benchmarks.py to run a benchmark and generate results in this format.')
+ parser.add_argument('--baseline-benchmark-results',
+ help='Optional. If specified, compares this file (considered the "before" state) with the one specified in --benchmark-results.')
+ parser.add_argument('--benchmark-tables-definition', help='The YAML file that defines the benchmark tables (e.g. fruit_wiki_bench_tables.yaml).')
+ args = parser.parse_args()
+
+ if args.benchmark_results is None:
+ raise Exception("You must specify a benchmark results file using --benchmark-results.")
+
+ if args.benchmark_tables_definition is None:
+ raise Exception("You must specify a benchmark tables definition file using --benchmark-tables-definition.")
+
+ with open(args.benchmark_results, 'r') as f:
+ bench_results = [json.loads(line) for line in f.readlines()]
+
+ if args.baseline_benchmark_results:
+ with open(args.baseline_benchmark_results, 'r') as f:
+ baseline_bench_results = [json.loads(line) for line in f.readlines()]
+ else:
+ baseline_bench_results = None
+
+
+ with open(args.benchmark_tables_definition, 'r') as f:
+ for table_definition in yaml.load(f)["tables"]:
+ try:
+ fixed_benchmark_params = {dimension_name: make_immutable(dimension_value) for dimension_name, dimension_value in table_definition['benchmark_filter'].items()}
+ table_data = extract_results(
+ bench_results,
+ fixed_benchmark_params=fixed_benchmark_params,
+ column_dimension=table_definition['columns']['dimension'],
+ row_dimension=table_definition['rows']['dimension'],
+ result_dimension=table_definition['results']['dimension'])
+ if baseline_bench_results:
+ baseline_table_data = extract_results(
+ baseline_bench_results,
+ fixed_benchmark_params=fixed_benchmark_params,
+ column_dimension=table_definition['columns']['dimension'],
+ row_dimension=table_definition['rows']['dimension'],
+ result_dimension=table_definition['results']['dimension'])
+ else:
+ baseline_table_data = None
+ rows_pretty_printer_definition = table_definition['rows']['pretty_printer']
+ columns_pretty_printer_definition = table_definition['columns']['pretty_printer']
+ results_unit = table_definition['results']['unit']
+ print_confidence_intervals_table(table_definition['name'],
+ table_data,
+ baseline_table_data,
+ column_header_pretty_printer=determine_column_pretty_printer(columns_pretty_printer_definition),
+ row_header_pretty_printer=determine_row_pretty_printer(rows_pretty_printer_definition),
+ value_pretty_printer=determine_value_pretty_printer(results_unit))
+ print()
+ print()
+ except Exception as e:
+ print('While processing table:\n' + table_definition)
+ print()
+ raise e
+
+
+if __name__ == "__main__":
+ main()
diff --git a/extras/benchmark/fruit_source_generator.py b/extras/benchmark/fruit_source_generator.py
new file mode 100644
index 0000000..8b2a9a6
--- /dev/null
+++ b/extras/benchmark/fruit_source_generator.py
@@ -0,0 +1,206 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class FruitSourceGenerator:
+ def __init__(self, use_fruit_2_x_syntax=False):
+ self.use_fruit_2_x_syntax = use_fruit_2_x_syntax
+
+ def _get_component_type(self, component_index):
+ if self.use_fruit_2_x_syntax:
+ return 'const fruit::Component<Interface{component_index}>&'.format(**locals())
+ else:
+ return 'fruit::Component<Interface{component_index}>'.format(**locals())
+
+ def generate_component_header(self, component_index):
+ component_type = self._get_component_type(component_index)
+ template = """
+#ifndef COMPONENT{component_index}_H
+#define COMPONENT{component_index}_H
+
+#include <fruit/fruit.h>
+
+struct Interface{component_index} {{
+ virtual ~Interface{component_index}() = default;
+}};
+
+{component_type} getComponent{component_index}();
+
+#endif // COMPONENT{component_index}_H
+"""
+ return template.format(**locals())
+
+ def generate_component_source(self, component_index, deps):
+ include_directives = ''.join(['#include "component%s.h"\n' % index for index in deps + [component_index]])
+
+ component_deps = ', '.join(['std::shared_ptr<Interface%s>' % dep for dep in deps])
+
+ if self.use_fruit_2_x_syntax:
+ install_expressions = ''.join([' .install(getComponent%s())\n' % dep for dep in deps])
+ else:
+ install_expressions = ''.join([' .install(getComponent%s)\n' % dep for dep in deps])
+
+ component_type = self._get_component_type(component_index)
+
+ template = """
+{include_directives}
+
+struct X{component_index} : public Interface{component_index} {{
+INJECT(X{component_index}({component_deps})) {{}}
+
+virtual ~X{component_index}() = default;
+}};
+
+"""
+
+ if self.use_fruit_2_x_syntax:
+ template += """
+{component_type} getComponent{component_index}() {{
+ static {component_type} comp = fruit::createComponent(){install_expressions}
+ .bind<Interface{component_index}, X{component_index}>();
+ return comp;
+}}
+"""
+ else:
+ template += """
+{component_type} getComponent{component_index}() {{
+ return fruit::createComponent(){install_expressions}
+ .bind<Interface{component_index}, X{component_index}>();
+}}
+"""
+
+ return template.format(**locals())
+
+ def generate_main(self, toplevel_component):
+ if self.use_fruit_2_x_syntax:
+ return self.generate_main_with_fruit_2_x_syntax(toplevel_component)
+ else:
+ return self.generate_main_with_fruit_3_x_syntax(toplevel_component)
+
+ def generate_main_with_fruit_2_x_syntax(self, toplevel_component):
+ template = """
+#include "component{toplevel_component}.h"
+
+#include <ctime>
+#include <iostream>
+#include <cstdlib>
+#include <iomanip>
+#include <chrono>
+
+using namespace std;
+
+int main(int argc, char* argv[]) {{
+ if (argc != 2) {{
+ std::cout << "Need to specify num_loops as argument." << std::endl;
+ exit(1);
+ }}
+ size_t num_loops = std::atoi(argv[1]);
+ double componentCreationTime = 0;
+ double componentNormalizationTime = 0;
+ std::chrono::high_resolution_clock::time_point start_time;
+
+ for (size_t i = 0; i < 1 + num_loops/100; i++) {{
+ start_time = std::chrono::high_resolution_clock::now();
+ fruit::Component<Interface{toplevel_component}> component(getComponent{toplevel_component}());
+ componentCreationTime += std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+ start_time = std::chrono::high_resolution_clock::now();
+ fruit::NormalizedComponent<Interface{toplevel_component}> normalizedComponent(std::move(component));
+ componentNormalizationTime += std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+ }}
+
+ start_time = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < 1 + num_loops/100; i++) {{
+ fruit::Injector<Interface{toplevel_component}> injector(getComponent{toplevel_component}());
+ injector.get<std::shared_ptr<Interface{toplevel_component}>>();
+ }}
+ double fullInjectionTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+
+ // The cast to Component<Interface{toplevel_component}> is needed for Fruit<2.1.0, where the constructor of
+ // NormalizedComponent only accepted a Component&&.
+ fruit::NormalizedComponent<Interface{toplevel_component}> normalizedComponent{{fruit::Component<Interface{toplevel_component}>{{getComponent{toplevel_component}()}}}};
+
+ start_time = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < num_loops; i++) {{
+ fruit::Injector<Interface{toplevel_component}> injector(normalizedComponent, fruit::Component<>(fruit::createComponent()));
+ injector.get<std::shared_ptr<Interface{toplevel_component}>>();
+ }}
+ double perRequestTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+
+ std::cout << std::fixed;
+ std::cout << std::setprecision(15);
+ std::cout << "componentNormalizationTime = " << componentNormalizationTime * 100 / num_loops << std::endl;
+ std::cout << "Total for setup = " << (componentCreationTime + componentNormalizationTime) * 100 / num_loops << std::endl;
+ std::cout << "Full injection time = " << fullInjectionTime * 100 / num_loops << std::endl;
+ std::cout << "Total per request = " << perRequestTime / num_loops << std::endl;
+ return 0;
+}}
+ """
+ return template.format(**locals())
+
+ def generate_main_with_fruit_3_x_syntax(self, toplevel_component):
+ template = """
+#include "component{toplevel_component}.h"
+
+#include <ctime>
+#include <iostream>
+#include <cstdlib>
+#include <iomanip>
+#include <chrono>
+
+using namespace std;
+
+fruit::Component<> getEmptyComponent() {{
+ return fruit::createComponent();
+}}
+
+int main(int argc, char* argv[]) {{
+ if (argc != 2) {{
+ std::cout << "Need to specify num_loops as argument." << std::endl;
+ exit(1);
+ }}
+ size_t num_loops = std::atoi(argv[1]);
+
+ std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < 1 + num_loops/100; i++) {{
+ fruit::NormalizedComponent<Interface{toplevel_component}> normalizedComponent(getComponent{toplevel_component});
+ (void)normalizedComponent;
+ }}
+ double componentNormalizationTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+
+ start_time = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < 1 + num_loops/100; i++) {{
+ fruit::Injector<Interface{toplevel_component}> injector(getComponent{toplevel_component});
+ injector.get<std::shared_ptr<Interface{toplevel_component}>>();
+ }}
+ double fullInjectionTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+
+ fruit::NormalizedComponent<Interface{toplevel_component}> normalizedComponent(getComponent{toplevel_component});
+
+ start_time = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < num_loops; i++) {{
+ fruit::Injector<Interface{toplevel_component}> injector(normalizedComponent, getEmptyComponent);
+ injector.get<std::shared_ptr<Interface{toplevel_component}>>();
+ }}
+ double perRequestTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
+
+ std::cout << std::fixed;
+ std::cout << std::setprecision(15);
+ std::cout << "componentNormalizationTime = " << componentNormalizationTime * 100 / num_loops << std::endl;
+ std::cout << "Total for setup = " << componentNormalizationTime * 100 / num_loops << std::endl;
+ std::cout << "Full injection time = " << fullInjectionTime * 100 / num_loops << std::endl;
+ std::cout << "Total per request = " << perRequestTime / num_loops << std::endl;
+ return 0;
+}}
+ """
+ return template.format(**locals())
diff --git a/extras/benchmark/generate_benchmark.py b/extras/benchmark/generate_benchmark.py
new file mode 100755
index 0000000..7a3d361
--- /dev/null
+++ b/extras/benchmark/generate_benchmark.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import random
+import os
+
+from fruit_source_generator import FruitSourceGenerator
+from boost_di_source_generator import BoostDiSourceGenerator
+from makefile_generator import generate_makefile
+import argparse
+
+
+def add_node(n, deps, source_generator, output_dir):
+ with open('%s/component%s.h' % (output_dir, n), 'w') as headerFile:
+ headerFile.write(source_generator.generate_component_header(n))
+ with open('%s/component%s.cpp' % (output_dir, n), 'w') as sourceFile:
+ sourceFile.write(source_generator.generate_component_source(n, deps))
+
+
+def generate_benchmark(
+ di_library,
+ compiler,
+ cxx_std,
+ fruit_build_dir,
+ fruit_sources_dir,
+ output_dir,
+ num_components_with_no_deps,
+ num_components_with_deps,
+ num_deps,
+ boost_di_sources_dir=None,
+ use_fruit_2_x_syntax=False,
+ generate_debuginfo=False):
+ """Generates a sample codebase using the specified DI library, meant for benchmarking.
+
+ :param boost_di_sources_dir: this is only used if di_library=='boost_di', it can be None otherwise.
+ """
+
+ if num_components_with_no_deps < num_deps:
+ raise Exception(
+ "Too few components with no deps. num_components_with_no_deps=%s but num_deps=%s." % (num_components_with_no_deps, num_deps))
+ if num_deps < 2:
+ raise Exception("num_deps should be at least 2.")
+
+ # This is a constant so that we always generate the same file (=> benchmark more repeatable).
+ random.seed(42)
+
+ if di_library == 'fruit':
+ source_generator = FruitSourceGenerator(
+ use_fruit_2_x_syntax = use_fruit_2_x_syntax)
+ include_dirs = [fruit_build_dir + '/include', fruit_sources_dir + '/include']
+ library_dirs = [fruit_build_dir + '/src']
+ link_libraries = ['fruit']
+ elif di_library == 'boost_di':
+ source_generator = BoostDiSourceGenerator()
+ include_dirs = [boost_di_sources_dir + '/include']
+ library_dirs = []
+ link_libraries = []
+ else:
+ raise Exception('Unrecognized di_library: %s' % di_library)
+
+ os.makedirs(output_dir, exist_ok=True)
+
+ num_used_ids = 0
+ is_toplevel = [True for i in range(0, num_components_with_no_deps + num_components_with_deps)]
+ toplevel_components = set()
+ for i in range(0, num_components_with_no_deps):
+ id = num_used_ids
+ num_used_ids += 1
+ add_node(id, [], source_generator=source_generator, output_dir=output_dir)
+ toplevel_components |= {id}
+
+ # Then the rest have num_deps deps, chosen (pseudo-)randomly from the previous components with no
+ # deps, plus the previous component with deps (if any).
+ # However, the last few components depend on multiple components with >1 deps, so that the last
+ # component transitively depends on everything.
+ for i in range(0, num_components_with_deps):
+ deps = set()
+
+ if len(toplevel_components) > (num_components_with_deps - 1 - i) * (num_deps - 1):
+ # We need at least 1 dep with deps, otherwise the last few components will not be enough
+ # to tie together all components.
+ num_deps_with_deps = len(toplevel_components) - (num_components_with_deps - 1 - i) * (num_deps - 1)
+ deps |= set(random.sample(toplevel_components, num_deps_with_deps))
+
+ if i != 0 and len(deps) < num_deps:
+ # Pick one random component with deps.
+ # If we picked num_deps random components here, the computation of the n-th component (during
+ # the benchmark) would take time super-linear in n, and we don't want that (if most time was
+ # spent constructing the component rather than constructing the injector and injecting objects,
+ # the benchmark would be slow and not very meaningful).
+ deps |= {num_components_with_no_deps + random.randint(0, i - 1)}
+
+ # Add other deps with no deps to get to the desired num_deps.
+ deps |= set(random.sample(range(0, num_components_with_no_deps), num_deps - len(deps)))
+
+ toplevel_components -= deps
+ for dep in deps:
+ is_toplevel[dep] = False
+
+ component_id = num_used_ids
+ toplevel_components |= {component_id}
+ num_used_ids += 1
+ deps_list = list(deps)
+ random.shuffle(deps_list)
+ add_node(component_id, deps_list, source_generator, output_dir=output_dir)
+
+ assert len(toplevel_components) == 1, toplevel_components
+ toplevel_component = num_used_ids - 1
+ assert is_toplevel[toplevel_component]
+
+ with open("%s/main.cpp" % output_dir, 'w') as mainFile:
+ mainFile.write(source_generator.generate_main(toplevel_component))
+
+ include_flags = ' '.join(['-I%s' % include_dir for include_dir in include_dirs])
+ library_dirs_flags = ' '.join(['-L%s' % library_dir for library_dir in library_dirs])
+ rpath_flags = ' '.join(['-Wl,-rpath,%s' % library_dir for library_dir in library_dirs])
+ link_libraries_flags = ' '.join(['-l%s' % library for library in link_libraries])
+ other_compile_flags = []
+ if use_fruit_2_x_syntax:
+ other_compile_flags.append('-Wno-deprecated-declarations')
+ if generate_debuginfo:
+ other_compile_flags.append('-g')
+ compile_command = '%s -std=%s -O2 -W -Wall -Werror -DNDEBUG -ftemplate-depth=1000 %s %s' % (compiler, cxx_std, include_flags, ' '.join(other_compile_flags))
+ link_command = '%s -std=%s -O2 -W -Wall -Werror %s %s' % (compiler, cxx_std, rpath_flags, library_dirs_flags)
+ # GCC requires passing the -lfruit flag *after* all object files to be linked for some reason.
+ link_command_suffix = link_libraries_flags
+
+ sources = ['component%s' % i for i in range(0, num_used_ids)]
+ sources += ['main']
+
+ with open("%s/Makefile" % output_dir, 'w') as makefile:
+ makefile.write(generate_makefile(sources, 'main', compile_command, link_command, link_command_suffix))
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Generates source files and a build script for benchmarks.')
+ parser.add_argument('--di-library', default='fruit', help='DI library to use. One of {fruit, boost_di}. (default: fruit)')
+ parser.add_argument('--compiler', help='Compiler to use')
+ parser.add_argument('--fruit-sources-dir', help='Path to the fruit sources (only used when di_library==\'fruit\')')
+ parser.add_argument('--fruit-build-dir', help='Path to the fruit build dir (only used with --di_library=\'fruit\')')
+ parser.add_argument('--boost-di-sources-dir', help='Path to the Boost.DI sources (only used with --di-library==\'boost_di\')')
+ parser.add_argument('--num-components-with-no-deps', default=10, help='Number of components with no deps that will be generated')
+ parser.add_argument('--num-components-with-deps', default=90, help='Number of components with deps that will be generated')
+ parser.add_argument('--num-deps', default=10, help='Number of deps in each component with deps that will be generated')
+ parser.add_argument('--output-dir', help='Output directory for generated files')
+ parser.add_argument('--cxx-std', default='c++11',
+ help='Version of the C++ standard to use. Typically one of \'c++11\' and \'c++14\'. (default: \'c++11\')')
+ parser.add_argument('--use-fruit-2-x-syntax', default=False, help='Set this to \'true\' to generate source files compatible with Fruit 2.x (instead of 3.x).')
+ parser.add_argument('--generate-debuginfo', default=False, help='Set this to \'true\' to generate debugging information (-g).')
+
+ args = parser.parse_args()
+
+ if args.compiler is None:
+ raise Exception('--compiler is required.')
+
+ if args.di_library == 'fruit':
+ if args.fruit_sources_dir is None:
+ raise Exception('--fruit-sources-dir is required with --di-library=\'fruit\'.')
+ if args.fruit_build_dir is None:
+ raise Exception('--fruit-build-dir is required with --di-library=\'fruit\'.')
+ elif args.di_library == 'boost_di':
+ if args.boost_di_sources_dir is None:
+ raise Exception('--boost-di-sources-dir is required with --di-library=\'boost_di\'.')
+ else:
+ raise Exception('Unrecognized --di-library: \'%s\'. Allowed values are %s' % (args.di_library, {'fruit', 'boost_di'}))
+
+ num_components_with_deps = int(args.num_components_with_deps)
+ num_components_with_no_deps = int(args.num_components_with_no_deps)
+ num_deps = int(args.num_deps)
+
+ if args.output_dir is None:
+ raise Exception("output_dir must be specified.")
+
+ generate_benchmark(
+ di_library=args.di_library,
+ fruit_sources_dir=args.fruit_sources_dir,
+ boost_di_sources_dir=args.boost_di_sources_dir,
+ output_dir=args.output_dir,
+ compiler=args.compiler,
+ cxx_std=args.cxx_std,
+ num_components_with_deps=num_components_with_deps,
+ num_components_with_no_deps=num_components_with_no_deps,
+ fruit_build_dir=args.fruit_build_dir,
+ num_deps=num_deps,
+ use_fruit_2_x_syntax=(args.use_fruit_2_x_syntax == 'true'),
+ generate_debuginfo=(args.generate_debuginfo == 'true'))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/extras/benchmark/makefile_generator.py b/extras/benchmark/makefile_generator.py
new file mode 100644
index 0000000..2a5b2c4
--- /dev/null
+++ b/extras/benchmark/makefile_generator.py
@@ -0,0 +1,51 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def generate_makefile(sources, executable_name, compile_command, link_command, link_command_suffix):
+ link_rule_template = """
+{executable_name}: {object_files}
+\t{link_command} {object_files} -o {executable_name} {link_command_suffix}
+"""
+ compile_rule_template = """
+{name}.o: {name}.cpp
+\t{compile_command} -c {name}.cpp -o {name}.o
+"""
+
+ clean_rule_template = """
+clean:
+\trm -f {object_files} {executable_name}
+"""
+
+ compile_rules = []
+ object_files = []
+ for source in sources:
+ compile_rule = compile_rule_template.format(
+ name=source,
+ compile_command=compile_command)
+ compile_rules += [compile_rule]
+ object_files += ['%s.o' % source]
+
+ link_rule = link_rule_template.format(
+ object_files=' '.join(object_files),
+ link_command=link_command,
+ link_command_suffix=link_command_suffix,
+ executable_name=executable_name)
+
+ clean_rule = clean_rule_template.format(
+ object_files=' '.join(object_files),
+ executable_name=executable_name)
+
+ # We put the link rule first so that it's the default Make target.
+ return link_rule + ''.join(compile_rules) + clean_rule
diff --git a/extras/benchmark/new_delete_benchmark.cpp b/extras/benchmark/new_delete_benchmark.cpp
new file mode 100644
index 0000000..8131eac
--- /dev/null
+++ b/extras/benchmark/new_delete_benchmark.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+#if MULTIPLIER == 1
+#define REPEAT(X) REPEAT_1(X, _)
+
+#elif MULTIPLIER == 10
+#define REPEAT(X) REPEAT_10(X, _)
+
+#elif MULTIPLIER == 100
+#define REPEAT(X) REPEAT_100(X, _)
+
+#elif MULTIPLIER == 1000
+#define REPEAT(X) REPEAT_1000(X, _)
+
+#else
+#error Multiplier not supported.
+#endif
+
+#define PLACEHOLDER
+
+#define EVAL0(...) __VA_ARGS__
+#define EVAL1(...) EVAL0(EVAL0(EVAL0(EVAL0(__VA_ARGS__))))
+#define EVAL2(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__))))
+#define EVAL(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__))))
+
+#define META_REPEAT_10(R, X, I) \
+ R PLACEHOLDER(X, I##0) R PLACEHOLDER(X, I##1) R PLACEHOLDER(X, I##2) R PLACEHOLDER(X, I##3) R PLACEHOLDER(X, I##4) \
+ R PLACEHOLDER(X, I##5) R PLACEHOLDER(X, I##6) R PLACEHOLDER(X, I##7) R PLACEHOLDER(X, I##8) \
+ R PLACEHOLDER(X, I##9)
+
+#define REPEAT_1(X, I) X(I)
+
+#define REPEAT_10(X, I) META_REPEAT_10(REPEAT_1, X, I)
+
+#define REPEAT_100(X, I) META_REPEAT_10(REPEAT_10, X, I)
+
+#define REPEAT_1000(X, I) META_REPEAT_10(REPEAT_100, X, I)
+
+using namespace std;
+
+#define DEFINITIONS(N) \
+ struct I##N { \
+ virtual ~I##N() = default; \
+ }; \
+ \
+ struct C##N : public I##N { \
+ virtual ~C##N() = default; \
+ };
+
+#define ALLOCATE(N) C##N* c##N = new C##N();
+
+#define DEALLOCATE(N) delete c##N;
+
+EVAL(REPEAT(DEFINITIONS))
+
+int main(int argc, const char* argv[]) {
+ if (argc != 2) {
+ std::cout << "Error: you need to specify the number of loops as argument." << std::endl;
+ return 1;
+ }
+ size_t num_loops = std::atoi(argv[1]);
+
+ std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();
+
+ for (size_t i = 0; i < num_loops; i++) {
+ EVAL(REPEAT(ALLOCATE))
+ EVAL(REPEAT(DEALLOCATE))
+ }
+ double totalTime =
+ std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time)
+ .count();
+
+ std::cout << std::fixed;
+ std::cout << std::setprecision(15);
+ std::cout << "Total = " << totalTime * 1.0 / num_loops << std::endl;
+
+ return 0;
+}
diff --git a/extras/benchmark/run_benchmarks.py b/extras/benchmark/run_benchmarks.py
new file mode 100755
index 0000000..cbbea78
--- /dev/null
+++ b/extras/benchmark/run_benchmarks.py
@@ -0,0 +1,633 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import re
+import textwrap
+from collections import defaultdict
+from timeit import default_timer as timer
+import tempfile
+import os
+import shutil
+import itertools
+import numpy
+import subprocess
+import yaml
+from numpy import floor, log10
+import scipy
+import multiprocessing
+import sh
+import json
+import statsmodels.stats.api as stats
+from generate_benchmark import generate_benchmark
+import git
+from functools import lru_cache as memoize
+
+class CommandFailedException(Exception):
+ def __init__(self, command, stdout, stderr, error_code):
+ self.command = command
+ self.stdout = stdout
+ self.stderr = stderr
+ self.error_code = error_code
+
+ def __str__(self):
+ return textwrap.dedent('''\
+ Ran command: {command}
+ Exit code {error_code}
+ Stdout:
+ {stdout}
+
+ Stderr:
+ {stderr}
+ ''').format(command=self.command, error_code=self.error_code, stdout=self.stdout, stderr=self.stderr)
+
+def run_command(executable, args=[], cwd=None, env=None):
+ args = [str(arg) for arg in args]
+ command = [executable] + args
+ try:
+ p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, cwd=cwd,
+ env=env)
+ (stdout, stderr) = p.communicate()
+ except Exception as e:
+ raise Exception("While executing: %s" % command)
+ if p.returncode != 0:
+ raise CommandFailedException(command, stdout, stderr, p.returncode)
+ return (stdout, stderr)
+
+compile_flags = ['-O2', '-DNDEBUG']
+
+make_args = ['-j', multiprocessing.cpu_count() + 1]
+
+def parse_results(result_lines):
+ """
+ Parses results from the format:
+ ['Dimension name1 = 123',
+ 'Long dimension name2 = 23.45']
+
+ Into a dict {'Dimension name1': 123.0, 'Dimension name2': 23.45}
+ """
+ result_dict = dict()
+ for line in result_lines:
+ line_splits = line.split('=')
+ metric = line_splits[0].strip()
+ value = float(line_splits[1].strip())
+ result_dict[metric] = value
+ return result_dict
+
+
+# We memoize the result since this might be called repeatedly and it's somewhat expensive.
+@memoize(maxsize=None)
+def determine_compiler_name(compiler_executable_name):
+ tmpdir = tempfile.gettempdir() + '/fruit-determine-compiler-version-dir'
+ ensure_empty_dir(tmpdir)
+ with open(tmpdir + '/CMakeLists.txt', 'w') as file:
+ file.write('message("@@@${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}@@@")\n')
+ modified_env = os.environ.copy()
+ modified_env['CXX'] = compiler_executable_name
+ # By converting to a list, we force all output to be read (so the command execution is guaranteed to be complete after this line).
+ # Otherwise, subsequent calls to determine_compiler_name might have trouble deleting the temporary directory because the cmake
+ # process is still writing files in there.
+ _, stderr = run_command('cmake', args=['.'], cwd=tmpdir, env=modified_env)
+ cmake_output = stderr.splitlines()
+ for line in cmake_output:
+ re_result = re.search('@@@(.*)@@@', line)
+ if re_result:
+ pretty_name = re_result.group(1)
+ # CMake calls GCC 'GNU', change it into 'GCC'.
+ return pretty_name.replace('GNU ', 'GCC ')
+ raise Exception('Unable to determine compiler. CMake output was: \n', cmake_output)
+
+
+# Returns a pair (sha256_hash, version_name), where version_name will be None if no version tag was found at HEAD.
+@memoize(maxsize=None)
+def git_repo_info(repo_path):
+ repo = git.Repo(repo_path)
+ head_tags = [tag.name for tag in repo.tags if tag.commit == repo.head.commit and re.match('v[0-9].*', tag.name)]
+ if head_tags == []:
+ head_tag = None
+ else:
+ # There should be only 1 version at any given commit.
+ [head_tag] = head_tags
+ # Remove the 'v' prefix.
+ head_tag = head_tag[1:]
+ return (repo.head.commit.hexsha, head_tag)
+
+
+# Some benchmark parameters, e.g. 'compiler_name' are synthesized automatically from other dimensions (e.g. 'compiler' dimension) or from the environment.
+# We put the compiler name/version in the results because the same 'compiler' value might refer to different compiler versions
+# (e.g. if GCC 6.0.0 is installed when benchmarks are run, then it's updated to GCC 6.0.1 and finally the results are formatted, we
+# want the formatted results to say "GCC 6.0.0" instead of "GCC 6.0.1").
+def add_synthetic_benchmark_parameters(original_benchmark_parameters, path_to_code_under_test):
+ benchmark_params = original_benchmark_parameters.copy()
+ benchmark_params['compiler_name'] = determine_compiler_name(original_benchmark_parameters['compiler'])
+ if path_to_code_under_test is not None:
+ sha256_hash, version_name = git_repo_info(path_to_code_under_test)
+ benchmark_params['di_library_git_commit_hash'] = sha256_hash
+ if version_name is not None:
+ benchmark_params['di_library_version_name'] = version_name
+ return benchmark_params
+
+
+class NewDeleteRunTimeBenchmark:
+ def __init__(self, benchmark_definition, fruit_benchmark_sources_dir):
+ self.benchmark_definition = add_synthetic_benchmark_parameters(benchmark_definition, path_to_code_under_test=None)
+ self.fruit_benchmark_sources_dir = fruit_benchmark_sources_dir
+
+ def prepare(self):
+ cxx_std = self.benchmark_definition['cxx_std']
+ num_classes = self.benchmark_definition['num_classes']
+ compiler_executable_name = self.benchmark_definition['compiler']
+
+ self.tmpdir = tempfile.gettempdir() + '/fruit-benchmark-dir'
+ ensure_empty_dir(self.tmpdir)
+ run_command(compiler_executable_name,
+ args=compile_flags + [
+ '-std=%s' % cxx_std,
+ '-DMULTIPLIER=%s' % num_classes,
+ self.fruit_benchmark_sources_dir + '/extras/benchmark/new_delete_benchmark.cpp',
+ '-o',
+ self.tmpdir + '/main',
+ ])
+
+ def run(self):
+ loop_factor = self.benchmark_definition['loop_factor']
+ stdout, _ = run_command(self.tmpdir + '/main', args = [int(5000000 * loop_factor)])
+ return parse_results(stdout.splitlines())
+
+ def describe(self):
+ return self.benchmark_definition
+
+
+class FruitSingleFileCompileTimeBenchmark:
+ def __init__(self, benchmark_definition, fruit_sources_dir, fruit_build_tmpdir, fruit_benchmark_sources_dir):
+ self.benchmark_definition = add_synthetic_benchmark_parameters(benchmark_definition, path_to_code_under_test=fruit_sources_dir)
+ self.fruit_sources_dir = fruit_sources_dir
+ self.fruit_build_tmpdir = fruit_build_tmpdir
+ self.fruit_benchmark_sources_dir = fruit_benchmark_sources_dir
+ num_bindings = self.benchmark_definition['num_bindings']
+ assert (num_bindings % 5) == 0, num_bindings
+
+ def prepare(self):
+ pass
+
+ def run(self):
+ start = timer()
+ cxx_std = self.benchmark_definition['cxx_std']
+ num_bindings = self.benchmark_definition['num_bindings']
+ compiler_executable_name = self.benchmark_definition['compiler']
+ benchmark_generation_flags = self.benchmark_definition['benchmark_generation_flags']
+
+ other_compile_flags = []
+ if 'use_fruit_2_x_syntax' in benchmark_generation_flags:
+ other_compile_flags.append('-DUSE_FRUIT_2_X_SYNTAX')
+ other_compile_flags.append('-Wno-deprecated-declarations')
+
+ run_command(compiler_executable_name,
+ args = compile_flags + other_compile_flags + [
+ '-std=%s' % cxx_std,
+ '-DMULTIPLIER=%s' % (num_bindings // 5),
+ '-I', self.fruit_sources_dir + '/include',
+ '-I', self.fruit_build_tmpdir + '/include',
+ '-ftemplate-depth=1000',
+ '-c',
+ self.fruit_benchmark_sources_dir + '/extras/benchmark/compile_time_benchmark.cpp',
+ '-o',
+ '/dev/null',
+ ])
+ end = timer()
+ return {"compile_time": end - start}
+
+ def describe(self):
+ return self.benchmark_definition
+
+
+def ensure_empty_dir(dirname):
+ # We start by creating the directory instead of just calling rmtree with ignore_errors=True because that would ignore
+ # all errors, so we might otherwise go ahead even if the directory wasn't properly deleted.
+ os.makedirs(dirname, exist_ok=True)
+ shutil.rmtree(dirname)
+ os.makedirs(dirname)
+
+
+class GenericGeneratedSourcesBenchmark:
+ def __init__(self, di_library, benchmark_definition, fruit_sources_dir, fruit_build_tmpdir, path_to_code_under_test, **other_args):
+ self.di_library = di_library
+ self.benchmark_definition = add_synthetic_benchmark_parameters(benchmark_definition, path_to_code_under_test=path_to_code_under_test)
+ self.fruit_sources_dir = fruit_sources_dir
+ self.fruit_build_tmpdir = fruit_build_tmpdir
+ self.other_args = other_args
+
+ def prepare_compile_benchmark(self):
+ num_classes = self.benchmark_definition['num_classes']
+ cxx_std = self.benchmark_definition['cxx_std']
+ compiler_executable_name = self.benchmark_definition['compiler']
+ benchmark_generation_flags = {flag_name: True for flag_name in self.benchmark_definition['benchmark_generation_flags']}
+
+ self.tmpdir = tempfile.gettempdir() + '/fruit-benchmark-dir'
+ ensure_empty_dir(self.tmpdir)
+ num_classes_with_no_deps = int(num_classes * 0.1)
+ generate_benchmark(
+ compiler=compiler_executable_name,
+ fruit_sources_dir=self.fruit_sources_dir,
+ fruit_build_dir=self.fruit_build_tmpdir,
+ num_components_with_no_deps=num_classes_with_no_deps,
+ num_components_with_deps=num_classes - num_classes_with_no_deps,
+ num_deps=10,
+ output_dir=self.tmpdir,
+ cxx_std=cxx_std,
+ di_library=self.di_library,
+ **benchmark_generation_flags,
+ **self.other_args)
+
+ def prepare_runtime_benchmark(self):
+ self.prepare_compile_benchmark()
+ run_command('make', args=make_args, cwd=self.tmpdir)
+
+ def prepare_executable_size_benchmark(self):
+ self.prepare_runtime_benchmark()
+ run_command('strip', args=[self.tmpdir + '/main'])
+
+ def run_compile_benchmark(self):
+ run_command('make',
+ args=make_args + ['clean'],
+ cwd=self.tmpdir)
+ start = timer()
+ run_command('make',
+ args=make_args,
+ cwd=self.tmpdir)
+ end = timer()
+ result = {'compile_time': end - start}
+ return result
+
+ def run_runtime_benchmark(self):
+ num_classes = self.benchmark_definition['num_classes']
+ loop_factor = self.benchmark_definition['loop_factor']
+
+ results, _ = run_command(self.tmpdir + '/main',
+ args = [
+ # 40M loops with 100 classes, 40M with 1000
+ int(4 * 1000 * 1000 * 1000 * loop_factor / num_classes),
+ ])
+ return parse_results(results.splitlines())
+
+ def run_executable_size_benchmark(self):
+ wc_result, _ = run_command('wc', args=['-c', self.tmpdir + '/main'])
+ num_bytes = wc_result.splitlines()[0].split(' ')[0]
+ return {'num_bytes': float(num_bytes)}
+
+ def describe(self):
+ return self.benchmark_definition
+
+
+class FruitCompileTimeBenchmark:
+ def __init__(self, benchmark_definition, fruit_sources_dir, fruit_build_tmpdir):
+ self.generic_benchmark = GenericGeneratedSourcesBenchmark(
+ di_library='fruit',
+ benchmark_definition=benchmark_definition,
+ fruit_sources_dir=fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ path_to_code_under_test=fruit_sources_dir)
+
+ def prepare(self):
+ self.generic_benchmark.prepare_compile_benchmark()
+
+ def run(self):
+ return self.generic_benchmark.run_compile_benchmark()
+
+ def describe(self):
+ return self.generic_benchmark.describe()
+
+
+class FruitRunTimeBenchmark:
+ def __init__(self, benchmark_definition, fruit_sources_dir, fruit_build_tmpdir):
+ self.generic_benchmark = GenericGeneratedSourcesBenchmark(
+ di_library='fruit',
+ benchmark_definition=benchmark_definition,
+ fruit_sources_dir=fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ path_to_code_under_test=fruit_sources_dir)
+
+ def prepare(self):
+ self.generic_benchmark.prepare_runtime_benchmark()
+
+ def run(self):
+ return self.generic_benchmark.run_runtime_benchmark()
+
+ def describe(self):
+ return self.generic_benchmark.describe()
+
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class FruitExecutableSizeBenchmark:
+ def __init__(self, benchmark_definition, fruit_sources_dir, fruit_build_tmpdir):
+ self.generic_benchmark = GenericGeneratedSourcesBenchmark(
+ di_library='fruit',
+ benchmark_definition=benchmark_definition,
+ fruit_sources_dir=fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ path_to_code_under_test=fruit_sources_dir)
+
+ def prepare(self):
+ self.generic_benchmark.prepare_executable_size_benchmark()
+
+ def run(self):
+ return self.generic_benchmark.run_executable_size_benchmark()
+
+ def describe(self):
+ return self.generic_benchmark.describe()
+
+
+class BoostDiCompileTimeBenchmark:
+ def __init__(self, benchmark_definition, boost_di_sources_dir, fruit_sources_dir, fruit_build_tmpdir):
+ self.generic_benchmark = GenericGeneratedSourcesBenchmark(
+ di_library='boost_di',
+ benchmark_definition=benchmark_definition,
+ boost_di_sources_dir=boost_di_sources_dir,
+ fruit_sources_dir=fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ path_to_code_under_test=boost_di_sources_dir)
+
+ def prepare(self):
+ self.generic_benchmark.prepare_compile_benchmark()
+
+ def run(self):
+ return self.generic_benchmark.run_compile_benchmark()
+
+ def describe(self):
+ return self.generic_benchmark.describe()
+
+
+class BoostDiRunTimeBenchmark:
+ def __init__(self, benchmark_definition, boost_di_sources_dir, fruit_sources_dir, fruit_build_tmpdir):
+ self.generic_benchmark = GenericGeneratedSourcesBenchmark(
+ di_library='boost_di',
+ benchmark_definition=benchmark_definition,
+ boost_di_sources_dir=boost_di_sources_dir,
+ fruit_sources_dir=fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ path_to_code_under_test=boost_di_sources_dir)
+
+ def prepare(self):
+ self.generic_benchmark.prepare_runtime_benchmark()
+
+ def run(self):
+ return self.generic_benchmark.run_runtime_benchmark()
+
+ def describe(self):
+ return self.generic_benchmark.describe()
+
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class BoostDiExecutableSizeBenchmark:
+ def __init__(self, benchmark_definition, boost_di_sources_dir, fruit_sources_dir, fruit_build_tmpdir):
+ self.generic_benchmark = GenericGeneratedSourcesBenchmark(
+ di_library='boost_di',
+ benchmark_definition=benchmark_definition,
+ boost_di_sources_dir=boost_di_sources_dir,
+ fruit_sources_dir=fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ path_to_code_under_test=boost_di_sources_dir)
+
+ def prepare(self):
+ self.generic_benchmark.prepare_executable_size_benchmark()
+
+ def run(self):
+ return self.generic_benchmark.run_executable_size_benchmark()
+
+ def describe(self):
+ return self.generic_benchmark.describe()
+
+
+def round_to_significant_digits(n, num_significant_digits):
+ if n <= 0:
+ # We special-case this, otherwise the log10 below will fail.
+ return 0
+ return round(n, num_significant_digits - int(floor(log10(n))) - 1)
+
+def run_benchmark(benchmark, max_runs, output_file, min_runs=3):
+ def run_benchmark_once():
+ print('Running benchmark... ', end='', flush=True)
+ result = benchmark.run()
+ print(result)
+ for dimension, value in result.items():
+ results_by_dimension[dimension] += [value]
+
+ results_by_dimension = defaultdict(lambda: [])
+ print('Preparing for benchmark... ', end='', flush=True)
+ benchmark.prepare()
+ print('Done.')
+
+ # Run at least min_runs times
+ for i in range(min_runs):
+ run_benchmark_once()
+
+ # Then consider running a few more times to get the desired precision.
+ while True:
+ for dimension, results in results_by_dimension.items():
+ if all(result == results[0] for result in results):
+ # If all results are exactly the same the code below misbehaves. We don't need to run again in this case.
+ continue
+ confidence_interval = stats.DescrStatsW(results).tconfint_mean(0.05)
+ confidence_interval_2dig = (round_to_significant_digits(confidence_interval[0], 2),
+ round_to_significant_digits(confidence_interval[1], 2))
+ if abs(confidence_interval_2dig[0] - confidence_interval_2dig[1]) > numpy.finfo(float).eps * 10:
+ if len(results) < max_runs:
+ print("Running again to get more precision on the metric %s. Current confidence interval: [%.3g, %.3g]" % (
+ dimension, confidence_interval[0], confidence_interval[1]))
+ break
+ else:
+ print("Warning: couldn't determine a precise result for the metric %s. Confidence interval: [%.3g, %.3g]" % (
+ dimension, confidence_interval[0], confidence_interval[1]))
+ else:
+ # We've reached sufficient precision in all metrics, or we've reached the max number of runs.
+ break
+
+ run_benchmark_once()
+
+ # We've reached the desired precision in all dimensions or reached the maximum number of runs. Record the results.
+ rounded_confidence_intervals_by_dimension = {}
+ confidence_intervals_by_dimension = {}
+ for dimension, results in results_by_dimension.items():
+ confidence_interval = stats.DescrStatsW(results).tconfint_mean(0.05)
+ confidence_interval_2dig = (round_to_significant_digits(confidence_interval[0], 2),
+ round_to_significant_digits(confidence_interval[1], 2))
+ rounded_confidence_intervals_by_dimension[dimension] = confidence_interval_2dig
+ confidence_intervals_by_dimension[dimension] = (confidence_interval, confidence_interval_2dig)
+ with open(output_file, 'a') as f:
+ json.dump({"benchmark": benchmark.describe(), "results": confidence_intervals_by_dimension}, f)
+ print(file=f)
+ print('Benchmark finished. Result: ', rounded_confidence_intervals_by_dimension)
+ print()
+
+
+def expand_benchmark_definition(benchmark_definition):
+ """
+ Takes a benchmark definition, e.g.:
+ [{name: 'foo', compiler: ['g++-5', 'g++-6']},
+ {name: ['bar', 'baz'], compiler: ['g++-5'], cxx_std: 'c++14'}]
+
+ And expands it into the individual benchmarks to run, in the example above:
+ [{name: 'foo', compiler: 'g++-5'},
+ {name: 'foo', compiler: 'g++-6'},
+ {name: 'bar', compiler: 'g++-5', cxx_std: 'c++14'},
+ {name: 'baz', compiler: 'g++-5', cxx_std: 'c++14'}]
+ """
+ dict_keys = sorted(benchmark_definition.keys())
+ # Turn non-list values into single-item lists.
+ benchmark_definition = {dict_key: value if isinstance(value, list)
+ else [value]
+ for dict_key, value in benchmark_definition.items()}
+ # Compute the cartesian product of the value lists
+ value_combinations = itertools.product(*(benchmark_definition[dict_key] for dict_key in dict_keys))
+ # Then turn the result back into a dict.
+ return [dict(zip(dict_keys, value_combination))
+ for value_combination in value_combinations]
+
+
+def expand_benchmark_definitions(benchmark_definitions):
+ return list(itertools.chain(*[expand_benchmark_definition(benchmark_definition) for benchmark_definition in benchmark_definitions]))
+
+def group_by(l, element_to_key):
+ """Takes a list and returns a dict of sublists, where the elements are grouped using the provided function"""
+ result = defaultdict(list)
+ for elem in l:
+ result[element_to_key(elem)].append(elem)
+ return result.items()
+
+def main():
+ # This configures numpy/scipy to raise an exception in case of errors, instead of printing a warning and going ahead.
+ numpy.seterr(all='raise')
+ scipy.seterr(all='raise')
+
+ parser = argparse.ArgumentParser(description='Runs a set of benchmarks defined in a YAML file.')
+ parser.add_argument('--fruit-benchmark-sources-dir', help='Path to the fruit sources (used for benchmarking code only)')
+ parser.add_argument('--fruit-sources-dir', help='Path to the fruit sources')
+ parser.add_argument('--boost-di-sources-dir', help='Path to the Boost.DI sources')
+ parser.add_argument('--output-file',
+ help='The output file where benchmark results will be stored (1 per line, with each line in JSON format). These can then be formatted by e.g. the format_bench_results script.')
+ parser.add_argument('--benchmark-definition', help='The YAML file that defines the benchmarks (see fruit_wiki_benchs_fruit.yml for an example).')
+ parser.add_argument('--continue-benchmark', help='If this is \'true\', continues a previous benchmark run instead of starting from scratch (taking into account the existing benchmark results in the file specified with --output-file).')
+ args = parser.parse_args()
+
+ if args.output_file is None:
+ raise Exception('You must specify --output_file')
+ if args.continue_benchmark == 'true':
+ try:
+ with open(args.output_file, 'r') as f:
+ previous_run_completed_benchmarks = [json.loads(line)['benchmark'] for line in f.readlines()]
+ except FileNotFoundError:
+ previous_run_completed_benchmarks = []
+ else:
+ previous_run_completed_benchmarks = []
+ run_command('rm', args=['-f', args.output_file])
+
+ fruit_build_tmpdir = tempfile.gettempdir() + '/fruit-benchmark-build-dir'
+
+ with open(args.benchmark_definition, 'r') as f:
+ yaml_file_content = yaml.load(f)
+ global_definitions = yaml_file_content['global']
+ benchmark_definitions = expand_benchmark_definitions(yaml_file_content['benchmarks'])
+
+ benchmark_index = 0
+
+ for (compiler_executable_name, additional_cmake_args), benchmark_definitions_with_current_config \
+ in group_by(benchmark_definitions,
+ lambda benchmark_definition:
+ (benchmark_definition['compiler'], tuple(benchmark_definition['additional_cmake_args']))):
+
+ print('Preparing for benchmarks with the compiler %s, with additional CMake args %s' % (compiler_executable_name, additional_cmake_args))
+ # We compute this here (and memoize the result) so that the benchmark's describe() will retrieve the cached
+ # value instantly.
+ determine_compiler_name(compiler_executable_name)
+
+ # Build Fruit in fruit_build_tmpdir, so that fruit_build_tmpdir points to a built Fruit (useful for e.g. the config header).
+ shutil.rmtree(fruit_build_tmpdir, ignore_errors=True)
+ os.makedirs(fruit_build_tmpdir)
+ modified_env = os.environ.copy()
+ modified_env['CXX'] = compiler_executable_name
+ run_command('cmake',
+ args=[
+ args.fruit_sources_dir,
+ '-DCMAKE_BUILD_TYPE=Release',
+ *additional_cmake_args,
+ ],
+ cwd=fruit_build_tmpdir,
+ env=modified_env)
+ run_command('make', args=make_args, cwd=fruit_build_tmpdir)
+
+ for benchmark_definition in benchmark_definitions_with_current_config:
+ benchmark_index += 1
+ print('%s/%s: %s' % (benchmark_index, len(benchmark_definitions), benchmark_definition))
+ benchmark_name = benchmark_definition['name']
+
+ if (benchmark_name in {'boost_di_compile_time', 'boost_di_run_time', 'boost_di_executable_size'}
+ and args.boost_di_sources_dir is None):
+ raise Exception('Error: you need to specify the --boost-di-sources-dir flag in order to run Boost.DI benchmarks.')
+
+ if benchmark_name == 'new_delete_run_time':
+ benchmark = NewDeleteRunTimeBenchmark(
+ benchmark_definition,
+ fruit_benchmark_sources_dir=args.fruit_benchmark_sources_dir)
+ elif benchmark_name == 'fruit_single_file_compile_time':
+ benchmark = FruitSingleFileCompileTimeBenchmark(
+ benchmark_definition,
+ fruit_sources_dir=args.fruit_sources_dir,
+ fruit_benchmark_sources_dir=args.fruit_benchmark_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir)
+ elif benchmark_name == 'fruit_compile_time':
+ benchmark = FruitCompileTimeBenchmark(
+ benchmark_definition,
+ fruit_sources_dir=args.fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir)
+ elif benchmark_name == 'fruit_run_time':
+ benchmark = FruitRunTimeBenchmark(
+ benchmark_definition,
+ fruit_sources_dir=args.fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir)
+ elif benchmark_name == 'fruit_executable_size':
+ benchmark = FruitExecutableSizeBenchmark(
+ benchmark_definition,
+ fruit_sources_dir=args.fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir)
+ elif benchmark_name == 'boost_di_compile_time':
+ benchmark = BoostDiCompileTimeBenchmark(
+ benchmark_definition,
+ fruit_sources_dir=args.fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ boost_di_sources_dir=args.boost_di_sources_dir)
+ elif benchmark_name == 'boost_di_run_time':
+ benchmark = BoostDiRunTimeBenchmark(
+ benchmark_definition,
+ fruit_sources_dir=args.fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ boost_di_sources_dir=args.boost_di_sources_dir)
+ elif benchmark_name == 'boost_di_executable_size':
+ benchmark = BoostDiExecutableSizeBenchmark(
+ benchmark_definition,
+ fruit_sources_dir=args.fruit_sources_dir,
+ fruit_build_tmpdir=fruit_build_tmpdir,
+ boost_di_sources_dir=args.boost_di_sources_dir)
+ else:
+ raise Exception("Unrecognized benchmark: %s" % benchmark_name)
+
+ if benchmark.describe() in previous_run_completed_benchmarks:
+ print("Skipping benchmark that was already run previously (due to --continue-benchmark):", benchmark.describe())
+ continue
+
+ run_benchmark(benchmark, output_file=args.output_file, max_runs=global_definitions['max_runs'])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/extras/benchmark/suites/boost_di.yml b/extras/benchmark/suites/boost_di.yml
new file mode 100644
index 0000000..7aa9750
--- /dev/null
+++ b/extras/benchmark/suites/boost_di.yml
@@ -0,0 +1,38 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+global:
+ max_runs: 20
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+ - "clang++-4.0"
+
+benchmarks:
+ - name:
+ - "boost_di_compile_time"
+ - "boost_di_run_time"
+ - "boost_di_executable_size"
+ loop_factor: 1.0
+ num_classes:
+ - 100
+ # Boost.DI fails to compile the generated example with 1000 classes.
+ compiler: *compilers
+ cxx_std: "c++14"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
diff --git a/extras/benchmark/suites/fruit_debug.yml b/extras/benchmark/suites/fruit_debug.yml
new file mode 100644
index 0000000..30ca90c
--- /dev/null
+++ b/extras/benchmark/suites/fruit_debug.yml
@@ -0,0 +1,97 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is a simplified version of fruit_wiki_benchs_{fruit,boost_di}.yml used to debug benchmarking code.
+# When using this, the benchmarks will run much faster, but the results will be unreliable and incomplete.
+
+global:
+ max_runs: 3
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+ - "clang++-4.0"
+ gcc: &gcc
+ - "g++-7"
+ clang: &clang
+ - "clang++-4.0"
+
+benchmarks:
+ - name: "fruit_single_file_compile_time"
+ num_bindings:
+ - 20
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
+ #- ["use_fruit_2_x_syntax"]
+
+ - name:
+ - "new_delete_run_time"
+ loop_factor: 0.01
+ num_classes:
+ - 100
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
+
+ - name:
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 0.01
+ num_classes:
+ - 100
+ compiler: *gcc
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ - ['-DFRUIT_USES_BOOST=False']
+ - ["-DBUILD_SHARED_LIBS=False"]
+ benchmark_generation_flags:
+ - []
+
+ - name:
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 0.01
+ num_classes:
+ - 100
+ compiler: *clang
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
+
+ - name:
+ - "boost_di_compile_time"
+ - "boost_di_run_time"
+ - "boost_di_executable_size"
+ loop_factor: 0.01
+ num_classes:
+ - 100
+ compiler: *compilers
+ cxx_std: "c++14"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
diff --git a/extras/benchmark/suites/fruit_full.yml b/extras/benchmark/suites/fruit_full.yml
new file mode 100644
index 0000000..aeff08b
--- /dev/null
+++ b/extras/benchmark/suites/fruit_full.yml
@@ -0,0 +1,66 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+global:
+ max_runs: 20
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+ - "clang++-4.0"
+ num_classes: &num_classes
+ - 100
+ - 1000
+
+benchmarks:
+ - name: "fruit_single_file_compile_time"
+ num_bindings:
+ - 20
+ - 80
+ - 320
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
+
+ - name:
+ - "new_delete_run_time"
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 1.0
+ num_classes: *num_classes
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
+
+ - name:
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 1.0
+ num_classes: *num_classes
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - ['-DFRUIT_USES_BOOST=False']
+ - ["-DBUILD_SHARED_LIBS=False"]
+ benchmark_generation_flags:
+ - []
diff --git a/extras/benchmark/suites/fruit_full_old_style.yml b/extras/benchmark/suites/fruit_full_old_style.yml
new file mode 100644
index 0000000..413a05f
--- /dev/null
+++ b/extras/benchmark/suites/fruit_full_old_style.yml
@@ -0,0 +1,66 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+global:
+ max_runs: 20
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+ - "clang++-4.0"
+ num_classes: &num_classes
+ - 100
+ - 1000
+
+benchmarks:
+ - name: "fruit_single_file_compile_time"
+ num_bindings:
+ - 20
+ - 80
+ - 320
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - ["use_fruit_2_x_syntax"]
+
+ - name:
+ - "new_delete_run_time"
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 1.0
+ num_classes: *num_classes
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - ["use_fruit_2_x_syntax"]
+
+ - name:
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 1.0
+ num_classes: *num_classes
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - ['-DFRUIT_USES_BOOST=False']
+ - ["-DBUILD_SHARED_LIBS=False"]
+ benchmark_generation_flags:
+ - ["use_fruit_2_x_syntax"]
diff --git a/extras/benchmark/suites/fruit_mostly_full.yml b/extras/benchmark/suites/fruit_mostly_full.yml
new file mode 100644
index 0000000..00ee0dc
--- /dev/null
+++ b/extras/benchmark/suites/fruit_mostly_full.yml
@@ -0,0 +1,51 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+global:
+ max_runs: 20
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+ - "clang++-4.0"
+ num_classes: &num_classes
+ - 100
+ - 1000
+
+benchmarks:
+ - name: "fruit_single_file_compile_time"
+ num_bindings:
+ - 20
+ - 80
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
+
+ - name:
+ - "new_delete_run_time"
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 1.0
+ num_classes: *num_classes
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
diff --git a/extras/benchmark/suites/fruit_quick.yml b/extras/benchmark/suites/fruit_quick.yml
new file mode 100644
index 0000000..778bf60
--- /dev/null
+++ b/extras/benchmark/suites/fruit_quick.yml
@@ -0,0 +1,49 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+global:
+ max_runs: 10
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-6"
+ - "clang++-4.0"
+ num_classes: &num_classes
+ - 100
+
+benchmarks:
+ - name: "fruit_single_file_compile_time"
+ num_bindings:
+ - 20
+ - 80
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
+
+ - name:
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 1.0
+ num_classes: *num_classes
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
diff --git a/extras/benchmark/suites/fruit_quick_old_style.yml b/extras/benchmark/suites/fruit_quick_old_style.yml
new file mode 100644
index 0000000..cb6b70e
--- /dev/null
+++ b/extras/benchmark/suites/fruit_quick_old_style.yml
@@ -0,0 +1,50 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+global:
+ max_runs: 10
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+ - "clang++-4.0"
+ num_classes: &num_classes
+ - 100
+ - 1000
+
+benchmarks:
+ - name: "fruit_single_file_compile_time"
+ num_bindings:
+ - 20
+ - 80
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - ["use_fruit_2_x_syntax"]
+
+ - name:
+ - "fruit_compile_time"
+ - "fruit_run_time"
+ - "fruit_executable_size"
+ loop_factor: 1.0
+ num_classes: *num_classes
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - ["use_fruit_2_x_syntax"]
diff --git a/extras/benchmark/suites/fruit_single.yml b/extras/benchmark/suites/fruit_single.yml
new file mode 100644
index 0000000..a6dae93
--- /dev/null
+++ b/extras/benchmark/suites/fruit_single.yml
@@ -0,0 +1,36 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a variant of fruit_wiki_benchs_fruit.yml that runs a single runtime benchmark.
+
+global:
+ max_runs: 8
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+
+benchmarks:
+ - name:
+ - "fruit_run_time"
+ loop_factor: 1.0
+ num_classes:
+ - 100
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - []
diff --git a/extras/benchmark/suites/fruit_single_old_style.yml b/extras/benchmark/suites/fruit_single_old_style.yml
new file mode 100644
index 0000000..32efa0f
--- /dev/null
+++ b/extras/benchmark/suites/fruit_single_old_style.yml
@@ -0,0 +1,36 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a variant of fruit_wiki_benchs_fruit.yml that runs a single runtime benchmark.
+
+global:
+ max_runs: 8
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ compilers: &compilers
+ - "g++-7"
+
+benchmarks:
+ - name:
+ - "fruit_run_time"
+ loop_factor: 1.0
+ num_classes:
+ - 100
+ compiler: *compilers
+ cxx_std: "c++11"
+ additional_cmake_args:
+ - []
+ benchmark_generation_flags:
+ - ["use_fruit_2_x_syntax"]
diff --git a/extras/benchmark/tables/fruit_internal.yml b/extras/benchmark/tables/fruit_internal.yml
new file mode 100644
index 0000000..4dcef73
--- /dev/null
+++ b/extras/benchmark/tables/fruit_internal.yml
@@ -0,0 +1,192 @@
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ num_bindings_column: &num_bindings_column
+ dimension: "num_bindings"
+ pretty_printer:
+ format_string: "%s bindings"
+
+ num_classes_column: &num_classes_column
+ dimension: "num_classes"
+ pretty_printer:
+ format_string: "%s classes"
+
+ compiler_name_row: &compiler_name_row
+ dimension: "compiler_name"
+ pretty_printer:
+ format_string: "%s"
+
+tables:
+ - name: "Fruit compile time (single file)"
+ benchmark_filter:
+ name: "fruit_single_file_compile_time"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_bindings_column
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit compile time"
+ benchmark_filter:
+ name: "fruit_compile_time"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit full injection time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Fruit component normalization time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "componentNormalizationTime"
+ unit: "seconds"
+
+ - name: "Fruit setup time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Fruit per-request time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "New/delete time"
+ benchmark_filter:
+ name: "new_delete_run_time"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total"
+ unit: "seconds"
+
+ - name: "Compile time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_compile_time": "Fruit"
+ "boost_di_compile_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit full injection time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Fruit component normalization time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "componentNormalizationTime"
+ unit: "seconds"
+
+
+ - name: "Setup time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Per-request time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "Executable size (stripped, 100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_executable_size": "Fruit"
+ "boost_di_executable_size": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "num_bytes"
+ unit: "bytes"
diff --git a/extras/benchmark/tables/fruit_internal_old_vs_new_style.yml b/extras/benchmark/tables/fruit_internal_old_vs_new_style.yml
new file mode 100644
index 0000000..3a74047
--- /dev/null
+++ b/extras/benchmark/tables/fruit_internal_old_vs_new_style.yml
@@ -0,0 +1,108 @@
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ num_bindings_column: &num_bindings_column
+ dimension: "num_bindings"
+ pretty_printer:
+ format_string: "%s bindings"
+
+ num_classes_column: &num_classes_column
+ dimension: "num_classes"
+ pretty_printer:
+ format_string: "%s classes"
+
+ compiler_name_row: &compiler_name_row
+ dimension: "compiler_name"
+ pretty_printer:
+ format_string: "%s"
+
+tables:
+ - name: "Compile time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_compile_time"
+ additional_cmake_args: []
+ columns:
+ dimension: "benchmark_generation_flags"
+ pretty_printer:
+ fixed_map:
+ - from: ["use_fruit_2_x_syntax"]
+ to: "Old-style install()"
+ - from: []
+ to: "New-style install()"
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit full injection time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ columns:
+ dimension: "benchmark_generation_flags"
+ pretty_printer:
+ fixed_map:
+ - from: ["use_fruit_2_x_syntax"]
+ to: "Old-style install()"
+ - from: []
+ to: "New-style install()"
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Setup time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ columns:
+ dimension: "benchmark_generation_flags"
+ pretty_printer:
+ fixed_map:
+ - from: ["use_fruit_2_x_syntax"]
+ to: "Old-style install()"
+ - from: []
+ to: "New-style install()"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Per-request time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ columns:
+ dimension: "benchmark_generation_flags"
+ pretty_printer:
+ fixed_map:
+ - from: ["use_fruit_2_x_syntax"]
+ to: "Old-style install()"
+ - from: []
+ to: "New-style install()"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "Executable size (stripped, 100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_executable_size"
+ additional_cmake_args: []
+ columns:
+ dimension: "benchmark_generation_flags"
+ pretty_printer:
+ fixed_map:
+ - from: ["use_fruit_2_x_syntax"]
+ to: "Old-style install()"
+ - from: []
+ to: "New-style install()"
+ rows: *compiler_name_row
+ results:
+ dimension: "num_bytes"
+ unit: "bytes"
diff --git a/extras/benchmark/tables/fruit_wiki.yml b/extras/benchmark/tables/fruit_wiki.yml
new file mode 100644
index 0000000..6a0c304
--- /dev/null
+++ b/extras/benchmark/tables/fruit_wiki.yml
@@ -0,0 +1,282 @@
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ num_bindings_column: &num_bindings_column
+ dimension: "num_bindings"
+ pretty_printer:
+ format_string: "%s bindings"
+
+ num_classes_column: &num_classes_column
+ dimension: "num_classes"
+ pretty_printer:
+ format_string: "%s classes"
+
+ compiler_name_row: &compiler_name_row
+ dimension: "compiler_name"
+ pretty_printer:
+ format_string: "%s"
+
+tables:
+ # Main Fruit benchmark tables
+
+ - name: "Fruit compile time (single file)"
+ benchmark_filter:
+ name: "fruit_single_file_compile_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns: *num_bindings_column
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit compile time"
+ benchmark_filter:
+ name: "fruit_compile_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit full injection time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Fruit setup time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Fruit per-request time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "New/delete time"
+ benchmark_filter:
+ name: "new_delete_run_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total"
+ unit: "seconds"
+
+ # The following tables compare Fruit to Boost.DI
+
+ - name: "Compile time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_compile_time": "Fruit"
+ "boost_di_compile_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Full injection time"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Setup time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Per-request time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "Executable size (stripped, 100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: []
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_executable_size": "Fruit"
+ "boost_di_executable_size": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "num_bytes"
+ unit: "bytes"
+
+
+ # The following tables compare various Fruit configurations
+
+ - name: "Compile time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_compile_time"
+ benchmark_generation_flags: []
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Full injection time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ benchmark_generation_flags: []
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Setup time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ benchmark_generation_flags: []
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Per-request time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ benchmark_generation_flags: []
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "Executable size (stripped)"
+ benchmark_filter:
+ name: "fruit_executable_size"
+ benchmark_generation_flags: []
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "num_bytes"
+ unit: "bytes"
+
+ - name: "Executable size (stripped, 100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_executable_size"
+ benchmark_generation_flags: []
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "num_bytes"
+ unit: "bytes"
diff --git a/extras/benchmark/tables/fruit_wiki_old_style.yml b/extras/benchmark/tables/fruit_wiki_old_style.yml
new file mode 100644
index 0000000..135445a
--- /dev/null
+++ b/extras/benchmark/tables/fruit_wiki_old_style.yml
@@ -0,0 +1,270 @@
+
+# These values are ignored, they are here just to be referenced below.
+constants:
+ num_bindings_column: &num_bindings_column
+ dimension: "num_bindings"
+ pretty_printer:
+ format_string: "%s bindings"
+
+ num_classes_column: &num_classes_column
+ dimension: "num_classes"
+ pretty_printer:
+ format_string: "%s classes"
+
+ compiler_name_row: &compiler_name_row
+ dimension: "compiler_name"
+ pretty_printer:
+ format_string: "%s"
+
+tables:
+ # Main Fruit benchmark tables
+
+ - name: "Fruit compile time (single file)"
+ benchmark_filter:
+ name: "fruit_single_file_compile_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns: *num_bindings_column
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit compile time"
+ benchmark_filter:
+ name: "fruit_compile_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Fruit full injection time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Fruit setup time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Fruit per-request time"
+ benchmark_filter:
+ name: "fruit_run_time"
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "New/delete time"
+ benchmark_filter:
+ name: "new_delete_run_time"
+ additional_cmake_args: []
+ columns: *num_classes_column
+ rows: *compiler_name_row
+ results:
+ dimension: "Total"
+ unit: "seconds"
+
+ # The following tables compare Fruit to Boost.DI
+
+ - name: "Compile time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_compile_time": "Fruit"
+ "boost_di_compile_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Full injection time"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Setup time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Per-request time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_run_time": "Fruit"
+ "boost_di_run_time": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "Executable size (stripped, 100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ additional_cmake_args: []
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "name"
+ pretty_printer:
+ fixed_map:
+ "fruit_executable_size": "Fruit"
+ "boost_di_executable_size": "Boost.DI"
+ rows: *compiler_name_row
+ results:
+ dimension: "num_bytes"
+ unit: "bytes"
+
+
+ # The following tables compare various Fruit configurations
+
+ - name: "Compile time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_compile_time"
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "compile_time"
+ unit: "seconds"
+
+ - name: "Full injection time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "Full injection time"
+ unit: "seconds"
+
+ - name: "Setup time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total for setup"
+ unit: "seconds"
+
+ - name: "Per-request time (100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_run_time"
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "Total per request"
+ unit: "seconds"
+
+ - name: "Executable size (stripped, 100 classes)"
+ benchmark_filter:
+ num_classes: 100
+ name: "fruit_executable_size"
+ benchmark_generation_flags: ["use_fruit_2_x_syntax"]
+ columns:
+ dimension: "additional_cmake_args"
+ pretty_printer:
+ fixed_map:
+ - from: []
+ to: "default"
+ - from: ["-DFRUIT_USES_BOOST=False"]
+ to: "With -DFRUIT_USES_BOOST=False"
+ - from: ["-DBUILD_SHARED_LIBS=False"]
+ to: "Statically-linking with Fruit"
+ rows: *compiler_name_row
+ results:
+ dimension: "num_bytes"
+ unit: "bytes"
diff --git a/extras/doc/CMakeLists.txt b/extras/doc/CMakeLists.txt
new file mode 100644
index 0000000..5907cf8
--- /dev/null
+++ b/extras/doc/CMakeLists.txt
@@ -0,0 +1,94 @@
+
+
+add_custom_command(OUTPUT tikz-uml.sty
+ COMMAND wget http://perso.ensta-paristech.fr/~kielbasi/tikzuml/var/files/src/tikzuml-v1.0-2016-03-29.tbz
+ COMMAND tar xf tikzuml-v1.0-2016-03-29.tbz
+ COMMAND mv tikzuml-v1.0-2016-03-29/tikz-uml.sty tikz-uml.sty
+ )
+add_custom_command(OUTPUT header.tex footer.tex
+ COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/common-header.tex header.tex
+ COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/common-footer.tex footer.tex
+ DEPENDS
+ ${CMAKE_CURRENT_SOURCE_DIR}/common-header.tex
+ ${CMAKE_CURRENT_SOURCE_DIR}/common-footer.tex
+ )
+
+set(LATEX_SOURCES
+bar_handler.tex
+bind.tex
+bind_instance.tex
+car_component.tex
+checked_adder.tex
+checked_incrementer.tex
+component_composition.tex
+component_dep_loop.tex
+foo_handler.tex
+greeter.tex
+incrementer.tex
+incrementer_component.tex
+inject_macro.tex
+inject_macro_no_args.tex
+inject_macro_template.tex
+inject_typedef_greeter.tex
+inject_typedef_writer.tex
+inject_typedef_writer2.tex
+inject_typedef_templated_constructor.tex
+multiplier.tex
+parametrized_component.tex
+provider.tex
+provider_functor.tex
+register_constructor.tex
+register_constructor_component.tex
+register_factory.tex
+register_factory_use.tex
+register_factory_macro.tex
+request_dispatcher.tex
+request_injector.tex
+scaler.tex
+server.tex
+simple_greeter.tex
+simple_incrementer.tex
+simple_adder.tex
+templated_component.tex
+)
+
+foreach(S ${LATEX_SOURCES})
+ string(REPLACE ".tex" "" N ${S})
+ add_custom_command(OUTPUT ${N}.png
+ COMMAND pdflatex -halt-on-error ${CMAKE_CURRENT_SOURCE_DIR}/${N}.tex
+ COMMAND convert -density 300 -trim ${N}.pdf -quality 100 -sharpen 0x1.0 ${N}.png
+ # This normalizes the PNG files, so that we avoid tracking multiple copies of the same file in the Github wiki repo.
+ COMMAND exiftool -all= -overwrite_original ${N}.png
+ DEPENDS
+ tikz-uml.sty
+ header.tex
+ footer.tex
+ ${N}.tex
+ )
+ add_custom_target(${N}-png ALL
+ DEPENDS ${N}.png)
+endforeach(S)
+
+set(EXAMPLE_DIRECTORIES
+hello_world
+server
+scaling_doubles
+multibindings
+simple_injection
+)
+
+foreach(D ${EXAMPLE_DIRECTORIES})
+ add_custom_command(OUTPUT ${D}-deps.png
+ COMMAND bash < ${CMAKE_CURRENT_SOURCE_DIR}/extract_dependencies.sh > ${CMAKE_CURRENT_BINARY_DIR}/${D}.dot
+ COMMAND dot -Goverlap=prism10000 ${CMAKE_CURRENT_BINARY_DIR}/${D}.dot -Tpng -o ${CMAKE_CURRENT_BINARY_DIR}/${D}-deps.png
+ # This normalizes the PNG files, so that we avoid tracking multiple copies of the same file in the Github wiki repo.
+ COMMAND exiftool -all= -overwrite_original ${CMAKE_CURRENT_BINARY_DIR}/${D}-deps.png
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../examples/${D}
+ DEPENDS
+ ../../examples/${D}
+ extract_dependencies.sh
+ )
+ add_custom_target(${D}-deps ALL
+ DEPENDS ${D}-deps.png)
+endforeach(D)
+
diff --git a/extras/doc/README b/extras/doc/README
new file mode 100644
index 0000000..3ebf297
--- /dev/null
+++ b/extras/doc/README
@@ -0,0 +1,2 @@
+This directory contains files needed to re-generate the images in the online documentation.
+It's not necessary to build this when building Fruit.
diff --git a/extras/doc/bar_handler.tex b/extras/doc/bar_handler.tex
new file mode 100644
index 0000000..5defad1
--- /dev/null
+++ b/extras/doc/bar_handler.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{2.5}{BarHandlerComponent}
+\umlprovidedinterface[interface=BarHandler, distance=3.3, padding=0.9cm]{BarHandlerComponent}
+\umlrequiredinterface[interface={Request, ServerContext}, distance=4.2, padding=0.9cm]{BarHandlerComponent}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/bind.tex b/extras/doc/bind.tex
new file mode 100644
index 0000000..0287716
--- /dev/null
+++ b/extras/doc/bind.tex
@@ -0,0 +1,14 @@
+
+\input{header}
+\begin{tikzpicture}[scale=0.91, transform shape]
+
+\newnamedcomponent{0}{2.5}{bind1}{bind}
+\newnamedcomponent{0}{0}{bind2}{bind}
+\umlprovidedinterface[interface=Impl, distance=2, padding=0.9cm]{bind1}
+\umlrequiredinterface[interface=Interface, distance=2.2, padding=0.9cm]{bind1}
+\umlprovidedinterface[interface={std::function<std::unique\_ptr<Impl(T1,..,Tn)>()>}, distance=5.2, padding=0.9cm]{bind2}
+\umlrequiredinterface[interface={std::function<std::unique\_ptr<Interface(T1,..,Tn)>()>}, distance=5.7, padding=0.9cm]{bind2}
+\node at (8.8,-0.2) {(for any types T1,...,Tn)};
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/bind_instance.tex b/extras/doc/bind_instance.tex
new file mode 100644
index 0000000..962f48b
--- /dev/null
+++ b/extras/doc/bind_instance.tex
@@ -0,0 +1,9 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{0}{bindInstance}
+\umlprovidedinterface[interface=Foo, distance=2.5, padding=0.9cm]{bindInstance}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/car_component.tex b/extras/doc/car_component.tex
new file mode 100644
index 0000000..0843636
--- /dev/null
+++ b/extras/doc/car_component.tex
@@ -0,0 +1,17 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{CarComponent}
+\newcomponent{0}{1.5}{CarImpl}
+\umlprovidedinterface[interface=Car, distance=2cm, padding=1cm]{CarImpl}
+\newcomponent{10}{0}{MainBrakeComponent}
+\umlassemblyconnector[interface={Annotated<MainBrake{,} Brake>}]{CarImpl}{MainBrakeComponent}
+\newcomponent{10}{3}{EmergencyBrakeComponent}
+\umlassemblyconnector[interface={Annotated<EmergencyBrake{,} Brake>}]{CarImpl}{EmergencyBrakeComponent}
+\end{component}
+\umlprovidedinterface[interface=Car, distance=9.7, with port, padding=0cm]{CarComponent}
+\umldep{CarComponent-west-port}{CarImpl-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/checked_adder.tex b/extras/doc/checked_adder.tex
new file mode 100644
index 0000000..3e5d86b
--- /dev/null
+++ b/extras/doc/checked_adder.tex
@@ -0,0 +1,9 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{2}{0}{CheckedAdderComponent}
+\umlprovidedinterface[interface=Adder, distance=4, padding=0.5cm]{CheckedAdderComponent}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/checked_incrementer.tex b/extras/doc/checked_incrementer.tex
new file mode 100644
index 0000000..f5673c5
--- /dev/null
+++ b/extras/doc/checked_incrementer.tex
@@ -0,0 +1,15 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{CheckedIncrementerComponent}
+\newcomponent{0}{0}{IncrementerImplComponent}
+\newcomponent{6}{0}{CheckedAdderComponent}
+\umlprovidedinterface[interface=Incrementer, distance=3.7, padding=0.9cm]{IncrementerImplComponent}
+\umlassemblyconnector[interface=Adder, distance=7]{IncrementerImplComponent}{CheckedAdderComponent}
+\end{component}
+\umlprovidedinterface[interface=Incrementer, distance=8, with port]{CheckedIncrementerComponent}
+\umlassoc{CheckedIncrementerComponent-west-port}{IncrementerImplComponent-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/common-footer.tex b/extras/doc/common-footer.tex
new file mode 100644
index 0000000..300f2b1
--- /dev/null
+++ b/extras/doc/common-footer.tex
@@ -0,0 +1,4 @@
+
+\end{center}
+\end{framed}
+\end{document}
diff --git a/extras/doc/common-header.tex b/extras/doc/common-header.tex
new file mode 100644
index 0000000..2c01e63
--- /dev/null
+++ b/extras/doc/common-header.tex
@@ -0,0 +1,36 @@
+
+\documentclass{article}
+
+\usepackage[T1]{fontenc}
+\usepackage[utf8]{inputenc}
+\usepackage[english]{babel}
+\usepackage{tikz-uml}
+\usepackage{graphicx}
+\usepackage[margin=0in, paperwidth=18.5cm]{geometry}
+\usepackage{color}
+\usepackage{framed}
+
+\renewcommand{\strut}{\rule[-.3\baselineskip]{0pt}{\baselineskip}}
+
+\newenvironment{component}[4]
+{\begin{umlcomponent}[width=#1,x=#2,y=#3,name=#4]{#4\strut}}
+{\end{umlcomponent}}
+
+\newcommand{\newcomponent}[3]{\umlbasiccomponent[x=#1,y=#2,name=#3]{#3\strut}}
+\newcommand{\newnamedcomponent}[4]{\umlbasiccomponent[x=#1,y=#2,name=#3]{#4\strut}}
+
+\definecolor{lightgray}{HTML}{FEFEFE}
+
+\begin{document}
+
+\pagecolor{white} % White background instead of transparent
+\pagenumbering{gobble}% Remove page number
+
+\tikzumlset{fill component=white}
+\tikzumlset{fill port=white}
+\tikzumlset{fill assembly connector=white}
+
+\def\FrameCommand
+{\fcolorbox{lightgray}{white}}%
+\begin{framed}
+\begin{center} \ No newline at end of file
diff --git a/extras/doc/component_composition.tex b/extras/doc/component_composition.tex
new file mode 100644
index 0000000..035a481
--- /dev/null
+++ b/extras/doc/component_composition.tex
@@ -0,0 +1,35 @@
+
+\input{header}
+\begin{tikzpicture}[scale=0.93, transform shape]
+
+\begin{component}{20cm}{1}{0}{U1Component}
+\newcomponent{11}{4}{SomeType}
+\newcomponent{0}{4}{T1Component}
+\newcomponent{6}{4}{bind}
+\newcomponent{2.5}{0}{U1U2Component}
+\umlassemblyconnector[interface=SomeType, distance=7]{bind}{SomeType}
+\umlassemblyconnector[interface=T2, distance=7, arm2=1cm]{T1Component}{bind}
+\umlassemblyconnector[distance=7, geometry=|-]{U1U2Component}{bind}
+\umlassemblyconnector[interface=T1, distance=7, anchor1=120, anchor2=-90]{U1U2Component}{T1Component}
+\umlprovidedinterface[interface=U2, distance=3.5, padding=0.9cm]{U1U2Component}
+\umlrequiredinterface[interface=T3, distance=3, padding=0.9cm]{U1U2Component}
+\umlrequiredinterface[interface=T3, distance=2, padding=0.9cm]{SomeType}
+
+% Hack to draw something similar to:
+% \umlprovidedinterface[interface=U2, distance=3.5, padding=0.9cm]{U1U2Component}
+% But at a different angle.
+\node[inner sep=0] (U1U2Component-west-port) at (U1U2Component.west) {};
+\draw (U1U2Component)+(-3cm,1.5cm) node[inner sep=0, text width=1em, circle, draw, fill=white, name=U1U2Component-west-interface] {};
+\node[above] at (U1U2Component-west-interface.north) {U1};
+\umlrelation[style={tikzuml connector style}]{U1U2Component-west-port}{U1U2Component-west-interface}
+\draw (U1U2Component-west-interface)+(-1cm,0) node[name=U1U2Component-west-padding] {};
+
+\end{component}
+\umlprovidedinterface[interface=U1, distance=9.5, with port, padding=0cm]{U1Component}
+\umlrequiredinterface[interface=T3, distance=9.5, with port, padding=0cm]{U1Component}
+\umldep{SomeType-east-interface}{U1Component-east-port}
+\umldep{U1U2Component-east-interface}{U1Component-east-port}
+\umldep{U1Component-west-port}{U1U2Component-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/component_dep_loop.tex b/extras/doc/component_dep_loop.tex
new file mode 100644
index 0000000..1b6a6bb
--- /dev/null
+++ b/extras/doc/component_dep_loop.tex
@@ -0,0 +1,11 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{0}{T1Component}
+\newcomponent{5}{0}{T2Component}
+\umlassemblyconnector[interface=T2, distance=7, geometry=|-|, arm1=1cm]{T1Component}{T2Component}
+\umlassemblyconnector[interface=T1, distance=7, geometry=|-|, arm1=-1cm]{T2Component}{T1Component}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/extract_dependencies.sh b/extras/doc/extract_dependencies.sh
new file mode 100755
index 0000000..b4c60f5
--- /dev/null
+++ b/extras/doc/extract_dependencies.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+echo "// Running from dir $PWD"
+echo 'digraph g {'
+for f in $(ls *.cpp *.h)
+do
+ echo "$(echo "$f" | sed 's/\./_/g') [label=\"$f\"]"
+ for g in $(fgrep -l "#include \"$f\"" *)
+ do
+ echo "$g -> $f" | sed 's/\./_/g'
+ done
+done
+echo '}'
diff --git a/extras/doc/foo_handler.tex b/extras/doc/foo_handler.tex
new file mode 100644
index 0000000..8f65620
--- /dev/null
+++ b/extras/doc/foo_handler.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{2.5}{FooHandlerComponent}
+\umlprovidedinterface[interface=FooHandler, distance=3.3, padding=0.9cm]{FooHandlerComponent}
+\umlrequiredinterface[interface={Request, ServerContext}, distance=4.2, padding=0.9cm]{FooHandlerComponent}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/generate_snippets.sh b/extras/doc/generate_snippets.sh
new file mode 100755
index 0000000..bf14662
--- /dev/null
+++ b/extras/doc/generate_snippets.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+for f in *.cpp *.h
+do
+ echo "// $f"
+ cat "$f"
+ echo
+ echo
+done | grep -v '^/\*' | grep -v '^ \*' | grep -v '^ \*/' | grep -v '_H$'
diff --git a/extras/doc/greeter.tex b/extras/doc/greeter.tex
new file mode 100644
index 0000000..689c3a0
--- /dev/null
+++ b/extras/doc/greeter.tex
@@ -0,0 +1,20 @@
+
+\input{header}
+\begin{tikzpicture}[scale=0.71, transform shape]
+
+\begin{component}{20cm}{1}{0}{GreeterComponent}
+\newnamedcomponent{0}{0}{bind1}{bind<Greeter, GreeterImpl>}
+\newcomponent{6}{0}{GreeterImpl}
+\newnamedcomponent{12}{0}{bind2}{bind<Writer, StdoutWriter>}
+\newcomponent{18}{0}{StdoutWriter}
+
+\umlassemblyconnector[interface=StdoutWriter]{bind2}{StdoutWriter}
+\umlassemblyconnector[interface=Writer]{GreeterImpl}{bind2}
+\umlassemblyconnector[interface=GreeterImpl]{bind1}{GreeterImpl}
+\umlprovidedinterface[interface=Greeter, distance=3, padding=0.5cm]{bind1}
+\end{component}
+\umlprovidedinterface[interface=Greeter, distance=13, with port, padding=0.2cm]{GreeterComponent}
+\umlassoc{GreeterComponent-west-port}{bind1-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/incrementer.tex b/extras/doc/incrementer.tex
new file mode 100644
index 0000000..c3599e3
--- /dev/null
+++ b/extras/doc/incrementer.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{2}{0}{IncrementerImplComponent}
+\umlprovidedinterface[interface=Incrementer, distance=4, padding=0.5cm]{IncrementerImplComponent}
+\umlrequiredinterface[interface=Adder, distance=4, padding=0.5cm]{IncrementerImplComponent}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/incrementer_component.tex b/extras/doc/incrementer_component.tex
new file mode 100644
index 0000000..47f5caa
--- /dev/null
+++ b/extras/doc/incrementer_component.tex
@@ -0,0 +1,16 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{IncrementerComponent}
+\newcomponent{0}{0}{SimpleIncrementerComponent}
+\newcomponent{0}{2.5}{CheckedIncrementerComponent}
+\umlprovidedinterface[interface=Incrementer, distance=4, padding=1cm]{SimpleIncrementerComponent}
+\umlprovidedinterface[interface=Incrementer, distance=4, padding=1cm]{CheckedIncrementerComponent}
+\end{component}
+\umlprovidedinterface[interface=Incrementer, distance=5.5, with port]{IncrementerComponent}
+\umldep{IncrementerComponent-west-port}{SimpleIncrementerComponent-west-interface}
+\umldep{IncrementerComponent-west-port}{CheckedIncrementerComponent-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/inject_macro.tex b/extras/doc/inject_macro.tex
new file mode 100644
index 0000000..eb2727d
--- /dev/null
+++ b/extras/doc/inject_macro.tex
@@ -0,0 +1,13 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{GreeterImpl1}{GreeterImpl}
+\newnamedcomponent{0}{0}{GreeterImpl2}{GreeterImpl}
+\umlprovidedinterface[interface=GreeterImpl, distance=2.5, padding=0.9cm]{GreeterImpl1}
+\umlrequiredinterface[interface=Writer, distance=2.5, padding=0.9cm]{GreeterImpl1}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<GreeterImpl>()>, distance=5.5, padding=0.9cm]{GreeterImpl2}
+\umlrequiredinterface[interface=Writer, distance=2.5, padding=0.9cm]{GreeterImpl2}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/inject_macro_no_args.tex b/extras/doc/inject_macro_no_args.tex
new file mode 100644
index 0000000..a4c2e2a
--- /dev/null
+++ b/extras/doc/inject_macro_no_args.tex
@@ -0,0 +1,11 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{StdoutWriter1}{StdoutWriter}
+\newnamedcomponent{0}{0}{StdoutWriter2}{StdoutWriter}
+\umlprovidedinterface[interface=StdoutWriter, distance=3, padding=0.9cm]{StdoutWriter1}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<StdoutWriter>()>, distance=5.8, padding=0.9cm]{StdoutWriter2}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/inject_macro_template.tex b/extras/doc/inject_macro_template.tex
new file mode 100644
index 0000000..8b85725
--- /dev/null
+++ b/extras/doc/inject_macro_template.tex
@@ -0,0 +1,15 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{GreeterImpl1}{GreeterImpl}
+\newnamedcomponent{0}{0}{GreeterImpl2}{GreeterImpl}
+\umlprovidedinterface[interface=GreeterImpl<W>, distance=3.2, padding=0.9cm]{GreeterImpl1}
+\umlrequiredinterface[interface=W, distance=2.5, padding=0.9cm]{GreeterImpl1}
+\node at (4.5,2.8) {(for any type W)};
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<GreeterImpl<W>{}>()>, distance=5.9, padding=0.9cm]{GreeterImpl2}
+\umlrequiredinterface[interface=W, distance=2.5, padding=0.9cm]{GreeterImpl2}
+\node at (4.5,0.3) {(for any type W)};
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/inject_typedef_greeter.tex b/extras/doc/inject_typedef_greeter.tex
new file mode 100644
index 0000000..eb2727d
--- /dev/null
+++ b/extras/doc/inject_typedef_greeter.tex
@@ -0,0 +1,13 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{GreeterImpl1}{GreeterImpl}
+\newnamedcomponent{0}{0}{GreeterImpl2}{GreeterImpl}
+\umlprovidedinterface[interface=GreeterImpl, distance=2.5, padding=0.9cm]{GreeterImpl1}
+\umlrequiredinterface[interface=Writer, distance=2.5, padding=0.9cm]{GreeterImpl1}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<GreeterImpl>()>, distance=5.5, padding=0.9cm]{GreeterImpl2}
+\umlrequiredinterface[interface=Writer, distance=2.5, padding=0.9cm]{GreeterImpl2}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/inject_typedef_templated_constructor.tex b/extras/doc/inject_typedef_templated_constructor.tex
new file mode 100644
index 0000000..eb2727d
--- /dev/null
+++ b/extras/doc/inject_typedef_templated_constructor.tex
@@ -0,0 +1,13 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{GreeterImpl1}{GreeterImpl}
+\newnamedcomponent{0}{0}{GreeterImpl2}{GreeterImpl}
+\umlprovidedinterface[interface=GreeterImpl, distance=2.5, padding=0.9cm]{GreeterImpl1}
+\umlrequiredinterface[interface=Writer, distance=2.5, padding=0.9cm]{GreeterImpl1}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<GreeterImpl>()>, distance=5.5, padding=0.9cm]{GreeterImpl2}
+\umlrequiredinterface[interface=Writer, distance=2.5, padding=0.9cm]{GreeterImpl2}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/inject_typedef_writer.tex b/extras/doc/inject_typedef_writer.tex
new file mode 100644
index 0000000..a4c2e2a
--- /dev/null
+++ b/extras/doc/inject_typedef_writer.tex
@@ -0,0 +1,11 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{StdoutWriter1}{StdoutWriter}
+\newnamedcomponent{0}{0}{StdoutWriter2}{StdoutWriter}
+\umlprovidedinterface[interface=StdoutWriter, distance=3, padding=0.9cm]{StdoutWriter1}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<StdoutWriter>()>, distance=5.8, padding=0.9cm]{StdoutWriter2}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/inject_typedef_writer2.tex b/extras/doc/inject_typedef_writer2.tex
new file mode 100644
index 0000000..a4c2e2a
--- /dev/null
+++ b/extras/doc/inject_typedef_writer2.tex
@@ -0,0 +1,11 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{StdoutWriter1}{StdoutWriter}
+\newnamedcomponent{0}{0}{StdoutWriter2}{StdoutWriter}
+\umlprovidedinterface[interface=StdoutWriter, distance=3, padding=0.9cm]{StdoutWriter1}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<StdoutWriter>()>, distance=5.8, padding=0.9cm]{StdoutWriter2}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/multiplier.tex b/extras/doc/multiplier.tex
new file mode 100644
index 0000000..5bfb236
--- /dev/null
+++ b/extras/doc/multiplier.tex
@@ -0,0 +1,9 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{2}{0}{MultiplierComponent}
+\umlprovidedinterface[interface=Multiplier, distance=3, padding=0.5cm]{MultiplierComponent}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/parametrized_component.tex b/extras/doc/parametrized_component.tex
new file mode 100644
index 0000000..a85663b
--- /dev/null
+++ b/extras/doc/parametrized_component.tex
@@ -0,0 +1,20 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{FooComponent}
+\newnamedcomponent{0}{0}{bind1}{bind}
+\newnamedcomponent{0}{2.5}{bind2}{bind}
+\newcomponent{5}{0}{OldFooImpl}
+\newcomponent{5}{2.5}{NewFooImpl}
+\umlprovidedinterface[interface=FooInterface, distance=2.3, padding=1cm]{bind1}
+\umlprovidedinterface[interface=FooInterface, distance=2.3, padding=1cm]{bind2}
+\umlassemblyconnector[interface=OldFooImpl]{bind1}{OldFooImpl}
+\umlassemblyconnector[interface=NewFooImpl]{bind2}{NewFooImpl}
+\end{component}
+\umlprovidedinterface[interface=FooInterface, distance=6.5, with port]{FooComponent}
+\umldep{FooComponent-west-port}{bind1-west-interface}
+\umldep{FooComponent-west-port}{bind2-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/provider.tex b/extras/doc/provider.tex
new file mode 100644
index 0000000..3709e30
--- /dev/null
+++ b/extras/doc/provider.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{2.5}{registerProvider}
+\umlprovidedinterface[interface=Database, distance=3, padding=0.9cm]{registerProvider}
+\umlrequiredinterface[interface=DbConnectionPool, distance=3.5, padding=0.9cm]{registerProvider}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/provider_functor.tex b/extras/doc/provider_functor.tex
new file mode 100644
index 0000000..543d28f
--- /dev/null
+++ b/extras/doc/provider_functor.tex
@@ -0,0 +1,12 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{0}{registerProvider}
+\newcomponent{0}{3.8}{bindInstance}
+\umlprovidedinterface[interface=MyClass, distance=2.5, padding=0.9cm]{registerProvider}
+\umlassemblyconnector[interface=Functor, geometry=|-|]{registerProvider}{bindInstance}
+\umlrequiredinterface[interface=Foo, distance=2.5, padding=0.9cm]{registerProvider}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/register_constructor.tex b/extras/doc/register_constructor.tex
new file mode 100644
index 0000000..2476b4f
--- /dev/null
+++ b/extras/doc/register_constructor.tex
@@ -0,0 +1,13 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newnamedcomponent{0}{2.5}{registerConstructor1}{registerConstructor}
+\newnamedcomponent{0}{0}{registerConstructor2}{registerConstructor}
+\umlprovidedinterface[interface=GreeterImpl, distance=3.3, padding=0.9cm]{registerConstructor1}
+\umlrequiredinterface[interface=Writer, distance=3, padding=0.9cm]{registerConstructor1}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<GreeterImpl>()>, distance=6, padding=0.9cm]{registerConstructor2}
+\umlrequiredinterface[interface=Writer, distance=3, padding=0.9cm]{registerConstructor2}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/register_constructor_component.tex b/extras/doc/register_constructor_component.tex
new file mode 100644
index 0000000..2970cb8
--- /dev/null
+++ b/extras/doc/register_constructor_component.tex
@@ -0,0 +1,18 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{GreeterComponent}
+\newcomponent{0}{0}{bind}
+\newcomponent{6}{0}{registerConstructor}
+\umlprovidedinterface[interface=Greeter, distance=2, padding=0.9cm]{bind}
+\umlassemblyconnector[interface=GreeterImpl, distance=7]{registerConstructor}{bind}
+\umlrequiredinterface[interface=Writer, distance=3, padding=0.9cm]{registerConstructor}
+\end{component}
+\umlprovidedinterface[interface=Greeter, distance=8, with port]{GreeterComponent}
+\umlrequiredinterface[interface=Writer, distance=8, with port]{GreeterComponent}
+\umlassoc{GreeterComponent-east-port}{registerConstructor-east-interface}
+\umlassoc{GreeterComponent-west-port}{bind-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/register_factory.tex b/extras/doc/register_factory.tex
new file mode 100644
index 0000000..13b97da
--- /dev/null
+++ b/extras/doc/register_factory.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{0}{registerFactory}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<MyClass>(int)>, distance=5.7, padding=0.9cm]{registerFactory}
+\umlrequiredinterface[interface=Foo, distance=2.5, padding=0.9cm]{registerFactory}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/register_factory_macro.tex b/extras/doc/register_factory_macro.tex
new file mode 100644
index 0000000..435bf4c
--- /dev/null
+++ b/extras/doc/register_factory_macro.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{0}{MyClass}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<MyClass>(int)>, distance=5.3, padding=0.9cm]{MyClass}
+\umlrequiredinterface[interface=Foo, distance=2, padding=0.9cm]{MyClass}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/register_factory_typedef.tex b/extras/doc/register_factory_typedef.tex
new file mode 100644
index 0000000..435bf4c
--- /dev/null
+++ b/extras/doc/register_factory_typedef.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{0}{MyClass}
+\umlprovidedinterface[interface=std::function<std::unique\_ptr<MyClass>(int)>, distance=5.3, padding=0.9cm]{MyClass}
+\umlrequiredinterface[interface=Foo, distance=2, padding=0.9cm]{MyClass}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/register_factory_use.tex b/extras/doc/register_factory_use.tex
new file mode 100644
index 0000000..23d4b3f
--- /dev/null
+++ b/extras/doc/register_factory_use.tex
@@ -0,0 +1,10 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{0}{Bar}
+\umlprovidedinterface[interface=Bar, distance=2, padding=0.9cm]{Bar}
+\umlrequiredinterface[interface=std::function<std::unique\_ptr<MyClass>(int)>, distance=5, padding=0.9cm]{Bar}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/request_dispatcher.tex b/extras/doc/request_dispatcher.tex
new file mode 100644
index 0000000..4834410
--- /dev/null
+++ b/extras/doc/request_dispatcher.tex
@@ -0,0 +1,22 @@
+
+\input{header}
+\begin{tikzpicture}[scale=0.7, transform shape]
+
+\begin{component}{20cm}{1}{0}{RequestDispatcherComponent}
+\newcomponent{7}{0}{FooHandlerComponent}
+\umlrequiredinterface[interface={Request, ServerContext}, distance=4.2, padding=1.5cm]{FooHandlerComponent}
+\newcomponent{7}{3}{BarHandlerComponent}
+\umlrequiredinterface[interface={Request, ServerContext}, distance=4.2, padding=1.5cm]{BarHandlerComponent}
+\newcomponent{0}{1.5}{RequestDispatcherImpl}
+\umlprovidedinterface[interface=RequestDispatcher, distance=3.8cm, padding=1.2cm]{RequestDispatcherImpl}
+\umlassemblyconnector[interface=FooHandler]{RequestDispatcherImpl}{FooHandlerComponent}
+\umlassemblyconnector[interface=BarHandler]{RequestDispatcherImpl}{BarHandlerComponent}
+\end{component}
+\umlprovidedinterface[interface=RequestDispatcher, distance=11.1, with port, padding=0cm]{RequestDispatcherComponent}
+\umlrequiredinterface[interface={Request, ServerContext}, distance=11.5, with port]{RequestDispatcherComponent}
+\umldep{RequestDispatcherComponent-west-port}{RequestDispatcherImpl-west-interface}
+\umldep{FooHandlerComponent-east-interface}{RequestDispatcherComponent-east-port}
+\umldep{BarHandlerComponent-east-interface}{RequestDispatcherComponent-east-port}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/request_injector.tex b/extras/doc/request_injector.tex
new file mode 100644
index 0000000..9b44605
--- /dev/null
+++ b/extras/doc/request_injector.tex
@@ -0,0 +1,17 @@
+
+\input{header}
+\begin{tikzpicture}[scale=0.95, transform shape]
+
+\begin{component}{20cm}{1}{0}{injector}
+\newcomponent{0}{1.5}{RequestDispatcherComponent}
+\umlprovidedinterface[interface=RequestDispatcher, distance=4.4cm, padding=1.5cm]{RequestDispatcherComponent}
+\newnamedcomponent{7.5}{0}{bindInstance1}{bindInstance}
+\umlassemblyconnector[interface=ServerContext]{RequestDispatcherComponent}{bindInstance1}
+\newnamedcomponent{7.5}{3}{bindInstance2}{bindInstance}
+\umlassemblyconnector[interface=Request]{RequestDispatcherComponent}{bindInstance2}
+\end{component}
+\umlprovidedinterface[interface=RequestDispatcher, distance=9.7, with port, padding=0cm]{injector}
+\umldep{injector-west-port}{RequestDispatcherComponent-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/scaler.tex b/extras/doc/scaler.tex
new file mode 100644
index 0000000..e25b009
--- /dev/null
+++ b/extras/doc/scaler.tex
@@ -0,0 +1,15 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{ScalerComponent}
+\newcomponent{0}{0}{ScalerImpl}
+\newcomponent{6}{0}{MultiplierComponent}
+\umlprovidedinterface[interface=ScalerFactory, distance=2.5, padding=0.9cm]{ScalerImpl}
+\umlassemblyconnector[interface=Multiplier, distance=6]{ScalerImpl}{MultiplierComponent}
+\end{component}
+\umlprovidedinterface[interface=ScalerFactory, distance=7.5, with port]{ScalerComponent}
+\umlassoc{ScalerComponent-west-port}{ScalerImpl-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/server.tex b/extras/doc/server.tex
new file mode 100644
index 0000000..540f3eb
--- /dev/null
+++ b/extras/doc/server.tex
@@ -0,0 +1,9 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{0}{2.5}{ServerComponent}
+\umlprovidedinterface[interface=Server, distance=2.8, padding=0.9cm]{ServerComponent}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/simple_adder.tex b/extras/doc/simple_adder.tex
new file mode 100644
index 0000000..fa37fea
--- /dev/null
+++ b/extras/doc/simple_adder.tex
@@ -0,0 +1,9 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\newcomponent{2}{0}{SimpleAdderComponent}
+\umlprovidedinterface[interface=Adder, distance=4, padding=0.5cm]{SimpleAdderComponent}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/simple_greeter.tex b/extras/doc/simple_greeter.tex
new file mode 100644
index 0000000..20d0db3
--- /dev/null
+++ b/extras/doc/simple_greeter.tex
@@ -0,0 +1,15 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{GreeterComponent}
+\newcomponent{0}{0}{GreeterImpl}
+\newcomponent{5}{0}{StdoutWriter}
+\umlprovidedinterface[interface=Greeter, distance=2.5, padding=0.5cm]{GreeterImpl}
+\umlassemblyconnector[interface=Writer, distance=5]{GreeterImpl}{StdoutWriter}
+\end{component}
+\umlprovidedinterface[interface=Greeter, distance=6.5, with port]{GreeterComponent}
+\umlassoc{GreeterComponent-west-port}{GreeterImpl-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/simple_incrementer.tex b/extras/doc/simple_incrementer.tex
new file mode 100644
index 0000000..aa8b700
--- /dev/null
+++ b/extras/doc/simple_incrementer.tex
@@ -0,0 +1,15 @@
+
+\input{header}
+\begin{tikzpicture}[scale=1, transform shape]
+
+\begin{component}{20cm}{1}{0}{SimpleIncrementerComponent}
+\newcomponent{0}{0}{IncrementerImplComponent}
+\newcomponent{6}{0}{SimpleAdderComponent}
+\umlprovidedinterface[interface=Incrementer, distance=3.7, padding=0.9cm]{IncrementerImplComponent}
+\umlassemblyconnector[interface=Adder, distance=7]{IncrementerImplComponent}{SimpleAdderComponent}
+\end{component}
+\umlprovidedinterface[interface=Incrementer, distance=8, with port]{SimpleIncrementerComponent}
+\umlassoc{SimpleIncrementerComponent-west-port}{IncrementerImplComponent-west-interface}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/templated_component.tex b/extras/doc/templated_component.tex
new file mode 100644
index 0000000..10fe8c6
--- /dev/null
+++ b/extras/doc/templated_component.tex
@@ -0,0 +1,16 @@
+
+\input{header}
+\begin{tikzpicture}[scale=0.91, transform shape]
+
+\begin{component}{20cm}{1}{0}{FooComponent}
+\newcomponent{0}{0}{bind}
+\newcomponent{5}{0}{FooImpl}
+\umlprovidedinterface[interface={FooInterface<T, std::vector<T>{}>}, distance=4, padding=3.5cm]{bind}
+\umlassemblyconnector[interface=FooImpl<T>]{bind}{FooImpl}
+\end{component}
+\umlprovidedinterface[interface={FooInterface<T, std::vector<T>{}>}, distance=10, with port]{FooComponent}
+\umlassoc{FooComponent-west-port}{bind-west-interface}
+\node at (9.1,0.3) {(for any type T)};
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/dockerfiles/Dockerfile.ubuntu-14.04 b/extras/dockerfiles/Dockerfile.ubuntu-14.04
new file mode 100644
index 0000000..42acb44
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu-14.04
@@ -0,0 +1,9 @@
+FROM ubuntu:14.04
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu-14.04_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu-14.04_install.sh /
+
+RUN bash -x /common_install.sh && \
+ bash -x /ubuntu-14.04_install.sh && \
+ bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/Dockerfile.ubuntu-16.04 b/extras/dockerfiles/Dockerfile.ubuntu-16.04
new file mode 100644
index 0000000..b53b883
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu-16.04
@@ -0,0 +1,9 @@
+FROM ubuntu:16.04
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu-16.04_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu-16.04_install.sh /
+
+RUN bash -x /common_install.sh && \
+ bash -x /ubuntu-16.04_install.sh && \
+ bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/Dockerfile.ubuntu-17.04 b/extras/dockerfiles/Dockerfile.ubuntu-17.04
new file mode 100644
index 0000000..6718523
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu-17.04
@@ -0,0 +1,9 @@
+FROM ubuntu:17.04
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu-17.04_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu-17.04_install.sh /
+
+RUN bash -x /common_install.sh && \
+ bash -x /ubuntu-17.04_install.sh && \
+ bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/Dockerfile.ubuntu-17.10 b/extras/dockerfiles/Dockerfile.ubuntu-17.10
new file mode 100644
index 0000000..3dae283
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu-17.10
@@ -0,0 +1,9 @@
+FROM ubuntu:17.10
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu-17.10_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu-17.10_install.sh /
+
+RUN bash -x /common_install.sh && \
+ bash -x /ubuntu-17.10_install.sh && \
+ bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/common_cleanup.sh b/extras/dockerfiles/common_cleanup.sh
new file mode 100644
index 0000000..a02258a
--- /dev/null
+++ b/extras/dockerfiles/common_cleanup.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -e
+
+# Strip some binaries that aren't already stripped, to save space.
+for f in $(find /usr/lib/ /usr/bin -type f | fgrep -v bazel | fgrep -v python)
+do
+ if file "$f" | fgrep 'executable' | fgrep -q 'stripped'
+ then
+ strip --strip-unneeded $f
+ fi
+done
+
+# This was only needed above, we don't need it in the final image.
+apt-get remove -y wget file python3-pip
+apt-get autoremove -y
+
+# Remove temporary files, to save space.
+apt-get clean
+rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
diff --git a/extras/dockerfiles/common_install.sh b/extras/dockerfiles/common_install.sh
new file mode 100644
index 0000000..8f32478
--- /dev/null
+++ b/extras/dockerfiles/common_install.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+set -e
+
+apt-get update -qq
+apt-get install -y --no-install-recommends wget
+
+wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
+
+# 1E9377A2BA9EF27F is the key for the ubuntu-toolchain-r PPA.
+apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F | cat
+
+# 15CF4D18AF4F7421 is the key for the http://apt.llvm.org/artful PPA.
+apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 15CF4D18AF4F7421 | cat
+
+apt-get update -qq
+apt-get install -y --allow-unauthenticated --no-install-recommends \
+ file \
+ valgrind \
+ make \
+ cmake \
+ libboost-dev \
+ g++-5 \
+ clang-3.8 \
+ clang-3.9 \
+ clang-4.0 \
+ libc++-dev \
+ python3-pip \
+ python3-setuptools \
+ dirmngr
+
+pip3 install --upgrade pip
+pip3 install wheel
+pip3 install pytest
+pip3 install pytest-xdist
+pip3 install sh
diff --git a/extras/dockerfiles/ubuntu-14.04_custom.list b/extras/dockerfiles/ubuntu-14.04_custom.list
new file mode 100644
index 0000000..b45b5b3
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-14.04_custom.list
@@ -0,0 +1,10 @@
+deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main
+deb-src http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main
+deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.7 main
+deb-src http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.7 main
+deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.8 main
+deb-src http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.8 main
+deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main
+deb-src http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main
+deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-4.0 main
+deb-src http://apt.llvm.org/trusty/ llvm-toolchain-trusty-4.0 main
diff --git a/extras/dockerfiles/ubuntu-14.04_install.sh b/extras/dockerfiles/ubuntu-14.04_install.sh
new file mode 100644
index 0000000..edb7825
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-14.04_install.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -e
+
+apt-get install -y --allow-unauthenticated --no-install-recommends \
+ clang-3.5 \
+ clang-3.6 \
+ clang-3.7 \
+ g++-4.9
diff --git a/extras/dockerfiles/ubuntu-16.04_custom.list b/extras/dockerfiles/ubuntu-16.04_custom.list
new file mode 100644
index 0000000..54c95ff
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-16.04_custom.list
@@ -0,0 +1,11 @@
+deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu xenial main
+deb-src http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu xenial main
+deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial main
+deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial main
+deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.8 main
+deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.8 main
+deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main
+deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main
+deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main
+deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main
+deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8
diff --git a/extras/dockerfiles/ubuntu-16.04_install.sh b/extras/dockerfiles/ubuntu-16.04_install.sh
new file mode 100644
index 0000000..1452630
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-16.04_install.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+apt-get install -y --no-install-recommends \
+ curl
+
+# For the Bazel repository
+curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
+
+apt-get install -y --allow-unauthenticated --no-install-recommends \
+ clang-3.5 \
+ clang-3.6 \
+ clang-3.7 \
+ g++-4.9 \
+ g++-6 \
+ python \
+ bazel \
+ openjdk-8-jdk \
+ clang-format
+
+pip3 install typed_ast
diff --git a/extras/dockerfiles/ubuntu-17.04_custom.list b/extras/dockerfiles/ubuntu-17.04_custom.list
new file mode 100644
index 0000000..dfafeb4
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-17.04_custom.list
@@ -0,0 +1,8 @@
+deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu zesty main
+deb-src http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu zesty main
+deb http://apt.llvm.org/zesty/ llvm-toolchain-zesty main
+deb-src http://apt.llvm.org/zesty/ llvm-toolchain-zesty main
+deb http://apt.llvm.org/zesty/ llvm-toolchain-zesty-3.9 main
+deb-src http://apt.llvm.org/zesty/ llvm-toolchain-zesty-3.9 main
+deb http://apt.llvm.org/zesty/ llvm-toolchain-zesty-4.0 main
+deb-src http://apt.llvm.org/zesty/ llvm-toolchain-zesty-4.0 main
diff --git a/extras/dockerfiles/ubuntu-17.04_install.sh b/extras/dockerfiles/ubuntu-17.04_install.sh
new file mode 100644
index 0000000..afa7b5e
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-17.04_install.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F
+
+apt-get install -y --allow-unauthenticated --no-install-recommends \
+ clang-3.7 \
+ g++-4.9 \
+ g++-6 \
+ python \
+ clang-format
+
+pip3 install typed_ast
diff --git a/extras/dockerfiles/ubuntu-17.10_custom.list b/extras/dockerfiles/ubuntu-17.10_custom.list
new file mode 100644
index 0000000..e72d24a
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-17.10_custom.list
@@ -0,0 +1,8 @@
+deb [trusted=yes] http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu artful main
+deb-src [trusted=yes] http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu artful main
+deb [trusted=yes] http://apt.llvm.org/artful/ llvm-toolchain-artful main
+deb-src [trusted=yes] http://apt.llvm.org/artful/ llvm-toolchain-artful main
+deb [trusted=yes] http://apt.llvm.org/artful/ llvm-toolchain-artful-4.0 main
+deb-src [trusted=yes] http://apt.llvm.org/artful/ llvm-toolchain-artful-4.0 main
+deb [trusted=yes] http://apt.llvm.org/artful/ llvm-toolchain-artful-5.0 main
+deb-src [trusted=yes] http://apt.llvm.org/artful/ llvm-toolchain-artful-5.0 main
diff --git a/extras/dockerfiles/ubuntu-17.10_install.sh b/extras/dockerfiles/ubuntu-17.10_install.sh
new file mode 100644
index 0000000..36fd97e
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-17.10_install.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F
+
+apt-get install -y --allow-unauthenticated --no-install-recommends \
+ g++-7 \
+ python \
+ python3-sh \
+ python3-typed-ast \
+ clang-format
+
+pip3 install typed_ast
diff --git a/extras/fruit-2.0.0.ebuild b/extras/fruit-2.0.0.ebuild
new file mode 100644
index 0000000..da476a9
--- /dev/null
+++ b/extras/fruit-2.0.0.ebuild
@@ -0,0 +1,28 @@
+
+# Copyright 2014 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# $Header: $
+
+EAPI=4
+inherit cmake-utils
+
+DESCRIPTION="Dependency Injection Framework For C++"
+HOMEPAGE="https://github.com/google/fruit"
+SRC_URI="https://github.com/google/fruit/archive/v${PV}.tar.gz"
+
+LICENSE="Apache-2.0"
+SLOT="0"
+KEYWORDS="amd64 ~x86"
+IUSE=""
diff --git a/extras/git_hooks/pre-commit b/extras/git_hooks/pre-commit
new file mode 100755
index 0000000..faf6ee0
--- /dev/null
+++ b/extras/git_hooks/pre-commit
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+check_travis_yml() {
+ X=`mktemp`
+ if ! (extras/scripts/travis_yml_generator.py >$X && cmp -s $X .travis.yml)
+ then
+ echo 'The .travis.yml file is out of date, run "extras/scripts/travis_yml_generator.py >.travis.yml" and try again.'
+ return 1
+ fi
+}
+
+check_travis_yml || exit 1
diff --git a/extras/packaging/CMakeLists.txt b/extras/packaging/CMakeLists.txt
new file mode 100644
index 0000000..7592bf0
--- /dev/null
+++ b/extras/packaging/CMakeLists.txt
@@ -0,0 +1,24 @@
+
+set(PACKAGING_FILES
+debian.control
+debian.rules
+debian.compat
+libfruit.dsc
+libfruit.install
+libfruit.spec
+)
+
+# This places configured files (build files with @FRUIT_VERSION@ replaced) in build/extras/packaging/built
+
+foreach(F ${PACKAGING_FILES})
+ configure_file(${F} built/${F} @ONLY)
+endforeach(F)
+
+configure_file(PKGBUILD PKGBUILD-template @ONLY)
+
+add_custom_target(fruit-${FRUIT_VERSION}.tar.gz ALL
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/PKGBUILD-template
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../..
+ COMMAND git archive -o ${CMAKE_CURRENT_BINARY_DIR}/built/fruit-${FRUIT_VERSION}.tar.gz --prefix=fruit-${FRUIT_VERSION}/ HEAD
+ COMMAND md5sum ${CMAKE_CURRENT_BINARY_DIR}/built/fruit-${FRUIT_VERSION}.tar.gz | awk '{print $$1}' >${CMAKE_CURRENT_BINARY_DIR}/tarball-md5
+ COMMAND sed "\"s/.*md5sums.*/md5sums=(`cat" "${CMAKE_CURRENT_BINARY_DIR}/tarball-md5`)/\"" <${CMAKE_CURRENT_BINARY_DIR}/PKGBUILD-template >${CMAKE_CURRENT_BINARY_DIR}/built/PKGBUILD)
diff --git a/extras/packaging/PKGBUILD b/extras/packaging/PKGBUILD
new file mode 100644
index 0000000..b7b06cc
--- /dev/null
+++ b/extras/packaging/PKGBUILD
@@ -0,0 +1,30 @@
+# Maintainer: Marco Poletti <poletti.marco@gmail.com>
+pkgname=libfruit
+pkgver=@FRUIT_VERSION@
+pkgrel=0
+pkgdesc="Fruit is a dependency injection framework for C++."
+url="https://github.com/google/fruit"
+arch=('x86_64' 'i686')
+license=('Apache')
+depends=('cmake' 'boost' 'gcc>=5.0.0')
+optdepends=()
+makedepends=()
+conflicts=()
+replaces=()
+backup=()
+install='libfruit.install'
+source=("fruit-${pkgver}.tar.gz")
+md5sums=('d66cc12861e105cc38e5b878776f9f79')
+
+build() {
+ cd "${srcdir}/fruit-${pkgver}"
+ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo .
+ make
+}
+
+package() {
+ cd "${srcdir}/fruit-${pkgver}"
+ make DESTDIR="${pkgdir}" install
+}
+
+# vim:set ts=2 sw=2 et: \ No newline at end of file
diff --git a/extras/packaging/debian.compat b/extras/packaging/debian.compat
new file mode 100644
index 0000000..7813681
--- /dev/null
+++ b/extras/packaging/debian.compat
@@ -0,0 +1 @@
+5 \ No newline at end of file
diff --git a/extras/packaging/debian.control b/extras/packaging/debian.control
new file mode 100644
index 0000000..6fb8f7a
--- /dev/null
+++ b/extras/packaging/debian.control
@@ -0,0 +1,23 @@
+Source: libfruit
+Section: devel
+Priority: optional
+Maintainer: Marco Poletti <poletti.marco@gmail.com>
+Build-Depends: debhelper (>= 4.1.16), cmake, libboost-dev, gcc (>= 4:5.0.0)
+
+Package: libfruit
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: Dependency Injection Framework For C++
+ Fruit is a dependency injection framework for C++, loosely inspired by the
+ Guice framework for Java.
+ It uses C++ metaprogramming together with some new C++11 features to detect
+ most injection problems at compile-time.
+
+Package: fruit-dev
+Architecture: any
+Depends: ${shlibs:Depends}, libfruit
+Description: Dependency Injection Framework For C++ - Development files
+ Fruit is a dependency injection framework for C++, loosely inspired by the
+ Guice framework for Java.
+ It uses C++ metaprogramming together with some new C++11 features to detect
+ most injection problems at compile-time.
diff --git a/extras/packaging/debian.rules b/extras/packaging/debian.rules
new file mode 100644
index 0000000..6ce5073
--- /dev/null
+++ b/extras/packaging/debian.rules
@@ -0,0 +1,74 @@
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+CFLAGS = -g
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+CFLAGS += -O0
+else
+CFLAGS += -O2
+endif
+
+build: build-stamp
+build-stamp:
+ dh_testdir
+
+ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ make all
+
+ touch build-stamp
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp
+
+ make clean || true
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ make install DESTDIR=/usr/src/packages/BUILD/debian/libfruit
+
+# Build architecture-independent files here.
+binary-indep: build install
+ # We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+# dh_installdebconf
+ dh_installdocs
+ dh_installexamples
+ dh_installmenu
+# dh_installlogrotate
+# dh_installemacsen
+# dh_installpam
+# dh_installmime
+# dh_installinit
+ dh_installcron
+ dh_installman
+ dh_installinfo
+# dh_undocumented
+ dh_installchangelogs
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+# dh_makeshlibs
+ dh_installdeb
+# dh_perl
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install \ No newline at end of file
diff --git a/extras/packaging/libfruit.dsc b/extras/packaging/libfruit.dsc
new file mode 100644
index 0000000..07bd92e
--- /dev/null
+++ b/extras/packaging/libfruit.dsc
@@ -0,0 +1,10 @@
+Format: 1.0
+Source: libfruit
+Version: @FRUIT_VERSION@-0
+Binary: libfruit
+Maintainer: Marco Poletti <poletti.marco@gmail.com>
+Architecture: any
+Build-Depends: debhelper (>= 4.1.16), cmake, libboost-dev, gcc (>= 4:5.0.0)
+Files:
+ d57283ebb8157ae919762c58419353c8 133282 libfruit_@FRUIT_VERSION@.orig.tar.gz
+ 2fecf324a32123b08cefc0f047bca5ee 63176 libfruit_@FRUIT_VERSION@-0.diff.tar.gz \ No newline at end of file
diff --git a/extras/packaging/libfruit.install b/extras/packaging/libfruit.install
new file mode 100644
index 0000000..bb7b160
--- /dev/null
+++ b/extras/packaging/libfruit.install
@@ -0,0 +1 @@
+# Intentionally empty
diff --git a/extras/packaging/libfruit.spec b/extras/packaging/libfruit.spec
new file mode 100644
index 0000000..b84e178
--- /dev/null
+++ b/extras/packaging/libfruit.spec
@@ -0,0 +1,64 @@
+#
+# spec file for package fruit
+#
+
+Name: libfruit
+Version: @FRUIT_VERSION@
+Release: 0
+Summary: Dependency Injection Framework For C++
+License: Apache-2.0
+Group: Development/Libraries/C and C++
+Url: https://github.com/google/fruit
+Source0: fruit-%{version}.tar.gz
+
+BuildRequires: cmake
+BuildRequires: boost-devel
+
+%if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version}
+BuildRequires: gcc-c++ >= 5.0.0
+%else
+# OpenSUSE doesn't include the bugfix release version component in the package version.
+BuildRequires: gcc-c++ >= 5.0
+%endif
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+%description
+Fruit is a dependency injection framework for C++, loosely inspired by the
+Guice framework for Java.
+It uses C++ metaprogramming together with some new C++11 features to detect
+most injection problems at compile-time.
+
+%package devel
+Summary: Dependency Injection Framework For C++ - Development Files
+License: Apache-2.0
+Group: Development/Libraries/C and C++
+Url: https://github.com/google/fruit
+Requires: libfruit = %{version}
+
+%description devel
+Fruit is a dependency injection framework for C++, loosely inspired by the
+Guice framework for Java.
+It uses C++ metaprogramming together with some new C++11 features to detect
+most injection problems at compile-time.
+
+%prep
+%setup -q -n fruit-%{version}
+
+%build
+cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} -DINSTALL_LIBRARY_DIR=%{_libdir} -DCMAKE_BUILD_TYPE=RelWithDebInfo
+
+%{__make} %{?jobs:-j%jobs}
+
+%install
+%{__make} DESTDIR=%{buildroot} install
+
+%files
+%defattr(-,root,root)
+%{_libdir}/libfruit.*
+
+%files devel
+%defattr(-,root,root)
+%{_includedir}/fruit
+
+%changelog
diff --git a/extras/scripts/analyze_template_instantiations_clang_diagnostics.py b/extras/scripts/analyze_template_instantiations_clang_diagnostics.py
new file mode 100755
index 0000000..3e661c8
--- /dev/null
+++ b/extras/scripts/analyze_template_instantiations_clang_diagnostics.py
@@ -0,0 +1,380 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from concurrent import futures
+
+import itertools
+import sys
+import re
+import pygraphviz as gv
+import ply.lex as lex
+import ply.yacc as yacc
+from functools import lru_cache as memoize
+
+diagnostic_header_pattern = re.compile('[^ ]+\.[^ ]+:[0-9]+:[0-9]+: ([^ ]*): (.*)')
+in_file_included_from_pattern = re.compile('In file included from .*:')
+in_instantiation_of_template_pattern = re.compile('in instantiation of (.*) (?:requested|required) here')
+static_warning_marked_deprecated_here_pattern = re.compile('\'static_warning\' has been explicitly marked deprecated here')
+
+class Diagnostic:
+ def __init__(self, kind, message):
+ self.kind = kind
+ self.message = message
+ self.template_instantiation_trace = []
+
+tokens = (
+ 'LPAREN',
+ 'RPAREN',
+ 'LBRACKET',
+ 'RBRACKET',
+ 'LBRACE',
+ 'RBRACE',
+ 'LESS_THAN',
+ 'GREATER_THAN',
+ 'DOUBLE_COLON',
+ 'COMMA',
+ 'IDENTIFIER',
+ 'ASTERISK',
+ 'AMPERSAND',
+)
+
+t_LPAREN = r'\('
+t_RPAREN = r'\)'
+t_LBRACKET = r'\['
+t_RBRACKET = r'\]'
+t_LBRACE = r'}'
+t_RBRACE = r'{'
+t_LESS_THAN = r'<'
+t_GREATER_THAN = r'>'
+t_DOUBLE_COLON = r'::'
+t_COMMA = r','
+t_ASTERISK = r'\*'
+t_AMPERSAND = r'&'
+# We conflate numbers as identifiers too, we don't care about the difference.
+t_IDENTIFIER = r'[a-zA-Z0-9_]+'
+
+t_ignore = ' \t'
+
+def t_error(t):
+ raise Exception("Illegal character '%s' followed by %s" % (t.value[0], t.value[1:]))
+
+class LayoutNeedsMultipleLinesException(Exception):
+ pass
+
+class AstNode:
+ def __str__(self):
+ return ''.join(self)
+
+class TerminalAstNode(AstNode):
+ def __init__(self, s):
+ self.s = s
+ self.is_multiline = (s == '\n')
+ # last_line_length is the string length if s is not a multiline string.
+ # For multiline strings ending in a newline, this is 0.
+ if self.is_multiline:
+ self.first_line_length = 0
+ self.last_line_length = 0
+ self.max_line_length = 0
+ else:
+ # This never happens ATM, so we don't handle it.
+ assert not '\n' in s
+
+ self.first_line_length = len(s)
+ self.last_line_length = len(s)
+ self.max_line_length = len(s)
+
+ def __iter__(self):
+ return iter((self.s,))
+
+class NonTerminalAstNode(AstNode):
+ def __init__(self, children_ast_nodes):
+ self.children_ast_nodes = children_ast_nodes
+ first_line_length = 0
+ last_line_length = 0
+ is_multiline = False
+ max_line_length = 0
+ for node in children_ast_nodes:
+ if node.is_multiline:
+ last_line_length = node.last_line_length
+ max_line_length = max(max_line_length, last_line_length + node.first_line_length, node.max_line_length)
+ is_multiline = True
+ else:
+ last_line_length += node.last_line_length
+ max_line_length = max(max_line_length, last_line_length)
+
+ self.first_line_length = first_line_length
+ self.last_line_length = last_line_length
+ self.is_multiline = is_multiline
+ self.max_line_length = max_line_length
+
+ def __iter__(self):
+ return itertools.chain(*self.children_ast_nodes)
+
+max_line_length = 80
+# Size of an indent in spaces.
+single_indent_length = 4
+
+class TerminalNodeFactory():
+ def __init__(self, s):
+ self.s = s
+
+ def __call__(self, current_indent, current_line_length, inside_meta_type, last_token_was_type_wrapper, accept_single_line_only):
+ return TerminalAstNode(self.s)
+
+# 'balanced_string' nodes evaluate to a function (or a callable object) taking these parameters:
+# current_indent (integer): the indentation in the current line (spaces only)
+# current_line_length (integer): the number of preceding characters in the current line (>=current_indent)
+# inside_meta_type (boolean): whether we're inside a Type<...>
+# last_token_was_type_wrapper (boolean): whether the immediately-preceding token was the identifier 'Type'
+# and returning an AstNode
+# 'comma_separated_balanced_string' nodes evaluate to a tuple of such functions
+
+def p_comma_separated_balanced_string_empty(p):
+ 'comma_separated_balanced_string : '
+ p[0] = tuple()
+
+def p_comma_separated_balanced_string_not_empty(p):
+ 'comma_separated_balanced_string : COMMA balanced_string comma_separated_balanced_string'
+ p[0] = (
+ p[2],
+ *(p[3])
+ )
+
+def p_optional_balanced_string_empty(p):
+ 'optional_balanced_string : '
+ p[0] = TerminalNodeFactory('')
+
+def p_optional_balanced_string_not_empty(p):
+ 'optional_balanced_string : balanced_string'
+ p[0] = p[1]
+
+class BalancedStringTerminalNodeFactory():
+ def __init__(self, first_token, node_factory):
+ self.first_token = first_token
+ self.node_factory = node_factory
+
+ def __call__(self, current_indent, current_line_length, inside_meta_type, last_token_was_type_wrapper, accept_single_line_only):
+ terminal_node = TerminalAstNode(self.first_token)
+ non_terminal_node = self.node_factory(
+ current_indent,
+ current_line_length + len(self.first_token),
+ inside_meta_type,
+ self.first_token == 'Type',
+ accept_single_line_only)
+ if non_terminal_node is None:
+ return None
+ return NonTerminalAstNode((terminal_node, non_terminal_node))
+
+def p_balanced_string_terminal(p):
+ '''balanced_string : DOUBLE_COLON balanced_string
+ | IDENTIFIER optional_balanced_string
+ | ASTERISK optional_balanced_string
+ | AMPERSAND optional_balanced_string
+ '''
+ first_token = p[1]
+ node_factory = p[2]
+
+ p[0] = BalancedStringTerminalNodeFactory(first_token, node_factory)
+
+def create_composite_node_from_factories(node_factory_inside_meta_type_pairs, current_line_length, accept_single_line_only):
+ nodes = []
+ for node_factory, current_indent, inside_meta_type in node_factory_inside_meta_type_pairs:
+ node = node_factory(current_indent, current_line_length, inside_meta_type, False, accept_single_line_only)
+ if node is None:
+ return None
+ nodes.append(node)
+ if node.is_multiline:
+ if accept_single_line_only:
+ raise Exception('Unexpected multiline, due to factory: ' + node_factory)
+ # Note that due to the way we break lines, the last line will have the same indent as the first.
+ # So we don't need to update current_indent here.
+ current_line_length = node.last_line_length
+ else:
+ current_line_length += node.last_line_length
+ return NonTerminalAstNode(nodes)
+
+def compute_layout(left_token, intermediate_node_factories, right_token, rhs_node_factory, current_indent, current_line_length, inside_meta_type, last_token_was_type_wrapper, accept_single_line_only):
+ # We lay out the result in one of two ways:
+ #
+ # $previousIndent $previousContent LPAREN x1, x2, x3 RPAREN balanced_string
+ #
+ # Or:
+ #
+ # $previousIndent $previousContent LPAREN
+ # $previousIndent $indent x1 ,
+ # $previousIndent $indent x2 ,
+ # $previousIndent $indent x3 RPAREN balanced_string
+
+ entering_meta_type = last_token_was_type_wrapper
+
+ # First, we try to use the first format if possible
+ node_factory_inside_meta_type_pairs = [
+ (TerminalNodeFactory(left_token), current_indent, inside_meta_type),
+ *((intermediate_node_factory, current_indent, (inside_meta_type or entering_meta_type))
+ for intermediate_node_factory in intermediate_node_factories),
+ (TerminalNodeFactory(right_token), current_indent, inside_meta_type),
+ (rhs_node_factory, current_indent, inside_meta_type),
+ ]
+ node_with_single_line_layout = create_composite_node_from_factories(node_factory_inside_meta_type_pairs, current_line_length, True)
+ if node_with_single_line_layout is not None and node_with_single_line_layout.max_line_length <= max_line_length:
+ assert not node_with_single_line_layout.is_multiline
+ return node_with_single_line_layout
+
+ if accept_single_line_only:
+ return None
+
+ # The result exceeds the line length, let's switch to the second one.
+ node_factory_inside_meta_type_pairs = [
+ (TerminalNodeFactory(left_token),
+ current_indent,
+ inside_meta_type)
+ ]
+ new_indent_length = current_indent + single_indent_length
+ comma_node_factory_inside_meta_type_pair = (TerminalNodeFactory(','), current_indent, inside_meta_type or entering_meta_type)
+ newline_node_factory_inside_meta_type_pair = (TerminalNodeFactory('\n'), current_indent, inside_meta_type or entering_meta_type)
+ indent_node_factory_inside_meta_type_pair = (TerminalNodeFactory(' ' * new_indent_length), current_indent, inside_meta_type or entering_meta_type)
+ for inner_node_factory in intermediate_node_factories:
+ node_factory_inside_meta_type_pairs.append(newline_node_factory_inside_meta_type_pair)
+ node_factory_inside_meta_type_pairs.append(indent_node_factory_inside_meta_type_pair)
+ node_factory_inside_meta_type_pairs.append((inner_node_factory, new_indent_length, inside_meta_type or entering_meta_type))
+ node_factory_inside_meta_type_pairs.append(comma_node_factory_inside_meta_type_pair)
+ node_factory_inside_meta_type_pairs.pop()
+ node_factory_inside_meta_type_pairs.append((TerminalNodeFactory(right_token), current_indent, inside_meta_type))
+ node_factory_inside_meta_type_pairs.append((rhs_node_factory, current_indent, inside_meta_type))
+ return create_composite_node_from_factories(node_factory_inside_meta_type_pairs, current_line_length, accept_single_line_only)
+
+
+def p_balanced_string_with_balanced_token_no_comma_separated_elems(p):
+ '''balanced_string : LPAREN RPAREN optional_balanced_string
+ | LBRACKET RBRACKET optional_balanced_string
+ | LBRACE RBRACE optional_balanced_string
+ | LESS_THAN GREATER_THAN optional_balanced_string
+ '''
+ p_1 = p[1]
+ p_2 = p[2]
+ p_3 = p[3]
+ def result(current_indent, current_line_length, inside_meta_type, last_token_was_type_wrapper, accept_single_line_only):
+ return compute_layout(p_1, [], p_2, p_3, current_indent, current_line_length, inside_meta_type, last_token_was_type_wrapper, accept_single_line_only)
+
+ p[0] = result
+
+def p_balanced_string_with_balanced_token_some_comma_separated_elems(p):
+ '''balanced_string : LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ | LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ | LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ | LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+ '''
+ p_1 = p[1]
+ p_2 = p[2]
+ p_3 = p[3]
+ p_4 = p[4]
+ p_5 = p[5]
+ def result(current_indent, current_line_length, inside_meta_type, last_token_was_type_wrapper, accept_single_line_only):
+ if not inside_meta_type:
+ if p_1 == '(' and p_4 == ')':
+ if len(p_3) == 0:
+ if isinstance(p_2, BalancedStringTerminalNodeFactory) and p_2.first_token == '*':
+ if isinstance(p_2.node_factory, TerminalNodeFactory) and p_2.node_factory.s == '':
+ # Special case: we're not inside a Type<...> and we've encountered a '(*)'.
+ # Discard it and just print the rhs.
+ return p_5(current_indent, current_line_length, inside_meta_type, False, accept_single_line_only)
+
+ return compute_layout(p_1, (p_2, *(p_3)), p_4, p_5, current_indent, current_line_length, inside_meta_type, last_token_was_type_wrapper, accept_single_line_only)
+
+ p[0] = result
+
+def p_error(p):
+ raise Exception("Syntax error when parsing meta type: ", p[:])
+
+lexer = lex.lex()
+parser = yacc.yacc(start='balanced_string')
+
+strings_to_remove = re.compile(r'template class |template type alias |function template specialization |member class |member function |default argument for |fruit::impl::meta::|fruit::impl::|fruit::')
+
+def do_simplify_template_trace_element(element):
+ element, _ = re.subn(strings_to_remove, '', element)
+ element = element.strip()
+ if element[0] != '\'' or element[-1] != '\'':
+ raise Exception('Expected single quotes in: ' + element)
+ element = element[1:-1]
+ if element.startswith('DoEval<') and element[-1] == '>':
+ element = element[7:-1]
+ result = ''.join(parser.parse(element, lexer)(0, 0, False, False, False))
+ return result
+
+@memoize(maxsize=1000)
+def simplify_template_trace_element(element, executor):
+ return executor.submit(do_simplify_template_trace_element, element)
+
+def to_dot_left_justified_string(s):
+ return '\\l'.join(s.splitlines() + [''])
+
+def main():
+ diagnostics = []
+
+ with futures.ProcessPoolExecutor() as executor:
+ lines = sys.stdin.readlines()
+ for line_number, line in enumerate(lines):
+ # Remove the newline
+ line = line[:-1]
+
+ matches = in_file_included_from_pattern.search(line)
+ if matches:
+ continue
+
+ matches = diagnostic_header_pattern.search(line)
+ if matches:
+ diagnostic_kind, diagnostic_message = matches.groups()
+ if diagnostic_kind == 'error':
+ diagnostics.append(Diagnostic(diagnostic_kind, diagnostic_message))
+ print('Processing diagnostic. (%s / %s) ' % (line_number, len(lines)), file=sys.stderr)
+ elif diagnostic_kind == 'note':
+ matches = in_instantiation_of_template_pattern.search(diagnostic_message)
+ if matches:
+ if not diagnostics:
+ raise Exception('Found template instantiation note before any error diagnostic: %s' % diagnostic_message)
+ if 'in instantiation of template type alias' in line:
+ pass
+ else:
+ group = matches.groups()[0]
+ trace_element_future = simplify_template_trace_element(group, executor)
+ diagnostics[-1].template_instantiation_trace.append(trace_element_future)
+ continue
+
+ matches = static_warning_marked_deprecated_here_pattern.search(diagnostic_message)
+ if matches:
+ continue
+
+ raise Exception('Found unknown note: %s' % diagnostic_message)
+
+ call_graph = {}
+ graph = gv.AGraph(directed=True)
+
+ for diagnostic_index, diagnostic in enumerate(diagnostics):
+ if diagnostic_index % 10 == 0:
+ print('Constructing dep graph: iteration %s/%s' % (diagnostic_index, len(diagnostics)), file=sys.stderr)
+
+ template_instantiation_trace = [trace_element_future.result() for trace_element_future in diagnostic.template_instantiation_trace]
+ for called, caller in zip(template_instantiation_trace[1:], template_instantiation_trace[2:]):
+ if called in call_graph and call_graph[called] != caller:
+ # Avoid this edge, so that the resulting graph is a tree
+ continue
+ graph.add_edge(to_dot_left_justified_string(caller), to_dot_left_justified_string(called))
+ call_graph[called] = caller
+
+ print(graph)
+
+if __name__ == '__main__':
+ main()
diff --git a/extras/scripts/filter_gcc_error.sh b/extras/scripts/filter_gcc_error.sh
new file mode 100755
index 0000000..2fecab6
--- /dev/null
+++ b/extras/scripts/filter_gcc_error.sh
@@ -0,0 +1,130 @@
+#!/bin/bash
+
+export CurPos=0
+export TOKENS=()
+
+mergeInSingleLine() {
+ OPENING_TOKEN="${TOKENS[$CurPos]}"
+ echo -n "${OPENING_TOKEN}"
+ CurPos=$[$CurPos + 1]
+ if ! [[ "${OPENING_TOKEN}" =~ \< ]] && ! [[ "${OPENING_TOKEN}" =~ \( ]]
+ then
+ # This is not an opening token, it's an atomic component.
+ return
+ fi
+ while true
+ do
+ if ! [[ "${TOKENS[$CurPos]}" =~ \> ]] && ! [[ "${TOKENS[$CurPos]}" =~ \) ]]
+ then
+ mergeInSingleLine
+ else
+ if [[ "${TOKENS[$CurPos]}" =~ \< ]] || [[ "${TOKENS[$CurPos]}" =~ \( ]]
+ then
+ # Both an opening and a closing token, print it and go ahead.
+ echo -n "${TOKENS[$CurPos]}"
+ CurPos=$[$CurPos + 1]
+ else
+ break
+ fi
+ fi
+ done
+ echo -n "${TOKENS[CurPos]}"
+ CurPos=$[$CurPos + 1]
+}
+
+considerMerging() {
+ OPENING_TOKEN="${TOKENS[CurPos]}"
+ if ! [[ "${OPENING_TOKEN}" =~ \< ]] && ! [[ "${OPENING_TOKEN}" =~ \( ]]
+ then
+ # This is not an opening token, it's an atomic component.
+ echo "${OPENING_TOKEN}"
+ CurPos=$[$CurPos + 1]
+ return;
+ fi
+ if [[ "${OPENING_TOKEN}" = "Type<" ]]
+ then
+ # Type<...> should be on a single line.
+ mergeInSingleLine
+ echo
+ return;
+ fi
+ echo "${OPENING_TOKEN}"
+ CurPos=$[$CurPos + 1]
+ while true
+ do
+ if ! [[ "${TOKENS[$CurPos]}" =~ \> ]] && ! [[ "${TOKENS[$CurPos]}" =~ \) ]]
+ then
+ considerMerging
+ else
+ if [[ "${TOKENS[$CurPos]}" =~ \< ]] || [[ "${TOKENS[$CurPos]}" =~ \( ]]
+ then
+ # Both an opening and a closing token, print it and go ahead.
+ echo "${TOKENS[$CurPos]}"
+ CurPos=$[$CurPos + 1]
+ else
+ break
+ fi
+ fi
+ done
+ echo "${TOKENS[$CurPos]}"
+ CurPos=$[$CurPos + 1]
+}
+
+while read line
+do
+ if [[ "${line}" =~ required\ from\ .*DoEval\< ]]
+ then
+ echo
+ IFS=$'\r\n' GLOBIGNORE='*' :;
+ TOKENS=($(echo "${line}" |
+ sed 's| (\*)||g;
+ s|.* required from .*DoEval<||;
+ s|>.$||;
+ s/fruit::impl::meta:://g;
+ s| >|>|g;
+ s|, |,|g' |
+ sed 's|[>]|,>|g;
+ s|[)]|,)|g' |
+ sed 's|[<]|<\n|g;
+ s|[(]|(\n|g;
+ s|[,]|,\n|g' |
+ grep -v "^,$" ))
+ considerMerging |
+ sed 's|,>|>|g;
+ s|,)|)|g' |
+ awk -F@ '/^[^()<>]*[>)][^()<>]*[(<][^()<>]*$/ {curIndent-=2; for (i=0; i<curIndent; i++) { printf(" "); } print; curIndent+=2; next;}
+ /[()<>].*[()<>]/ || !/[()<>]/ {for (i=0; i<curIndent; i++) { printf(" "); } print; next;}
+ /[(<]/ { for (i=0; i<curIndent; i++) { printf(" "); } print; curIndent+=2; next; }
+ /[)>]/ || /^[^()<>]*>/ { curIndent-=2; for (i=0; i<curIndent; i++) { printf(" "); } print; next; }
+ '
+ elif [[ "${line}" =~ required\ from\ .*EvalFun\< ]]
+ then
+ echo
+ IFS=$'\r\n' GLOBIGNORE='*' :;
+ TOKENS=($(echo "${line}" |
+ sed 's|,|(|' |
+ sed 's| (\*)||g;
+ s|.* required from .*EvalFun<||;
+ s|>.$||;
+ s/fruit::impl::meta:://g;
+ s| >|>|g;
+ s|, |,|g' |
+ sed 's|[>]|,>|g;
+ s|[)]|,)|g' |
+ sed 's|[<]|<\n|g;
+ s|[(]|(\n|g;
+ s|[,]|,\n|g' |
+ grep -v "^,$";
+ echo ')'))
+ considerMerging |
+ sed 's|,>|>|g;
+ s|,)|)|g' |
+ awk -F@ '/^[^()<>]*[>)][^()<>]*[(<][^()<>]*$/ {curIndent-=2; for (i=0; i<curIndent; i++) { printf(" "); } print; curIndent+=2; next;}
+ /[()<>].*[()<>]/ || !/[()<>]/ {for (i=0; i<curIndent; i++) { printf(" "); } print; next;}
+ /[(<]/ { for (i=0; i<curIndent; i++) { printf(" "); } print; curIndent+=2; next; }
+ /[)>]/ || /^[^()<>]*>/ { curIndent-=2; for (i=0; i<curIndent; i++) { printf(" "); } print; next; }
+ '
+ else
+ echo "${line}"
+ fi
+done | sed 's/fruit::impl::meta:://g;s/struct //g' | grep -v "required from .EvalIf<"
diff --git a/extras/scripts/find_untested_types.sh b/extras/scripts/find_untested_types.sh
new file mode 100755
index 0000000..11f31eb
--- /dev/null
+++ b/extras/scripts/find_untested_types.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+F=$(mktemp)
+
+find include/fruit/impl/ -type f | fgrep -v 'component_functors.defn.h' | xargs cat \
+| egrep "^(struct|class) [A-Za-z0-9_]* " \
+| sed -r 's/struct ([A-Za-z0-9_]*).*/\1/' \
+| egrep -v 'Error(Tag)?$' | fgrep -v Helper \
+| sort | uniq \
+>$F
+
+for t in `cat $F`
+do
+ egrep -qR "[^a-zA-Z0-9]$t[^a-zA-Z0-9]" tests/ || echo $t
+done
diff --git a/extras/scripts/find_unused_types.sh b/extras/scripts/find_unused_types.sh
new file mode 100755
index 0000000..b14c1d0
--- /dev/null
+++ b/extras/scripts/find_unused_types.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# This script greps the source code trying to find unused types.
+
+TYPES=($(egrep -R '^( *class| *struct|using) ' include/ src/ | sed -e 's/.*\(class\|struct\|using\) \([a-zA-Z0-9_]*\).*/\2/' | sort | uniq))
+
+echo Candidates:
+for I in ${TYPES[@]}
+do
+ N=$(fgrep -Rl "$I" include/ src/ | wc -l)
+ if [ $N == 1 ]
+ then
+ echo "$I"
+ grep -R "$I" include/ src/
+ echo
+ fi
+done
+
+
+echo Strong candidates:
+for I in ${TYPES[@]}
+do
+ N=$(fgrep -R "$I" include/ src/ | egrep -v '.*: *(class|struct) '"$I" | wc -l)
+ if [ $N == 0 ]
+ then
+ echo "$I"
+ fgrep -R "$I" include/ src/
+ echo
+ fi
+done
diff --git a/extras/scripts/parser.out b/extras/scripts/parser.out
new file mode 100644
index 0000000..32b5fa7
--- /dev/null
+++ b/extras/scripts/parser.out
@@ -0,0 +1,897 @@
+Created by PLY version 3.7 (http://www.dabeaz.com/ply)
+
+Grammar
+
+Rule 0 S' -> balanced_string
+Rule 1 comma_separated_balanced_string -> <empty>
+Rule 2 comma_separated_balanced_string -> COMMA balanced_string comma_separated_balanced_string
+Rule 3 optional_balanced_string -> <empty>
+Rule 4 optional_balanced_string -> balanced_string
+Rule 5 balanced_string -> ASTERISK optional_balanced_string
+Rule 6 balanced_string -> AMPERSAND optional_balanced_string
+Rule 7 balanced_string -> DOUBLE_COLON balanced_string
+Rule 8 balanced_string -> IDENTIFIER optional_balanced_string
+Rule 9 balanced_string -> LPAREN RPAREN optional_balanced_string
+Rule 10 balanced_string -> LBRACKET RBRACKET optional_balanced_string
+Rule 11 balanced_string -> LBRACE RBRACE optional_balanced_string
+Rule 12 balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string
+Rule 13 balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+Rule 14 balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+Rule 15 balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+Rule 16 balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+Terminals, with rules where they appear
+
+AMPERSAND : 6
+ASTERISK : 5
+COMMA : 2
+DOUBLE_COLON : 7
+GREATER_THAN : 12 16
+IDENTIFIER : 8
+LBRACE : 11 15
+LBRACKET : 10 14
+LESS_THAN : 12 16
+LPAREN : 9 13
+RBRACE : 11 15
+RBRACKET : 10 14
+RPAREN : 9 13
+error :
+
+Nonterminals, with rules where they appear
+
+balanced_string : 2 4 7 13 14 15 16 0
+comma_separated_balanced_string : 2 13 14 15 16
+optional_balanced_string : 5 6 8 9 10 11 12 13 14 15 16
+
+Parsing method: LALR
+
+state 0
+
+ (0) S' -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 1
+
+state 1
+
+ (0) S' -> balanced_string .
+
+
+
+state 2
+
+ (12) balanced_string -> LESS_THAN . GREATER_THAN optional_balanced_string
+ (16) balanced_string -> LESS_THAN . balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ GREATER_THAN shift and go to state 11
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 10
+
+state 3
+
+ (6) balanced_string -> AMPERSAND . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 13
+
+state 4
+
+ (5) balanced_string -> ASTERISK . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 14
+
+state 5
+
+ (10) balanced_string -> LBRACKET . RBRACKET optional_balanced_string
+ (14) balanced_string -> LBRACKET . balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ RBRACKET shift and go to state 16
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 15
+
+state 6
+
+ (8) balanced_string -> IDENTIFIER . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 17
+
+state 7
+
+ (7) balanced_string -> DOUBLE_COLON . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 18
+
+state 8
+
+ (9) balanced_string -> LPAREN . RPAREN optional_balanced_string
+ (13) balanced_string -> LPAREN . balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ RPAREN shift and go to state 20
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 19
+
+state 9
+
+ (11) balanced_string -> LBRACE . RBRACE optional_balanced_string
+ (15) balanced_string -> LBRACE . balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ RBRACE shift and go to state 22
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 21
+
+state 10
+
+ (16) balanced_string -> LESS_THAN balanced_string . comma_separated_balanced_string GREATER_THAN optional_balanced_string
+ (1) comma_separated_balanced_string -> .
+ (2) comma_separated_balanced_string -> . COMMA balanced_string comma_separated_balanced_string
+
+ GREATER_THAN reduce using rule 1 (comma_separated_balanced_string -> .)
+ COMMA shift and go to state 23
+
+ comma_separated_balanced_string shift and go to state 24
+
+state 11
+
+ (12) balanced_string -> LESS_THAN GREATER_THAN . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 25
+
+state 12
+
+ (4) optional_balanced_string -> balanced_string .
+
+ $end reduce using rule 4 (optional_balanced_string -> balanced_string .)
+ COMMA reduce using rule 4 (optional_balanced_string -> balanced_string .)
+ GREATER_THAN reduce using rule 4 (optional_balanced_string -> balanced_string .)
+ RBRACKET reduce using rule 4 (optional_balanced_string -> balanced_string .)
+ RPAREN reduce using rule 4 (optional_balanced_string -> balanced_string .)
+ RBRACE reduce using rule 4 (optional_balanced_string -> balanced_string .)
+
+
+state 13
+
+ (6) balanced_string -> AMPERSAND optional_balanced_string .
+
+ $end reduce using rule 6 (balanced_string -> AMPERSAND optional_balanced_string .)
+ COMMA reduce using rule 6 (balanced_string -> AMPERSAND optional_balanced_string .)
+ GREATER_THAN reduce using rule 6 (balanced_string -> AMPERSAND optional_balanced_string .)
+ RBRACKET reduce using rule 6 (balanced_string -> AMPERSAND optional_balanced_string .)
+ RPAREN reduce using rule 6 (balanced_string -> AMPERSAND optional_balanced_string .)
+ RBRACE reduce using rule 6 (balanced_string -> AMPERSAND optional_balanced_string .)
+
+
+state 14
+
+ (5) balanced_string -> ASTERISK optional_balanced_string .
+
+ $end reduce using rule 5 (balanced_string -> ASTERISK optional_balanced_string .)
+ COMMA reduce using rule 5 (balanced_string -> ASTERISK optional_balanced_string .)
+ GREATER_THAN reduce using rule 5 (balanced_string -> ASTERISK optional_balanced_string .)
+ RBRACKET reduce using rule 5 (balanced_string -> ASTERISK optional_balanced_string .)
+ RPAREN reduce using rule 5 (balanced_string -> ASTERISK optional_balanced_string .)
+ RBRACE reduce using rule 5 (balanced_string -> ASTERISK optional_balanced_string .)
+
+
+state 15
+
+ (14) balanced_string -> LBRACKET balanced_string . comma_separated_balanced_string RBRACKET optional_balanced_string
+ (1) comma_separated_balanced_string -> .
+ (2) comma_separated_balanced_string -> . COMMA balanced_string comma_separated_balanced_string
+
+ RBRACKET reduce using rule 1 (comma_separated_balanced_string -> .)
+ COMMA shift and go to state 23
+
+ comma_separated_balanced_string shift and go to state 26
+
+state 16
+
+ (10) balanced_string -> LBRACKET RBRACKET . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 27
+
+state 17
+
+ (8) balanced_string -> IDENTIFIER optional_balanced_string .
+
+ $end reduce using rule 8 (balanced_string -> IDENTIFIER optional_balanced_string .)
+ COMMA reduce using rule 8 (balanced_string -> IDENTIFIER optional_balanced_string .)
+ GREATER_THAN reduce using rule 8 (balanced_string -> IDENTIFIER optional_balanced_string .)
+ RBRACKET reduce using rule 8 (balanced_string -> IDENTIFIER optional_balanced_string .)
+ RPAREN reduce using rule 8 (balanced_string -> IDENTIFIER optional_balanced_string .)
+ RBRACE reduce using rule 8 (balanced_string -> IDENTIFIER optional_balanced_string .)
+
+
+state 18
+
+ (7) balanced_string -> DOUBLE_COLON balanced_string .
+
+ $end reduce using rule 7 (balanced_string -> DOUBLE_COLON balanced_string .)
+ COMMA reduce using rule 7 (balanced_string -> DOUBLE_COLON balanced_string .)
+ GREATER_THAN reduce using rule 7 (balanced_string -> DOUBLE_COLON balanced_string .)
+ RBRACKET reduce using rule 7 (balanced_string -> DOUBLE_COLON balanced_string .)
+ RPAREN reduce using rule 7 (balanced_string -> DOUBLE_COLON balanced_string .)
+ RBRACE reduce using rule 7 (balanced_string -> DOUBLE_COLON balanced_string .)
+
+
+state 19
+
+ (13) balanced_string -> LPAREN balanced_string . comma_separated_balanced_string RPAREN optional_balanced_string
+ (1) comma_separated_balanced_string -> .
+ (2) comma_separated_balanced_string -> . COMMA balanced_string comma_separated_balanced_string
+
+ RPAREN reduce using rule 1 (comma_separated_balanced_string -> .)
+ COMMA shift and go to state 23
+
+ comma_separated_balanced_string shift and go to state 28
+
+state 20
+
+ (9) balanced_string -> LPAREN RPAREN . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 29
+
+state 21
+
+ (15) balanced_string -> LBRACE balanced_string . comma_separated_balanced_string RBRACE optional_balanced_string
+ (1) comma_separated_balanced_string -> .
+ (2) comma_separated_balanced_string -> . COMMA balanced_string comma_separated_balanced_string
+
+ RBRACE reduce using rule 1 (comma_separated_balanced_string -> .)
+ COMMA shift and go to state 23
+
+ comma_separated_balanced_string shift and go to state 30
+
+state 22
+
+ (11) balanced_string -> LBRACE RBRACE . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 31
+
+state 23
+
+ (2) comma_separated_balanced_string -> COMMA . balanced_string comma_separated_balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 32
+
+state 24
+
+ (16) balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string . GREATER_THAN optional_balanced_string
+
+ GREATER_THAN shift and go to state 33
+
+
+state 25
+
+ (12) balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string .
+
+ $end reduce using rule 12 (balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string .)
+ COMMA reduce using rule 12 (balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string .)
+ GREATER_THAN reduce using rule 12 (balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string .)
+ RBRACKET reduce using rule 12 (balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string .)
+ RPAREN reduce using rule 12 (balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string .)
+ RBRACE reduce using rule 12 (balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string .)
+
+
+state 26
+
+ (14) balanced_string -> LBRACKET balanced_string comma_separated_balanced_string . RBRACKET optional_balanced_string
+
+ RBRACKET shift and go to state 34
+
+
+state 27
+
+ (10) balanced_string -> LBRACKET RBRACKET optional_balanced_string .
+
+ $end reduce using rule 10 (balanced_string -> LBRACKET RBRACKET optional_balanced_string .)
+ COMMA reduce using rule 10 (balanced_string -> LBRACKET RBRACKET optional_balanced_string .)
+ GREATER_THAN reduce using rule 10 (balanced_string -> LBRACKET RBRACKET optional_balanced_string .)
+ RBRACKET reduce using rule 10 (balanced_string -> LBRACKET RBRACKET optional_balanced_string .)
+ RPAREN reduce using rule 10 (balanced_string -> LBRACKET RBRACKET optional_balanced_string .)
+ RBRACE reduce using rule 10 (balanced_string -> LBRACKET RBRACKET optional_balanced_string .)
+
+
+state 28
+
+ (13) balanced_string -> LPAREN balanced_string comma_separated_balanced_string . RPAREN optional_balanced_string
+
+ RPAREN shift and go to state 35
+
+
+state 29
+
+ (9) balanced_string -> LPAREN RPAREN optional_balanced_string .
+
+ $end reduce using rule 9 (balanced_string -> LPAREN RPAREN optional_balanced_string .)
+ COMMA reduce using rule 9 (balanced_string -> LPAREN RPAREN optional_balanced_string .)
+ GREATER_THAN reduce using rule 9 (balanced_string -> LPAREN RPAREN optional_balanced_string .)
+ RBRACKET reduce using rule 9 (balanced_string -> LPAREN RPAREN optional_balanced_string .)
+ RPAREN reduce using rule 9 (balanced_string -> LPAREN RPAREN optional_balanced_string .)
+ RBRACE reduce using rule 9 (balanced_string -> LPAREN RPAREN optional_balanced_string .)
+
+
+state 30
+
+ (15) balanced_string -> LBRACE balanced_string comma_separated_balanced_string . RBRACE optional_balanced_string
+
+ RBRACE shift and go to state 36
+
+
+state 31
+
+ (11) balanced_string -> LBRACE RBRACE optional_balanced_string .
+
+ $end reduce using rule 11 (balanced_string -> LBRACE RBRACE optional_balanced_string .)
+ COMMA reduce using rule 11 (balanced_string -> LBRACE RBRACE optional_balanced_string .)
+ GREATER_THAN reduce using rule 11 (balanced_string -> LBRACE RBRACE optional_balanced_string .)
+ RBRACKET reduce using rule 11 (balanced_string -> LBRACE RBRACE optional_balanced_string .)
+ RPAREN reduce using rule 11 (balanced_string -> LBRACE RBRACE optional_balanced_string .)
+ RBRACE reduce using rule 11 (balanced_string -> LBRACE RBRACE optional_balanced_string .)
+
+
+state 32
+
+ (2) comma_separated_balanced_string -> COMMA balanced_string . comma_separated_balanced_string
+ (1) comma_separated_balanced_string -> .
+ (2) comma_separated_balanced_string -> . COMMA balanced_string comma_separated_balanced_string
+
+ GREATER_THAN reduce using rule 1 (comma_separated_balanced_string -> .)
+ RBRACKET reduce using rule 1 (comma_separated_balanced_string -> .)
+ RPAREN reduce using rule 1 (comma_separated_balanced_string -> .)
+ RBRACE reduce using rule 1 (comma_separated_balanced_string -> .)
+ COMMA shift and go to state 23
+
+ comma_separated_balanced_string shift and go to state 37
+
+state 33
+
+ (16) balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 38
+
+state 34
+
+ (14) balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 39
+
+state 35
+
+ (13) balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 40
+
+state 36
+
+ (15) balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE . optional_balanced_string
+ (3) optional_balanced_string -> .
+ (4) optional_balanced_string -> . balanced_string
+ (5) balanced_string -> . ASTERISK optional_balanced_string
+ (6) balanced_string -> . AMPERSAND optional_balanced_string
+ (7) balanced_string -> . DOUBLE_COLON balanced_string
+ (8) balanced_string -> . IDENTIFIER optional_balanced_string
+ (9) balanced_string -> . LPAREN RPAREN optional_balanced_string
+ (10) balanced_string -> . LBRACKET RBRACKET optional_balanced_string
+ (11) balanced_string -> . LBRACE RBRACE optional_balanced_string
+ (12) balanced_string -> . LESS_THAN GREATER_THAN optional_balanced_string
+ (13) balanced_string -> . LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string
+ (14) balanced_string -> . LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string
+ (15) balanced_string -> . LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string
+ (16) balanced_string -> . LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string
+
+ $end reduce using rule 3 (optional_balanced_string -> .)
+ COMMA reduce using rule 3 (optional_balanced_string -> .)
+ GREATER_THAN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACKET reduce using rule 3 (optional_balanced_string -> .)
+ RPAREN reduce using rule 3 (optional_balanced_string -> .)
+ RBRACE reduce using rule 3 (optional_balanced_string -> .)
+ ASTERISK shift and go to state 4
+ AMPERSAND shift and go to state 3
+ DOUBLE_COLON shift and go to state 7
+ IDENTIFIER shift and go to state 6
+ LPAREN shift and go to state 8
+ LBRACKET shift and go to state 5
+ LBRACE shift and go to state 9
+ LESS_THAN shift and go to state 2
+
+ balanced_string shift and go to state 12
+ optional_balanced_string shift and go to state 41
+
+state 37
+
+ (2) comma_separated_balanced_string -> COMMA balanced_string comma_separated_balanced_string .
+
+ RBRACE reduce using rule 2 (comma_separated_balanced_string -> COMMA balanced_string comma_separated_balanced_string .)
+ GREATER_THAN reduce using rule 2 (comma_separated_balanced_string -> COMMA balanced_string comma_separated_balanced_string .)
+ RBRACKET reduce using rule 2 (comma_separated_balanced_string -> COMMA balanced_string comma_separated_balanced_string .)
+ RPAREN reduce using rule 2 (comma_separated_balanced_string -> COMMA balanced_string comma_separated_balanced_string .)
+
+
+state 38
+
+ (16) balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string .
+
+ $end reduce using rule 16 (balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string .)
+ COMMA reduce using rule 16 (balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string .)
+ GREATER_THAN reduce using rule 16 (balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string .)
+ RBRACKET reduce using rule 16 (balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string .)
+ RPAREN reduce using rule 16 (balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string .)
+ RBRACE reduce using rule 16 (balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string .)
+
+
+state 39
+
+ (14) balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string .
+
+ $end reduce using rule 14 (balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string .)
+ COMMA reduce using rule 14 (balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string .)
+ GREATER_THAN reduce using rule 14 (balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string .)
+ RBRACKET reduce using rule 14 (balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string .)
+ RPAREN reduce using rule 14 (balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string .)
+ RBRACE reduce using rule 14 (balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string .)
+
+
+state 40
+
+ (13) balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string .
+
+ $end reduce using rule 13 (balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string .)
+ COMMA reduce using rule 13 (balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string .)
+ GREATER_THAN reduce using rule 13 (balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string .)
+ RBRACKET reduce using rule 13 (balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string .)
+ RPAREN reduce using rule 13 (balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string .)
+ RBRACE reduce using rule 13 (balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string .)
+
+
+state 41
+
+ (15) balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string .
+
+ $end reduce using rule 15 (balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string .)
+ COMMA reduce using rule 15 (balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string .)
+ GREATER_THAN reduce using rule 15 (balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string .)
+ RBRACKET reduce using rule 15 (balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string .)
+ RPAREN reduce using rule 15 (balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string .)
+ RBRACE reduce using rule 15 (balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string .)
+
diff --git a/extras/scripts/parsetab.py b/extras/scripts/parsetab.py
new file mode 100644
index 0000000..119d710
--- /dev/null
+++ b/extras/scripts/parsetab.py
@@ -0,0 +1,45 @@
+
+# parsetab.py
+# This file is automatically generated. Do not edit.
+_tabversion = '3.5'
+
+_lr_method = 'LALR'
+
+_lr_signature = 'A4908DFFF94F3402E0156DF82495775C'
+
+_lr_action_items = {'GREATER_THAN':([2,3,4,6,10,11,12,13,14,16,17,18,20,22,24,25,27,29,31,32,33,34,35,36,37,38,39,40,41,],[11,-3,-3,-3,-1,-3,-4,-6,-5,-3,-8,-7,-3,-3,33,-12,-10,-9,-11,-1,-3,-3,-3,-3,-2,-16,-14,-13,-15,]),'AMPERSAND':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,]),'RBRACE':([3,4,6,9,11,12,13,14,16,17,18,20,21,22,25,27,29,30,31,32,33,34,35,36,37,38,39,40,41,],[-3,-3,-3,22,-3,-4,-6,-5,-3,-8,-7,-3,-1,-3,-12,-10,-9,36,-11,-1,-3,-3,-3,-3,-2,-16,-14,-13,-15,]),'RPAREN':([3,4,6,8,11,12,13,14,16,17,18,19,20,22,25,27,28,29,31,32,33,34,35,36,37,38,39,40,41,],[-3,-3,-3,20,-3,-4,-6,-5,-3,-8,-7,-1,-3,-3,-12,-10,35,-9,-11,-1,-3,-3,-3,-3,-2,-16,-14,-13,-15,]),'ASTERISK':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,]),'$end':([1,3,4,6,11,12,13,14,16,17,18,20,22,25,27,29,31,33,34,35,36,38,39,40,41,],[0,-3,-3,-3,-3,-4,-6,-5,-3,-8,-7,-3,-3,-12,-10,-9,-11,-3,-3,-3,-3,-16,-14,-13,-15,]),'COMMA':([3,4,6,10,11,12,13,14,15,16,17,18,19,20,21,22,25,27,29,31,32,33,34,35,36,38,39,40,41,],[-3,-3,-3,23,-3,-4,-6,-5,23,-3,-8,-7,23,-3,23,-3,-12,-10,-9,-11,23,-3,-3,-3,-3,-16,-14,-13,-15,]),'LESS_THAN':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,]),'LBRACKET':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,]),'IDENTIFIER':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,]),'RBRACKET':([3,4,5,6,11,12,13,14,15,16,17,18,20,22,25,26,27,29,31,32,33,34,35,36,37,38,39,40,41,],[-3,-3,16,-3,-3,-4,-6,-5,-1,-3,-8,-7,-3,-3,-12,34,-10,-9,-11,-1,-3,-3,-3,-3,-2,-16,-14,-13,-15,]),'DOUBLE_COLON':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,]),'LPAREN':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,]),'LBRACE':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,]),}
+
+_lr_action = {}
+for _k, _v in _lr_action_items.items():
+ for _x,_y in zip(_v[0],_v[1]):
+ if not _x in _lr_action: _lr_action[_x] = {}
+ _lr_action[_x][_k] = _y
+del _lr_action_items
+
+_lr_goto_items = {'balanced_string':([0,2,3,4,5,6,7,8,9,11,16,20,22,23,33,34,35,36,],[1,10,12,12,15,12,18,19,21,12,12,12,12,32,12,12,12,12,]),'comma_separated_balanced_string':([10,15,19,21,32,],[24,26,28,30,37,]),'optional_balanced_string':([3,4,6,11,16,20,22,33,34,35,36,],[13,14,17,25,27,29,31,38,39,40,41,]),}
+
+_lr_goto = {}
+for _k, _v in _lr_goto_items.items():
+ for _x, _y in zip(_v[0], _v[1]):
+ if not _x in _lr_goto: _lr_goto[_x] = {}
+ _lr_goto[_x][_k] = _y
+del _lr_goto_items
+_lr_productions = [
+ ("S' -> balanced_string","S'",1,None,None,None),
+ ('comma_separated_balanced_string -> <empty>','comma_separated_balanced_string',0,'p_comma_separated_balanced_string_empty','analyze_template_instantiations_clang_diagnostics.py',156),
+ ('comma_separated_balanced_string -> COMMA balanced_string comma_separated_balanced_string','comma_separated_balanced_string',3,'p_comma_separated_balanced_string_not_empty','analyze_template_instantiations_clang_diagnostics.py',160),
+ ('optional_balanced_string -> <empty>','optional_balanced_string',0,'p_optional_balanced_string_empty','analyze_template_instantiations_clang_diagnostics.py',167),
+ ('optional_balanced_string -> balanced_string','optional_balanced_string',1,'p_optional_balanced_string_not_empty','analyze_template_instantiations_clang_diagnostics.py',171),
+ ('balanced_string -> ASTERISK optional_balanced_string','balanced_string',2,'p_balanced_string_terminal_symbol','analyze_template_instantiations_clang_diagnostics.py',200),
+ ('balanced_string -> AMPERSAND optional_balanced_string','balanced_string',2,'p_balanced_string_terminal_symbol','analyze_template_instantiations_clang_diagnostics.py',201),
+ ('balanced_string -> DOUBLE_COLON balanced_string','balanced_string',2,'p_balanced_string_terminal','analyze_template_instantiations_clang_diagnostics.py',206),
+ ('balanced_string -> IDENTIFIER optional_balanced_string','balanced_string',2,'p_balanced_string_terminal','analyze_template_instantiations_clang_diagnostics.py',207),
+ ('balanced_string -> LPAREN RPAREN optional_balanced_string','balanced_string',3,'p_balanced_string_with_balanced_token_no_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',283),
+ ('balanced_string -> LBRACKET RBRACKET optional_balanced_string','balanced_string',3,'p_balanced_string_with_balanced_token_no_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',284),
+ ('balanced_string -> LBRACE RBRACE optional_balanced_string','balanced_string',3,'p_balanced_string_with_balanced_token_no_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',285),
+ ('balanced_string -> LESS_THAN GREATER_THAN optional_balanced_string','balanced_string',3,'p_balanced_string_with_balanced_token_no_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',286),
+ ('balanced_string -> LPAREN balanced_string comma_separated_balanced_string RPAREN optional_balanced_string','balanced_string',5,'p_balanced_string_with_balanced_token_some_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',297),
+ ('balanced_string -> LBRACKET balanced_string comma_separated_balanced_string RBRACKET optional_balanced_string','balanced_string',5,'p_balanced_string_with_balanced_token_some_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',298),
+ ('balanced_string -> LBRACE balanced_string comma_separated_balanced_string RBRACE optional_balanced_string','balanced_string',5,'p_balanced_string_with_balanced_token_some_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',299),
+ ('balanced_string -> LESS_THAN balanced_string comma_separated_balanced_string GREATER_THAN optional_balanced_string','balanced_string',5,'p_balanced_string_with_balanced_token_some_comma_separated_elems','analyze_template_instantiations_clang_diagnostics.py',300),
+]
diff --git a/extras/scripts/postsubmit-helper.sh b/extras/scripts/postsubmit-helper.sh
new file mode 100755
index 0000000..4f6fcaa
--- /dev/null
+++ b/extras/scripts/postsubmit-helper.sh
@@ -0,0 +1,154 @@
+#!/bin/bash -x
+
+set -e
+
+# This only exists in OS X, but it doesn't cause issues in Linux (the dir doesn't exist, so it's
+# ignored).
+export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
+
+case $COMPILER in
+gcc-4.9)
+ export CC=gcc-4.9
+ export CXX=g++-4.9
+ ;;
+
+gcc-5)
+ export CC=gcc-5
+ export CXX=g++-5
+ ;;
+
+gcc-6)
+ export CC=gcc-6
+ export CXX=g++-6
+ ;;
+
+gcc-7)
+ export CC=gcc-7
+ export CXX=g++-7
+ ;;
+
+clang-3.5)
+ export CC=clang-3.5
+ export CXX=clang++-3.5
+ ;;
+
+clang-3.6)
+ export CC=clang-3.6
+ export CXX=clang++-3.6
+ ;;
+
+clang-3.7)
+ export CC=clang-3.7
+ export CXX=clang++-3.7
+ ;;
+
+clang-3.8)
+ export CC=clang-3.8
+ export CXX=clang++-3.8
+ ;;
+
+clang-3.9)
+ export CC=clang-3.9
+ export CXX=clang++-3.9
+ ;;
+
+clang-4.0)
+ case "$OS" in
+ linux)
+ export CC=clang-4.0
+ export CXX=clang++-4.0
+ ;;
+ osx)
+ export CC=/usr/local/opt/llvm/bin/clang
+ export CXX=/usr/local/opt/llvm/bin/clang++
+ ;;
+ *) echo "Error: unexpected OS: $OS"; exit 1 ;;
+ esac
+ ;;
+
+clang-default)
+ export CC=clang
+ export CXX=clang++
+ ;;
+
+bazel)
+ ;;
+
+*)
+ echo "Unrecognized value of COMPILER: $COMPILER"
+ exit 1
+esac
+
+run_make() {
+ make -j$N_JOBS
+}
+
+if [[ "${COMPILER}" != "bazel" ]]
+then
+ echo CXX version: $($CXX --version)
+ echo C++ Standard library location: $(echo '#include <vector>' | $CXX -x c++ -E - | grep 'vector\"' | awk '{print $3}' | sed 's@/vector@@;s@\"@@g' | head -n 1)
+ echo Normalized C++ Standard library location: $(readlink -f $(echo '#include <vector>' | $CXX -x c++ -E - | grep 'vector\"' | awk '{print $3}' | sed 's@/vector@@;s@\"@@g' | head -n 1))
+
+ case "$1" in
+ DebugPlain) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O2") ;;
+ DebugPlainNoPch) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O2" -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+ DebugAsan) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O0 -fsanitize=address") ;;
+ DebugAsanNoPch) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O0 -fsanitize=address" -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+ DebugAsanUbsan) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O0 -fsanitize=address,undefined") ;;
+ DebugAsanUbsanNoPch) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O0 -fsanitize=address,undefined" -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+ DebugValgrind) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O2" -DRUN_TESTS_UNDER_VALGRIND=TRUE) ;;
+ DebugValgrindNoPch) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -O2" -DRUN_TESTS_UNDER_VALGRIND=TRUE -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+ ReleasePlain) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic") ;;
+ ReleasePlainNoPch) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic" -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+ ReleaseValgrind) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic" -DRUN_TESTS_UNDER_VALGRIND=TRUE) ;;
+ ReleaseValgrindNoPch) CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic" -DRUN_TESTS_UNDER_VALGRIND=TRUE -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+ *) echo "Error: you need to specify one of the supported postsubmit modes (see postsubmit.sh)."; exit 1 ;;
+ esac
+
+ SOURCES_PATH="$PWD"
+
+ # This is not needed on Travis CI, but it's sometimes needed when running postsubmit.sh locally, to avoid "import
+ # file mismatch" errors.
+ rm -rf tests/__pycache__/ tests/*.pyc tests/*/__pycache__/ tests/*/*.pyc
+
+ rm -rf build
+ mkdir build
+ cd build
+ cmake .. "${CMAKE_ARGS[@]}"
+ echo
+ echo "Content of CMakeFiles/CMakeError.log:"
+ if [ -f "CMakeFiles/CMakeError.log" ]
+ then
+ cat CMakeFiles/CMakeError.log
+ fi
+ echo
+ run_make
+
+ cd examples
+ run_make
+ cd ..
+
+ cd tests
+ run_make
+
+ # We specify the path explicitly because old versions of pytest (e.g. the one in Ubuntu 14.04)
+ # don't support the testpaths setting in pytest.ini, so they will ignore it and they would
+ # otherwise run no tests.
+ py.test -n auto -r a "$SOURCES_PATH"/tests
+ cd ..
+
+ make install
+else
+ # COMPILER=bazel
+
+ BAZEL_FLAGS=("--python_path=$(which python3)")
+ case "$1" in
+ DebugPlain) ;;
+ ReleasePlain) BAZEL_FLAGS+=("-c" "opt") ;;
+ *) echo "Error: you need to specify one of the supported postsubmit modes (see postsubmit.sh)."; exit 1 ;;
+ esac
+
+ cd extras/bazel_root/third_party/fruit
+ bazel build "${BAZEL_FLAGS[@]}" :fruit examples/... tests/...
+ bazel test "${BAZEL_FLAGS[@]}" --test_output=errors tests/...
+fi
diff --git a/extras/scripts/postsubmit.bat b/extras/scripts/postsubmit.bat
new file mode 100644
index 0000000..59fbb4c
--- /dev/null
+++ b/extras/scripts/postsubmit.bat
@@ -0,0 +1,49 @@
+
+echo on
+setlocal EnableDelayedExpansion
+
+SET POWERSHELL_PATH=
+FOR /F "delims=" %%F IN ('where powershell.exe') DO (SET POWERSHELL_PATH=!POWERSHELL_PATH!%%~dpF.;)
+
+set NEW_PATH=C:\Windows\system32;C:\Windows;%PYTHON3_PATH%;%PYTHON3_PATH%\Scripts;C:\Program Files (x86)\CMake\bin;
+set PATH=%NEW_PATH%
+
+SET CL_PATH=
+SET MSBUILD_PATH=
+
+if not "%VCVARSALL_DIR%" == "" (
+ CALL "%VCVARSALL_DIR%\vcvarsall.bat" amd64
+ echo on
+ FOR /F "delims=" %%F IN ('where cl.exe') DO (SET CL_PATH=!CL_PATH!%%~dpF.;)
+ FOR /F "delims=" %%F IN ('where msbuild.exe') DO (SET MSBUILD_PATH=!MSBUILD_PATH!%%~dpF.;)
+)
+
+set NEW_PATH=%NEW_PATH%%CL_PATH%%MSBUILD_PATH%%POWERSHELL_PATH%
+
+if not "%MINGW_PATH%" == "" SET NEW_PATH=%NEW_PATH%%MINGW_PATH%;
+
+set PATH=%NEW_PATH%
+setx PATH "%PATH%"
+
+mkdir C:\Fruit\build-%CONFIGURATION%
+cd C:\Fruit\build-%CONFIGURATION%
+
+cmake.exe -G "%CMAKE_GENERATOR%" .. -DCMAKE_BUILD_TYPE=%CONFIGURATION% %ADDITIONAL_CMAKE_ARGS%
+
+echo "Content of CMakeFiles\CMakeError.log:"
+if exist "CMakeFiles\CMakeError.log" (
+ type "CMakeFiles\CMakeError.log"
+)
+
+IF "%CMAKE_GENERATOR%"=="MinGW Makefiles" (
+ mingw32-make -j12 || exit /b 1
+) ELSE (
+ type ALL_BUILD.vcxproj
+ msbuild ALL_BUILD.vcxproj || exit /b 1
+)
+
+pip3 install pytest
+pip3 install pytest-xdist
+
+cd tests
+py.test -r a -n 1 || exit /b 1
diff --git a/extras/scripts/postsubmit.sh b/extras/scripts/postsubmit.sh
new file mode 100755
index 0000000..96c277a
--- /dev/null
+++ b/extras/scripts/postsubmit.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+set -e
+
+: ${N_JOBS:=2}
+
+if [ "$STL" != "" ]
+then
+ STLARG="-stdlib=$STL"
+fi
+
+case $OS in
+linux)
+ docker rm -f fruit &>/dev/null || true
+ docker run -d -it --name fruit --privileged polettimarco/fruit-basesystem:ubuntu-$UBUNTU
+ docker exec fruit mkdir fruit
+ docker cp . fruit:/fruit
+
+ docker exec fruit bash -c "
+ export COMPILER=$COMPILER;
+ export N_JOBS=$N_JOBS;
+ export STLARG=$STLARG;
+ export ASAN_OPTIONS=$ASAN_OPTIONS;
+ export OS=$OS;
+ cd fruit; extras/scripts/postsubmit-helper.sh $1"
+ exit $?
+ ;;
+
+osx)
+ export COMPILER
+ export N_JOBS
+ export STLARG
+ export ASAN_OPTIONS
+ export OS
+ extras/scripts/postsubmit-helper.sh "$@"
+ exit $?
+ ;;
+
+*)
+ echo "Unsupported OS: $OS"
+ exit 1
+esac
diff --git a/extras/scripts/run_benchs.sh b/extras/scripts/run_benchs.sh
new file mode 100755
index 0000000..78e2ad9
--- /dev/null
+++ b/extras/scripts/run_benchs.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+set -e
+set -o pipefail
+
+prefix_with() (
+ local PREFIX=("$@")
+ while read line
+ do
+ echo "${PREFIX[@]}" "$line"
+ done
+)
+
+progress() (
+ local EXPECTED_LINES=$1
+ local I=0
+ echo "0/$EXPECTED_LINES (0%)" >&2
+ while read line
+ do
+ echo "$line"
+ I="$[$I + 1]"
+ echo "$I/$EXPECTED_LINES ($[100 * $I / $EXPECTED_LINES]%)" >&2
+ done
+)
+
+print_stats() (
+ local TOTAL=0
+ local N=0
+ for VALUE in "$@"
+ do
+ TOTAL="$(echo $TOTAL + $VALUE | bc -l)"
+ N="$[$N + 1]"
+ done
+ if [[ $N == 0 ]]
+ then
+ echo "N/A ()"
+ else
+ echo "$(echo $TOTAL / $N | bc -l) ($@)"
+ fi
+)
+
+BASEDIR="$PWD"
+COMPILERS=(g++ clang++)
+# NUM_ITERATIONS = ITERATIONS_FACTOR/NUM_BINDINGS
+ITERATIONS_FACTOR="$[400 * 1000 * 1000]"
+# Must be multiples of 10
+NUM_BINDINGS_FOR_RUNTIME_TESTS=(100 1000)
+# Must be multiples of 5
+NUM_BINDINGS_FOR_COMPILE_TESTS=(20 80 320)
+NUM_LINES="$[${#COMPILERS[@]} * (${#NUM_BINDINGS_FOR_RUNTIME_TESTS[@]} * 3 + ${#NUM_BINDINGS_FOR_COMPILE_TESTS[@]})]"
+
+# All result lines are of the form:
+# <compiler> <n> <test> <avg. time> (<time>...)
+for compiler in ${COMPILERS[@]}
+do
+ rm -rf build
+ mkdir build
+ (
+ cd build
+ cmake .. -DCMAKE_CXX_COMPILER=$(which $compiler) -DCMAKE_BUILD_TYPE=Release &>/dev/null
+ (
+ cd examples/benchmark
+ for N in ${NUM_BINDINGS_FOR_RUNTIME_TESTS[@]}
+ do
+ (
+ NUM_ITERATIONS="$[$ITERATIONS_FACTOR / $N]"
+ sed -i "s/num_components_with_no_deps = .*/num_components_with_no_deps = $[$N / 10];/" $BASEDIR/examples/benchmark/generate_benchmark.cpp
+ sed -i "s/num_components_with_deps = .*/num_components_with_deps = $[9 * ($N / 10)];/" $BASEDIR/examples/benchmark/generate_benchmark.cpp
+ make benchmark &>/dev/null
+ SETUP_TIMES=()
+ REQUEST_TIMES=()
+ for i in $(seq 1 4)
+ do
+ RESULTS=($(echo $NUM_ITERATIONS | ./main $NUM_ITERATIONS | fgrep Total | awk '{print $5}'))
+ SETUP_TIMES+=("${RESULTS[0]}")
+ REQUEST_TIMES+=("${RESULTS[1]}")
+ done
+ print_stats "${SETUP_TIMES[@]}" | prefix_with "fruit_setup_time"
+ print_stats "${REQUEST_TIMES[@]}" | prefix_with "fruit_request_time"
+ sed -i "s/#define MULTIPLIER .*/#define MULTIPLIER $N/" $BASEDIR/examples/benchmark/new_delete_benchmark.cpp
+ make new_delete_benchmark &>/dev/null
+ NEW_DELETE_TIMES=()
+ for i in $(seq 1 4)
+ do
+ NEW_DELETE_TIMES+=($(echo $NUM_ITERATIONS | ./new_delete_benchmark | awk '{print $3}'))
+ done
+ print_stats "${NEW_DELETE_TIMES[@]}" | prefix_with "new_delete_time"
+ ) | prefix_with $N
+ done
+ )
+ (
+ cd examples/compile_time_benchmark
+ for N in ${NUM_BINDINGS_FOR_COMPILE_TESTS[@]}
+ do
+ (
+ sed -i "s/#define MULTIPLIER .*/#define MULTIPLIER $[$N/5]/" $BASEDIR/examples/compile_time_benchmark/module.cpp
+ COMPILE_TIMES=()
+ for i in $(seq 1 4)
+ do
+ COMPILE_TIMES+=($(make compile_time_benchmark 2>&1 | fgrep real | awk '{print $2}' | tr -d s | sed 's/m/*60+/' | bc))
+ done
+ print_stats "${COMPILE_TIMES[@]}" | prefix_with "fruit_compile_time"
+ ) | prefix_with "$[$N * 5]"
+ done
+ )
+ ) | prefix_with $compiler
+done | progress $NUM_LINES
diff --git a/extras/scripts/test_coverage.sh b/extras/scripts/test_coverage.sh
new file mode 100755
index 0000000..25004b5
--- /dev/null
+++ b/extras/scripts/test_coverage.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+rm -rf coverage
+mkdir coverage
+cd coverage
+mkdir binaries
+
+COMPILE_COMMAND=(g++ -O0 -W -Wall -Werror -std=c++11 -fprofile-arcs -fno-exceptions -ftest-coverage -I../include)
+FRUIT_OBJS=()
+
+
+for s in $(cd ../src; echo *.cpp)
+do
+ "${COMPILE_COMMAND[@]}" -c ../src/"$s" -o "src-${s/.cpp/.o}" &
+ FRUIT_OBJS+=("src-${s/.cpp/.o}")
+done
+
+wait || exit 1
+
+for testdir in $(find ../tests -type d)
+do
+ for t in $(cd $testdir; ls -1 *.cpp | fgrep -v include_test.cpp)
+ do
+ fgrep -q expect-compile-error $testdir/"$t" || \
+ "${COMPILE_COMMAND[@]}" $testdir/"$t" ${FRUIT_OBJS[@]} -o binaries/${t/.cpp/} &
+ done
+done
+
+wait || exit 1
+
+for b in binaries/*
+do
+ ./"$b" || true
+done &>/dev/null
+
+PROJECT_DIR="$(cd ..; echo $PWD)"
+
+lcov --rc lcov_branch_coverage=1 -capture --directory . --output-file all-coverage.info
+lcov --rc lcov_branch_coverage=1 --base-directory "$PROJECT_DIR" \
+ --extract all-coverage.info "$PROJECT_DIR/src/*" \
+ --extract all-coverage.info "$PROJECT_DIR/include/*" \
+ --output-file coverage.info
+genhtml --branch-coverage --demangle-cpp coverage.info --output-directory html
+
+xdg-open html/index.html
diff --git a/extras/scripts/travis_ci_install_linux.sh b/extras/scripts/travis_ci_install_linux.sh
new file mode 100755
index 0000000..708e5b9
--- /dev/null
+++ b/extras/scripts/travis_ci_install_linux.sh
@@ -0,0 +1,4 @@
+#!/bin/bash -x
+
+set -e
+
diff --git a/extras/scripts/travis_ci_install_osx.sh b/extras/scripts/travis_ci_install_osx.sh
new file mode 100755
index 0000000..bf4524b
--- /dev/null
+++ b/extras/scripts/travis_ci_install_osx.sh
@@ -0,0 +1,48 @@
+#!/bin/bash -x
+
+set -e
+
+install_brew_package() {
+ if brew list -1 | grep -q "^$1\$"; then
+ # Package is installed, upgrade if needed
+ time (brew outdated "$1" || brew upgrade "$@")
+ else
+ # Package not installed yet, install.
+ # If there are conflicts, try overwriting the files (these are in /usr/local anyway so it should be ok).
+ time (brew install "$@" || brew link --overwrite gcc49)
+ fi
+}
+
+time brew update
+
+# For md5sum
+install_brew_package md5sha1sum
+# For `timeout'
+install_brew_package coreutils
+
+if [[ "${INSTALL_VALGRIND}" == "1" ]]
+then
+ install_brew_package valgrind
+fi
+
+which cmake &>/dev/null || install_brew_package cmake
+
+case "${COMPILER}" in
+gcc-4.9) install_brew_package gcc@4.9 ;;
+gcc-5) install_brew_package gcc@5 ;;
+gcc-6) install_brew_package gcc@6 ;;
+clang-default) ;;
+clang-3.7) install_brew_package llvm@3.7 --with-clang --with-libcxx;;
+clang-3.8) install_brew_package llvm@3.8 --with-clang --with-libcxx;;
+clang-3.9) install_brew_package llvm@3.9 --with-clang --with-libcxx;;
+clang-4.0) install_brew_package llvm --with-clang --with-libcxx;;
+*) echo "Compiler not supported: ${COMPILER}. See travis_ci_install_osx.sh"; exit 1 ;;
+esac
+
+time brew upgrade python
+time pip3 install pytest
+time pip3 install pytest-xdist
+time pip3 install sh
+
+# This adds python-installed executables to PATH (notably py.test).
+export PATH="$(brew --prefix)/bin:$PATH"
diff --git a/extras/scripts/travis_yml_generator.py b/extras/scripts/travis_yml_generator.py
new file mode 100755
index 0000000..514c2c4
--- /dev/null
+++ b/extras/scripts/travis_yml_generator.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import yaml
+
+# "smoke tests" are run before other build matrix rows.
+build_matrix_smoke_test_rows = []
+build_matrix_rows = []
+
+def determine_compiler_kind(compiler):
+ if compiler.startswith('gcc'):
+ return 'gcc'
+ elif compiler.startswith('clang'):
+ return 'clang'
+ else:
+ raise Exception('Unexpected compiler: %s' % compiler)
+
+def determine_tests(asan, ubsan, smoke_tests, use_precompiled_headers_in_tests, exclude_tests,
+ include_only_tests):
+ tests = []
+ has_debug_build = False
+ tests += ['ReleasePlain']
+ if asan:
+ has_debug_build = True
+ if ubsan:
+ tests += ['DebugAsanUbsan']
+ else:
+ tests += ['DebugAsan']
+ if ubsan and not asan:
+ raise Exception('Enabling UBSan but not ASan is not currently supported.')
+ if not has_debug_build:
+ tests += ['DebugPlain']
+ for smoke_test in smoke_tests:
+ if smoke_test not in tests:
+ tests += [smoke_test]
+ excessive_excluded_tests = set(exclude_tests) - set(tests)
+ if excessive_excluded_tests:
+ raise Exception(
+ 'Some tests were excluded but were not going to run anyway: %s. '
+ 'Tests to run (ignoring the possible NoPch prefix): %s'
+ % (excessive_excluded_tests, tests))
+ if include_only_tests is not None:
+ if exclude_tests != []:
+ raise Exception('Using exclude_tests and include_only_tests together is not supported.')
+ tests = include_only_tests
+ else:
+ tests = [test for test in tests if test not in exclude_tests]
+ if not use_precompiled_headers_in_tests:
+ tests = [test + 'NoPch' for test in tests]
+ return tests
+
+def generate_export_statements_for_env(env):
+ return ' '.join(['export %s=\'%s\';' % (var_name, value) for (var_name, value) in sorted(env.items())])
+
+def generate_env_string_for_env(env):
+ return ' '.join(['%s=%s' % (var_name, value) for (var_name, value) in sorted(env.items())])
+
+def add_ubuntu_tests(ubuntu_version, compiler, stl=None, asan=True, ubsan=True,
+ use_precompiled_headers_in_tests=True, smoke_tests=[], exclude_tests=[], include_only_tests=None):
+ env = {
+ 'UBUNTU': ubuntu_version,
+ 'COMPILER': compiler
+ }
+ if stl is not None:
+ env['STL'] = stl
+ compiler_kind = determine_compiler_kind(compiler)
+ export_statements = 'export OS=linux; ' + generate_export_statements_for_env(env=env)
+ test_environment_template = {'os': 'linux', 'compiler': compiler_kind,
+ 'install': '%s extras/scripts/travis_ci_install_linux.sh' % export_statements}
+ tests = determine_tests(asan, ubsan, smoke_tests,
+ use_precompiled_headers_in_tests=use_precompiled_headers_in_tests,
+ exclude_tests=exclude_tests,
+ include_only_tests=include_only_tests)
+ for test in tests:
+ test_environment = test_environment_template.copy()
+ test_environment['script'] = '%s extras/scripts/postsubmit.sh %s' % (export_statements, test)
+ # The TEST variable has no effect on the test run, but allows to see the test name in the Travis CI dashboard.
+ test_environment['env'] = generate_env_string_for_env(env) + " TEST=%s" % test
+ if test in smoke_tests:
+ build_matrix_smoke_test_rows.append(test_environment)
+ else:
+ build_matrix_rows.append(test_environment)
+
+
+def add_osx_tests(compiler, xcode_version=None, stl=None, asan=True, ubsan=True,
+ use_precompiled_headers_in_tests=True, smoke_tests=[], exclude_tests=[], include_only_tests=None):
+ env = {'COMPILER': compiler}
+ if stl is not None:
+ env['STL'] = stl
+ compiler_kind = determine_compiler_kind(compiler)
+ export_statements = 'export OS=osx; ' + generate_export_statements_for_env(env=env)
+ test_environment_template = {'os': 'osx', 'compiler': compiler_kind,
+ 'install': '%s extras/scripts/travis_ci_install_osx.sh' % export_statements}
+ if xcode_version is not None:
+ test_environment_template['osx_image'] = 'xcode%s' % xcode_version
+
+ tests = determine_tests(asan, ubsan, smoke_tests,
+ use_precompiled_headers_in_tests=use_precompiled_headers_in_tests,
+ exclude_tests=exclude_tests, include_only_tests=include_only_tests)
+ for test in tests:
+ test_environment = test_environment_template.copy()
+ test_environment['script'] = '%s extras/scripts/postsubmit.sh %s' % (export_statements, test)
+ # The TEST variable has no effect on the test run, but allows to see the test name in the Travis CI dashboard.
+ test_environment['env'] = generate_env_string_for_env(env) + " TEST=%s" % test
+ if test in smoke_tests:
+ build_matrix_smoke_test_rows.append(test_environment)
+ else:
+ build_matrix_rows.append(test_environment)
+
+
+def add_bazel_tests(ubuntu_version, smoke_tests=[]):
+ env = {
+ 'UBUNTU': ubuntu_version,
+ 'COMPILER': 'bazel',
+ }
+ test = 'DebugPlain'
+ export_statements = 'export OS=linux; ' + generate_export_statements_for_env(env=env)
+ test_environment = {'os': 'linux',
+ 'compiler': 'gcc',
+ 'env': generate_env_string_for_env(env),
+ 'install': '%s extras/scripts/travis_ci_install_linux.sh' % export_statements,
+ 'script': '%s extras/scripts/postsubmit.sh %s' % (export_statements, test)}
+ if test in smoke_tests:
+ build_matrix_smoke_test_rows.append(test_environment)
+ else:
+ build_matrix_rows.append(test_environment)
+
+# TODO: re-enable ASan/UBSan once they work in Travis CI. ATM (as of 18 November 2017) they fail due to https://github.com/google/sanitizers/issues/837
+add_ubuntu_tests(ubuntu_version='17.10', compiler='gcc-7', asan=False, ubsan=False, smoke_tests=['DebugPlain', 'ReleasePlain'])
+add_ubuntu_tests(ubuntu_version='17.10', compiler='clang-4.0', stl='libstdc++', smoke_tests=['DebugPlain', 'DebugAsanUbsan', 'ReleasePlain'])
+
+add_bazel_tests(ubuntu_version='16.04', smoke_tests=['DebugPlain'])
+
+# ASan/UBSan are disabled for all these, the analysis on later versions is better anyway.
+# Also, in some combinations they wouldn't work.
+add_ubuntu_tests(ubuntu_version='14.04', compiler='gcc-5', asan=False, ubsan=False)
+add_ubuntu_tests(ubuntu_version='14.04', compiler='clang-3.5', stl='libstdc++', asan=False, ubsan=False)
+add_ubuntu_tests(ubuntu_version='14.04', compiler='clang-3.9', stl='libstdc++', asan=False, ubsan=False)
+add_ubuntu_tests(ubuntu_version='14.04', compiler='clang-3.5', stl='libc++', asan=False, ubsan=False)
+add_ubuntu_tests(ubuntu_version='14.04', compiler='clang-3.9', stl='libc++', asan=False, ubsan=False)
+
+# Asan/Ubsan are disabled because it generates lots of warnings like:
+# warning: direct access in [...] to global weak symbol guard variable for [...] means the weak symbol cannot be
+# overridden at runtime. This was likely caused by different translation units being compiled with different
+# visibility settings.
+# and the build eventually fails or times out.
+add_osx_tests(compiler='gcc-5', xcode_version='8', asan=False, ubsan=False)
+add_osx_tests(compiler='gcc-6', xcode_version='8', asan=False, ubsan=False, smoke_tests=['DebugPlain'])
+# ASan/UBSan are disabled because it would hit errors like:
+# ld: file not found: [...]/libclang_rt.asan_osx_dynamic.dylib
+# ld: file not found: [...]/libclang_rt.ubsan_osx.a
+# Not sure if that's a limitation of Clang on OS X or just of the brew-provided binaries.
+add_osx_tests(compiler='clang-3.7', stl='libc++', asan=False, ubsan=False)
+add_osx_tests(compiler='clang-4.0', xcode_version='8', stl='libc++', smoke_tests=['DebugPlain'])
+
+# UBSan is disabled because AppleClang does not support -fsanitize=undefined.
+add_osx_tests(compiler='clang-default', xcode_version='7.3', stl='libc++', ubsan=False)
+# UBSan is disabled because AppleClang does not support -fsanitize=undefined.
+add_osx_tests(compiler='clang-default', xcode_version='8.2', stl='libc++', ubsan=False, smoke_tests=['DebugPlain'])
+
+# ** Disabled combinations **
+#
+# These fail with "'type_traits' file not found" (the <type_traits> header is missing).
+#
+# add_osx_tests('gcc-default', stl='libstdc++')
+# add_osx_tests('clang-default', stl='libstdc++')
+# add_osx_tests('clang-3.5', stl='libstdc++')
+# add_osx_tests('clang-3.6', stl='libstdc++')
+#
+#
+# The compiler complains that the 2-argument constructor of std::pair is ambiguous, even after
+# adding explicit casts to the exact types of the expected overload.
+#
+# add_osx_tests('clang-default', stl='libc++')
+#
+#
+# This triggers an assert error in the compiler, with the message:
+# "expected to get called on an inlined function!" [...] function isMSExternInline, file Decl.cpp, line 2647.
+#
+# add_osx_tests('clang-3.5', stl='libc++', asan=False, ubsan=False)
+#
+#
+# This fails with this error:
+# /usr/include/c++/v1/string:1938:44: error: 'basic_string<_CharT, _Traits, _Allocator>' is missing
+# exception specification 'noexcept(is_nothrow_copy_constructible<allocator_type>::value)'
+# TODO: Try again every once in a while (to re-enable these once the bug in libc++ is fixed).
+#
+# add_ubuntu_tests(ubuntu_version='16.04', compiler='clang-3.8', stl='libc++', asan=False, ubsan=False)
+#
+
+
+yaml_file = {
+ 'sudo': 'required',
+ 'dist': 'trusty',
+ 'services' : ['docker'],
+ 'language': 'cpp',
+ 'branches': {
+ 'only': ['master'],
+ },
+ 'matrix': {
+ 'fast_finish': True,
+ 'include': build_matrix_smoke_test_rows + build_matrix_rows,
+ },
+}
+
+class CustomDumper(yaml.SafeDumper):
+ def ignore_aliases(self, _data):
+ return True
+
+print('#')
+print('# This file was auto-generated from extras/scripts/travis_yml_generator.py, DO NOT EDIT')
+print('#')
+print(yaml.dump(yaml_file, default_flow_style=False, Dumper=CustomDumper))
diff --git a/include/fruit/component.h b/include/fruit/component.h
new file mode 100644
index 0000000..5fbfdc6
--- /dev/null
+++ b/include/fruit/component.h
@@ -0,0 +1,1050 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_COMPONENT_H
+#define FRUIT_COMPONENT_H
+
+// This include is not required here, but having it here shortens the include trace in error messages.
+#include <fruit/impl/injection_errors.h>
+
+#include <fruit/fruit_forward_decls.h>
+#include <fruit/impl/bindings.h>
+#include <fruit/impl/component_functors.defn.h>
+#include <fruit/impl/component_storage/component_storage.h>
+#include <fruit/impl/component_storage/partial_component_storage.h>
+#include <fruit/impl/meta/component.h>
+
+namespace fruit {
+
+/**
+ * A component (aka module) represents a collection of bindings.
+ * You can think of a Component object as a UML component.
+ *
+ * The parameters can be of the form <P...> or <Required<R...>, P...> where:
+ * * R... are the required types (types required to inject some types in P... but that are not provided by this
+ * Component), if any
+ * * P... are the types provided by this Component.
+ * No type can appear twice, not even once in R and once in P.
+ *
+ * See PartialComponent below for how to construct a Component.
+ */
+template <typename... Params>
+class Component {
+public:
+ Component(Component&&) = default;
+
+ Component& operator=(Component&&) = delete;
+ Component& operator=(const Component&) = delete;
+
+ /**
+ * Converts a PartialComponent to an arbitrary Component, auto-injecting the missing types (if
+ * any).
+ * This is usually called implicitly when returning a component from a function. See PartialComponent for an example.
+ */
+ template <typename... Bindings>
+ Component(PartialComponent<Bindings...>&& component);
+
+private:
+ // Do not use. Use fruit::createComponent() instead.
+ Component() = default;
+
+ template <typename... Types>
+ friend class Component;
+
+ template <typename... Bindings>
+ friend class PartialComponent;
+
+ template <typename... OtherParams>
+ friend class NormalizedComponent;
+
+ template <typename... OtherParams>
+ friend class Injector;
+
+ template <typename... Bindings>
+ friend class fruit::impl::PartialComponentStorage;
+
+ template <typename Component, typename... Args>
+ friend class fruit::impl::LazyComponentImpl;
+
+ friend struct fruit::impl::ComponentStorageEntry::LazyComponentWithNoArgs;
+
+ template <typename Component, typename... Args>
+ friend class fruit::impl::ComponentInterfaceImpl;
+
+ fruit::impl::ComponentStorage storage;
+
+ using Comp = fruit::impl::meta::Eval<fruit::impl::meta::ConstructComponentImpl(fruit::impl::meta::Type<Params>...)>;
+
+ using Check1 = typename fruit::impl::meta::CheckIfError<Comp>::type;
+ // Force instantiation of Check1.
+ static_assert(true || sizeof(Check1), "");
+};
+
+/**
+ * Constructs an empty component.
+ *
+ * Example usage:
+ *
+ * fruit::Component<Foo> getFooComponent() {
+ * return fruit::createComponent()
+ * .install(getComponent1)
+ * .install(getComponent2)
+ * .bind<Foo, FooImpl>();
+ * }
+ *
+ * Since types are auto-injected when needed, just converting this to the desired component can suffice in some cases,
+ * e.g.:
+ *
+ * fruit::Component<Foo> getFooComponent() {
+ * return fruit::createComponent();
+ * }
+ *
+ * That works if Foo has an Inject typedef or a constructor wrapped in INJECT.
+ */
+PartialComponent<> createComponent();
+
+/**
+ * A partially constructed component.
+ *
+ * Client code should never write `PartialComponent'; always start the construction of a component with
+ * fruit::createComponent(), and end it by casting the PartialComponent to the desired Component (often done implicitly
+ * by returning a PartialComponent from a function that has Component<...> as the return type).
+ *
+ * The template parameter is used to propagate information about bound types, it is purely an implementation detail;
+ * users of the Fruit library can pretend that this class is not templated, in no case a specific template parameter is
+ * required. All methods of this class return a PartialComponent, which allows to chain method invocations without
+ * declaring a variable of type PartialComponent.
+ *
+ * Example usage:
+ *
+ * fruit::Component<Foo> getFooComponent() {
+ * return fruit::createComponent()
+ * .install(getComponent1)
+ * .install(getComponent2)
+ * .bind<Foo, FooImpl>();
+ * }
+ *
+ * Note that no variable of type PartialComponent has been declared; this class should only be used for temporary
+ * values.
+ */
+template <typename... Bindings>
+class PartialComponent {
+public:
+ PartialComponent& operator=(PartialComponent&&) = delete;
+ PartialComponent& operator=(const PartialComponent&) = delete;
+
+ /**
+ * This tells Fruit that "the implementation of I is C".
+ * I must be a base class of C, and it's typically (but not necessarily) an abstract class.
+ * C is typically a concrete class, but it doesn't have to be: for example, if A inherits from B and B inherits from C
+ * you can specify bind<C, B>() and bind<B, A>().
+ *
+ * The most common use of this is to bind the type I to the type C, for example:
+ *
+ * class MyInterface {
+ * public:
+ * virtual void foo() = 0;
+ * };
+ *
+ * class MyImplementation {
+ * public:
+ * INJECT(MyImplementation()) {}
+ *
+ * void foo() {
+ * ...
+ * }
+ * };
+ *
+ * fruit::Component<MyInterface> getMyInterfaceComponent() {
+ * return fruit::createComponent()
+ * .bind<MyInterface, MyImplementation>();
+ * }
+ *
+ * The implementation class can be bound in any way, it doesn't need to be bound using constructor injection as above
+ * (although that's a very common use case).
+ *
+ * You can bind an interface to a type bound using a constant binding (see the bindInstance method that takes a const&
+ * for more details), however the interface will then also be considered bound with a constant binding, and you must
+ * declare that in the returned Component's type. For example:
+ *
+ * fruit::Component<const MyInterface> getMyInterfaceComponent() {
+ * static const MyImplementation my_implementation = MyImplementation();
+ * return fruit::createComponent()
+ * .bindInstance(my_implementation)
+ * .bind<MyInterface, MyImplementation>();
+ * }
+ *
+ * In addition to binding the type I to the type C, a `bind()` can also be used to bind a
+ * std::function<std::unique_ptr<I>(Args...)> to a std::function<std::unique_ptr<C>(Args...)> or a
+ * std::function<C(Args...)>. For example:
+ *
+ * fruit::Component<std::function<std::unique_ptr<MyInterface>(int)>> getIFactoryComponent() {
+ * static const std::function<MyImplementation(int)> cFactory = [](int n) { return MyImplementation(n); };
+ * return fruit::createComponent()
+ * .bind<MyInterface, MyImplementation>()
+ * .bindInstance(cFactory);
+ * }
+ *
+ * Or alternatively you can do the same using constructor injection:
+ *
+ * class MyImplementation {
+ * public:
+ * INJECT(MyImplementation(ASSISTED(int) n))
+ * : n(n) {
+ * }
+ * ...
+ * };
+ *
+ * fruit::Component<std::function<std::unique_ptr<MyInterface>(int)>> getIFactoryComponent() {
+ * return fruit::createComponent()
+ * .bind<MyInterface, MyImplementation>();
+ * }
+ *
+ * Fruit determines the actual binding(s) to generate based on the types you declare as provided in the returned
+ * Component<> type (e.g. in the last example std::function<std::unique_ptr<MyInterface>(int)> is declared as provided
+ * so Fruit will generate a factory binding instead of a normal MyInterface->MyImplementation binding.
+ *
+ * Fruit can also detect types that are needed for constructor/provider/factory bindings, and it will then use that
+ * information to determine the desired binding. For example:
+ *
+ * class MyImplementation {
+ * public:
+ * INJECT(MyImplementation(ASSISTED(int) n))
+ * : n(n) {
+ * }
+ * ...
+ * };
+ *
+ * class Foo {
+ * private:
+ * std::function<std::unique_ptr<MyInterface>(int)> factory;
+ * public:
+ * INJECT(Foo(std::function<std::unique_ptr<MyInterface>(int)> factory))
+ * : factory(factory) {
+ * }
+ * };
+ *
+ * fruit::Component<Foo> getIFactoryComponent() {
+ * return fruit::createComponent()
+ * // We want to bind Foo, which requires std::function<std::unique_ptr<MyInterface>(int)>.
+ * // So std::function<std::unique_ptr<MyInterface>(int)> will be bound to
+ * // std::function<std::unique_ptr<MyImplementation>(int)>, and that will be bound using constructor injection.
+ * .bind<MyInterface, MyImplementation>();
+ * }
+ *
+ * All these cases support annotated injection, just wrap I and/or C in fruit::Annotated<> if desired. For example:
+ *
+ * struct MyAnnotation{};
+ *
+ * fruit::Component<fruit::Annotated<MyAnnotation, MyInterface>> getMyInterfaceComponent() {
+ * return fruit::createComponent()
+ * .bind<fruit::Annotated<MyAnnotation, MyInterface>, MyImplementation>();
+ * }
+ */
+ template <typename I, typename C>
+ PartialComponent<fruit::impl::Bind<I, C>, Bindings...> bind();
+
+ /**
+ * Registers Signature as the constructor signature to use to inject a type.
+ *
+ * Example usage:
+ *
+ * fruit::createComponent()
+ * .registerConstructor<Foo(Bar*,Baz*)>() // Registers the constructor Foo::Foo(Bar*,Baz*)
+ *
+ * It's usually more convenient to use an INJECT macro or Inject typedef instead, e.g.:
+ *
+ * class Foo {
+ * public:
+ * // This also declares the constructor
+ * INJECT(Foo(Bar* bar, Baz* baz));
+ * ...
+ * };
+ *
+ * or (equivalent):
+ *
+ * class Foo {
+ * public:
+ * using Inject = Foo(Bar*, Baz*);
+ * Foo(Bar* bar, Baz* baz);
+ * ...
+ * };
+ *
+ * Use registerConstructor() when you want to inject the class C in different ways in different components (just make
+ * sure those don't end up in the same injector, or use annotated injection to prevent the bindings from clashing),
+ * or when C is a third-party class that can't be modified.
+ *
+ * This supports annotated injection, just wrap the desired types (return type and/or argument types of the signature)
+ * with fruit::Annotated<> if desired. For example:
+ *
+ * struct Annotation1 {};
+ * struct Annotation2 {};
+ *
+ * struct Foo {
+ * Foo(Bar* bar) {...}
+ * };
+ *
+ * fruit::Component<fruit::Annotated<Annotation1, Bar>> getBarComponent() {...}
+ *
+ * fruit::Component<fruit::Annotated<Annotation2, Foo>> getFooComponent() {
+ * return fruit::createComponent()
+ * .install(getBarComponent)
+ * .registerConstructor<fruit::Annotated<Annotation2, Foo>(fruit::Annotated<Annotation1, Bar*>)>();
+ * }
+ *
+ * This does *not* support assisted injection, for that you should use registerFactory() instead.
+ *
+ * The allowed argument types in the signature are, for any class (or fundamental) type C:
+ *
+ * C
+ * C*
+ * C&
+ * const C*
+ * const C&
+ * shared_ptr<C>
+ * Provider<C>
+ * Provider<const C>
+ * Annotated<Annotation, C> (for any type `Annotation')
+ * Annotated<Annotation, C*> (for any type `Annotation')
+ * Annotated<Annotation, C&> (for any type `Annotation')
+ * Annotated<Annotation, const C*> (for any type `Annotation')
+ * Annotated<Annotation, const C&> (for any type `Annotation')
+ * Annotated<Annotation, shared_ptr<C>> (for any type `Annotation')
+ * Annotated<Annotation, Provider<C>> (for any type `Annotation')
+ * Annotated<Annotation, Provider<const C>> (for any type `Annotation')
+ */
+ template <typename Signature>
+ PartialComponent<fruit::impl::RegisterConstructor<Signature>, Bindings...> registerConstructor();
+
+ /**
+ * Use this method to bind the type C to a specific instance.
+ * The caller must ensure that the provided reference is valid for the entire lifetime of the component and of any
+ * components or injectors that install this component; the caller must also ensure that the object is destroyed after
+ * the last components/injectors using it are destroyed.
+ *
+ * Example usage:
+ *
+ * Component<Request> getRequestComponent(Request* request) {
+ * return fruit::createComponent()
+ * .bindInstance(*request);
+ * }
+ *
+ * NormalizedComponent<...> normalizedComponent = ...;
+ * Request request;
+ * Injector<...> injector(normalizedComponent,
+ * getRequestComponent,
+ * request));
+ *
+ * This should be used sparingly (you should let Fruit handle the object lifetime when possible), but in some cases it
+ * is necessary; for example, if a web server creates an injector to handle each request, this method can be used to
+ * inject the request itself as in the example above (see the Server page in the Fruit tutorial for more details).
+ *
+ * It's also possible to bind constants, see the documentation of the bindInstance() method taking a const& for
+ * details.
+ */
+ template <typename C>
+ PartialComponent<fruit::impl::BindInstance<C, C>, Bindings...> bindInstance(C& instance);
+
+ /**
+ * Similar to the previous, but binds a const&. Note that the reference must still outlive the component/injector
+ * as in the non-const case.
+ * When using this method, you must declare that the type is constant in the Component type. For example:
+ *
+ * Component<const MyExpensiveClass> getMyExpensiveClassComponent() {
+ * static const MyExpensiveClass my_expensive_class = createMyExpensiveClass();
+ * return fruit::createComponent()
+ * .bindInstance(my_expensive_class);
+ * }
+ *
+ * Constant bindings can be used as other bindings, except that you can only inject the constant type (e.g. as a
+ * constructor parameter) as:
+ *
+ * C
+ * const C*
+ * const C&
+ * Provider<const C>
+ * Annotated<Annotation, C> (for any type `Annotation')
+ * Annotated<Annotation, const C*> (for any type `Annotation')
+ * Annotated<Annotation, const C&> (for any type `Annotation')
+ * Annotated<Annotation, Provider<const C>> (for any type `Annotation')
+ *
+ * While you can't inject it as:
+ *
+ * C*
+ * C&
+ * shared_ptr<C>
+ * Provider<C>
+ * Annotated<Annotation, C*> (for any type `Annotation')
+ * Annotated<Annotation, C&> (for any type `Annotation')
+ * Annotated<Annotation, shared_ptr<C>> (for any type `Annotation')
+ * Annotated<Annotation, Provider<C>> (for any type `Annotation')
+ */
+ template <typename C>
+ PartialComponent<fruit::impl::BindConstInstance<C, C>, Bindings...> bindInstance(const C& instance);
+
+ /**
+ * This is deleted to catch cases where the instance would likely be destroyed before the component/injectors.
+ */
+ template <typename C>
+ PartialComponent<fruit::impl::BindConstInstance<C, C>, Bindings...> bindInstance(C&&) = delete;
+
+ /**
+ * Similar to the first version of bindInstance(), but allows to specify an annotated type that
+ * will be bound to the specified value.
+ * For example, to bind an instance to the type fruit::Annotated<Hostname, std::string>, you can use:
+ *
+ * fruit::Component<fruit::Annotated<Hostname, std::string>> getHostnameComponent(std::string* hostname) {
+ * fruit::createComponent()
+ * .bindInstance<fruit::Annotated<Hostname, std::string>>(*hostname);
+ * }
+ */
+ template <typename AnnotatedType, typename C>
+ PartialComponent<fruit::impl::BindInstance<AnnotatedType, C>, Bindings...> bindInstance(C& instance);
+
+ /**
+ * Similar to the previous, but binds a const&. Example usage:
+ *
+ * fruit::Component<fruit::Annotated<Hostname, const std::string>> getHostnameComponent() {
+ * static const std::string hostname = determineHostname();
+ * fruit::createComponent()
+ * .bindInstance<fruit::Annotated<Hostname, std::string>>(hostname);
+ * }
+ *
+ * See the documentation for the bindInstance() overload that takes a non-annotated const& for more details.
+ */
+ template <typename AnnotatedType, typename C>
+ PartialComponent<fruit::impl::BindConstInstance<AnnotatedType, C>, Bindings...> bindInstance(const C& instance);
+
+ /**
+ * This is deleted to catch cases where the instance would likely be destroyed before the component/injectors.
+ */
+ template <typename AnnotatedType, typename C>
+ PartialComponent<fruit::impl::BindConstInstance<AnnotatedType, C>, Bindings...> bindInstance(C&& instance);
+
+ /**
+ * Registers `provider' as a provider of C, where provider is a lambda with no captures returning either C or C*
+ * (prefer returning a C by value instead of allocating a C using `new C', to avoid the allocation).
+ *
+ * When injecting a C, the arguments of the provider will be injected and the provider will then be called to create
+ * the C instance, that will then be stored in the injector.
+ *
+ * If `provider' returns a pointer, it must be non-null; otherwise the program will abort.
+ *
+ * Example:
+ *
+ * fruit::Component<Foo> getFooComponent() {
+ * return fruit::createComponent()
+ * .install(getBarComponent)
+ * .install(getBazComponent)
+ * .registerProvider([](Bar* bar, Baz* baz) {
+ * Foo foo(bar, baz);
+ * foo.initialize();
+ * return foo;
+ * });
+ * }
+ *
+ * As in the previous example, it's not necessary to specify the type parameter, it will be inferred by the compiler.
+ *
+ * registerProvider() can't be called with a plain function, but you can write a lambda that wraps the function to
+ * achieve the same result.
+ *
+ * Registering stateful functors (including lambdas with captures) is NOT supported.
+ * However, you can write something like:
+ *
+ * struct Functor {
+ * Functor(int n) {...}
+ * MyClass operator()(Foo* foo) const {...}
+ * };
+ *
+ * Component<MyClass> getMyClassComponent() {
+ * static const Functor aFunctor(42);
+ * return fruit::createComponent()
+ * .install(getFooComponent)
+ * .bindInstance(aFunctor)
+ * .registerProvider([](const Functor& functor, Foo* foo) { return functor(foo); });
+ * }
+ */
+ template <typename Lambda>
+ PartialComponent<fruit::impl::RegisterProvider<Lambda>, Bindings...> registerProvider(Lambda lambda);
+
+ /**
+ * Similar to the previous version of registerProvider(), but allows to specify an annotated type
+ * for the provider. This allows to inject annotated types in the parameters and/or bind the
+ * provider to an annotated type. For example:
+ *
+ * struct MyAnnotation1 {};
+ * struct MyAnnotation2 {};
+ *
+ * Component<fruit::Annotated<Annotation1, Bar>> getBarComponent() {...}
+ * Component<Baz> getBazComponent() {...}
+ *
+ * fruit::Component<fruit::Annotated<Annotation2, Foo>> getFooComponent() {
+ * return fruit::createComponent()
+ * .install(getBarComponent)
+ * .install(getBazComponent)
+ * .registerProvider<
+ * fruit::Annotated<Annotation2, Foo>(
+ * fruit::Annotated<Annotation1, Bar*>,
+ * Baz*)
+ * >([](Bar* bar, Baz* baz) {
+ * Foo foo(bar, baz);
+ * foo.initialize();
+ * return foo;
+ * });
+ * }
+ */
+ template <typename AnnotatedSignature, typename Lambda>
+ PartialComponent<fruit::impl::RegisterProvider<AnnotatedSignature, Lambda>, Bindings...>
+ registerProvider(Lambda lambda);
+
+ /**
+ * Similar to bind<I, C>(), but adds a multibinding instead.
+ *
+ * Multibindings are independent from bindings; creating a binding with bind doesn't count as a multibinding, and
+ * adding a multibinding doesn't allow to inject the type (it only allows to retrieve multibindings through the
+ * getMultibindings method of the injector).
+ *
+ * Unlike bindings, where adding a the same binding twice is allowed (and ignored), adding the same multibinding
+ * multiple times will result in the creation of multiple "equivalent" instances, that will all be returned by
+ * getMultibindings().
+ *
+ * Another difference compared with normal bindings is that this can't be used to bind a
+ * std::function<std::unique_ptr<I>(Args...)> to a std::function<std::unique_ptr<C>(Args...)> or a
+ * std::function<C(Args...)>.
+ *
+ * As bind(), this supports annotated injection, just wrap I and/or C in fruit::Annotated<> if desired. See the
+ * documentation of bind() for more details.
+ */
+ template <typename I, typename C>
+ PartialComponent<fruit::impl::AddMultibinding<I, C>, Bindings...> addMultibinding();
+
+ /**
+ * Similar to bindInstance(), but adds a multibinding instead.
+ *
+ * Multibindings are independent from bindings; creating a binding with bindInstance doesn't count as a
+ * multibinding, and adding a multibinding doesn't allow to inject the type (it only allows to retrieve
+ * multibindings through the getMultibindings method of the injector).
+ *
+ * Unlike bindings, where adding a the same binding twice is allowed (and ignored), adding several multibindings for
+ * the same instance will result in duplicated values in the result of getMultibindings.
+ *
+ * Another difference compared to bindInstance() is that you can't use this to bind a const& (note that there is no
+ * overload of this method that takes a const&).
+ *
+ * This method adds a multibinding for C. If the object implements an interface I and you want to add a multibinding
+ * for that interface instead, you must cast the object to I& before passing it to this method.
+ *
+ * Note that this takes the instance by reference, not by value; it must remain valid for the entire lifetime of this
+ * component and of any injectors created from this component.
+ *
+ * Example use:
+ *
+ * class MyClass {
+ * ...
+ * };
+ *
+ * fruit::Component<> getMyComponent() {
+ * static MyClass x = MyClass(...);
+ * static MyClass y = MyClass(...);
+ * return fruit::createComponent()
+ * .addInstanceMultibinding(x)
+ * .addInstanceMultibinding(y);
+ * }
+ *
+ * fruit::Injector<> injector(getMyComponent);
+ * // This vector contains {&x, &y}.
+ * const std::vector<MyClass*>& objects = injector.getMultibindings<MyClass>();
+ */
+ template <typename C>
+ PartialComponent<fruit::impl::AddInstanceMultibinding<C>, Bindings...> addInstanceMultibinding(C& instance);
+
+ /**
+ * Similar to the previous version of addInstanceMultibinding(), but allows to specify an
+ * annotated type.
+ * Example use:
+ *
+ * struct MyAnnotation {};
+ *
+ * class MyClass {
+ * ...
+ * };
+ *
+ * fruit::Component<> getMyComponent() {
+ * static MyClass x = MyClass(...);
+ * static MyClass y = MyClass(...);
+ * return fruit::createComponent()
+ * .addInstanceMultibinding<fruit::Annotated<MyAnnotation, MyClass>>(x)
+ * .addInstanceMultibinding<fruit::Annotated<MyAnnotation, MyClass>>(y);
+ * }
+ *
+ * fruit::Injector<> injector(getMyComponent);
+ * // This vector contains {&x, &y}.
+ * const std::vector<MyClass*>& objects = injector.getMultibindings<fruit::Annotated<MyAnnotation, MyClass>>();
+ */
+ template <typename AnnotatedC, typename C>
+ PartialComponent<fruit::impl::AddInstanceMultibinding<AnnotatedC>, Bindings...> addInstanceMultibinding(C& instance);
+
+ /**
+ * Equivalent to calling addInstanceMultibinding() for each elements of `instances'.
+ * See the documentation of addInstanceMultibinding() for more details.
+ *
+ * Note that this takes the vector by reference, not by value; the vector (and its elements) must remain valid for the
+ * entire lifetime of this component and of any injectors created from this component.
+ *
+ * Example use:
+ *
+ * class MyClass {
+ * ...
+ * };
+ *
+ * fruit::Component<> getMyComponent() {
+ * static MyClass x = MyClass(...);
+ * static std::vector<MyClass> other_objects{MyClass(...), MyClass(...)};
+ * return fruit::createComponent()
+ * .addInstanceMultibinding(x)
+ * .addInstanceMultibindings(other_objects);
+ * }
+ *
+ * fruit::Injector<> injector(getMyComponent);
+ * // This vector contains {&x, &(other_objects[0]), &(other_objects[1])}.
+ * const std::vector<MyClass*>& objects = injector.getMultibindings<MyClass>();
+ */
+ template <typename C>
+ PartialComponent<fruit::impl::AddInstanceVectorMultibindings<C>, Bindings...>
+ addInstanceMultibindings(std::vector<C>& instances);
+
+ /**
+ * Similar to the previous version of addInstanceMultibindings(), but it allows to specify an annotated type.
+ *
+ * Example use:
+ *
+ * class MyClass {
+ * ...
+ * };
+ *
+ * fruit::Component<> getMyComponent() {
+ * static MyClass x = MyClass(...);
+ * static std::vector<MyClass> other_objects{MyClass(...), MyClass(...)};
+ * return fruit::createComponent()
+ * .addInstanceMultibinding<fruit::Annotated<MyAnnotation, MyClass>>(x)
+ * .addInstanceMultibindings<fruit::Annotated<MyAnnotation, MyClass>>(other_objects);
+ * }
+ *
+ * fruit::Injector<> injector(getMyComponent);
+ * // This vector contains {&x, &(other_objects[0]), &(other_objects[1])}.
+ * const std::vector<MyClass*>& objects = injector.getMultibindings<fruit::Annotated<MyAnnotation, MyClass>>();
+ */
+ template <typename AnnotatedC, typename C>
+ PartialComponent<fruit::impl::AddInstanceVectorMultibindings<AnnotatedC>, Bindings...>
+ addInstanceMultibindings(std::vector<C>& instances);
+
+ /**
+ * Similar to registerProvider, but adds a multibinding instead.
+ *
+ * Multibindings are independent from bindings; creating a binding with registerProvider doesn't count as a
+ * multibinding, and adding a multibinding doesn't allow to inject the type (it only allows to retrieve multibindings
+ * through the getMultibindings method of the injector).
+ *
+ * Unlike bindings, where adding a the same binding twice is allowed (and ignored), adding the same multibinding
+ * provider multiple times will result in the creation of multiple "equivalent" instances, that will all be returned
+ * by getMultibindings.
+ * It is good practice to add the multibindings in a component that is "close" to the injector in the get*Component
+ * call chain, to avoid adding the same multibinding more than once.
+ *
+ * Example use:
+ *
+ * class MyClass {
+ * public:
+ * MyClass(int n) {...}
+ * };
+ *
+ * fruit::Component<> getMyComponent() {
+ * return fruit::createComponent()
+ * .addMultibindingProvider([]() { return MyClass(10); })
+ * .addMultibindingProvider([]() { return MyClass(10); })
+ * .addMultibindingProvider([]() { return MyClass(20); });
+ * }
+ *
+ * fruit::Injector<> injector(getMyComponent);
+ * // This vector contains {&x, &y, &z} where x and y are MyClass objects constructed with 10 and z is a MyClass
+ * // object constructed with 20.
+ * const std::vector<MyClass*>& objects = injector.getMultibindings<MyClass>();
+ *
+ * Note that this method adds a multibinding for the type returned by the provider. If the returned object implements
+ * an interface I and you want to add a multibinding for that interface instead, you should cast the pointer to I*
+ * before returning it.
+ */
+ template <typename Lambda>
+ PartialComponent<fruit::impl::AddMultibindingProvider<Lambda>, Bindings...> addMultibindingProvider(Lambda lambda);
+
+ /**
+ * Similar to the previous version of addMultibindingProvider(), but allows to specify an annotated type
+ * for the provider. This allows to inject annotated types in the parameters and/or bind the
+ * provider to an annotated type.
+ *
+ * Example use:
+ *
+ * struct MyAnnotation1 {};
+ * struct MyAnnotation2 {};
+ *
+ * Component<fruit::Annotated<Annotation1, Bar>> getBarComponent() {...}
+ * Component<Baz> getBazComponent() {...}
+ *
+ * fruit::Component<> getFooComponent() {
+ * return fruit::createComponent()
+ * .install(getBarComponent)
+ * .install(getBazComponent)
+ * .registerMultibindingProvider<
+ * fruit::Annotated<Annotation2, Foo>(
+ * fruit::Annotated<Annotation1, Bar*>,
+ * Baz*)
+ * >([](Bar* bar, Baz* baz) {
+ * Foo foo(bar, baz);
+ * foo.initialize();
+ * return foo;
+ * });
+ * }
+ *
+ *
+ * fruit::Injector<> injector(getFooComponent);
+ * // This vector contains {&x} where x is an instance of Foo constructed using the lambda above, with injected
+ * // instances of Bar and Baz.
+ * const std::vector<MyClass*>& objects = injector.getMultibindings<fruit::Annotated<Annotation2, Foo>>();
+ */
+ template <typename AnnotatedSignature, typename Lambda>
+ PartialComponent<fruit::impl::AddMultibindingProvider<AnnotatedSignature, Lambda>, Bindings...>
+ addMultibindingProvider(Lambda lambda);
+
+ /**
+ * Registers `factory' as a factory of C, where `factory' is a lambda with no captures returning C.
+ * This is typically used for assisted injection (but it can also be used if no parameters are assisted).
+ *
+ * C can be any class (or fundamental) type. If C is std::unique_ptr<T>, the factory together with a bind<I,C> in the
+ * same component will automatically bind the corresponding std::function that returns a std::unique_ptr<I>.
+ * See the documentation of bind() for more details.
+ *
+ * The returned type can't be a pointer type. If you don't want to return it by value, you should return a
+ * std::unique_ptr instead.
+ *
+ * Example:
+ *
+ * Component<std::function<std::unique_ptr<MyClass>(int)>> getMyClassComponent() {
+ * fruit::createComponent()
+ * .install(getFooComponent)
+ * .registerFactory<std::unique_ptr<MyClass>(Foo*, fruit::Assisted<int>)>(
+ * [](Foo* foo, int n) {
+ * return std::unique_ptr<MyClass>(new MyClass(foo, n));
+ * });
+ * }
+ *
+ * Injector<std::function<std::unique_ptr<MyClass>(int)>> injector(getMyClassComponent);
+ *
+ * std::function<std::unique_ptr<MyClass>(int)> factory(injector);
+ * std::unique_ptr<MyClass> x = factory(42);
+ *
+ * The parameters marked as Assisted will become parameters of the std::function (in the same order), while the others
+ * (e.g. Foo in the example above) will be injected.
+ *
+ * Unlike registerProvider(), where the signature is inferred, for this method the signature (including any Assisted
+ * annotations) must be specified explicitly, while the second template parameter is inferred.
+ *
+ * If the only thing that the factory does is to call new and the constructor of the class, it's usually more
+ * convenient to use an Inject typedef or INJECT macro instead, e.g. the following are equivalent to the above:
+ *
+ * class MyClass {
+ * public:
+ * using Inject = MyClass(Foo*, Assisted<int>);
+ *
+ * MyClass(Foo* foo, int n) {...}
+ * };
+ *
+ * or:
+ *
+ * class MyClass {
+ * public:
+ * INJECT(MyClass(Foo* foo, ASSISTED(int) n)) {...}
+ * };
+ *
+ * Use registerFactory() when you want to inject the class in different ways in different components (just make sure
+ * those don't end up in the same injector), or when MyClass is a third-party class that can't be modified.
+ *
+ * registerFactory() can't be called with a plain function, but you can write a lambda that wraps the function to
+ * achieve the same result.
+ *
+ * Registering stateful functors (including lambdas with captures) is NOT supported.
+ * However, you can write something like:
+ *
+ * struct Functor {
+ * Functor(float x) {...}
+ * std::unique_ptr<MyClass> operator()(Foo* foo, int n) {...}
+ * };
+ *
+ * Component<std::function<std::unique_ptr<MyClass>(int)>> getMyClassComponent() {
+ * static const Functor aFunctor(42.0);
+ * return fruit::createComponent()
+ * ... // Bind Foo
+ * .bindInstance(aFunctor)
+ * .registerFactory<
+ * std::unique_ptr<MyClass>(
+ * Functor functor,
+ * Foo*,
+ * Assisted<int>)
+ * >([](Functor functor, Foo* foo, int n) {
+ * return functor(foo, n);
+ * });
+ * }
+ */
+ template <typename DecoratedSignature, typename Factory>
+ PartialComponent<fruit::impl::RegisterFactory<DecoratedSignature, Factory>, Bindings...>
+ registerFactory(Factory factory);
+
+ /**
+ * Adds the bindings (and multibindings) in the Component obtained by calling fun(args...) to the current component.
+ *
+ * For example, these component functions:
+ * fruit::Component<Foo> getComponent1();
+ * fruit::Component<Bar> getComponent2(int n, std::string s);
+ *
+ * can be combined as:
+ *
+ * fruit::Component<Foo, Bar> getFooBarComponent() {
+ * return fruit::createComponent()
+ * .install(getComponent1)
+ * .install(getComponent2, 5, std::string("Hello"));
+ * }
+ *
+ * If any `args` are provided, they must be:
+ * - Copy-constructible
+ * - Move-constructible
+ * - Assignable
+ * - Move-assignable
+ * - Equality comparable (i.e., operator== must be defined for two values of that type)
+ * - Hashable (i.e., std::hash must be defined for values of that type)
+ *
+ * Note that this only applies to `args`. E.g. in the example above `int` and `std::string` must satisfy this
+ * requirement (and they do), but `Foo` and `Bar` don't need to.
+ *
+ * Args and FormalArgs (if any) must be the same types; or to be precise, each type in Args must be convertible into
+ * the corresponding type in FormalArgs.
+ *
+ * A lambda with no captures can also be used as the first argument, for example:
+ *
+ * fruit::Component<Foo, Bar> getFooBarComponent() {
+ * return fruit::createComponent()
+ * .install([]() { return getComponent1(); })
+ * .install([](int n) { return getComponent2(n, std::string("Hello")); }, 5);
+ * }
+ *
+ * These two install() calls are equivalent to the previous ones.
+ *
+ * As in the example, the template parameters for this method will be inferred by the compiler, it's not necessary to
+ * specify them explicitly.
+ *
+ * Fruit automatically de-duplicates install() calls, so they're effectively memoized (within each injector).
+ * For example, in this code:
+ *
+ * fruit::Component<Foo> getFooComponent() {...}
+ *
+ * fruit::Component<Bar> getBarComponent() {
+ * return fruit::createComponent()
+ * .install(getFooComponent)
+ * .bind<Bar, BarImpl>();
+ * }
+ *
+ * fruit::Component<Baz> getBazComponent() {
+ * return fruit::createComponent()
+ * .install(getFooComponent)
+ * .bind<Baz, BazImpl>();
+ * }
+ *
+ * fruit::Component<Bar, Baz> getBarBazComponent() {
+ * return fruit::createComponent()
+ * .install(getBarComponent)
+ * .install(getBazComponent);
+ * }
+ *
+ * fruit::Injector<Bar, Baz> injector(getBarBazComponent);
+ *
+ *
+ * getFooComponent() will only be called once.
+ * For Component functions with arguments, only one call will be done for each set of arguments, but multiple calls
+ * might be made if multiple sets of arguments are used.
+ *
+ * If you actually want a Component function to be called/installed multiple times (e.g. if it binds a multibinding
+ * and you actually want multiple multibindings to be bound) you can add a dummy argument and specify different values
+ * for that argument when installing the component.
+ */
+ template <typename... OtherComponentParams, typename... FormalArgs, typename... Args>
+ PartialComponent<fruit::impl::InstallComponent<fruit::Component<OtherComponentParams...>(FormalArgs...)>, Bindings...>
+ install(fruit::Component<OtherComponentParams...> (*)(FormalArgs...), Args&&... args);
+
+ /**
+ * This class is returned by PartialComponent::replace, see the documentation of that method for more information.
+ */
+ template <typename ReplacedComponent, typename... GetReplacedComponentFormalArgs>
+ class PartialComponentWithReplacementInProgress {
+ private:
+ using storage_t = fruit::impl::PartialComponentStorage<
+ fruit::impl::PartialReplaceComponent<ReplacedComponent(GetReplacedComponentFormalArgs...)>, Bindings...>;
+
+ public:
+ template <typename... FormalArgs, typename... Args>
+ PartialComponent<fruit::impl::ReplaceComponent<ReplacedComponent(GetReplacedComponentFormalArgs...),
+ ReplacedComponent(FormalArgs...)>,
+ Bindings...>
+ with(ReplacedComponent (*)(FormalArgs...), Args&&... args);
+
+ PartialComponentWithReplacementInProgress(storage_t storage) : storage(storage) {}
+
+ private:
+ storage_t storage;
+
+ PartialComponentWithReplacementInProgress() = delete;
+ };
+
+ /**
+ * This allows to replace an installed Component with another one. This is useful for testing.
+ * For example, if you have these components:
+ *
+ * fruit::Component<MyDependency> getDependencyComponent() {...}
+ *
+ * fruit::Component<Foo> getFooComponent() {
+ * return fruit::createComponent()
+ * .install(getDependencyComponent)
+ * .bind<Foo, FooImpl>();
+ * }
+ *
+ * fruit::Component<Bar> getBarComponent() {
+ * return fruit::createComponent()
+ * .install(getFooComponent)
+ * .bind<Bar, BarImpl>();
+ * }
+ *
+ * When you test Bar, you might want to replace getDependencyComponent with a component that binds a fake
+ * MyDependency:
+ *
+ * fruit::Component<MyDependency> getFakeDependencyComponent() {...}
+ *
+ * To do so, you can define a component like:
+ *
+ * fruit::Component<Bar> getBarComponentWithFakeDependency() {
+ * return fruit::createComponent()
+ * .replace(getDependencyComponent).with(getFakeDependencyComponent)
+ * .install(getBarComponent);
+ * }
+ *
+ * This component is equivalent to:
+ *
+ * fruit::Component<Bar> getBarComponentWithFakeDependency() {
+ * return fruit::createComponent()
+ * .install(getFakeDependencyComponent)
+ * .bind<Foo, FooImpl>()
+ * .bind<Bar, BarImpl>();
+ * }
+ *
+ * However this way you don't need to duplicate the bindings for Foo and Bar, and you don't even need to include them
+ * in the translation unit (i.e., cc/cpp file) that defines getBarComponentWithFakeDependency().
+ * In codebases with many layers, this can save a lot of duplication.
+ *
+ * Note that the .replace(...).with(...) must appear *before* installing the component to which it's applied to;
+ * e.g., in the example above note how we install getBarComponent after the replacement in
+ * getBarComponentWithFakeDependency.
+ * If you add a replacement after the replaced component has been installed, Fruit will report an error at run-time.
+ *
+ * In the example above, the replaced and replacement component functions had no arguments, however it's also possible
+ * to replace component function with args. The arguments of the replaced and replacement component functions are
+ * independent; for example .replace(getDependencyComponentWithArgs, 15).with(myFakeComponentWithNoArgs) is allowed
+ * and it would replace all install(getDependencyComponentWithArgs, 15) calls with install(myFakeComponentWithNoArgs).
+ *
+ * The component types returned by the replaced and replacement components must be the same. For example, this is NOT
+ * allowed:
+ *
+ * fruit::Component<MyDependency, SomethingElse> getFakeDependencyComponentWithSomethingElse() {...}
+ *
+ * fruit::Component<Bar> getBarComponentWithFakeDependency() {
+ * return fruit::createComponent()
+ * .replace(getDependencyComponent).with(getFakeDependencyComponentWithSomethingElse) // error!
+ * .install(getBarComponent);
+ * }
+ *
+ * But replacing a replaced component is allowed:
+ *
+ * fruit::Component<MyDependency> getOtherFakeDependencyComponent() {...}
+ *
+ * fruit::Component<Bar> getBarComponentWithOtherFakeDependency() {
+ * return fruit::createComponent()
+ * // The two replacements can appear in any order, but they must both be before the install().
+ * .replace(getFakeDependencyComponent).with(getOtherFakeDependencyComponent)
+ * .replace(getDependencyComponent).with(getFakeDependencyComponent)
+ * .install(getBarComponent);
+ * }
+ *
+ * Of course this is a simple example, in the real world the replacements and the install would probably come from
+ * other components.
+ *
+ * And note that you can also replace components that define replacements, for example:
+ *
+ * fruit::Component<> getFakeDependencyReplacementComponent() {
+ * return fruit::createComponent()
+ * .replace(getDependencyComponent).with(getFakeDependencyComponentWithSomethingElse);
+ * }
+ *
+ * fruit::Component<...> getComponent() {
+ * return fruit::createComponent()
+ * .replace(getFakeDependencyReplacementComponent).with(...)
+ * .install(...);
+ * }
+ *
+ * Replacements are only installed if the replaced component is installed, otherwise they are ignored.
+ * In the first example above, if getFooComponent didn't install getDependencyComponent, when a test creates an
+ * injector for getBarComponentWithFakeDependency it would not install getFakeDependencyComponent.
+ */
+ template <typename... OtherComponentParams, typename... FormalArgs, typename... Args>
+ typename PartialComponent<Bindings...>::template PartialComponentWithReplacementInProgress<
+ fruit::Component<OtherComponentParams...>, FormalArgs...>
+ replace(fruit::Component<OtherComponentParams...> (*)(FormalArgs...), Args&&... args);
+
+ ~PartialComponent();
+
+private:
+ template <typename... OtherBindings>
+ friend class PartialComponent;
+
+ template <typename... Types>
+ friend class Component;
+
+ fruit::impl::PartialComponentStorage<Bindings...> storage;
+
+ // Do not use. Use fruit::createComponent() instead.
+ PartialComponent() = delete;
+
+ // Do not use. Only use PartialComponent for temporaries, and then convert it to a Component.
+ PartialComponent(const PartialComponent&) = delete;
+ PartialComponent(PartialComponent&&) = delete;
+
+ PartialComponent(fruit::impl::PartialComponentStorage<Bindings...> storage);
+
+ template <typename NewBinding>
+ using OpFor = typename fruit::impl::meta::OpForComponent<Bindings...>::template AddBinding<NewBinding>;
+
+ friend PartialComponent<> createComponent();
+};
+
+} // namespace fruit
+
+#include <fruit/impl/component.defn.h>
+
+#endif // FRUIT_COMPONENT_H
diff --git a/include/fruit/fruit.h b/include/fruit/fruit.h
new file mode 100644
index 0000000..ff208e7
--- /dev/null
+++ b/include/fruit/fruit.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FRUIT_H
+#define FRUIT_FRUIT_H
+
+// This header includes all the public Fruit headers.
+// To limit the amount of included code, you might want to only include the necessary headers (if only forward
+// declarations are needed you can include fruit_forward_decls.h).
+
+// This include is not required here, but having it here shortens the include trace in error messages.
+#include <fruit/impl/injection_errors.h>
+
+#include <fruit/component.h>
+#include <fruit/fruit_forward_decls.h>
+#include <fruit/injector.h>
+#include <fruit/macro.h>
+#include <fruit/normalized_component.h>
+#include <fruit/provider.h>
+
+#endif // FRUIT_FRUIT_H
diff --git a/include/fruit/fruit_forward_decls.h b/include/fruit/fruit_forward_decls.h
new file mode 100644
index 0000000..af02706
--- /dev/null
+++ b/include/fruit/fruit_forward_decls.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FRUIT_FORWARD_DECLS_H
+#define FRUIT_FRUIT_FORWARD_DECLS_H
+
+// This header contains forward declarations of all types in the `fruit' namespace.
+// Avoid writing forward declarations yourself; use this header instead.
+
+namespace fruit {
+
+/**
+ * This is used to group the requirements of Component. See Component for details.
+ * Note: this type is declared but not defined; that's by design since instances of this type are not meaningful.
+ */
+template <typename... Types>
+struct Required;
+
+/**
+ * This is used to "annotate" T as a type that uses assisted injection. See PartialComponent for details.
+ * Note: this type is declared but not defined; that's by design since instances of this type are not meaningful.
+ */
+template <typename T>
+struct Assisted;
+
+/**
+ * This is used to annotate T as a type that will be user-supplied instead of being injected.
+ * See PartialComponent::registerFactory for details.
+ */
+template <typename Annotation, typename T>
+struct Annotated {};
+
+template <typename... Types>
+class Component;
+
+class EmptyPartialComponent;
+
+template <typename... Bindings>
+class PartialComponent;
+
+template <typename... Types>
+class NormalizedComponent;
+
+template <typename C>
+class Provider;
+
+template <typename... P>
+class Injector;
+
+} // namespace fruit
+
+#endif // FRUIT_FRUIT_FORWARD_DECLS_H
diff --git a/include/fruit/impl/bindings.h b/include/fruit/impl/bindings.h
new file mode 100644
index 0000000..c8694e5
--- /dev/null
+++ b/include/fruit/impl/bindings.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_BINDINGS_H
+#define FRUIT_BINDINGS_H
+
+#include <fruit/impl/meta/metaprogramming.h>
+
+namespace fruit {
+namespace impl {
+
+// The types here represent individual entries added in a PartialComponent.
+
+/**
+ * Binds the base class I to the implementation C.
+ * I must be a base class of C. I=C is not allowed.
+ * I and/or C may be annotated using fruit::Annotated<>.
+ */
+template <typename I, typename C>
+struct Bind {};
+
+/**
+ * Registers Signature as the constructor signature to use to inject a type.
+ * Signature must be a valid signature and its return type must be constructible with those argument
+ * types.
+ * The arguments and the return type can be annotated using fruit::Annotated<>.
+ */
+template <typename Signature>
+struct RegisterConstructor {};
+
+/**
+ * Binds an instance (i.e., object) to the type C.
+ * AnnotatedC may be annotated using fruit::Annotated<>.
+ * NOTE: for this binding, the runtime binding is added in advance.
+ */
+template <typename AnnotatedC, typename C>
+struct BindInstance {};
+
+/**
+ * A variant of BindInstance that binds a constant reference.
+ */
+template <typename AnnotatedC, typename C>
+struct BindConstInstance {};
+
+template <typename... Params>
+struct RegisterProvider;
+
+/**
+ * Registers `provider' as a provider of C, where provider is a lambda with no captures returning
+ * either C or C*.
+ */
+template <typename Lambda>
+struct RegisterProvider<Lambda> {};
+
+/**
+ * Registers `provider' as a provider of C, where provider is a lambda with no captures returning
+ * either C or C*. Lambda must have the signature AnnotatedSignature (ignoring annotations).
+ */
+template <typename AnnotatedSignature, typename Lambda>
+struct RegisterProvider<Lambda, AnnotatedSignature> {};
+
+/**
+ * Adds a multibinding for an instance (as a C&).
+ */
+template <typename C>
+struct AddInstanceMultibinding {};
+
+/**
+ * Adds multibindings for a vector of instances (as a std::vector<C>&).
+ */
+template <typename C>
+struct AddInstanceVectorMultibindings {};
+
+/**
+ * Similar to Bind<I, C>, but adds a multibinding instead.
+ */
+template <typename I, typename C>
+struct AddMultibinding {};
+
+template <typename... Params>
+struct AddMultibindingProvider;
+
+/**
+ * Similar to RegisterProvider, but adds a multibinding instead.
+ */
+template <typename Lambda>
+struct AddMultibindingProvider<Lambda> {};
+
+/**
+ * Similar to RegisterProvider, but adds a multibinding instead.
+ * Lambda must have the signature AnnotatedSignature (ignoring annotations).
+ */
+template <typename AnnotatedSignature, typename Lambda>
+struct AddMultibindingProvider<AnnotatedSignature, Lambda> {};
+
+/**
+ * Registers `Lambda' as a factory of C, where `Lambda' is a lambda with no captures returning C.
+ * Lambda must have signature DecoratedSignature (ignoring any fruit::Annotated<> and
+ * fruit::Assisted<>).
+ * Lambda must return a C by value, or a std::unique_ptr<C>.
+ */
+template <typename DecoratedSignature, typename Lambda>
+struct RegisterFactory {};
+
+/**
+ * Adds the bindings (and multibindings) in `component' to the current component.
+ * OtherComponent must be of the form Component<...>.
+ * NOTE: for this binding, the runtime binding is added in advance.
+ */
+template <typename GetComponentFunction>
+struct InstallComponent {};
+
+/**
+ * An in-progress ReplaceComponent operation, where we don't have all the required information yet.
+ */
+template <typename GetReplacedComponent>
+struct PartialReplaceComponent {};
+
+/**
+ * Replaces install()s for a component with install()s for another one.
+ * The two Get*Component function signatures must return the same Component<...> type.
+ */
+template <typename GetReplacedComponent, typename GetReplacementComponent>
+struct ReplaceComponent {};
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_BINDINGS_H
diff --git a/include/fruit/impl/component.defn.h b/include/fruit/impl/component.defn.h
new file mode 100644
index 0000000..ce3fb90
--- /dev/null
+++ b/include/fruit/impl/component.defn.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_COMPONENT_DEFN_H
+#define FRUIT_COMPONENT_DEFN_H
+
+#include <fruit/component.h>
+
+#include <fruit/impl/component_storage/component_storage.h>
+#include <fruit/impl/injection_errors.h>
+
+#include <memory>
+
+namespace fruit {
+
+namespace impl {
+namespace meta {
+// This is a helper class used in the implementation of Component and PartialComponent.
+// It's in fruit::impl::meta so that we don't need to qualify everything with fruit::impl::meta.
+template <typename... PreviousBindings>
+struct OpForComponent {
+ template <typename Comp>
+ using ConvertTo = Eval<Call(ReverseComposeFunctors(Id<ComponentFunctor(ConvertComponent, Comp)>,
+ ProcessDeferredBindings, Id<ProcessBinding(PreviousBindings)>...),
+ ConstructComponentImpl())>;
+
+ template <typename Binding>
+ using AddBinding =
+ Eval<Call(ReverseComposeFunctors(Id<ProcessBinding(Binding)>, Id<ProcessBinding(PreviousBindings)>...),
+ ConstructComponentImpl())>;
+};
+} // namespace meta
+} // namespace impl
+
+template <typename... Params>
+template <typename... Bindings>
+inline Component<Params...>::Component(PartialComponent<Bindings...>&& partial_component) : storage() {
+
+ (void)typename fruit::impl::meta::CheckIfError<Comp>::type();
+
+ using Op = typename fruit::impl::meta::OpForComponent<Bindings...>::template ConvertTo<Comp>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+#ifndef FRUIT_NO_LOOP_CHECK
+ (void)typename fruit::impl::meta::CheckIfError<
+ fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
+#endif // !FRUIT_NO_LOOP_CHECK
+
+ std::size_t num_entries = partial_component.storage.numBindings() + Op().numEntries();
+ fruit::impl::FixedSizeVector<fruit::impl::ComponentStorageEntry> entries(num_entries);
+
+ Op()(entries);
+
+ // addBindings may modify the storage member of PartialComponent.
+ // Therefore, it should not be used after this operation.
+ partial_component.storage.addBindings(entries);
+
+ // TODO: re-enable this check somehow.
+ // component.component.already_converted_to_component = true;
+
+ FruitAssert(entries.size() == num_entries);
+
+ storage = fruit::impl::ComponentStorage(std::move(entries));
+}
+
+template <typename... Bindings>
+inline PartialComponent<Bindings...>::~PartialComponent() {}
+
+inline PartialComponent<> createComponent() {
+ return {{}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedI, typename AnnotatedC>
+inline PartialComponent<fruit::impl::Bind<AnnotatedI, AnnotatedC>, Bindings...> PartialComponent<Bindings...>::bind() {
+ using Op = OpFor<fruit::impl::Bind<AnnotatedI, AnnotatedC>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedSignature>
+inline PartialComponent<fruit::impl::RegisterConstructor<AnnotatedSignature>, Bindings...>
+PartialComponent<Bindings...>::registerConstructor() {
+ using Op = OpFor<fruit::impl::RegisterConstructor<AnnotatedSignature>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage}};
+}
+
+template <typename... Bindings>
+template <typename C>
+inline PartialComponent<fruit::impl::BindInstance<C, C>, Bindings...>
+PartialComponent<Bindings...>::bindInstance(C& instance) {
+ using Op = OpFor<fruit::impl::BindInstance<C, C>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage, instance}};
+}
+
+template <typename... Bindings>
+template <typename C>
+inline PartialComponent<fruit::impl::BindConstInstance<C, C>, Bindings...>
+PartialComponent<Bindings...>::bindInstance(const C& instance) {
+ using Op = OpFor<fruit::impl::BindConstInstance<C, C>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage, instance}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedC, typename C>
+inline PartialComponent<fruit::impl::BindInstance<AnnotatedC, C>, Bindings...>
+PartialComponent<Bindings...>::bindInstance(C& instance) {
+ using Op = OpFor<fruit::impl::BindInstance<AnnotatedC, C>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage, instance}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedC, typename C>
+inline PartialComponent<fruit::impl::BindConstInstance<AnnotatedC, C>, Bindings...>
+PartialComponent<Bindings...>::bindInstance(const C& instance) {
+ using Op = OpFor<fruit::impl::BindConstInstance<AnnotatedC, C>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage, instance}};
+}
+
+template <typename... Bindings>
+template <typename Lambda>
+inline PartialComponent<fruit::impl::RegisterProvider<Lambda>, Bindings...>
+PartialComponent<Bindings...>::registerProvider(Lambda) {
+ using Op = OpFor<fruit::impl::RegisterProvider<Lambda>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedSignature, typename Lambda>
+inline PartialComponent<fruit::impl::RegisterProvider<AnnotatedSignature, Lambda>, Bindings...>
+PartialComponent<Bindings...>::registerProvider(Lambda) {
+ using Op = OpFor<fruit::impl::RegisterProvider<AnnotatedSignature, Lambda>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedI, typename AnnotatedC>
+inline PartialComponent<fruit::impl::AddMultibinding<AnnotatedI, AnnotatedC>, Bindings...>
+PartialComponent<Bindings...>::addMultibinding() {
+ using Op = OpFor<fruit::impl::AddMultibinding<AnnotatedI, AnnotatedC>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage}};
+}
+
+template <typename... Bindings>
+template <typename C>
+inline PartialComponent<fruit::impl::AddInstanceMultibinding<C>, Bindings...>
+PartialComponent<Bindings...>::addInstanceMultibinding(C& instance) {
+ using Op = fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
+ fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>)>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage, instance}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedC, typename C>
+inline PartialComponent<fruit::impl::AddInstanceMultibinding<AnnotatedC>, Bindings...>
+PartialComponent<Bindings...>::addInstanceMultibinding(C& instance) {
+ using Op = fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
+ fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>)>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage, instance}};
+}
+
+template <typename... Bindings>
+template <typename C>
+inline PartialComponent<fruit::impl::AddInstanceVectorMultibindings<C>, Bindings...>
+PartialComponent<Bindings...>::addInstanceMultibindings(std::vector<C>& instances) {
+ using Op = fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
+ fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>)>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return {{storage, instances}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedC, typename C>
+inline PartialComponent<fruit::impl::AddInstanceVectorMultibindings<AnnotatedC>, Bindings...>
+PartialComponent<Bindings...>::addInstanceMultibindings(std::vector<C>& instances) {
+ using Op = fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
+ fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>)>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage, instances}};
+}
+
+template <typename... Bindings>
+template <typename Lambda>
+inline PartialComponent<fruit::impl::AddMultibindingProvider<Lambda>, Bindings...>
+PartialComponent<Bindings...>::addMultibindingProvider(Lambda) {
+ using Op = OpFor<fruit::impl::AddMultibindingProvider<Lambda>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage}};
+}
+
+template <typename... Bindings>
+template <typename AnnotatedSignature, typename Lambda>
+inline PartialComponent<fruit::impl::AddMultibindingProvider<AnnotatedSignature, Lambda>, Bindings...>
+PartialComponent<Bindings...>::addMultibindingProvider(Lambda) {
+ using Op = OpFor<fruit::impl::AddMultibindingProvider<AnnotatedSignature, Lambda>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage}};
+}
+
+template <typename... Bindings>
+template <typename DecoratedSignature, typename Lambda>
+inline PartialComponent<fruit::impl::RegisterFactory<DecoratedSignature, Lambda>, Bindings...>
+PartialComponent<Bindings...>::registerFactory(Lambda) {
+ using Op = OpFor<fruit::impl::RegisterFactory<DecoratedSignature, Lambda>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return {{storage}};
+}
+
+template <typename... Bindings>
+inline PartialComponent<Bindings...>::PartialComponent(fruit::impl::PartialComponentStorage<Bindings...> storage)
+ : storage(std::move(storage)) {}
+
+template <typename T>
+FRUIT_ALWAYS_INLINE inline int checkAcceptableComponentInstallArg() {
+ // This lambda checks that the required operations on T exist.
+ // Note that the lambda is never actually executed.
+ auto checkRequirements = [](const T& constRef, T value) {
+ T x1(constRef);
+ T x2(std::move(value));
+ x1 = constRef;
+ x2 = std::move(value);
+ bool b = (constRef == constRef);
+ std::size_t h = std::hash<T>()(constRef);
+ (void)x1;
+ (void)x2;
+ (void)b;
+ (void)h;
+ };
+ (void)checkRequirements;
+ return 0;
+}
+
+template <typename... Bindings>
+template <typename... OtherComponentParams, typename... FormalArgs, typename... Args>
+inline PartialComponent<fruit::impl::InstallComponent<fruit::Component<OtherComponentParams...>(FormalArgs...)>,
+ Bindings...>
+PartialComponent<Bindings...>::install(fruit::Component<OtherComponentParams...> (*getComponent)(FormalArgs...),
+ Args&&... args) {
+ using IntCollector = int[];
+ (void)IntCollector{0, checkAcceptableComponentInstallArg<FormalArgs>()...};
+
+ using Op = OpFor<fruit::impl::InstallComponent<fruit::Component<OtherComponentParams...>(FormalArgs...)>>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ std::tuple<FormalArgs...> args_tuple{std::forward<Args>(args)...};
+
+ return {{storage, getComponent, std::move(args_tuple)}};
+}
+
+template <typename... Bindings>
+template <typename... OtherComponentParams, typename... FormalArgs, typename... Args>
+inline typename PartialComponent<Bindings...>::template PartialComponentWithReplacementInProgress<
+ fruit::Component<OtherComponentParams...>, FormalArgs...>
+PartialComponent<Bindings...>::replace(fruit::Component<OtherComponentParams...> (*getReplacedComponent)(FormalArgs...),
+ Args&&... args) {
+ using IntCollector = int[];
+ (void)IntCollector{0, checkAcceptableComponentInstallArg<FormalArgs>()...};
+
+ std::tuple<FormalArgs...> args_tuple{std::forward<Args>(args)...};
+
+ return {{storage, getReplacedComponent, std::move(args_tuple)}};
+}
+
+template <typename... Bindings>
+template <typename OtherComponent, typename... GetReplacedComponentFormalArgs>
+template <typename... GetReplacementComponentFormalArgs, typename... Args>
+inline PartialComponent<fruit::impl::ReplaceComponent<OtherComponent(GetReplacedComponentFormalArgs...),
+ OtherComponent(GetReplacementComponentFormalArgs...)>,
+ Bindings...>
+PartialComponent<Bindings...>::
+ PartialComponentWithReplacementInProgress<OtherComponent, GetReplacedComponentFormalArgs...>::with(
+ OtherComponent (*getReplacementComponent)(GetReplacementComponentFormalArgs...), Args&&... args) {
+ using IntCollector = int[];
+ (void)IntCollector{0, checkAcceptableComponentInstallArg<GetReplacementComponentFormalArgs>()...};
+
+ std::tuple<GetReplacementComponentFormalArgs...> args_tuple{std::forward<Args>(args)...};
+
+ return {{storage, getReplacementComponent, std::move(args_tuple)}};
+}
+
+} // namespace fruit
+
+#endif // FRUIT_COMPONENT_DEFN_H
diff --git a/include/fruit/impl/component_functors.defn.h b/include/fruit/impl/component_functors.defn.h
new file mode 100644
index 0000000..c65f953
--- /dev/null
+++ b/include/fruit/impl/component_functors.defn.h
@@ -0,0 +1,1262 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_COMPONENT_FUNCTORS_DEFN_H
+#define FRUIT_COMPONENT_FUNCTORS_DEFN_H
+
+#include <fruit/component.h>
+
+#include <fruit/impl/injection_debug_errors.h>
+#include <fruit/impl/injection_errors.h>
+#include <fruit/impl/injector/injector_storage.h>
+
+#include <memory>
+
+/*********************************************************************************************************************************
+ This file contains functors that take a Comp and return a struct Op with the form:
+ struct {
+ using Result = Comp1;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {...}
+ std::size_t numEntries() {...}
+ }
+*********************************************************************************************************************************/
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+struct GetResult {
+ template <typename F>
+ struct apply {
+ using type = typename F::Result;
+ };
+};
+
+// Call(ComponentFunctor(F, Args...), Comp)
+// is equivalent to:
+// F(Comp, Args...)
+struct ComponentFunctor {
+ template <typename F, typename... Args>
+ struct apply {
+ struct type {
+ template <typename Comp>
+ struct apply {
+ using type = F(Comp, Args...);
+ };
+ };
+ };
+};
+
+struct ComponentFunctorIdentity {
+ template <typename Comp>
+ struct apply {
+ struct type {
+ using Result = Comp;
+ void operator()(FixedSizeVector<ComponentStorageEntry>&) {}
+ std::size_t numEntries() {
+ return 0;
+ }
+ };
+ };
+};
+
+struct Compose2ComponentFunctors {
+ template <typename F1, typename F2>
+ struct apply {
+ struct type {
+ template <typename Comp>
+ struct apply {
+ using Op1 = F1(Comp);
+ using Op2 = F2(GetResult(Op1));
+ struct Op {
+ using Result = Eval<GetResult(Op2)>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ Eval<Op2>()(entries);
+ Eval<Op1>()(entries);
+ }
+ std::size_t numEntries() {
+ return Eval<Op1>().numEntries() + Eval<Op2>().numEntries();
+ }
+ };
+ using type = PropagateError(Op1, PropagateError(Op2, Op));
+ };
+ };
+ };
+};
+
+// ComposeFunctors(F1,..,Fn) returns a functor that executes F1,..,Fn in order (stopping at the
+// first Error).
+struct ComposeFunctors {
+ template <typename... Functors>
+ struct apply {
+ using type = Fold(Compose2ComponentFunctors, ComponentFunctorIdentity, Functors...);
+ };
+};
+
+// ReverseComposeFunctors(T1, ..., Tn) is equivalent to ComposeFunctors(Tn, ..., T1), but it's more
+// efficient when all of the following must be evaluated:
+// ReverseComposeFunctors<T1>
+// ReverseComposeFunctors<T2, T1>
+// ReverseComposeFunctors<T3, T2, T1>
+// In that case, this implementation shares many more instantiations with previous invocations
+struct ReverseComposeFunctors {
+ template <typename... Functors>
+ struct apply {
+ using type = ComponentFunctorIdentity;
+ };
+
+ template <typename Functor>
+ struct apply<Functor> {
+ using type = Functor;
+ };
+
+ template <typename Functor, typename... Functors>
+ struct apply<Functor, Functors...> {
+ using type = Compose2ComponentFunctors(ReverseComposeFunctors(Functors...), Functor);
+ };
+};
+
+struct EnsureProvidedType;
+
+struct EnsureProvidedTypes;
+
+// Doesn't actually bind in ComponentStorage. The binding is added later (if needed) using ProcessInterfaceBinding.
+struct AddDeferredInterfaceBinding {
+ template <typename Comp, typename AnnotatedI, typename AnnotatedC>
+ struct apply {
+ using Comp1 = ConsComp(typename Comp::RsSuperset, typename Comp::Ps, typename Comp::NonConstRsPs,
+#ifndef FRUIT_NO_LOOP_CHECK
+ typename Comp::Deps,
+#endif
+ PushFront(typename Comp::InterfaceBindings, Pair<AnnotatedI, AnnotatedC>),
+ typename Comp::DeferredBindingFunctors);
+ struct Op {
+ // Note that we do NOT call AddProvidedType here. We'll only know the right required type
+ // when the binding will be used.
+ using Result = Eval<Comp1>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>&) {}
+ std::size_t numEntries() {
+ return 0;
+ }
+ };
+ using I = RemoveAnnotations(AnnotatedI);
+ using C = RemoveAnnotations(AnnotatedC);
+ using type =
+ If(IsSame(I, C), ConstructError(InterfaceBindingToSelfErrorTag, C),
+ If(Not(IsBaseOf(I, C)), ConstructError(NotABaseClassOfErrorTag, I, C),
+ If(Not(IsSame(I, NormalizeType(I))), ConstructError(NonClassTypeErrorTag, I, NormalizeUntilStable(I)),
+ If(Not(IsSame(C, NormalizeType(C))),
+ // We handle this case too, just to be on the safe side, but this should never happen.
+ ConstructError(NonClassTypeErrorTag, C, NormalizeUntilStable(C)),
+ If(IsInSet(AnnotatedI, typename Comp::Ps), ConstructError(TypeAlreadyBoundErrorTag, AnnotatedI),
+ If(MapContainsKey(typename Comp::InterfaceBindings, AnnotatedI),
+ ConstructError(TypeAlreadyBoundErrorTag, AnnotatedI), Op))))));
+ };
+};
+
+struct ProcessInterfaceBinding {
+ template <typename Comp, typename AnnotatedI, typename AnnotatedC, typename NonConstBindingRequired>
+ struct apply {
+ using R = If(NonConstBindingRequired,
+ AddProvidedTypeIgnoringInterfaceBindings(Comp, AnnotatedI, NonConstBindingRequired, Vector<AnnotatedC>,
+ Vector<AnnotatedC>),
+ AddProvidedTypeIgnoringInterfaceBindings(Comp, AnnotatedI, NonConstBindingRequired, Vector<AnnotatedC>,
+ Vector<>));
+ struct ConstOp {
+ // This must be here (and not in AddDeferredInterfaceBinding) because the binding might be
+ // used to bind functors instead, so we might never need to add C to the requirements.
+ using Result = Eval<R>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForConstBind<UnwrapType<AnnotatedI>, UnwrapType<AnnotatedC>>());
+ };
+
+ std::size_t numEntries() {
+ return 1;
+ }
+ };
+ struct NonConstOp {
+ // This must be here (and not in AddDeferredInterfaceBinding) because the binding might be
+ // used to bind functors instead, so we might never need to add C to the requirements.
+ using Result = Eval<R>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForBind<UnwrapType<AnnotatedI>, UnwrapType<AnnotatedC>>());
+ };
+
+ std::size_t numEntries() {
+ return 1;
+ }
+ };
+ using type = PropagateError(R, If(NonConstBindingRequired, NonConstOp, ConstOp));
+ };
+};
+
+struct AddInterfaceMultibinding {
+ template <typename Comp, typename AnnotatedI, typename AnnotatedC>
+ struct apply {
+ using I = RemoveAnnotations(AnnotatedI);
+ using C = RemoveAnnotations(AnnotatedC);
+ using R = AddRequirements(Comp, Vector<AnnotatedC>, Vector<AnnotatedC>);
+ struct Op {
+ using Result = Eval<R>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(InjectorStorage::createComponentStorageEntryForMultibinding<UnwrapType<AnnotatedI>,
+ UnwrapType<AnnotatedC>>());
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator<UnwrapType<AnnotatedI>>());
+ };
+
+ std::size_t numEntries() {
+ return 2;
+ }
+ };
+ using type = If(Not(IsBaseOf(I, C)), ConstructError(NotABaseClassOfErrorTag, I, C), Op);
+ };
+};
+
+template <typename AnnotatedSignature, typename Lambda, typename OptionalAnnotatedI>
+struct PostProcessRegisterProviderHelper;
+
+template <typename AnnotatedSignature, typename Lambda, typename AnnotatedI>
+struct PostProcessRegisterProviderHelper;
+
+template <typename AnnotatedSignature, typename Lambda, typename AnnotatedI>
+struct PostProcessRegisterProviderHelper<AnnotatedSignature, Lambda, Type<AnnotatedI>> {
+ inline void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForCompressedProvider<AnnotatedSignature, Lambda, AnnotatedI>());
+ entries.push_back(InjectorStorage::createComponentStorageEntryForProvider<AnnotatedSignature, Lambda>());
+ }
+
+ std::size_t numEntries() {
+ return 2;
+ }
+};
+
+template <typename AnnotatedSignature, typename Lambda>
+struct PostProcessRegisterProviderHelper<AnnotatedSignature, Lambda, None> {
+ inline void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(InjectorStorage::createComponentStorageEntryForProvider<AnnotatedSignature, Lambda>());
+ }
+
+ std::size_t numEntries() {
+ return 1;
+ }
+};
+
+// T can't be any injectable type, it must match the return type of the provider in one of
+// the registerProvider() overloads in ComponentStorage.
+struct PostProcessRegisterProvider {
+ template <typename Comp, typename AnnotatedSignature, typename Lambda>
+ struct apply {
+ using AnnotatedC = NormalizeType(SignatureType(AnnotatedSignature));
+ using OptionalAnnotatedI = FindValueInMap(typename Comp::InterfaceBindings, AnnotatedC);
+ struct Op {
+ using Result = Comp;
+
+ using Helper = PostProcessRegisterProviderHelper<UnwrapType<AnnotatedSignature>, UnwrapType<Lambda>,
+ Eval<OptionalAnnotatedI>>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ Helper()(entries);
+ }
+ std::size_t numEntries() {
+ return Helper().numEntries();
+ }
+ };
+ using type = Op;
+ };
+};
+
+struct PreProcessRegisterProvider {
+ template <typename Comp, typename AnnotatedSignature, typename Lambda>
+ struct apply {
+ using Signature = RemoveAnnotationsFromSignature(AnnotatedSignature);
+ using SignatureFromLambda = FunctionSignature(Lambda);
+
+ using AnnotatedC = NormalizeType(SignatureType(AnnotatedSignature));
+ using AnnotatedCDeps = NormalizeTypeVector(SignatureArgs(AnnotatedSignature));
+ using R = AddProvidedType(Comp, AnnotatedC, Bool<true>, AnnotatedCDeps,
+ Id<NormalizedNonConstTypesIn(SignatureArgs(AnnotatedSignature))>);
+ using type =
+ If(Not(IsSame(Signature, SignatureFromLambda)),
+ ConstructError(AnnotatedSignatureDifferentFromLambdaSignatureErrorTag, Signature, SignatureFromLambda),
+ PropagateError(
+ CheckInjectableType(RemoveAnnotations(SignatureType(AnnotatedSignature))),
+ PropagateError(
+ CheckInjectableTypeVector(RemoveAnnotationsFromVector(AnnotatedCDeps)),
+ PropagateError(
+ CheckInjectableType(SignatureType(SignatureFromLambda)),
+ PropagateError(
+ CheckInjectableTypeVector(SignatureArgs(SignatureFromLambda)),
+ If(And(IsPointer(SignatureType(SignatureFromLambda)),
+ And(IsAbstract(RemovePointer(SignatureType(SignatureFromLambda))),
+ Not(HasVirtualDestructor(RemovePointer(SignatureType(SignatureFromLambda)))))),
+ ConstructError(ProviderReturningPointerToAbstractClassWithNoVirtualDestructorErrorTag,
+ RemovePointer(SignatureType(SignatureFromLambda))),
+ ComponentFunctorIdentity(R)))))));
+ };
+};
+
+// The registration is actually deferred until the PartialComponent is converted to a component.
+struct DeferredRegisterProviderWithAnnotations {
+ template <typename Comp, typename AnnotatedSignature, typename Lambda>
+ struct apply {
+ using Comp1 = AddDeferredBinding(Comp, ComponentFunctor(PostProcessRegisterProvider, AnnotatedSignature, Lambda));
+ using type = PreProcessRegisterProvider(Comp1, AnnotatedSignature, Lambda);
+ };
+};
+
+// The registration is actually deferred until the PartialComponent is converted to a component.
+struct DeferredRegisterProvider {
+ template <typename Comp, typename Lambda>
+ struct apply {
+ using type = DeferredRegisterProviderWithAnnotations(Comp, FunctionSignature(Lambda), Lambda);
+ };
+};
+
+// T can't be any injectable type, it must match the return type of the provider in one of
+// the registerMultibindingProvider() overloads in ComponentStorage.
+struct RegisterMultibindingProviderWithAnnotations {
+ template <typename Comp, typename AnnotatedSignature, typename Lambda>
+ struct apply {
+ using Signature = RemoveAnnotationsFromSignature(AnnotatedSignature);
+ using SignatureFromLambda = FunctionSignature(Lambda);
+
+ using AnnotatedArgs = SignatureArgs(AnnotatedSignature);
+ using AnnotatedArgVector = NormalizeTypeVector(AnnotatedArgs);
+ using NonConstRequirements = NormalizedNonConstTypesIn(AnnotatedArgs);
+ using R = AddRequirements(Comp, AnnotatedArgVector, NonConstRequirements);
+ struct Op {
+ using Result = Eval<R>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForMultibindingProvider<UnwrapType<AnnotatedSignature>,
+ UnwrapType<Lambda>>());
+ entries.push_back(InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator<
+ UnwrapType<Eval<NormalizeType(SignatureType(AnnotatedSignature))>>>());
+ }
+ std::size_t numEntries() {
+ return 2;
+ }
+ };
+ using type = If(
+ Not(IsValidSignature(AnnotatedSignature)), ConstructError(NotASignatureErrorTag, AnnotatedSignature),
+ PropagateError(
+ CheckInjectableType(RemoveAnnotations(SignatureType(AnnotatedSignature))),
+ PropagateError(
+ CheckInjectableTypeVector(RemoveAnnotationsFromVector(SignatureArgs(AnnotatedSignature))),
+ PropagateError(
+ CheckInjectableType(SignatureType(SignatureFromLambda)),
+ PropagateError(
+ CheckInjectableTypeVector(SignatureArgs(SignatureFromLambda)),
+ If(IsAbstract(RemoveAnnotations(SignatureType(AnnotatedSignature))),
+ ConstructError(CannotConstructAbstractClassErrorTag,
+ RemoveAnnotations(SignatureType(AnnotatedSignature))),
+ If(Not(IsSame(Signature, SignatureFromLambda)),
+ ConstructError(AnnotatedSignatureDifferentFromLambdaSignatureErrorTag, Signature,
+ SignatureFromLambda),
+ If(And(IsPointer(SignatureType(SignatureFromLambda)),
+ And(IsAbstract(RemovePointer(SignatureType(SignatureFromLambda))),
+ Not(HasVirtualDestructor(RemovePointer(SignatureType(SignatureFromLambda)))))),
+ ConstructError(
+ MultibindingProviderReturningPointerToAbstractClassWithNoVirtualDestructorErrorTag,
+ RemovePointer(SignatureType(SignatureFromLambda))),
+ PropagateError(R, Op)))))))));
+ };
+};
+
+// T can't be any injectable type, it must match the return type of the provider in one of
+// the registerMultibindingProvider() overloads in ComponentStorage.
+struct RegisterMultibindingProvider {
+ template <typename Comp, typename Lambda>
+ struct apply {
+ using type = RegisterMultibindingProviderWithAnnotations(Comp, FunctionSignature(Lambda), Lambda);
+ };
+};
+
+// Non-assisted case.
+template <int numAssistedBefore, int numNonAssistedBefore, typename Arg>
+struct GetAssistedArg {
+ template <typename InjectedArgsTuple, typename UserProvidedArgsTuple>
+ inline Arg operator()(InjectedArgsTuple& injected_args, UserProvidedArgsTuple&) {
+ return std::get<numNonAssistedBefore>(injected_args);
+ }
+};
+
+// Assisted case.
+template <int numAssistedBefore, int numNonAssistedBefore, typename Arg>
+struct GetAssistedArg<numAssistedBefore, numNonAssistedBefore, Assisted<Arg>> {
+ template <typename InjectedArgsTuple, typename UserProvidedArgsTuple>
+ inline Arg operator()(InjectedArgsTuple&, UserProvidedArgsTuple& user_provided_args) {
+ return std::get<numAssistedBefore>(user_provided_args);
+ }
+};
+
+struct RegisterFactoryHelper {
+
+ template <typename Comp, typename DecoratedSignature, typename Lambda,
+ // std::function<InjectedSignature> is the injected type (possibly with an Annotation<> wrapping it)
+ typename InjectedSignature, typename RequiredLambdaSignature, typename InjectedAnnotatedArgs,
+ // The types that are injected, unwrapped from any Annotation<>.
+ typename InjectedArgs, typename IndexSequence>
+ struct apply;
+
+ template <typename Comp, typename DecoratedSignature, typename Lambda, typename NakedC,
+ typename... NakedUserProvidedArgs, typename... NakedAllArgs, typename... InjectedAnnotatedArgs,
+ typename... NakedInjectedArgs, typename... Indexes>
+ struct apply<Comp, DecoratedSignature, Lambda, Type<NakedC(NakedUserProvidedArgs...)>, Type<NakedC(NakedAllArgs...)>,
+ Vector<InjectedAnnotatedArgs...>, Vector<Type<NakedInjectedArgs>...>, Vector<Indexes...>> {
+ // Here we call "decorated" the types that might be wrapped in Annotated<> or Assisted<>,
+ // while we call "annotated" the ones that might only be wrapped in Annotated<> (but not Assisted<>).
+ using AnnotatedT = SignatureType(DecoratedSignature);
+ using T = RemoveAnnotations(AnnotatedT);
+ using DecoratedArgs = SignatureArgs(DecoratedSignature);
+ using NakedInjectedSignature = NakedC(NakedUserProvidedArgs...);
+ using NakedRequiredSignature = NakedC(NakedAllArgs...);
+ using NakedFunctor = std::function<NakedInjectedSignature>;
+ // This is usually the same as Functor, but this might be annotated.
+ using AnnotatedFunctor = CopyAnnotation(AnnotatedT, Type<NakedFunctor>);
+ using FunctorDeps = NormalizeTypeVector(Vector<InjectedAnnotatedArgs...>);
+ using FunctorNonConstDeps = NormalizedNonConstTypesIn(Vector<InjectedAnnotatedArgs...>);
+ using R = AddProvidedType(Comp, AnnotatedFunctor, Bool<true>, FunctorDeps, FunctorNonConstDeps);
+ struct Op {
+ using Result = Eval<R>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ auto function_provider = [](NakedInjectedArgs... args) {
+ auto injected_args = std::make_tuple(args...);
+ auto object_provider = [injected_args](NakedUserProvidedArgs... params) mutable {
+ auto user_provided_args = std::tie(params...);
+ // These are unused if they are 0-arg tuples. Silence the unused-variable warnings anyway.
+ (void)injected_args;
+ (void)user_provided_args;
+
+ return LambdaInvoker::invoke<UnwrapType<Lambda>, NakedAllArgs...>(
+ GetAssistedArg<
+ Eval<NumAssistedBefore(Indexes, DecoratedArgs)>::value,
+ getIntValue<Indexes>() - Eval<NumAssistedBefore(Indexes, DecoratedArgs)>::value,
+ // Note that the Assisted<> wrapper (if any) remains, we just remove any wrapping Annotated<>.
+ UnwrapType<Eval<RemoveAnnotations(GetNthType(Indexes, DecoratedArgs))>>>()(injected_args,
+ user_provided_args)...);
+ };
+ return NakedFunctor(object_provider);
+ };
+ entries.push_back(InjectorStorage::createComponentStorageEntryForProvider<
+ UnwrapType<Eval<ConsSignatureWithVector(AnnotatedFunctor, Vector<InjectedAnnotatedArgs...>)>>,
+ decltype(function_provider)>());
+ }
+ std::size_t numEntries() {
+ return 1;
+ }
+ };
+ // The first two IsValidSignature checks are a bit of a hack, they are needed to make the F2/RealF2 split
+ // work in the caller (we need to allow Lambda to be a function type).
+ using type = If(Not(IsSame(Type<NakedRequiredSignature>, FunctionSignature(Lambda))),
+ ConstructError(FunctorSignatureDoesNotMatchErrorTag, Type<NakedRequiredSignature>,
+ FunctionSignature(Lambda)),
+ If(IsPointer(T), ConstructError(FactoryReturningPointerErrorTag, DecoratedSignature),
+ PropagateError(R, Op)));
+ };
+};
+
+struct RegisterFactory {
+ template <typename Comp, typename DecoratedSignature, typename Lambda>
+ struct apply {
+ using LambdaReturnType = SignatureType(FunctionSignature(Lambda));
+ using type =
+ If(Not(IsValidSignature(DecoratedSignature)), ConstructError(NotASignatureErrorTag, DecoratedSignature),
+ PropagateError(
+ CheckInjectableType(RemoveAnnotations(SignatureType(DecoratedSignature))),
+ PropagateError(
+ CheckInjectableTypeVector(
+ RemoveAnnotationsFromVector(RemoveAssisted(SignatureArgs(DecoratedSignature)))),
+ If(IsAbstract(RemoveAnnotations(SignatureType(DecoratedSignature))),
+ // We error out early in this case. Calling RegisterFactoryHelper would also produce an error, but
+ // it'd be
+ // much less user-friendly.
+ ConstructError(CannotConstructAbstractClassErrorTag,
+ RemoveAnnotations(SignatureType(DecoratedSignature))),
+ If(Not(Or(IsEmpty(Lambda), IsValidSignature(Lambda))),
+ ConstructError(LambdaWithCapturesErrorTag, Lambda),
+ If(Not(Or(IsTriviallyCopyable(Lambda), IsValidSignature(Lambda))),
+ ConstructError(NonTriviallyCopyableLambdaErrorTag, Lambda),
+ If(And(IsUniquePtr(LambdaReturnType),
+ And(IsAbstract(RemoveUniquePtr(LambdaReturnType)),
+ Not(HasVirtualDestructor(RemoveUniquePtr(LambdaReturnType))))),
+ ConstructError(RegisterFactoryForUniquePtrOfAbstractClassWithNoVirtualDestructorErrorTag,
+ RemoveUniquePtr(LambdaReturnType)),
+ RegisterFactoryHelper(
+ Comp, DecoratedSignature, Lambda,
+ InjectedSignatureForAssistedFactory(DecoratedSignature),
+ RequiredLambdaSignatureForAssistedFactory(DecoratedSignature),
+ RemoveAssisted(SignatureArgs(DecoratedSignature)),
+ RemoveAnnotationsFromVector(RemoveAssisted(SignatureArgs(DecoratedSignature))),
+ GenerateIntSequence(
+ VectorSize(RequiredLambdaArgsForAssistedFactory(DecoratedSignature)))))))))));
+ };
+};
+
+struct PostProcessRegisterConstructor;
+
+template <typename AnnotatedSignature, typename OptionalAnnotatedI>
+struct PostProcessRegisterConstructorHelper;
+
+template <typename AnnotatedSignature, typename AnnotatedI>
+struct PostProcessRegisterConstructorHelper;
+
+template <typename AnnotatedSignature, typename AnnotatedI>
+struct PostProcessRegisterConstructorHelper<AnnotatedSignature, Type<AnnotatedI>> {
+ inline void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForCompressedConstructor<AnnotatedSignature, AnnotatedI>());
+ entries.push_back(InjectorStorage::createComponentStorageEntryForConstructor<AnnotatedSignature>());
+ }
+ std::size_t numEntries() {
+ return 2;
+ }
+};
+
+template <typename AnnotatedSignature>
+struct PostProcessRegisterConstructorHelper<AnnotatedSignature, None> {
+ inline void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(InjectorStorage::createComponentStorageEntryForConstructor<AnnotatedSignature>());
+ }
+ std::size_t numEntries() {
+ return 1;
+ }
+};
+
+struct PostProcessRegisterConstructor {
+ template <typename Comp, typename AnnotatedSignature>
+ struct apply {
+ struct type {
+ using AnnotatedC = NormalizeType(SignatureType(AnnotatedSignature));
+ using Result = Comp;
+ using Helper =
+ PostProcessRegisterConstructorHelper<UnwrapType<AnnotatedSignature>,
+ Eval<FindValueInMap(typename Comp::InterfaceBindings, AnnotatedC)>>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ Helper()(entries);
+ }
+ std::size_t numEntries() {
+ return Helper().numEntries();
+ }
+ };
+ };
+};
+
+struct PreProcessRegisterConstructor {
+ template <typename Comp, typename AnnotatedSignature>
+ struct apply {
+ using Signature = RemoveAnnotationsFromSignature(AnnotatedSignature);
+ using C = SignatureType(Signature);
+ using Args = SignatureArgs(Signature);
+ using AnnotatedT = SignatureType(AnnotatedSignature);
+ using AnnotatedArgs = SignatureArgs(AnnotatedSignature);
+ using AnnotatedC = NormalizeType(AnnotatedT);
+ using CDeps = NormalizeTypeVector(AnnotatedArgs);
+ using CNonConstDeps = NormalizedNonConstTypesIn(AnnotatedArgs);
+ using R = AddProvidedType(Comp, AnnotatedC, Bool<true>, CDeps, CNonConstDeps);
+ using type = If(
+ Not(IsValidSignature(AnnotatedSignature)), ConstructError(NotASignatureErrorTag, AnnotatedSignature),
+ PropagateError(CheckInjectableType(RemoveAnnotations(C)),
+ PropagateError(CheckInjectableTypeVector(RemoveAnnotationsFromVector(Args)),
+ If(IsAbstract(RemoveAnnotations(SignatureType(AnnotatedSignature))),
+ ConstructError(CannotConstructAbstractClassErrorTag,
+ RemoveAnnotations(SignatureType(AnnotatedSignature))),
+ If(Not(IsConstructibleWithVector(C, Args)),
+ ConstructError(NoConstructorMatchingInjectSignatureErrorTag, C, Signature),
+ PropagateError(R, ComponentFunctorIdentity(R)))))));
+ };
+};
+
+struct DeferredRegisterConstructor {
+ template <typename Comp, typename AnnotatedSignature>
+ struct apply {
+ using Comp1 = AddDeferredBinding(Comp, ComponentFunctor(PostProcessRegisterConstructor, AnnotatedSignature));
+ using type = PreProcessRegisterConstructor(Comp1, AnnotatedSignature);
+ };
+};
+
+struct RegisterInstance {
+ template <typename Comp, typename AnnotatedC, typename C, typename IsNonConst>
+ struct apply {
+ using R = AddProvidedType(Comp, AnnotatedC, IsNonConst, Vector<>, Vector<>);
+ struct Op {
+ using Result = Eval<R>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>&) {}
+ std::size_t numEntries() {
+ return 0;
+ }
+ };
+ using type = PropagateError(
+ CheckNormalizedTypes(ConsVector(RemoveAnnotations(AnnotatedC))),
+ PropagateError(
+ CheckNormalizedTypes(ConsVector(C)),
+ If(Not(IsSame(C, NormalizeType(C))), ConstructError(NonClassTypeErrorTag, C, NormalizeUntilStable(C)),
+ If(Not(IsSame(RemoveAnnotations(AnnotatedC), NormalizeType(RemoveAnnotations(AnnotatedC)))),
+ ConstructError(NonClassTypeErrorTag, RemoveAnnotations(AnnotatedC),
+ NormalizeUntilStable(RemoveAnnotations(C))),
+ // The IsSame check is not redundant because IsBaseOf returns false for non-class types (e.g. int).
+ If(Not(Or(IsSame(RemoveAnnotations(AnnotatedC), C), IsBaseOf(RemoveAnnotations(AnnotatedC), C))),
+ ConstructError(TypeMismatchInBindInstanceErrorTag, RemoveAnnotations(AnnotatedC), C),
+ PropagateError(R, Op))))));
+ };
+};
+
+struct RegisterConstructorAsValueFactory {
+ template <typename Comp, typename DecoratedSignature,
+ typename RequiredSignature = Eval<RequiredLambdaSignatureForAssistedFactory(DecoratedSignature)>>
+ struct apply;
+
+ template <typename Comp, typename DecoratedSignature, typename NakedT, typename... NakedArgs>
+ struct apply<Comp, DecoratedSignature, Type<NakedT(NakedArgs...)>> {
+ using RequiredSignature = Type<NakedT(NakedArgs...)>;
+ using Op1 = RegisterFactory(Comp, DecoratedSignature, RequiredSignature);
+ struct Op {
+ using Result = Eval<GetResult(Op1)>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ auto provider = [](NakedArgs... args) { return NakedT(std::forward<NakedArgs>(args)...); };
+ using RealOp = RegisterFactory(Comp, DecoratedSignature, Type<decltype(provider)>);
+ FruitStaticAssert(IsSame(GetResult(Op1), GetResult(RealOp)));
+ Eval<RealOp>()(entries);
+ }
+ std::size_t numEntries() {
+#ifdef FRUIT_EXTRA_DEBUG
+ auto provider = [](NakedArgs... args) { return NakedT(std::forward<NakedArgs>(args)...); };
+ using RealOp = RegisterFactory(Comp, DecoratedSignature, Type<decltype(provider)>);
+ FruitAssert(Eval<Op1>().numEntries() == Eval<RealOp>().numEntries());
+#endif
+ return Eval<Op1>().numEntries();
+ }
+ };
+ using type = PropagateError(Op1, Op);
+ };
+};
+
+struct RegisterConstructorAsUniquePtrFactory {
+ template <typename Comp, typename DecoratedSignature,
+ typename RequiredSignature = Eval<RequiredLambdaSignatureForAssistedFactory(DecoratedSignature)>>
+ struct apply;
+
+ template <typename Comp, typename DecoratedSignature, typename NakedT, typename... NakedArgs>
+ struct apply<Comp, DecoratedSignature, Type<std::unique_ptr<NakedT>(NakedArgs...)>> {
+ using RequiredSignature = Type<std::unique_ptr<NakedT>(NakedArgs...)>;
+ using Op1 = RegisterFactory(Comp, DecoratedSignature, RequiredSignature);
+ struct Op {
+ using Result = Eval<GetResult(Op1)>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ auto provider = [](NakedArgs... args) {
+ return std::unique_ptr<NakedT>(new NakedT(std::forward<NakedArgs>(args)...));
+ };
+ using RealOp = RegisterFactory(Comp, DecoratedSignature, Type<decltype(provider)>);
+ FruitStaticAssert(IsSame(GetResult(Op1), GetResult(RealOp)));
+ Eval<RealOp>()(entries);
+ };
+ std::size_t numEntries() {
+#ifdef FRUIT_EXTRA_DEBUG
+ auto provider = [](NakedArgs... args) {
+ return std::unique_ptr<NakedT>(new NakedT(std::forward<NakedArgs>(args)...));
+ };
+ using RealOp = RegisterFactory(Comp, DecoratedSignature, Type<decltype(provider)>);
+ FruitAssert(Eval<Op1>().numEntries() == Eval<RealOp>().numEntries());
+#endif
+ return Eval<Op1>().numEntries();
+ }
+ };
+
+ using type = PropagateError(Op1, Op);
+ };
+};
+
+struct InstallComponent {
+ template <typename Comp, typename OtherComp>
+ struct apply {
+ using new_RsSuperset = SetUnion(typename OtherComp::RsSuperset, typename Comp::RsSuperset);
+ using new_Ps = SetUncheckedUnion(typename OtherComp::Ps, typename Comp::Ps);
+ using new_NonConstRsPs = SetUnion(typename OtherComp::NonConstRsPs, typename Comp::NonConstRsPs);
+#ifndef FRUIT_NO_LOOP_CHECK
+ using new_Deps = ConcatVectors(typename OtherComp::Deps, typename Comp::Deps);
+#endif
+ FruitStaticAssert(IsSame(typename OtherComp::InterfaceBindings, Vector<>));
+ using new_InterfaceBindings = typename Comp::InterfaceBindings;
+
+ FruitStaticAssert(IsSame(typename OtherComp::DeferredBindingFunctors, EmptyList));
+ using new_DeferredBindingFunctors = typename Comp::DeferredBindingFunctors;
+
+ using R = ConsComp(new_RsSuperset, new_Ps, new_NonConstRsPs,
+#ifndef FRUIT_NO_LOOP_CHECK
+ new_Deps,
+#endif
+ new_InterfaceBindings, new_DeferredBindingFunctors);
+ struct Op {
+ using Result = Eval<R>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>&) {}
+ std::size_t numEntries() {
+ return 0;
+ }
+ };
+ using InterfacePs = VectorToSetUnchecked(GetMapKeys(typename Comp::InterfaceBindings));
+ using AllPs = SetUncheckedUnion(InterfacePs, typename Comp::Ps);
+ using DuplicateTypes = SetIntersection(typename OtherComp::Ps, AllPs);
+ using CompConstPs = SetDifference(typename Comp::Ps, typename Comp::NonConstRsPs);
+ using CompRs = SetDifference(typename Comp::RsSuperset, typename Comp::Ps);
+ using CompNonConstRs = SetIntersection(CompRs, typename Comp::NonConstRsPs);
+
+ using OtherCompConstPs = SetDifference(typename OtherComp::Ps, typename OtherComp::NonConstRsPs);
+ using OtherCompRs = SetDifference(typename OtherComp::RsSuperset, typename OtherComp::Ps);
+ using OtherCompNonConstRs = SetIntersection(OtherCompRs, typename OtherComp::NonConstRsPs);
+
+ using type = If(Not(IsDisjoint(typename OtherComp::Ps, AllPs)),
+ ConstructErrorWithArgVector(DuplicateTypesInComponentErrorTag, SetToVector(DuplicateTypes)),
+ If(Not(IsDisjoint(CompConstPs, OtherCompNonConstRs)),
+ ConstructError(NonConstBindingRequiredButConstBindingProvidedErrorTag,
+ GetArbitrarySetElement(SetIntersection(CompConstPs, OtherCompNonConstRs))),
+ If(Not(IsDisjoint(CompNonConstRs, OtherCompConstPs)),
+ ConstructError(NonConstBindingRequiredButConstBindingProvidedErrorTag,
+ GetArbitrarySetElement(SetIntersection(CompNonConstRs, OtherCompConstPs))),
+ Op)));
+ };
+};
+
+struct InstallComponentHelper {
+ template <typename Comp, typename... OtherCompParams>
+ struct apply {
+ using OtherComp = ConstructComponentImpl(OtherCompParams...);
+ using type = InstallComponent(Comp, OtherComp);
+ };
+};
+
+// CatchAll(PropagateError(Expr, Bool<false>), IsErrorExceptionHandler) evaluates to Bool<true> if Expr throws an error,
+// and Bool<false> otherwise.
+struct IsErrorExceptionHandler {
+ template <typename E>
+ struct apply {
+ using type = Bool<true>;
+ };
+};
+
+struct ConvertComponent {
+ template <typename SourceComp, typename DestComp>
+ struct apply {
+ using SourcePs = typename SourceComp::Ps;
+ using DestPs = typename DestComp::Ps;
+ using SourceRs = SetDifference(typename SourceComp::RsSuperset, typename SourceComp::Ps);
+ using DestRs = SetDifference(typename DestComp::RsSuperset, typename DestComp::Ps);
+ using NonConstSourceRs = SetIntersection(SourceRs, typename SourceComp::NonConstRsPs);
+ using NonConstDestPs = SetIntersection(DestPs, typename DestComp::NonConstRsPs);
+ using NonConstDestRs = SetIntersection(DestRs, typename DestComp::NonConstRsPs);
+
+ using ConstSourcePs = SetDifference(SourcePs, typename SourceComp::NonConstRsPs);
+ using ConstDestRs = SetDifference(DestRs, typename DestComp::NonConstRsPs);
+
+ // We need to register:
+ // * All the types provided by the new component
+ // * All the types required by the old component
+ // except:
+ // * The ones already provided by the old component (if they have the right constness).
+ // * The ones required by the new one (if they have the right constness).
+ using ToRegister = SetUnion(
+ // The types that we must provide and aren't currently provided
+ SetDifference(SetUnion(DestPs, SourceRs), SetUnion(DestRs, SourcePs)),
+ // And the ones that are currently provided as const but that we need to provide as non-const
+ SetIntersection(SetUnion(NonConstDestPs, NonConstSourceRs), SetUnion(ConstDestRs, ConstSourcePs)));
+ using NonConstTypesToRegister = SetIntersection(ToRegister, SetUnion(typename SourceComp::NonConstRsPs,
+ typename DestComp::NonConstRsPs));
+ using type = EnsureProvidedTypes(SourceComp, DestRs, NonConstDestRs, SetToVector(ToRegister),
+ NonConstTypesToRegister);
+
+// Not needed, just double-checking.
+// Uses FruitStaticAssert instead of FruitDelegateCheck so that it's checked only in debug mode.
+#ifdef FRUIT_EXTRA_DEBUG
+ FruitDelegateCheck(
+ If(CatchAll(PropagateError(type, PropagateError(Id<GetResult(type)>, Bool<false>)), IsErrorExceptionHandler),
+ // We're going to return an error soon anyway, we don't want to interfere by reporting this one.
+ None, CheckComponentEntails(GetResult(type), DestComp)));
+#endif // FRUIT_EXTRA_DEBUG
+ };
+};
+
+struct ProcessDeferredBindings {
+ template <typename Comp>
+ struct apply;
+
+ template <typename RsSupersetParam, typename PsParam, typename NonConstRsPsParam,
+#ifndef FRUIT_NO_LOOP_CHECK
+ typename DepsParam,
+#endif
+ typename InterfaceBindingsParam, typename DeferredBindingFunctors>
+ struct apply<Comp<RsSupersetParam, PsParam, NonConstRsPsParam,
+#ifndef FRUIT_NO_LOOP_CHECK
+ DepsParam,
+#endif
+ InterfaceBindingsParam, DeferredBindingFunctors>> {
+ // Comp1 is the same as Comp, but without the DeferredBindingFunctors.
+ using Comp1 = ConsComp(RsSupersetParam, PsParam, NonConstRsPsParam,
+#ifndef FRUIT_NO_LOOP_CHECK
+ DepsParam,
+#endif
+ InterfaceBindingsParam, EmptyList);
+ using type = Call(FoldList(DeferredBindingFunctors, Compose2ComponentFunctors, ComponentFunctorIdentity), Comp1);
+ };
+};
+
+template <typename AnnotatedCFunctor, typename AnnotatedCUniquePtrFunctor>
+struct AutoRegisterFactoryHelperErrorHandler {
+ template <typename E>
+ struct apply {
+ using type = E;
+ };
+
+ template <typename T>
+ struct apply<Error<NoBindingFoundErrorTag, T>> {
+ using type = If(IsSame(Type<T>, AnnotatedCFunctor), ConstructNoBindingFoundError(AnnotatedCUniquePtrFunctor),
+ ConstructError(NoBindingFoundErrorTag, Type<T>));
+ };
+
+ template <typename T1, typename T2>
+ struct apply<Error<NoBindingFoundForAbstractClassErrorTag, T1, T2>> {
+ using type = If(IsSame(Type<T1>, AnnotatedCFunctor), ConstructNoBindingFoundError(AnnotatedCUniquePtrFunctor),
+ ConstructError(NoBindingFoundForAbstractClassErrorTag, Type<T1>, Type<T2>));
+ };
+};
+
+struct AutoRegisterFactoryHelper {
+
+ // General case, no way to bind it.
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename InterfaceBinding,
+ typename has_inject_annotation, typename is_abstract, typename C, typename AnnotatedSignature,
+ typename... Args>
+ struct apply {
+ using AnnotatedC = SignatureType(AnnotatedSignature);
+ using CFunctor = ConsStdFunction(RemoveAnnotationsFromSignature(AnnotatedSignature));
+ using AnnotatedCFunctor = CopyAnnotation(AnnotatedC, CFunctor);
+ using type = If(IsAbstract(C), ConstructError(NoBindingFoundForAbstractClassErrorTag, AnnotatedCFunctor, C),
+ ConstructError(NoBindingFoundErrorTag, AnnotatedCFunctor));
+ };
+
+ // No way to bind it (we need this specialization too to ensure that the specialization below
+ // is not chosen for AnnotatedC=None).
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename unused1,
+ typename unused2, typename NakedI, typename AnnotatedSignature, typename... Args>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements, None, unused1, unused2,
+ Type<std::unique_ptr<NakedI>>, AnnotatedSignature, Args...> {
+ using AnnotatedC = SignatureType(AnnotatedSignature);
+ using CFunctor = ConsStdFunction(RemoveAnnotationsFromSignature(AnnotatedSignature));
+ using AnnotatedCFunctor = CopyAnnotation(AnnotatedC, CFunctor);
+ using type = If(IsAbstract(Type<NakedI>),
+ ConstructError(NoBindingFoundForAbstractClassErrorTag, AnnotatedCFunctor, Type<NakedI>),
+ ConstructError(NoBindingFoundErrorTag, AnnotatedCFunctor));
+ };
+
+ // AnnotatedI has an interface binding, use it and look for a factory that returns the type that AnnotatedI is bound
+ // to.
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename AnnotatedC,
+ typename unused1, typename unused2, typename NakedI, typename AnnotatedSignature, typename... Args>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements, AnnotatedC, unused1, unused2,
+ Type<std::unique_ptr<NakedI>>, AnnotatedSignature, Args...> {
+ using I = Type<NakedI>;
+ using AnnotatedI = CopyAnnotation(SignatureType(AnnotatedSignature), I);
+ using C = RemoveAnnotations(AnnotatedC);
+ using IFunctor = ConsStdFunction(ConsSignature(ConsUniquePtr(I), Args...));
+ using CFunctor = ConsStdFunction(ConsSignature(ConsUniquePtr(C), Args...));
+ using AnnotatedIFunctor = CopyAnnotation(AnnotatedI, IFunctor);
+ using AnnotatedCFunctor = CopyAnnotation(AnnotatedC, CFunctor);
+
+ using ProvidedSignature = ConsSignature(AnnotatedIFunctor,
+ CopyAnnotation(AnnotatedC, ConsConstReference(CFunctor)));
+ using LambdaSignature = ConsSignature(IFunctor, ConsConstReference(CFunctor));
+
+ using F1 = ComponentFunctor(EnsureProvidedType, TargetRequirements, TargetNonConstRequirements, AnnotatedCFunctor,
+ Bool<false>);
+ using F2 = ComponentFunctor(PreProcessRegisterProvider, ProvidedSignature, LambdaSignature);
+ using F3 = ComponentFunctor(PostProcessRegisterProvider, ProvidedSignature, LambdaSignature);
+ using R = Call(ComposeFunctors(F1, F2, F3), Comp);
+ struct Op {
+ using Result = Eval<GetResult(R)>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ using NakedC = UnwrapType<Eval<C>>;
+ auto provider = [](const UnwrapType<Eval<CFunctor>>& fun) {
+ return UnwrapType<Eval<IFunctor>>([=](typename TypeUnwrapper<Args>::type... args) {
+ NakedC* c = fun(args...).release();
+ NakedI* i = static_cast<NakedI*>(c);
+ return std::unique_ptr<NakedI>(i);
+ });
+ };
+ using RealF2 = ComponentFunctor(PreProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealF3 = ComponentFunctor(PostProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealOp = Call(ComposeFunctors(F1, RealF2, RealF3), Comp);
+ FruitStaticAssert(IsSame(GetResult(RealOp), GetResult(R)));
+ Eval<RealOp>()(entries);
+ }
+ std::size_t numEntries() {
+#ifdef FRUIT_EXTRA_DEBUG
+ using NakedC = UnwrapType<Eval<C>>;
+ auto provider = [](const UnwrapType<Eval<CFunctor>>& fun) {
+ return UnwrapType<Eval<IFunctor>>([=](typename TypeUnwrapper<Args>::type... args) {
+ NakedC* c = fun(args...).release();
+ NakedI* i = static_cast<NakedI*>(c);
+ return std::unique_ptr<NakedI>(i);
+ });
+ };
+ using RealF2 = ComponentFunctor(PreProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealF3 = ComponentFunctor(PostProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealOp = Call(ComposeFunctors(F1, RealF2, RealF3), Comp);
+ FruitAssert(Eval<R>().numEntries() == Eval<RealOp>().numEntries());
+#endif
+ return Eval<R>().numEntries();
+ }
+ };
+ using type = PropagateError(R, If(Not(HasVirtualDestructor(I)),
+ ConstructError(FactoryBindingForUniquePtrOfClassWithNoVirtualDestructorErrorTag,
+ IFunctor, CFunctor),
+ Op));
+ };
+
+ // C doesn't have an interface binding as interface, nor an INJECT annotation, and is not an abstract class.
+ // Bind std::function<unique_ptr<C>(Args...)> to std::function<C(Args...)> (possibly with annotations).
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename NakedC,
+ typename AnnotatedSignature, typename... Args>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements, None, Bool<false>, Bool<false>,
+ Type<std::unique_ptr<NakedC>>, AnnotatedSignature, Args...> {
+ using C = Type<NakedC>;
+ using CFunctor = ConsStdFunction(ConsSignature(C, Args...));
+ using CUniquePtrFunctor = ConsStdFunction(ConsSignature(ConsUniquePtr(C), Args...));
+ using AnnotatedCUniquePtr = SignatureType(AnnotatedSignature);
+ using AnnotatedC = CopyAnnotation(AnnotatedCUniquePtr, C);
+ using AnnotatedCFunctor = CopyAnnotation(AnnotatedCUniquePtr, CFunctor);
+ using AnnotatedCUniquePtrFunctor = CopyAnnotation(AnnotatedCUniquePtr, CUniquePtrFunctor);
+ using AnnotatedCFunctorRef = CopyAnnotation(AnnotatedCUniquePtr, ConsConstReference(CFunctor));
+
+ using ProvidedSignature = ConsSignature(AnnotatedCUniquePtrFunctor, AnnotatedCFunctorRef);
+ using LambdaSignature = ConsSignature(CUniquePtrFunctor, ConsConstReference(CFunctor));
+
+ using F1 = ComponentFunctor(EnsureProvidedType, TargetRequirements, TargetNonConstRequirements, AnnotatedCFunctor,
+ Bool<false>);
+ using F2 = ComponentFunctor(PreProcessRegisterProvider, ProvidedSignature, LambdaSignature);
+ using F3 = ComponentFunctor(PostProcessRegisterProvider, ProvidedSignature, LambdaSignature);
+ using R = Call(ComposeFunctors(F1, F2, F3), Comp);
+ struct Op {
+ using Result = Eval<GetResult(R)>;
+ void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
+ auto provider = [](const UnwrapType<Eval<CFunctor>>& fun) {
+ return UnwrapType<Eval<CUniquePtrFunctor>>([=](typename TypeUnwrapper<Args>::type... args) {
+ NakedC* c = new NakedC(fun(args...));
+ return std::unique_ptr<NakedC>(c);
+ });
+ };
+ using RealF2 = ComponentFunctor(PreProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealF3 = ComponentFunctor(PostProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealOp = Call(ComposeFunctors(F1, RealF2, RealF3), Comp);
+ FruitStaticAssert(IsSame(GetResult(RealOp), GetResult(R)));
+ Eval<RealOp>()(entries);
+ }
+ std::size_t numEntries() {
+#ifdef FRUIT_EXTRA_DEBUG
+ auto provider = [](const UnwrapType<Eval<CFunctor>>& fun) {
+ return UnwrapType<Eval<CUniquePtrFunctor>>([=](typename TypeUnwrapper<Args>::type... args) {
+ NakedC* c = new NakedC(fun(args...));
+ return std::unique_ptr<NakedC>(c);
+ });
+ };
+ using RealF2 = ComponentFunctor(PreProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealF3 = ComponentFunctor(PostProcessRegisterProvider, ProvidedSignature, Type<decltype(provider)>);
+ using RealOp = Call(ComposeFunctors(F1, RealF2, RealF3), Comp);
+ FruitAssert(Eval<R>().numEntries() == Eval<RealOp>().numEntries());
+#endif
+ return Eval<R>().numEntries();
+ }
+ };
+
+ using ErrorHandler =
+ AutoRegisterFactoryHelperErrorHandler<Eval<AnnotatedCFunctor>, Eval<AnnotatedCUniquePtrFunctor>>;
+
+ // If we are about to report a NoBindingFound/NoBindingFoundForAbstractClass error for AnnotatedCFunctor,
+ // report one for std::function<std::unique_ptr<C>(Args...)> instead,
+ // otherwise we'd report an error about a type that the user doesn't expect.
+ using type = PropagateError(Catch(Catch(R, NoBindingFoundErrorTag, ErrorHandler),
+ NoBindingFoundForAbstractClassErrorTag, ErrorHandler),
+ Op);
+ };
+
+ // C has an Inject typedef, use it. unique_ptr case.
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename unused,
+ typename NakedC, typename AnnotatedSignature, typename... Args>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements, None, Bool<true>, unused,
+ Type<std::unique_ptr<NakedC>>, AnnotatedSignature, Args...> {
+ using AnnotatedCUniquePtr = SignatureType(AnnotatedSignature);
+ using AnnotatedC = CopyAnnotation(AnnotatedCUniquePtr, RemoveUniquePtr(RemoveAnnotations(AnnotatedCUniquePtr)));
+ using DecoratedSignatureReturningValue = GetInjectAnnotation(AnnotatedC);
+ using DecoratedSignature = ConsSignatureWithVector(AnnotatedCUniquePtr,
+ SignatureArgs(DecoratedSignatureReturningValue));
+ using DecoratedSignatureArgs = SignatureArgs(DecoratedSignature);
+ using ActualSignatureInInjectionTypedef = ConsSignatureWithVector(SignatureType(DecoratedSignature),
+ RemoveNonAssisted(DecoratedSignatureArgs));
+ using NonAssistedArgs = RemoveAssisted(DecoratedSignatureArgs);
+
+ using F1 = ComponentFunctor(RegisterConstructorAsUniquePtrFactory, DecoratedSignature);
+ using F2 = ComponentFunctor(EnsureProvidedTypes, TargetRequirements, TargetNonConstRequirements,
+ NormalizeTypeVector(NonAssistedArgs), NormalizedNonConstTypesIn(NonAssistedArgs));
+
+ using type = If(Not(IsSame(AnnotatedSignature, ActualSignatureInInjectionTypedef)),
+ ConstructError(FunctorSignatureDoesNotMatchErrorTag, AnnotatedSignature,
+ ActualSignatureInInjectionTypedef),
+ Call(ComposeFunctors(F1, F2), Comp));
+ };
+
+ // C has an Inject typedef, use it. Value (not unique_ptr) case.
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename unused,
+ typename NakedC, typename AnnotatedSignature, typename... Args>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements, None, Bool<true>, unused, Type<NakedC>,
+ AnnotatedSignature, Args...> {
+ using AnnotatedC = SignatureType(AnnotatedSignature);
+ using DecoratedSignature = GetInjectAnnotation(AnnotatedC);
+ using DecoratedSignatureArgs = SignatureArgs(DecoratedSignature);
+ using ActualSignatureInInjectionTypedef = ConsSignatureWithVector(SignatureType(DecoratedSignature),
+ RemoveNonAssisted(DecoratedSignatureArgs));
+ using NonAssistedArgs = RemoveAssisted(DecoratedSignatureArgs);
+
+ using F1 = ComponentFunctor(RegisterConstructorAsValueFactory, DecoratedSignature);
+ using F2 = ComponentFunctor(EnsureProvidedTypes, TargetRequirements, TargetNonConstRequirements,
+ NormalizeTypeVector(NonAssistedArgs), NormalizedNonConstTypesIn(NonAssistedArgs));
+
+ using type = If(Not(IsSame(AnnotatedSignature, ActualSignatureInInjectionTypedef)),
+ ConstructError(FunctorSignatureDoesNotMatchErrorTag, AnnotatedSignature,
+ ActualSignatureInInjectionTypedef),
+ Call(ComposeFunctors(F1, F2), Comp));
+ };
+};
+
+struct AutoRegister {
+ // The types in TargetRequirements will not be auto-registered.
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename AnnotatedC>
+ struct apply;
+
+ // Tries to register C by looking for a typedef called Inject inside C.
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename AnnotatedC>
+ struct apply {
+ using CHasInjectAnnotation = HasInjectAnnotation(RemoveAnnotations(AnnotatedC));
+ using Inject = GetInjectAnnotation(AnnotatedC);
+ using CRequirements = NormalizeTypeVector(SignatureArgs(Inject));
+ using CNonConstRequirements = NormalizedNonConstTypesIn(SignatureArgs(Inject));
+ using F = ComposeFunctors(ComponentFunctor(PreProcessRegisterConstructor, Inject),
+ ComponentFunctor(PostProcessRegisterConstructor, Inject),
+ ComponentFunctor(EnsureProvidedTypes, TargetRequirements, TargetNonConstRequirements,
+ CRequirements, CNonConstRequirements));
+ using type = If(CHasInjectAnnotation, Call(F, Comp), ConstructNoBindingFoundError(AnnotatedC));
+ };
+
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename NakedC,
+ typename... NakedArgs>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements, Type<std::function<NakedC(NakedArgs...)>>> {
+ using type = AutoRegisterFactoryHelper(Comp, TargetRequirements, TargetNonConstRequirements,
+ FindInMap(typename Comp::InterfaceBindings, Type<NakedC>),
+ HasInjectAnnotation(Type<NakedC>), IsAbstract(Type<NakedC>), Type<NakedC>,
+ Type<NakedC(NakedArgs...)>, Id<RemoveAnnotations(Type<NakedArgs>)>...);
+ };
+
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename NakedC,
+ typename... NakedArgs>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements,
+ Type<std::function<std::unique_ptr<NakedC>(NakedArgs...)>>> {
+ using type = AutoRegisterFactoryHelper(Comp, TargetRequirements, TargetNonConstRequirements,
+ FindInMap(typename Comp::InterfaceBindings, Type<NakedC>),
+ HasInjectAnnotation(Type<NakedC>), IsAbstract(Type<NakedC>),
+ Type<std::unique_ptr<NakedC>>, Type<std::unique_ptr<NakedC>(NakedArgs...)>,
+ Id<RemoveAnnotations(Type<NakedArgs>)>...);
+ };
+
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename Annotation,
+ typename NakedC, typename... NakedArgs>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements,
+ Type<fruit::Annotated<Annotation, std::function<NakedC(NakedArgs...)>>>> {
+ using type = AutoRegisterFactoryHelper(Comp, TargetRequirements, TargetNonConstRequirements,
+ FindInMap(typename Comp::InterfaceBindings,
+ Type<fruit::Annotated<Annotation, NakedC>>),
+ HasInjectAnnotation(Type<NakedC>), IsAbstract(Type<NakedC>), Type<NakedC>,
+ Type<fruit::Annotated<Annotation, NakedC>(NakedArgs...)>,
+ Id<RemoveAnnotations(Type<NakedArgs>)>...);
+ };
+
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename Annotation,
+ typename NakedC, typename... NakedArgs>
+ struct apply<Comp, TargetRequirements, TargetNonConstRequirements,
+ Type<fruit::Annotated<Annotation, std::function<std::unique_ptr<NakedC>(NakedArgs...)>>>> {
+ using type = AutoRegisterFactoryHelper(Comp, TargetRequirements, TargetNonConstRequirements,
+ FindInMap(typename Comp::InterfaceBindings,
+ Type<fruit::Annotated<Annotation, NakedC>>),
+ HasInjectAnnotation(Type<NakedC>), IsAbstract(Type<NakedC>),
+ Type<std::unique_ptr<NakedC>>,
+ Type<fruit::Annotated<Annotation, std::unique_ptr<NakedC>>(NakedArgs...)>,
+ Id<RemoveAnnotations(Type<NakedArgs>)>...);
+ };
+};
+
+template <typename AnnotatedT>
+struct EnsureProvidedTypeErrorHandler {
+ template <typename E>
+ struct apply {
+ using type = E;
+ };
+
+ template <typename T>
+ struct apply<Error<NoBindingFoundErrorTag, T>> {
+ using type = If(IsSame(Type<T>, AnnotatedT),
+ ConstructError(ConstBindingDeclaredAsRequiredButNonConstBindingRequiredErrorTag, AnnotatedT),
+ ConstructError(NoBindingFoundErrorTag, Type<T>));
+ };
+
+ template <typename T1, typename T2>
+ struct apply<Error<NoBindingFoundForAbstractClassErrorTag, T1, T2>> {
+ using type = If(IsSame(Type<T1>, AnnotatedT),
+ ConstructError(ConstBindingDeclaredAsRequiredButNonConstBindingRequiredErrorTag, AnnotatedT),
+ ConstructError(NoBindingFoundForAbstractClassErrorTag, Type<T1>, Type<T2>));
+ };
+};
+
+struct EnsureProvidedType {
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename AnnotatedT,
+ typename NonConstBindingRequired>
+ struct apply {
+ using AnnotatedC = NormalizeType(AnnotatedT);
+ using AnnotatedCImpl = FindInMap(typename Comp::InterfaceBindings, AnnotatedC);
+ using AutoRegisterResult = AutoRegister(Comp, TargetRequirements, TargetNonConstRequirements, AnnotatedC);
+ using ErrorHandler = EnsureProvidedTypeErrorHandler<AnnotatedT>;
+ using type = If(
+ IsInSet(AnnotatedC, typename Comp::Ps),
+ If(And(NonConstBindingRequired, Not(IsInSet(AnnotatedC, typename Comp::NonConstRsPs))),
+ ConstructError(NonConstBindingRequiredButConstBindingProvidedErrorTag, AnnotatedC),
+ ComponentFunctorIdentity(Comp)),
+ If(And(IsInSet(AnnotatedC, TargetRequirements),
+ Or(Not(NonConstBindingRequired), IsInSet(AnnotatedC, TargetNonConstRequirements))),
+ // The type is already in the target requirements with the desired constness, nothing to do.
+ ComponentFunctorIdentity(Comp),
+ If(Not(IsNone(AnnotatedCImpl)),
+ // Has an interface binding.
+ Call(ComposeFunctors(ComponentFunctor(ProcessInterfaceBinding, AnnotatedC, AnnotatedCImpl,
+ NonConstBindingRequired),
+ ComponentFunctor(EnsureProvidedType, TargetRequirements, TargetNonConstRequirements,
+ AnnotatedCImpl, NonConstBindingRequired)),
+ Comp),
+ // If we are about to report a NoBindingFound/NoBindingFoundForAbstractClass error for AnnotatedT and the
+ // target
+ // component has a Required<const T>, we can report a more specific error (rather than the usual
+ // "binding not found").
+ If(And(NonConstBindingRequired, IsInSet(AnnotatedC, TargetRequirements)),
+ Catch(Catch(AutoRegisterResult, NoBindingFoundErrorTag, ErrorHandler),
+ NoBindingFoundForAbstractClassErrorTag, ErrorHandler),
+ AutoRegisterResult))));
+ };
+};
+
+struct EnsureProvidedTypes {
+ template <typename Comp, typename TargetRequirements, typename TargetNonConstRequirements, typename TypesToProvide,
+ typename NonConstTypesToProvide>
+ struct apply {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = Compose2ComponentFunctors(ComponentFunctor(EnsureProvidedType, TargetRequirements,
+ TargetNonConstRequirements, T,
+ IsInSet(T, NonConstTypesToProvide)),
+ CurrentResult);
+ };
+ };
+
+ using type = Call(FoldVector(TypesToProvide, Helper, ComponentFunctorIdentity), Comp);
+ };
+};
+
+struct ProcessBinding {
+ template <typename Binding>
+ struct apply;
+
+ template <typename I, typename C>
+ struct apply<fruit::impl::Bind<I, C>> {
+ using type = ComponentFunctor(AddDeferredInterfaceBinding, Type<I>, Type<C>);
+ };
+
+ template <typename Signature>
+ struct apply<fruit::impl::RegisterConstructor<Signature>> {
+ using type = ComponentFunctor(DeferredRegisterConstructor, Type<Signature>);
+ };
+
+ template <typename AnnotatedC, typename C>
+ struct apply<fruit::impl::BindInstance<AnnotatedC, C>> {
+ using type = ComponentFunctor(RegisterInstance, Type<AnnotatedC>, Type<C>, Bool<true>);
+ };
+
+ template <typename AnnotatedC, typename C>
+ struct apply<fruit::impl::BindConstInstance<AnnotatedC, C>> {
+ using type = ComponentFunctor(RegisterInstance, Type<AnnotatedC>, Type<C>, Bool<false>);
+ };
+
+ template <typename Lambda>
+ struct apply<fruit::impl::RegisterProvider<Lambda>> {
+ using type = ComponentFunctor(DeferredRegisterProvider, Type<Lambda>);
+ };
+
+ template <typename AnnotatedSignature, typename Lambda>
+ struct apply<fruit::impl::RegisterProvider<AnnotatedSignature, Lambda>> {
+ using type = ComponentFunctor(DeferredRegisterProviderWithAnnotations, Type<AnnotatedSignature>, Type<Lambda>);
+ };
+
+ template <typename AnnotatedC>
+ struct apply<fruit::impl::AddInstanceMultibinding<AnnotatedC>> {
+ using type = ComponentFunctorIdentity;
+ };
+
+ template <typename AnnotatedC>
+ struct apply<fruit::impl::AddInstanceVectorMultibindings<AnnotatedC>> {
+ using type = ComponentFunctorIdentity;
+ };
+
+ template <typename I, typename C>
+ struct apply<fruit::impl::AddMultibinding<I, C>> {
+ using type = ComponentFunctor(AddInterfaceMultibinding, Type<I>, Type<C>);
+ };
+
+ template <typename Lambda>
+ struct apply<fruit::impl::AddMultibindingProvider<Lambda>> {
+ using type = ComponentFunctor(RegisterMultibindingProvider, Type<Lambda>);
+ };
+
+ template <typename AnnotatedSignature, typename Lambda>
+ struct apply<fruit::impl::AddMultibindingProvider<AnnotatedSignature, Lambda>> {
+ using type = ComponentFunctor(RegisterMultibindingProviderWithAnnotations, Type<AnnotatedSignature>, Type<Lambda>);
+ };
+
+ template <typename DecoratedSignature, typename Lambda>
+ struct apply<fruit::impl::RegisterFactory<DecoratedSignature, Lambda>> {
+ using type = ComponentFunctor(RegisterFactory, Type<DecoratedSignature>, Type<Lambda>);
+ };
+
+ template <typename... Params, typename... Args>
+ struct apply<fruit::impl::InstallComponent<fruit::Component<Params...>(Args...)>> {
+ using type = ComponentFunctor(InstallComponentHelper, Type<Params>...);
+ };
+
+ template <typename GetReplacedComponent, typename GetReplacementComponent>
+ struct apply<fruit::impl::ReplaceComponent<GetReplacedComponent, GetReplacementComponent>> {
+ using type = ComponentFunctorIdentity;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_COMPONENT_FUNCTORS_DEFN_H
diff --git a/include/fruit/impl/component_storage/binding_deps.defn.h b/include/fruit/impl/component_storage/binding_deps.defn.h
new file mode 100644
index 0000000..f7e63ef
--- /dev/null
+++ b/include/fruit/impl/component_storage/binding_deps.defn.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_BINDING_DEPS_DEFN_H
+#define FRUIT_BINDING_DEPS_DEFN_H
+
+#include <fruit/impl/component_storage/binding_deps.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename L>
+struct GetBindingDepsHelper;
+
+template <typename... Ts>
+struct GetBindingDepsHelper<fruit::impl::meta::Vector<fruit::impl::meta::Type<Ts>...>> {
+ inline const BindingDeps* operator()() {
+ static const TypeId types[] = {getTypeId<Ts>()..., TypeId{nullptr}}; // LCOV_EXCL_BR_LINE
+ static const BindingDeps deps = {types, sizeof...(Ts)};
+ return &deps;
+ }
+};
+
+// We specialize the "no Ts" case to avoid declaring types[] as an array of length 0.
+template <>
+struct GetBindingDepsHelper<fruit::impl::meta::Vector<>> {
+ inline const BindingDeps* operator()() {
+ static const TypeId types[] = {TypeId{nullptr}};
+ static const BindingDeps deps = {types, 0};
+ return &deps;
+ }
+};
+
+template <typename Deps>
+inline const BindingDeps* getBindingDeps() {
+ return GetBindingDepsHelper<Deps>()();
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_BINDING_DEPS_DEFN_H
diff --git a/include/fruit/impl/component_storage/binding_deps.h b/include/fruit/impl/component_storage/binding_deps.h
new file mode 100644
index 0000000..5a18a83
--- /dev/null
+++ b/include/fruit/impl/component_storage/binding_deps.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_BINDING_DEPS_H
+#define FRUIT_BINDING_DEPS_H
+
+#include <fruit/impl/util/type_info.h>
+
+namespace fruit {
+namespace impl {
+
+struct BindingDeps {
+ // A C-style array of deps
+ const TypeId* deps;
+
+ // The size of the above array.
+ std::size_t num_deps;
+};
+
+template <typename Deps>
+const BindingDeps* getBindingDeps();
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/component_storage/binding_deps.defn.h>
+
+#endif // FRUIT_BINDING_DEPS_H
diff --git a/include/fruit/impl/component_storage/component_storage.defn.h b/include/fruit/impl/component_storage/component_storage.defn.h
new file mode 100644
index 0000000..c44efb9
--- /dev/null
+++ b/include/fruit/impl/component_storage/component_storage.defn.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_COMPONENT_STORAGE_DEFN_H
+#define FRUIT_COMPONENT_STORAGE_DEFN_H
+
+#include <fruit/impl/component_storage/component_storage.h>
+#include <fruit/impl/component_storage/component_storage_entry.h>
+
+namespace fruit {
+namespace impl {
+
+inline ComponentStorage::ComponentStorage(FixedSizeVector<ComponentStorageEntry>&& entries)
+ : entries(std::move(entries)) {}
+
+inline ComponentStorage::ComponentStorage(const ComponentStorage& other) {
+ *this = other;
+}
+
+inline ComponentStorage::ComponentStorage(ComponentStorage&& other) {
+ *this = std::move(other);
+}
+
+inline void ComponentStorage::destroy() {
+ for (ComponentStorageEntry& entry : entries) {
+ entry.destroy();
+ }
+ entries.clear();
+}
+
+inline ComponentStorage::~ComponentStorage() {
+ destroy();
+}
+
+inline FixedSizeVector<ComponentStorageEntry> ComponentStorage::release() && {
+ return std::move(entries);
+}
+
+inline std::size_t ComponentStorage::numEntries() const {
+ return entries.size();
+}
+
+inline ComponentStorage& ComponentStorage::operator=(const ComponentStorage& other) {
+ destroy();
+
+ entries = FixedSizeVector<ComponentStorageEntry>(other.entries.size());
+ for (const ComponentStorageEntry& entry : other.entries) {
+ entries.push_back(entry.copy());
+ }
+
+ return *this;
+}
+
+inline ComponentStorage& ComponentStorage::operator=(ComponentStorage&& other) {
+ entries = std::move(other.entries);
+
+ // We don't want other to have any entries after this operation because we might otherwise end up destroying those
+ // ComponentStorageEntry objects twice.
+ other.entries.clear();
+
+ return *this;
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_COMPONENT_STORAGE_DEFN_H
diff --git a/include/fruit/impl/component_storage/component_storage.h b/include/fruit/impl/component_storage/component_storage.h
new file mode 100644
index 0000000..45e3f05
--- /dev/null
+++ b/include/fruit/impl/component_storage/component_storage.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_COMPONENT_STORAGE_H
+#define FRUIT_COMPONENT_STORAGE_H
+
+#include <fruit/impl/data_structures/fixed_size_vector.h>
+#include <fruit/impl/fruit_internal_forward_decls.h>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * A component where all types have to be explicitly registered, and all checks are at runtime.
+ * Used to implement Component<>, don't use directly.
+ * This merely stores the ComponentStorageEntry objects. The real processing will be done in NormalizedComponentStorage
+ * and InjectorStorage.
+ *
+ * This class handles the creation of bindings for types of the forms:
+ * - shared_ptr<C>, [const] C*, [const] C&, C (where C is an atomic type)
+ * - Annotated<Annotation, T> (with T of the above forms)
+ * - Injector<T1, ..., Tk> (with T1, ..., Tk of the above forms).
+ */
+class ComponentStorage {
+private:
+ // The entries for this component storage (potentially including lazy component), *in reverse order*.
+ FixedSizeVector<ComponentStorageEntry> entries;
+
+ void destroy();
+
+public:
+ ComponentStorage() = default;
+ ComponentStorage(FixedSizeVector<ComponentStorageEntry>&& entries);
+ ComponentStorage(const ComponentStorage&);
+ ComponentStorage(ComponentStorage&&);
+
+ ~ComponentStorage();
+
+ FixedSizeVector<ComponentStorageEntry> release() &&;
+
+ std::size_t numEntries() const;
+
+ ComponentStorage& operator=(const ComponentStorage&);
+ ComponentStorage& operator=(ComponentStorage&&);
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/component_storage/component_storage.defn.h>
+
+#endif // FRUIT_COMPONENT_STORAGE_H
diff --git a/include/fruit/impl/component_storage/component_storage_entry.defn.h b/include/fruit/impl/component_storage/component_storage_entry.defn.h
new file mode 100644
index 0000000..a9d139c
--- /dev/null
+++ b/include/fruit/impl/component_storage/component_storage_entry.defn.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_COMPONENT_STORAGE_ENTRY_DEFN_H
+#define FRUIT_COMPONENT_STORAGE_ENTRY_DEFN_H
+
+#include <fruit/impl/component_storage/component_storage_entry.h>
+#include <fruit/impl/util/call_with_tuple.h>
+#include <fruit/impl/util/hash_codes.h>
+
+namespace fruit {
+namespace impl {
+
+// We use a custom method instead of a real copy constructor so that all copies are explicit (since copying is a
+// fairly expensive operation).
+inline ComponentStorageEntry ComponentStorageEntry::copy() const {
+ FruitAssert(kind != Kind::INVALID);
+ ComponentStorageEntry result;
+ switch (kind) {
+ case Kind::LAZY_COMPONENT_WITH_ARGS:
+ case Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS:
+ case Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ result.kind = kind;
+ result.type_id = type_id;
+ result.lazy_component_with_args = lazy_component_with_args.copy();
+ break;
+
+ default:
+ result = *this;
+ }
+ return result;
+}
+
+// We use a custom method instead of a real destructor, so that we can hold these in a std::vector but still destroy
+// them when desired.
+inline void ComponentStorageEntry::destroy() const {
+ FruitAssert(kind != Kind::INVALID);
+ switch (kind) {
+ case Kind::LAZY_COMPONENT_WITH_ARGS:
+ case Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS:
+ case Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ lazy_component_with_args.destroy();
+#ifdef FRUIT_EXTRA_DEBUG
+ kind = Kind::INVALID;
+#endif
+ break;
+
+ default:
+ break;
+ }
+}
+
+inline ComponentStorageEntry::LazyComponentWithArgs::ComponentInterface::ComponentInterface(erased_fun_t erased_fun)
+ : erased_fun(erased_fun) {}
+
+template <typename Component, typename... Args>
+class ComponentInterfaceImpl : public ComponentStorageEntry::LazyComponentWithArgs::ComponentInterface {
+private:
+ using ComponentInterface = ComponentStorageEntry::LazyComponentWithArgs::ComponentInterface;
+
+ using fun_t = Component (*)(Args...);
+ std::tuple<Args...> args_tuple;
+
+public:
+ inline ComponentInterfaceImpl(fun_t fun, std::tuple<Args...> args_tuple)
+ : ComponentInterface(reinterpret_cast<erased_fun_t>(fun)), args_tuple(std::move(args_tuple)) {}
+
+ inline bool
+ areParamsEqual(const ComponentStorageEntry::LazyComponentWithArgs::ComponentInterface& other) const final {
+ if (getFunTypeId() != other.getFunTypeId()) {
+ return false;
+ }
+ const auto& casted_other = static_cast<const ComponentInterfaceImpl<Component, Args...>&>(other);
+ return args_tuple == casted_other.args_tuple;
+ }
+
+ inline void addBindings(entry_vector_t& entries) const final {
+ Component component = callWithTuple<Component, Args...>(reinterpret_cast<fun_t>(erased_fun), args_tuple);
+ FixedSizeVector<ComponentStorageEntry> component_entries = std::move(component.storage).release();
+ entries.insert(entries.end(), component_entries.begin(), component_entries.end());
+ }
+
+ inline std::size_t hashCode() const final {
+ std::size_t fun_hash = std::hash<fun_t>()(reinterpret_cast<fun_t>(erased_fun));
+ std::size_t args_hash = hashTuple(args_tuple);
+ return combineHashes(fun_hash, args_hash);
+ }
+
+ inline ComponentInterface* copy() const final {
+ return new ComponentInterfaceImpl{reinterpret_cast<fun_t>(erased_fun), args_tuple};
+ }
+
+ inline TypeId getFunTypeId() const final {
+ return fruit::impl::getTypeId<Component (*)(Args...)>();
+ }
+};
+
+template <typename Component, typename... Args>
+inline ComponentStorageEntry ComponentStorageEntry::LazyComponentWithArgs::create(Component (*fun)(Args...),
+ std::tuple<Args...> args_tuple) {
+ ComponentStorageEntry result;
+ result.type_id = getTypeId<Component (*)(Args...)>();
+ result.kind = ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS;
+ result.lazy_component_with_args.component =
+ new ComponentInterfaceImpl<Component, Args...>(fun, std::move(args_tuple));
+ return result;
+}
+
+template <typename Component, typename... Args>
+inline ComponentStorageEntry
+ComponentStorageEntry::LazyComponentWithArgs::createReplacedComponentEntry(Component (*fun)(Args...),
+ std::tuple<Args...> args_tuple) {
+ ComponentStorageEntry result;
+ result.type_id = getTypeId<Component (*)(Args...)>();
+ result.kind = ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS;
+ result.lazy_component_with_args.component =
+ new ComponentInterfaceImpl<Component, Args...>(fun, std::move(args_tuple));
+ return result;
+}
+
+template <typename Component, typename... Args>
+inline ComponentStorageEntry
+ComponentStorageEntry::LazyComponentWithArgs::createReplacementComponentEntry(Component (*fun)(Args...),
+ std::tuple<Args...> args_tuple) {
+ ComponentStorageEntry result;
+ result.type_id = getTypeId<Component (*)(Args...)>();
+ result.kind = ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS;
+ result.lazy_component_with_args.component =
+ new ComponentInterfaceImpl<Component, Args...>(fun, std::move(args_tuple));
+ return result;
+}
+
+inline ComponentStorageEntry::LazyComponentWithArgs ComponentStorageEntry::LazyComponentWithArgs::copy() const {
+ LazyComponentWithArgs result;
+ result.component = component->copy();
+ return result;
+}
+
+inline void ComponentStorageEntry::LazyComponentWithArgs::destroy() const {
+ delete component;
+}
+
+inline bool ComponentStorageEntry::LazyComponentWithArgs::ComponentInterface::
+operator==(const ComponentInterface& other) const {
+ return erased_fun == other.erased_fun && areParamsEqual(other);
+}
+
+template <typename Component>
+void ComponentStorageEntry::LazyComponentWithNoArgs::addBindings(erased_fun_t erased_fun, entry_vector_t& entries) {
+ Component component = reinterpret_cast<Component (*)()>(erased_fun)();
+ FixedSizeVector<ComponentStorageEntry> component_entries = std::move(component.storage).release();
+ entries.insert(entries.end(), component_entries.begin(), component_entries.end());
+}
+
+template <typename Component>
+inline ComponentStorageEntry ComponentStorageEntry::LazyComponentWithNoArgs::create(Component (*fun)()) {
+ FruitAssert(fun != nullptr);
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS;
+ result.type_id = getTypeId<Component (*)()>();
+ result.lazy_component_with_no_args.erased_fun = reinterpret_cast<erased_fun_t>(fun);
+ result.lazy_component_with_no_args.add_bindings_fun = LazyComponentWithNoArgs::addBindings<Component>;
+ return result;
+}
+
+template <typename Component>
+inline ComponentStorageEntry
+ComponentStorageEntry::LazyComponentWithNoArgs::createReplacedComponentEntry(Component (*fun)()) {
+ FruitAssert(fun != nullptr);
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS;
+ result.type_id = getTypeId<Component (*)()>();
+ result.lazy_component_with_no_args.erased_fun = reinterpret_cast<erased_fun_t>(fun);
+ result.lazy_component_with_no_args.add_bindings_fun = LazyComponentWithNoArgs::addBindings<Component>;
+ return result;
+}
+
+template <typename Component>
+inline ComponentStorageEntry
+ComponentStorageEntry::LazyComponentWithNoArgs::createReplacementComponentEntry(Component (*fun)()) {
+ FruitAssert(fun != nullptr);
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS;
+ result.type_id = getTypeId<Component (*)()>();
+ result.lazy_component_with_no_args.erased_fun = reinterpret_cast<erased_fun_t>(fun);
+ result.lazy_component_with_no_args.add_bindings_fun = LazyComponentWithNoArgs::addBindings<Component>;
+ return result;
+}
+
+inline bool ComponentStorageEntry::LazyComponentWithNoArgs::isValid() const {
+ return erased_fun != nullptr;
+}
+
+inline bool ComponentStorageEntry::LazyComponentWithNoArgs::
+operator==(const ComponentStorageEntry::LazyComponentWithNoArgs& other) const {
+ if (erased_fun == other.erased_fun) {
+ // These must be equal in this case, no need to compare them.
+ FruitAssert(add_bindings_fun == other.add_bindings_fun);
+ return true;
+ } else {
+ // type_id and add_bindings_fun may or may not be different from the ones in `other`.
+ return false;
+ }
+}
+
+inline void ComponentStorageEntry::LazyComponentWithNoArgs::addBindings(entry_vector_t& entries) const {
+ FruitAssert(isValid());
+ add_bindings_fun(erased_fun, entries);
+}
+
+inline std::size_t ComponentStorageEntry::LazyComponentWithNoArgs::hashCode() const {
+ // We only need to hash this field (for the same reason that we only compare this field in operator==).
+ return std::hash<erased_fun_t>()(erased_fun);
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_COMPONENT_STORAGE_ENTRY_DEFN_H
diff --git a/include/fruit/impl/component_storage/component_storage_entry.h b/include/fruit/impl/component_storage/component_storage_entry.h
new file mode 100644
index 0000000..a320b49
--- /dev/null
+++ b/include/fruit/impl/component_storage/component_storage_entry.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_COMPONENT_STORAGE_ENTRY_H
+#define FRUIT_COMPONENT_STORAGE_ENTRY_H
+
+#include <fruit/impl/component_storage/binding_deps.h>
+#include <fruit/impl/data_structures/arena_allocator.h>
+#include <fruit/impl/data_structures/semistatic_graph.h>
+#include <fruit/impl/fruit_internal_forward_decls.h>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * This represents a generic entry in ComponentStorage.
+ * We use a single POD (this struct) to represent any binding so that ComponentStorage can hold a single vector, instead
+ * of having to hold multiple vectors (each of which potentially requiring allocation/deallocation when a
+ * ComponentStorage is constructed/destroyed).
+ * This way each ComponentStorage can hold a single vector and do a single allocation.
+ */
+struct ComponentStorageEntry {
+ enum class Kind {
+#ifdef FRUIT_EXTRA_DEBUG
+ INVALID,
+#endif
+ BINDING_FOR_CONSTRUCTED_OBJECT,
+ BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION,
+ BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION,
+ BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION,
+ COMPRESSED_BINDING,
+ MULTIBINDING_FOR_CONSTRUCTED_OBJECT,
+ MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION,
+ MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION,
+ // This is not an actual binding, it's an "addendum" to multibinding bindings that specifies how the multibinding
+ // vector can be created. Unlike real multibinding entries, this *can* be deduped.
+ MULTIBINDING_VECTOR_CREATOR,
+
+ LAZY_COMPONENT_WITH_NO_ARGS,
+ LAZY_COMPONENT_WITH_ARGS,
+
+ // Component replacements are stored as a REPLACEMENT_LAZY_COMPONENT_* entry followed by a REPLACED_LAZY_COMPONENT_*
+ // entry. Note that the args are independent: e.g. a component with args can be replaced by a component with no
+ // args. This also means that the type_id of the two entries can be different (since it's the type_id of the
+ // function signature rather than just of the Component<...>).
+ REPLACED_LAZY_COMPONENT_WITH_NO_ARGS,
+ REPLACED_LAZY_COMPONENT_WITH_ARGS,
+ REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS,
+ REPLACEMENT_LAZY_COMPONENT_WITH_ARGS,
+
+ // These markers are used in expandLazyComponents(), see the comments there for details.
+ COMPONENT_WITH_ARGS_END_MARKER,
+ COMPONENT_WITHOUT_ARGS_END_MARKER,
+ };
+
+#ifdef FRUIT_EXTRA_DEBUG
+ mutable
+#endif
+ Kind kind;
+
+ // This is usually the TypeId for the bound type, except:
+ // * when kind==COMPRESSED_BINDING, this is the interface's TypeId
+ // * when kind==*LAZY_COMPONENT_*, this is the TypeId of the
+ // Component<...>-returning function.
+ TypeId type_id;
+
+ /**
+ * This represents an entry in ComponentStorage for a binding (not a multibinding) that holds an already-constructed
+ * object.
+ */
+ struct BindingForConstructedObject {
+ using object_ptr_t = const void*;
+
+ // The already-constructed object. We do *not* own this, this object must outlive the injector.
+ // This is a const pointer because in some cases it might be a const binding.
+ // We can cast this to a non-const pointer when we're sure that the original binding was for a non-const reference.
+ object_ptr_t object_ptr;
+
+#ifdef FRUIT_EXTRA_DEBUG
+ bool is_nonconst;
+#endif
+ };
+
+ /**
+ * This represents an entry in ComponentStorage for a binding (not a multibinding) that holds an object that needs to
+ * be constructed (potentially after injecting any dependencies).
+ */
+ struct BindingForObjectToConstruct {
+ // This is a const pointer because this might be a const binding. If not, we'll cast this back to a non-const
+ // pointer when we need to.
+ using object_t = const void*;
+ using create_t = object_t (*)(InjectorStorage&, SemistaticGraph<TypeId, NormalizedBinding>::node_iterator);
+
+ // The return value of this function is a pointer to the constructed object (guaranteed to be !=nullptr).
+ // Once the object is constructed (at injection time), the injector owns that object.
+ create_t create;
+
+ // The type IDs that this type depends on.
+ const BindingDeps* deps;
+
+#ifdef FRUIT_EXTRA_DEBUG
+ bool is_nonconst;
+#endif
+ };
+
+ /**
+ * This represents an entry in ComponentStorage for a multibinding that holds an already-constructed
+ * object.
+ */
+ struct MultibindingForConstructedObject {
+ using object_ptr_t = void*;
+
+ // The already-constructed object. We do *not* own this, this object must outlive the injector.
+ object_ptr_t object_ptr;
+ };
+
+ /**
+ * This represents an entry in ComponentStorage for a multibinding that holds an object that needs to
+ * be constructed (potentially after injecting any dependencies).
+ */
+ struct MultibindingForObjectToConstruct {
+
+ using object_t = void*;
+ using create_t = object_t (*)(InjectorStorage&);
+
+ // The return value of this function is a pointer to the constructed object (guaranteed to be !=nullptr).
+ // Once the object is constructed (at injection time), the injector owns that object.
+ create_t create;
+
+ // The type IDs that this type depends on.
+ const BindingDeps* deps;
+ };
+
+ /**
+ * This is not an actual binding, it's an "addendum" to multibinding bindings that specifies how the multibinding
+ * vector can be created. Unlike real multibinding entries, this *can* be deduped.
+ */
+ struct MultibindingVectorCreator {
+
+ using get_multibindings_vector_t = std::shared_ptr<char> (*)(InjectorStorage&);
+
+ // Returns the std::vector<T*> of instances, or nullptr if none.
+ // Caches the result in the `v' member of NormalizedMultibindingData.
+ get_multibindings_vector_t get_multibindings_vector;
+ };
+
+ // A CompressedBinding with interface_id==getTypeId<I>() and class_id==getTypeId<C>() means that if:
+ // * C is not exposed by the component
+ // * I is the only node that depends on C
+ // * There are no multibindings that directly depend on C
+ // The BindingData for C is BindingForObjectToConstruct(
+ // Then, taken create1, needs_reallocation such that the ComponentStorageEntry for c_type_id is
+ // BindingForObjectToConstruct(createC, deps, needs_allocation), we can remove the binding for I and C and replace
+ // them
+ // with just a binding for I, with BindingForObjectToConstruct(create, deps, needs_allocation).
+ struct CompressedBinding {
+
+ using create_t = BindingForObjectToConstruct::create_t;
+
+ // TypeId for the implementation.
+ TypeId c_type_id;
+
+ // The return value of this function is a pointer to the constructed object (guaranteed to be !=nullptr).
+ // Once the object is constructed (at injection time), the injector owns that object.
+ create_t create;
+ };
+
+ /**
+ * This represents an entry in ComponentStorage for a lazy component with no arguments.
+ */
+ struct LazyComponentWithNoArgs {
+ // An arbitrary function type, used as type for the field `erased_fun`.
+ // Note that we can't use void* here, since data pointers might not have the same size as function pointers.
+ using erased_fun_t = void (*)();
+
+ // The function that will be invoked to create the Component.
+ // Here we don't know the type, it's only known at construction time.
+ erased_fun_t erased_fun;
+
+ using entry_vector_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
+
+ // The function that allows to add this component's bindings to the given ComponentStorage.
+ using add_bindings_fun_t = void (*)(erased_fun_t, entry_vector_t&);
+ add_bindings_fun_t add_bindings_fun;
+
+ template <typename Component>
+ static void addBindings(erased_fun_t erased_fun, entry_vector_t& entries);
+
+ template <typename Component>
+ static ComponentStorageEntry create(Component (*fun)());
+
+ template <typename Component>
+ static ComponentStorageEntry createReplacedComponentEntry(Component (*fun)());
+
+ template <typename Component>
+ static ComponentStorageEntry createReplacementComponentEntry(Component (*fun)());
+
+ bool operator==(const LazyComponentWithNoArgs&) const;
+
+ void addBindings(entry_vector_t& entries) const;
+
+ std::size_t hashCode() const;
+
+ bool isValid() const;
+ };
+
+ /**
+ * This represents an entry in ComponentStorage for a lazy component with arguments.
+ */
+ struct LazyComponentWithArgs {
+ class ComponentInterface {
+ public:
+ // An arbitrary function type, used as type for the field `erased_fun`.
+ // Note that we can't use void* here, since data pointers might not have the same size as function pointers.
+ using erased_fun_t = void (*)();
+
+ // The function that will be invoked to create the Component.
+ // Here we don't know the type, it's only known to the LazyComponent implementation.
+ // We store this here instead of in the LazyComponent implementation so that we can do a quick comparison on the
+ // pointer without virtual calls (and we can then do the rest of the comparison via virtual call if needed).
+ erased_fun_t erased_fun;
+
+ using entry_vector_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
+
+ ComponentInterface(erased_fun_t erased_fun);
+
+ virtual ~ComponentInterface() = default;
+
+ // Checks if *this and other are equal, assuming that this->fun and other.fun are equal.
+ virtual bool areParamsEqual(const ComponentInterface& other) const = 0;
+
+ bool operator==(const ComponentInterface& other) const;
+
+ virtual void addBindings(entry_vector_t& component_storage_entries) const = 0;
+ virtual std::size_t hashCode() const = 0;
+ virtual ComponentInterface* copy() const = 0;
+
+ /**
+ * Returns the type ID of the real `fun` object stored by the implementation.
+ * We use this instead of the `typeid` operator so that we don't require RTTI.
+ */
+ virtual TypeId getFunTypeId() const = 0;
+ };
+
+ template <typename Component, typename... Args>
+ static ComponentStorageEntry create(Component (*fun)(Args...), std::tuple<Args...> args_tuple);
+
+ template <typename Component, typename... Args>
+ static ComponentStorageEntry createReplacedComponentEntry(Component (*fun)(Args...),
+ std::tuple<Args...> args_tuple);
+
+ template <typename Component, typename... Args>
+ static ComponentStorageEntry createReplacementComponentEntry(Component (*fun)(Args...),
+ std::tuple<Args...> args_tuple);
+
+ LazyComponentWithArgs(LazyComponentWithArgs&&) = default;
+ LazyComponentWithArgs& operator=(LazyComponentWithArgs&&) = default;
+
+ // Note: we must allow these (and use the default implementations) since this class is used in a union so it must be
+ // a POD. However when we need a real object we must call the other constructor above, and when we need a copy we
+ // must
+ // call copy() explicitly.
+ LazyComponentWithArgs() = default; // LCOV_EXCL_LINE
+ LazyComponentWithArgs(const LazyComponentWithArgs&) = default;
+ LazyComponentWithArgs& operator=(const LazyComponentWithArgs&) = default;
+
+ LazyComponentWithArgs copy() const;
+ void destroy() const;
+
+ ComponentInterface* component;
+ };
+
+ union {
+ // Valid iff kind is BINDING_FOR_CONSTRUCTED_OBJECT.
+ BindingForConstructedObject binding_for_constructed_object;
+
+ // Valid iff kind is BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_[NO_]ALLOCATION
+ // or BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION.
+ BindingForObjectToConstruct binding_for_object_to_construct;
+
+ // Valid iff kind is MULTIBINDING_FOR_CONSTRUCTED_OBJECT.
+ MultibindingForConstructedObject multibinding_for_constructed_object;
+
+ // Valid iff kind is MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_[NO_]ALLOCATION.
+ MultibindingForObjectToConstruct multibinding_for_object_to_construct;
+
+ // Valid iff kind is MULTIBINDING_VECTOR_CREATOR.
+ MultibindingVectorCreator multibinding_vector_creator;
+
+ // Valid iff kind is COMPRESSED_BINDING.
+ CompressedBinding compressed_binding;
+
+ // Valid iff kind is LAZY_COMPONENT_WITH_NO_ARGS, REPLACED_LAZY_COMPONENT_WITH_NO_ARGS or
+ // REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS.
+ LazyComponentWithNoArgs lazy_component_with_no_args;
+
+ // Valid iff kind is LAZY_COMPONENT_WITH_ARGS, REPLACED_LAZY_COMPONENT_WITH_ARGS or
+ // REPLACEMENT_LAZY_COMPONENT_WITH_ARGS.
+ LazyComponentWithArgs lazy_component_with_args;
+ };
+
+ // We use a custom method instead of a real copy constructor so that all copies are explicit (since copying is a
+ // fairly expensive operation).
+ ComponentStorageEntry copy() const;
+
+ // We use a custom method instead of a real destructor, so that we can hold these in a std::vector but still destroy
+ // them when desired.
+ void destroy() const;
+};
+
+// We can't have this assert in debug mode because we add debug-only fields that increase the size.
+#ifndef FRUIT_EXTRA_DEBUG
+// This is not required for correctness, but 4 64-bit words should be enough to hold this object, if not we'd end up
+// using more memory/CPU than expected.
+static_assert(sizeof(ComponentStorageEntry) <= 4 * sizeof(std::uint64_t),
+ "Error: a ComponentStorageEntry doesn't fit in 32 bytes as we expected");
+#endif
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/component_storage/component_storage_entry.defn.h>
+
+#endif // FRUIT_COMPONENT_STORAGE_ENTRY_H
diff --git a/include/fruit/impl/component_storage/partial_component_storage.defn.h b/include/fruit/impl/component_storage/partial_component_storage.defn.h
new file mode 100644
index 0000000..09e0c66
--- /dev/null
+++ b/include/fruit/impl/component_storage/partial_component_storage.defn.h
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_PARTIAL_COMPONENT_STORAGE_DEFN_H
+#define FRUIT_PARTIAL_COMPONENT_STORAGE_DEFN_H
+
+#include <fruit/impl/component_storage/partial_component_storage.h>
+
+#include <fruit/impl/bindings.h>
+#include <fruit/impl/injector/injector_storage.h>
+#include <fruit/impl/util/call_with_tuple.h>
+#include <fruit/impl/util/type_info.h>
+#include <utility>
+
+namespace fruit {
+namespace impl {
+
+template <>
+class PartialComponentStorage<> {
+public:
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ (void)entries;
+ }
+
+ std::size_t numBindings() const {
+ return 0;
+ }
+};
+
+template <typename I, typename C, typename... PreviousBindings>
+class PartialComponentStorage<Bind<I, C>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage)
+ : previous_storage(previous_storage) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings();
+ }
+};
+
+template <typename Signature, typename... PreviousBindings>
+class PartialComponentStorage<RegisterConstructor<Signature>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage)
+ : previous_storage(previous_storage) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings();
+ }
+};
+
+template <typename C, typename C1, typename... PreviousBindings>
+class PartialComponentStorage<BindInstance<C, C1>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ C& instance;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, C& instance)
+ : previous_storage(previous_storage), instance(instance) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(InjectorStorage::createComponentStorageEntryForBindInstance<C, C>(instance));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename C, typename C1, typename... PreviousBindings>
+class PartialComponentStorage<BindConstInstance<C, C1>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ const C& instance;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, const C& instance)
+ : previous_storage(previous_storage), instance(instance) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(InjectorStorage::createComponentStorageEntryForBindConstInstance<C, C>(instance));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename C, typename Annotation, typename C1, typename... PreviousBindings>
+class PartialComponentStorage<BindInstance<fruit::Annotated<Annotation, C>, C1>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ C& instance;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, C& instance)
+ : previous_storage(previous_storage), instance(instance) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForBindInstance<fruit::Annotated<Annotation, C>, C>(instance));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename C, typename Annotation, typename C1, typename... PreviousBindings>
+class PartialComponentStorage<BindConstInstance<fruit::Annotated<Annotation, C>, C1>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ const C& instance;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, const C& instance)
+ : previous_storage(previous_storage), instance(instance) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForBindConstInstance<fruit::Annotated<Annotation, C>, C>(instance));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename... Params, typename... PreviousBindings>
+class PartialComponentStorage<RegisterProvider<Params...>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage)
+ : previous_storage(previous_storage) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings();
+ }
+};
+
+template <typename C, typename... PreviousBindings>
+class PartialComponentStorage<AddInstanceMultibinding<C>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ C& instance;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, C& instance)
+ : previous_storage(previous_storage), instance(instance) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(InjectorStorage::createComponentStorageEntryForInstanceMultibinding<C, C>(instance));
+ entries.push_back(InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator<C>());
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 2;
+ }
+};
+
+template <typename C, typename Annotation, typename... PreviousBindings>
+class PartialComponentStorage<AddInstanceMultibinding<fruit::Annotated<Annotation, C>>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ C& instance;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, C& instance)
+ : previous_storage(previous_storage), instance(instance) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForInstanceMultibinding<fruit::Annotated<Annotation, C>, C>(
+ instance));
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator<fruit::Annotated<Annotation, C>>());
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 2;
+ }
+};
+
+template <typename C, typename... PreviousBindings>
+class PartialComponentStorage<AddInstanceVectorMultibindings<C>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ std::vector<C>& instances;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, std::vector<C>& instances)
+ : previous_storage(previous_storage), instances(instances) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ for (auto i = instances.rbegin(), i_end = instances.rend(); i != i_end; ++i) {
+ // TODO: consider optimizing this so that we need just 1 MULTIBINDING_VECTOR_CREATOR entry (removing the
+ // assumption that each multibinding entry is always preceded by that).
+ entries.push_back(InjectorStorage::createComponentStorageEntryForInstanceMultibinding<C, C>(*i));
+ entries.push_back(InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator<C>());
+ }
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + instances.size() * 2;
+ }
+};
+
+template <typename C, typename Annotation, typename... PreviousBindings>
+class PartialComponentStorage<AddInstanceVectorMultibindings<fruit::Annotated<Annotation, C>>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ std::vector<C>& instances;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, std::vector<C>& instances)
+ : previous_storage(previous_storage), instances(instances) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ for (auto i = instances.rbegin(), i_end = instances.rend(); i != i_end; ++i) {
+ // TODO: consider optimizing this so that we need just 1 MULTIBINDING_VECTOR_CREATOR entry (removing the
+ // assumption that each multibinding entry is always preceded by that).
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForInstanceMultibinding<fruit::Annotated<Annotation, C>, C>(*i));
+ entries.push_back(
+ InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator<fruit::Annotated<Annotation, C>>());
+ }
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + instances.size() * 2;
+ }
+};
+
+template <typename I, typename C, typename... PreviousBindings>
+class PartialComponentStorage<AddMultibinding<I, C>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage)
+ : previous_storage(previous_storage) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings();
+ }
+};
+
+template <typename... Params, typename... PreviousBindings>
+class PartialComponentStorage<AddMultibindingProvider<Params...>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage)
+ : previous_storage(previous_storage) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings();
+ }
+};
+
+template <typename DecoratedSignature, typename Lambda, typename... PreviousBindings>
+class PartialComponentStorage<RegisterFactory<DecoratedSignature, Lambda>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage)
+ : previous_storage(previous_storage) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings();
+ }
+};
+
+template <typename OtherComponent, typename... PreviousBindings>
+class PartialComponentStorage<InstallComponent<OtherComponent()>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ OtherComponent (*fun)();
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, OtherComponent (*fun1)(),
+ std::tuple<>)
+ : previous_storage(previous_storage), fun(fun1) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(ComponentStorageEntry::LazyComponentWithNoArgs::create(fun));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename OtherComponent, typename... Args, typename... PreviousBindings>
+class PartialComponentStorage<InstallComponent<OtherComponent(Args...)>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ OtherComponent (*fun)(Args...);
+ std::tuple<Args...> args_tuple;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage,
+ OtherComponent (*fun1)(Args...), std::tuple<Args...> args_tuple)
+ : previous_storage(previous_storage), fun(fun1), args_tuple(std::move(args_tuple)) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(ComponentStorageEntry::LazyComponentWithArgs::create(fun, std::move(args_tuple)));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename OtherComponent, typename... PreviousBindings>
+class PartialComponentStorage<PartialReplaceComponent<OtherComponent()>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ OtherComponent (*fun)();
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage, OtherComponent (*fun1)(),
+ std::tuple<>)
+ : previous_storage(previous_storage), fun(fun1) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(ComponentStorageEntry::LazyComponentWithNoArgs::createReplacedComponentEntry(fun));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename OtherComponent, typename... ReplacedFunArgs, typename... PreviousBindings>
+class PartialComponentStorage<PartialReplaceComponent<OtherComponent(ReplacedFunArgs...)>, PreviousBindings...> {
+private:
+ PartialComponentStorage<PreviousBindings...>& previous_storage;
+ OtherComponent (*fun)(ReplacedFunArgs...);
+ std::tuple<ReplacedFunArgs...> args_tuple;
+
+public:
+ PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage,
+ OtherComponent (*fun1)(ReplacedFunArgs...), std::tuple<ReplacedFunArgs...> args_tuple)
+ : previous_storage(previous_storage), fun(fun1), args_tuple(std::move(args_tuple)) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(
+ ComponentStorageEntry::LazyComponentWithArgs::createReplacedComponentEntry(fun, std::move(args_tuple)));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename OtherComponent, typename... PreviousBindings, typename... ReplacedFunArgs>
+class PartialComponentStorage<ReplaceComponent<OtherComponent(ReplacedFunArgs...), OtherComponent()>,
+ PreviousBindings...> {
+private:
+ using previous_storage_t =
+ PartialComponentStorage<PartialReplaceComponent<OtherComponent(ReplacedFunArgs...)>, PreviousBindings...>;
+
+ previous_storage_t& previous_storage;
+ OtherComponent (*fun)();
+
+public:
+ PartialComponentStorage(previous_storage_t& previous_storage, OtherComponent (*fun1)(), std::tuple<>)
+ : previous_storage(previous_storage), fun(fun1) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
+ entries.push_back(ComponentStorageEntry::LazyComponentWithNoArgs::createReplacementComponentEntry(fun));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+template <typename OtherComponent, typename... ReplacedFunArgs, typename... ReplacementFunArgs,
+ typename... PreviousBindings>
+class PartialComponentStorage<
+ ReplaceComponent<OtherComponent(ReplacedFunArgs...), OtherComponent(ReplacementFunArgs...)>, PreviousBindings...> {
+private:
+ using previous_storage_t =
+ PartialComponentStorage<PartialReplaceComponent<OtherComponent(ReplacedFunArgs...)>, PreviousBindings...>;
+
+ previous_storage_t& previous_storage;
+ OtherComponent (*fun)(ReplacementFunArgs...);
+ std::tuple<ReplacementFunArgs...> args_tuple;
+
+public:
+ PartialComponentStorage(previous_storage_t& previous_storage, OtherComponent (*fun1)(ReplacementFunArgs...),
+ std::tuple<ReplacementFunArgs...> args_tuple)
+ : previous_storage(previous_storage), fun(fun1), args_tuple(std::move(args_tuple)) {}
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+ entries.push_back(
+ ComponentStorageEntry::LazyComponentWithArgs::createReplacementComponentEntry(fun, std::move(args_tuple)));
+ previous_storage.addBindings(entries);
+ }
+
+ std::size_t numBindings() const {
+ return previous_storage.numBindings() + 1;
+ }
+};
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_PARTIAL_COMPONENT_STORAGE_DEFN_H
diff --git a/include/fruit/impl/component_storage/partial_component_storage.h b/include/fruit/impl/component_storage/partial_component_storage.h
new file mode 100644
index 0000000..7d7b6f3
--- /dev/null
+++ b/include/fruit/impl/component_storage/partial_component_storage.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_PARTIAL_COMPONENT_STORAGE_H
+#define FRUIT_PARTIAL_COMPONENT_STORAGE_H
+
+#include <cstddef>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * This class stores the data in a PartialComponent<Bindings...>.
+ * Instead of dynamically-allocating space for the elements (and then having to move the storage from each
+ * PartialComponent class to the next) we only store a reference to the previous PartialComponent's storage
+ * and the data needed for the binding (if any).
+ * We rely on the fact that the previous PartialComponent objects will only be destroyed after the current
+ * one.
+ */
+template <typename... Bindings>
+class PartialComponentStorage; /* {
+All specializations support the following methods:
+
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries);
+ std::size_t numBindings();
+};*/
+
+template <typename... Bindings>
+class PartialComponentStorage {};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/component_storage/partial_component_storage.defn.h>
+
+#endif // FRUIT_PARTIAL_COMPONENT_STORAGE_H
diff --git a/include/fruit/impl/data_structures/arena_allocator.defn.h b/include/fruit/impl/data_structures/arena_allocator.defn.h
new file mode 100644
index 0000000..51a0adb
--- /dev/null
+++ b/include/fruit/impl/data_structures/arena_allocator.defn.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_ARENA_ALLOCATOR_DEFN_H
+#define FRUIT_ARENA_ALLOCATOR_DEFN_H
+
+#include <fruit/impl/data_structures/arena_allocator.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename T>
+inline ArenaAllocator<T>::ArenaAllocator(MemoryPool& memory_pool) : pool(&memory_pool) {}
+
+template <typename T>
+template <typename U>
+inline ArenaAllocator<T>::ArenaAllocator(const ArenaAllocator<U>& other) : pool(other.pool) {}
+
+template <typename T>
+inline T* ArenaAllocator<T>::allocate(std::size_t n) {
+ return pool->allocate<T>(n);
+}
+
+template <typename T>
+void ArenaAllocator<T>::deallocate(T*, std::size_t) {
+ // Intentionally empty, the memory will be deallocated when the MemoryPool is destroyed.
+}
+
+template <class T, class U>
+bool operator==(const ArenaAllocator<T>& x, const ArenaAllocator<U>& y) {
+ return x.pool == y.pool;
+}
+
+template <class T, class U>
+bool operator!=(const ArenaAllocator<T>& x, const ArenaAllocator<U>& y) {
+ return x.pool != y.pool;
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_ARENA_ALLOCATOR_DEFN_H
diff --git a/include/fruit/impl/data_structures/arena_allocator.h b/include/fruit/impl/data_structures/arena_allocator.h
new file mode 100644
index 0000000..3ba0521
--- /dev/null
+++ b/include/fruit/impl/data_structures/arena_allocator.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_ARENA_ALLOCATOR_H
+#define FRUIT_ARENA_ALLOCATOR_H
+
+#include <fruit/impl/data_structures/memory_pool.h>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * An allocator that allocates memory in pages and only de-allocates memory on destruction.
+ * This is useful in code that needs many short-lived allocations.
+ * Each ArenaAllocator object should only be accessed by a single thread.
+ */
+template <typename T>
+class ArenaAllocator {
+private:
+ MemoryPool* pool;
+
+ template <class U>
+ friend class ArenaAllocator;
+
+ template <class U, class V>
+ friend bool operator==(const ArenaAllocator<U>& x, const ArenaAllocator<V>& y);
+
+ template <class U, class V>
+ friend bool operator!=(const ArenaAllocator<U>& x, const ArenaAllocator<V>& y);
+
+public:
+ using value_type = T;
+
+ template <typename U>
+ struct rebind {
+ using other = ArenaAllocator<U>;
+ };
+
+ /**
+ * Constructs an arena allocator using the specified memory pool.
+ * The MemoryPool object must outlive all allocators constructed with it and all allocated objects.
+ */
+ ArenaAllocator(MemoryPool& memory_pool);
+
+ template <typename U>
+ ArenaAllocator(const ArenaAllocator<U>&);
+
+ T* allocate(std::size_t n);
+ void deallocate(T* p, std::size_t);
+};
+
+template <class T, class U>
+bool operator==(const ArenaAllocator<T>&, const ArenaAllocator<U>&);
+
+template <class T, class U>
+bool operator!=(const ArenaAllocator<T>&, const ArenaAllocator<U>&);
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/data_structures/arena_allocator.defn.h>
+
+#endif // FRUIT_ARENA_ALLOCATOR_H
diff --git a/include/fruit/impl/data_structures/fixed_size_allocator.defn.h b/include/fruit/impl/data_structures/fixed_size_allocator.defn.h
new file mode 100644
index 0000000..383b737
--- /dev/null
+++ b/include/fruit/impl/data_structures/fixed_size_allocator.defn.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LITENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR TONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FIXED_SIZE_ALLOTATOR_DEFN_H
+#define FRUIT_FIXED_SIZE_ALLOTATOR_DEFN_H
+
+#include <fruit/impl/fruit_assert.h>
+
+#include <cassert>
+
+#ifdef FRUIT_EXTRA_DEBUG
+#include <iostream>
+#endif
+
+// Redundant, but makes KDevelop happy.
+#include <fruit/impl/data_structures/fixed_size_allocator.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename C>
+void FixedSizeAllocator::destroyObject(void* p) {
+ C* cPtr = reinterpret_cast<C*>(p);
+ cPtr->C::~C();
+}
+
+template <typename C>
+void FixedSizeAllocator::destroyExternalObject(void* p) {
+ C* cPtr = reinterpret_cast<C*>(p);
+ delete cPtr; // LCOV_EXCL_BR_LINE
+}
+
+inline void FixedSizeAllocator::FixedSizeAllocatorData::addType(TypeId typeId) {
+#ifdef FRUIT_EXTRA_DEBUG
+ types[typeId]++;
+#endif
+ if (!typeId.type_info->isTriviallyDestructible()) {
+ num_types_to_destroy++;
+ }
+ total_size += maximumRequiredSpace(typeId);
+}
+
+inline void FixedSizeAllocator::FixedSizeAllocatorData::addExternallyAllocatedType(TypeId typeId) {
+ (void)typeId;
+ num_types_to_destroy++;
+}
+
+inline std::size_t FixedSizeAllocator::FixedSizeAllocatorData::maximumRequiredSpace(TypeId type) {
+ return type.type_info->alignment() + type.type_info->size() - 1;
+}
+
+template <typename AnnotatedT, typename... Args>
+FRUIT_ALWAYS_INLINE inline fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<AnnotatedT>)>>*
+FixedSizeAllocator::constructObject(Args&&... args) {
+ using T = fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<AnnotatedT>)>>;
+
+ char* p = storage_last_used;
+ size_t misalignment = std::uintptr_t(p) % alignof(T);
+#ifdef FRUIT_EXTRA_DEBUG
+ FruitAssert(remaining_types[getTypeId<AnnotatedT>()] != 0);
+ remaining_types[getTypeId<AnnotatedT>()]--;
+#endif
+ p += alignof(T) - misalignment;
+ FruitAssert(std::uintptr_t(p) % alignof(T) == 0);
+ T* x = reinterpret_cast<T*>(p);
+ storage_last_used = p + sizeof(T) - 1;
+
+ // This runs arbitrary code (T's constructor), which might end up calling
+ // constructObject recursively. We must make sure all invariants are satisfied before
+ // calling this.
+ new (x) T(std::forward<Args>(args)...); // LCOV_EXCL_BR_LINE
+
+ // We still run this later though, since if T's constructor throws we don't want to
+ // destruct this object in FixedSizeAllocator's destructor.
+ if (!std::is_trivially_destructible<T>::value) {
+ on_destruction.push_back(std::pair<destroy_t, void*>{destroyObject<T>, x});
+ }
+ return x;
+}
+
+template <typename T>
+inline void FixedSizeAllocator::registerExternallyAllocatedObject(T* p) {
+ on_destruction.push_back(std::pair<destroy_t, void*>{destroyExternalObject<T>, p});
+}
+
+inline FixedSizeAllocator::FixedSizeAllocator(FixedSizeAllocatorData allocator_data)
+ : on_destruction(allocator_data.num_types_to_destroy) {
+ // The +1 is because we waste the first byte (storage_last_used points to the beginning of storage).
+ storage_begin = new char[allocator_data.total_size + 1];
+ storage_last_used = storage_begin;
+#ifdef FRUIT_EXTRA_DEBUG
+ remaining_types = allocator_data.types;
+ std::cerr << "Constructing allocator for types:";
+ for (auto x : remaining_types) {
+ std::cerr << " " << x.first;
+ }
+ std::cerr << std::endl;
+#endif
+}
+
+inline FixedSizeAllocator::FixedSizeAllocator(FixedSizeAllocator&& x) : FixedSizeAllocator() {
+ std::swap(storage_begin, x.storage_begin);
+ std::swap(storage_last_used, x.storage_last_used);
+ std::swap(on_destruction, x.on_destruction);
+#ifdef FRUIT_EXTRA_DEBUG
+ std::swap(remaining_types, x.remaining_types);
+#endif
+}
+
+inline FixedSizeAllocator& FixedSizeAllocator::operator=(FixedSizeAllocator&& x) {
+ std::swap(storage_begin, x.storage_begin);
+ std::swap(storage_last_used, x.storage_last_used);
+ std::swap(on_destruction, x.on_destruction);
+#ifdef FRUIT_EXTRA_DEBUG
+ std::swap(remaining_types, x.remaining_types);
+#endif
+ return *this;
+}
+
+} // namespace fruit
+} // namespace impl
+
+#endif // FRUIT_FIXED_SIZE_ALLOTATOR_DEFN_H
diff --git a/include/fruit/impl/data_structures/fixed_size_allocator.h b/include/fruit/impl/data_structures/fixed_size_allocator.h
new file mode 100644
index 0000000..fa0480c
--- /dev/null
+++ b/include/fruit/impl/data_structures/fixed_size_allocator.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FIXED_SIZE_ALLOCATOR_H
+#define FRUIT_FIXED_SIZE_ALLOCATOR_H
+
+#include <fruit/impl/data_structures/fixed_size_vector.h>
+#include <fruit/impl/meta/component.h>
+#include <fruit/impl/util/type_info.h>
+
+#ifdef FRUIT_EXTRA_DEBUG
+#include <unordered_map>
+#endif
+
+namespace fruit {
+namespace impl {
+
+/**
+ * An allocator where the maximum total size is fixed at construction, and all memory is retained until the allocator
+ * object itself is destructed.
+ */
+class FixedSizeAllocator {
+public:
+ using destroy_t = void (*)(void*);
+
+private:
+ // A pointer to the last used byte in the allocated memory chunk starting at storage_begin.
+ char* storage_last_used = nullptr;
+
+ // The chunk of memory that will be used for all allocations.
+ char* storage_begin = nullptr;
+
+#ifdef FRUIT_EXTRA_DEBUG
+ std::unordered_map<TypeId, std::size_t> remaining_types;
+#endif
+
+ // This vector contains the destroy operations that have to be performed at destruction, and
+ // the pointers that they must be invoked with. Allows destruction in the correct order.
+ // These must be called in reverse order.
+ FixedSizeVector<std::pair<destroy_t, void*>> on_destruction;
+
+ // Destroys an object previously created using constructObject().
+ template <typename C>
+ static void destroyObject(void* p);
+
+ // Calls delete on an object previously allocated using new.
+ template <typename C>
+ static void destroyExternalObject(void* p);
+
+public:
+ // Data used to construct an allocator for a fixed set of types.
+ class FixedSizeAllocatorData {
+ private:
+ std::size_t total_size = 0;
+ std::size_t num_types_to_destroy = 0;
+#ifdef FRUIT_EXTRA_DEBUG
+ std::unordered_map<TypeId, std::size_t> types;
+#endif
+
+ static std::size_t maximumRequiredSpace(TypeId type);
+
+ friend class FixedSizeAllocator;
+
+ public:
+ // Adds 1 `typeId' to the type set. Multiple copies of the same type are allowed.
+ // Each call to this method allows 1 constructObject<T>(...) call on the resulting allocator.
+ void addType(TypeId typeId);
+
+ // Each call to this method with getTypeId<T>() allows 1 registerExternallyAllocatedType<T>(...) call on the
+ // resulting
+ // allocator.
+ void addExternallyAllocatedType(TypeId typeId);
+ };
+
+ // Constructs an empty allocator (no allocations are allowed).
+ FixedSizeAllocator() = default;
+
+ // Constructs an allocator for the type set in FixedSizeAllocatorData.
+ FixedSizeAllocator(FixedSizeAllocatorData allocator_data);
+
+ FixedSizeAllocator(FixedSizeAllocator&&);
+ FixedSizeAllocator& operator=(FixedSizeAllocator&&);
+
+ FixedSizeAllocator(const FixedSizeAllocator&) = delete;
+ FixedSizeAllocator& operator=(const FixedSizeAllocator&) = delete;
+
+ // On destruction, all objects allocated with constructObject() and all externally-allocated objects registered with
+ // registerExternallyAllocatedObject() are destroyed.
+ ~FixedSizeAllocator();
+
+ // Allocates an object of type T, constructing it with the specified arguments. Similar to:
+ // new C(args...)
+ template <typename AnnotatedT, typename... Args>
+ fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<AnnotatedT>)>>*
+ constructObject(Args&&... args);
+
+ template <typename T>
+ void registerExternallyAllocatedObject(T* p);
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/data_structures/fixed_size_allocator.defn.h>
+
+#endif // FRUIT_FIXED_SIZE_ALLOCATOR_H
diff --git a/include/fruit/impl/data_structures/fixed_size_vector.defn.h b/include/fruit/impl/data_structures/fixed_size_vector.defn.h
new file mode 100644
index 0000000..79d0c97
--- /dev/null
+++ b/include/fruit/impl/data_structures/fixed_size_vector.defn.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FIXED_SIZE_VECTOR_DEFN_H
+#define FRUIT_FIXED_SIZE_VECTOR_DEFN_H
+
+#include <fruit/impl/data_structures/fixed_size_vector.h>
+
+#include <fruit/impl/fruit_assert.h>
+
+#include <cassert>
+#include <cstring>
+#include <utility>
+
+namespace fruit {
+namespace impl {
+
+template <typename T, typename Allocator>
+inline FixedSizeVector<T, Allocator>::FixedSizeVector(std::size_t capacity, Allocator allocator)
+ : capacity(capacity), allocator(allocator) {
+ if (capacity == 0) { // LCOV_EXCL_BR_LINE
+ v_begin = 0;
+ } else {
+ v_begin = allocator.allocate(capacity);
+ }
+ v_end = v_begin;
+}
+
+template <typename T, typename Allocator>
+inline FixedSizeVector<T, Allocator>::~FixedSizeVector() {
+ clear();
+ if (capacity != 0) {
+ allocator.deallocate(v_begin, capacity);
+ }
+}
+
+template <typename T, typename Allocator>
+inline FixedSizeVector<T, Allocator>::FixedSizeVector(FixedSizeVector&& other) : FixedSizeVector() {
+ swap(other);
+}
+
+template <typename T, typename Allocator>
+inline FixedSizeVector<T, Allocator>& FixedSizeVector<T, Allocator>::operator=(FixedSizeVector&& other) {
+ swap(other);
+ return *this;
+}
+
+template <typename T, typename Allocator>
+inline std::size_t FixedSizeVector<T, Allocator>::size() const {
+ return end() - begin();
+}
+
+template <typename T, typename Allocator>
+inline T& FixedSizeVector<T, Allocator>::operator[](std::size_t i) {
+ FruitAssert(begin() + i < end());
+ return begin()[i];
+}
+
+template <typename T, typename Allocator>
+inline const T& FixedSizeVector<T, Allocator>::operator[](std::size_t i) const {
+ FruitAssert(begin() + i < end());
+ return begin()[i];
+}
+
+template <typename T, typename Allocator>
+inline void FixedSizeVector<T, Allocator>::swap(FixedSizeVector& x) {
+ std::swap(v_end, x.v_end);
+ std::swap(v_begin, x.v_begin);
+ std::swap(capacity, x.capacity);
+}
+
+template <typename T, typename Allocator>
+inline void FixedSizeVector<T, Allocator>::push_back(T x) {
+#ifdef FRUIT_EXTRA_DEBUG
+ FruitAssert(v_end != v_begin + capacity);
+#endif
+ new (v_end) T(x); // LCOV_EXCL_BR_LINE
+ ++v_end;
+#ifdef FRUIT_EXTRA_DEBUG
+ FruitAssert(v_end <= v_begin + capacity);
+#endif
+}
+
+// This method is covered by tests, even though lcov doesn't detect that.
+template <typename T, typename Allocator>
+inline T* FixedSizeVector<T, Allocator>::data() {
+ return v_begin;
+}
+
+template <typename T, typename Allocator>
+inline T* FixedSizeVector<T, Allocator>::begin() {
+ return v_begin;
+}
+
+template <typename T, typename Allocator>
+inline T* FixedSizeVector<T, Allocator>::end() {
+ return v_end;
+}
+
+template <typename T, typename Allocator>
+inline const T* FixedSizeVector<T, Allocator>::data() const {
+ return v_begin;
+}
+
+template <typename T, typename Allocator>
+inline const T* FixedSizeVector<T, Allocator>::begin() const {
+ return v_begin;
+}
+
+template <typename T, typename Allocator>
+inline const T* FixedSizeVector<T, Allocator>::end() const {
+ return v_end;
+}
+
+template <typename T, typename Allocator>
+inline void FixedSizeVector<T, Allocator>::clear() {
+ for (T* p = v_begin; p != v_end; ++p) {
+ p->~T();
+ }
+ v_end = v_begin;
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_FIXED_SIZE_VECTOR_DEFN_H
diff --git a/include/fruit/impl/data_structures/fixed_size_vector.h b/include/fruit/impl/data_structures/fixed_size_vector.h
new file mode 100644
index 0000000..7450bf1
--- /dev/null
+++ b/include/fruit/impl/data_structures/fixed_size_vector.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FIXED_SIZE_VECTOR_H
+#define FRUIT_FIXED_SIZE_VECTOR_H
+
+#include <cstdlib>
+#include <memory>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * Similar to std::vector<T>, but the capacity is fixed at construction time, and no reallocations ever happen.
+ * The type T must be trivially copyable.
+ */
+template <typename T, typename Allocator = std::allocator<T>>
+class FixedSizeVector {
+private:
+ // This is not yet implemented in libstdc++ (the STL implementation) shipped with GCC (checked until version 4.9.1).
+ // static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable.");
+
+ // v_end is before v_begin here, because it's the most commonly accessed field.
+ T* v_end;
+ T* v_begin;
+
+ std::size_t capacity;
+ Allocator allocator;
+
+public:
+ using iterator = T*;
+ using const_iterator = const T*;
+
+ FixedSizeVector(std::size_t capacity = 0, Allocator allocator = Allocator());
+ // Creates a vector with the specified size (and equal capacity) initialized with the specified value.
+ FixedSizeVector(std::size_t size, const T& value, Allocator allocator = Allocator());
+ ~FixedSizeVector();
+
+ // Copy construction is not allowed, you need to specify the capacity in order to construct the copy.
+ FixedSizeVector(const FixedSizeVector& other) = delete;
+ FixedSizeVector(const FixedSizeVector& other, std::size_t capacity);
+
+ FixedSizeVector(FixedSizeVector&& other);
+
+ FixedSizeVector& operator=(const FixedSizeVector& other) = delete;
+ FixedSizeVector& operator=(FixedSizeVector&& other);
+
+ std::size_t size() const;
+
+ T& operator[](std::size_t i);
+ const T& operator[](std::size_t i) const;
+
+ // This yields undefined behavior (instead of reallocating) if the vector's capacity is exceeded.
+ void push_back(T x);
+
+ void swap(FixedSizeVector& x);
+
+ // Removes all elements, so size() becomes 0 (but maintains the capacity).
+ void clear();
+
+ T* data();
+ iterator begin();
+ iterator end();
+
+ const T* data() const;
+ const_iterator begin() const;
+ const_iterator end() const;
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/data_structures/fixed_size_vector.defn.h>
+
+#endif // FRUIT_FIXED_SIZE_VECTOR_H
diff --git a/include/fruit/impl/data_structures/fixed_size_vector.templates.h b/include/fruit/impl/data_structures/fixed_size_vector.templates.h
new file mode 100644
index 0000000..5ce3959
--- /dev/null
+++ b/include/fruit/impl/data_structures/fixed_size_vector.templates.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FIXED_SIZE_VECTOR_TEMPLATES_H
+#define FRUIT_FIXED_SIZE_VECTOR_TEMPLATES_H
+
+#ifndef IN_FRUIT_CPP_FILE
+#error "Fruit .template.h file included in non-cpp file."
+#endif
+
+#include <fruit/impl/data_structures/fixed_size_vector.h>
+
+#include <fruit/impl/fruit_assert.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename T, typename Allocator>
+FixedSizeVector<T, Allocator>::FixedSizeVector(const FixedSizeVector& other, std::size_t capacity)
+ : FixedSizeVector(capacity, other.allocator) {
+ FruitAssert(other.size() <= capacity);
+ // This is not just an optimization, we also want to make sure that other.capacity (and therefore
+ // also this.capacity) is >0, or we'd pass nullptr to memcpy (although with a size of 0).
+ if (other.size() != 0) {
+ FruitAssert(v_begin != nullptr);
+ FruitAssert(other.v_begin != nullptr);
+ std::memcpy(v_begin, other.v_begin, other.size() * sizeof(T));
+ }
+ v_end = v_begin + other.size();
+}
+
+template <typename T, typename Allocator>
+FixedSizeVector<T, Allocator>::FixedSizeVector(std::size_t size, const T& value, Allocator allocator)
+ : FixedSizeVector(size, allocator) {
+ for (std::size_t i = 0; i < size; ++i) {
+ push_back(value);
+ }
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_FIXED_SIZE_VECTOR_TEMPLATES_H
diff --git a/include/fruit/impl/data_structures/memory_pool.defn.h b/include/fruit/impl/data_structures/memory_pool.defn.h
new file mode 100644
index 0000000..4d892d4
--- /dev/null
+++ b/include/fruit/impl/data_structures/memory_pool.defn.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_MEMORY_POOL_DEFN_H
+#define FRUIT_MEMORY_POOL_DEFN_H
+
+#include <fruit/impl/data_structures/memory_pool.h>
+#include <fruit/impl/fruit-config.h>
+#include <fruit/impl/fruit_assert.h>
+
+#include <cstdint>
+
+namespace fruit {
+namespace impl {
+
+inline MemoryPool::MemoryPool() : first_free(nullptr), capacity(0) {}
+
+inline MemoryPool::MemoryPool(MemoryPool&& other)
+ : allocated_chunks(std::move(other.allocated_chunks)), first_free(other.first_free), capacity(other.capacity) {
+ // This is to be sure that we don't double-deallocate.
+ other.allocated_chunks.clear();
+}
+
+inline MemoryPool& MemoryPool::operator=(MemoryPool&& other) {
+ destroy();
+
+ allocated_chunks = std::move(other.allocated_chunks);
+ first_free = other.first_free;
+ capacity = other.capacity;
+
+ // This is to be sure that we don't double-deallocate.
+ other.allocated_chunks.clear();
+
+ return *this;
+}
+
+inline MemoryPool::~MemoryPool() {
+ destroy();
+}
+
+template <typename T>
+FRUIT_ALWAYS_INLINE inline T* MemoryPool::allocate(std::size_t n) {
+#ifdef FRUIT_DISABLE_ARENA_ALLOCATION
+ void* p = operator new(n * sizeof(T));
+ allocated_chunks.push_back(p);
+ return static_cast<T*>(p);
+#else
+
+ if (n == 0) {
+ n = 1;
+ }
+ std::size_t misalignment = std::uintptr_t(first_free) % alignof(T);
+ std::size_t padding = alignof(T) - (sizeof(T) % alignof(T));
+ std::size_t required_space = n * (sizeof(T) + padding);
+ std::size_t required_space_in_chunk = required_space + (alignof(T) - misalignment);
+ if (required_space_in_chunk > capacity) {
+ // This is to make sure that the push_back below won't throw.
+ if (allocated_chunks.size() == allocated_chunks.capacity()) {
+ allocated_chunks.reserve(1 + 2 * allocated_chunks.size());
+ }
+ void* p;
+ if (required_space > CHUNK_SIZE) {
+ p = operator new(required_space); // LCOV_EXCL_BR_LINE
+ } else {
+ p = operator new(CHUNK_SIZE);
+ first_free = static_cast<char*>(p) + required_space;
+ capacity = CHUNK_SIZE - required_space;
+ }
+ allocated_chunks.push_back(p);
+ return static_cast<T*>(p);
+ } else {
+ FruitAssert(first_free != nullptr);
+ void* p = first_free + misalignment;
+ first_free += required_space_in_chunk;
+ capacity -= required_space_in_chunk;
+ return static_cast<T*>(p);
+ }
+#endif
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_MEMORY_POOL_DEFN_H
diff --git a/include/fruit/impl/data_structures/memory_pool.h b/include/fruit/impl/data_structures/memory_pool.h
new file mode 100644
index 0000000..05e4eff
--- /dev/null
+++ b/include/fruit/impl/data_structures/memory_pool.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_MEMORY_POOL_H
+#define FRUIT_MEMORY_POOL_H
+
+#include <vector>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * A pool of memory that never shrinks and is only deallocated on destruction.
+ * See also ArenaAllocator, an Allocator backed by a MemoryPool object.
+ */
+class MemoryPool {
+private:
+ // 4KB - 64B.
+ // We don't use the full 4KB because malloc also needs to store some metadata for each block, and we want
+ // malloc to request <=4KB from the OS.
+ constexpr static const std::size_t CHUNK_SIZE = 4 * 1024 - 64;
+
+ std::vector<void*> allocated_chunks;
+ // The memory block [first_free, first_free + capacity) is available for allocation
+ char* first_free;
+ std::size_t capacity;
+
+ void destroy();
+
+public:
+ MemoryPool();
+
+ MemoryPool(const MemoryPool&) = delete;
+ MemoryPool(MemoryPool&&);
+ MemoryPool& operator=(const MemoryPool&) = delete;
+ MemoryPool& operator=(MemoryPool&&);
+ ~MemoryPool();
+
+ /**
+ * Returns a chunk of memory that can hold n T objects.
+ * Note that this does *not* construct any T objects at that location.
+ */
+ template <typename T>
+ T* allocate(std::size_t n);
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/data_structures/memory_pool.defn.h>
+
+#endif // FRUIT_MEMORY_POOL_H
diff --git a/include/fruit/impl/data_structures/packed_pointer_and_bool.defn.h b/include/fruit/impl/data_structures/packed_pointer_and_bool.defn.h
new file mode 100644
index 0000000..e5365fa
--- /dev/null
+++ b/include/fruit/impl/data_structures/packed_pointer_and_bool.defn.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LITENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR TONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_PACKED_POINTER_AND_BOOL_DEFN_H
+#define FRUIT_PACKED_POINTER_AND_BOOL_DEFN_H
+
+#include <fruit/impl/data_structures/packed_pointer_and_bool.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename T>
+inline std::uintptr_t PackedPointerAndBool<T>::encode(T* p, bool b) {
+ return reinterpret_cast<std::uintptr_t>(p) | std::uintptr_t(b);
+}
+
+template <typename T>
+inline T* PackedPointerAndBool<T>::decodePointer(std::uintptr_t value) {
+ return reinterpret_cast<T*>(value & ~std::uintptr_t(1));
+}
+
+template <typename T>
+inline bool PackedPointerAndBool<T>::decodeBool(std::uintptr_t value) {
+ return value & 1;
+}
+
+template <typename T>
+inline PackedPointerAndBool<T>::PackedPointerAndBool(T* p, bool b) {
+ value = encode(p, b);
+}
+
+template <typename T>
+inline T* PackedPointerAndBool<T>::getPointer() const {
+ return decodePointer(value);
+}
+
+template <typename T>
+inline void PackedPointerAndBool<T>::setPointer(T* p) {
+ value = encode(p, decodeBool(value));
+}
+
+template <typename T>
+inline bool PackedPointerAndBool<T>::getBool() const {
+ return decodeBool(value);
+}
+
+template <typename T>
+inline void PackedPointerAndBool<T>::setBool(bool b) {
+ value = encode(decodePointer(value), b);
+}
+
+template <typename T>
+inline bool PackedPointerAndBool<T>::operator==(const PackedPointerAndBool<T>& other) const {
+ return value == other.value;
+}
+
+template <typename T>
+inline bool PackedPointerAndBool<T>::operator!=(const PackedPointerAndBool<T>& other) const {
+ return value != other.value;
+}
+
+} // namespace fruit
+} // namespace impl
+
+#endif // FRUIT_PACKED_POINTER_AND_BOOL_DEFN_H
diff --git a/include/fruit/impl/data_structures/packed_pointer_and_bool.h b/include/fruit/impl/data_structures/packed_pointer_and_bool.h
new file mode 100644
index 0000000..8ac84de
--- /dev/null
+++ b/include/fruit/impl/data_structures/packed_pointer_and_bool.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_PACKED_POINTER_AND_BOOL_H
+#define FRUIT_PACKED_POINTER_AND_BOOL_H
+
+namespace fruit {
+namespace impl {
+
+/**
+ * This class stores a T* and a bool in the space of a pointer.
+ * alignof(T) must be at least 2.
+ */
+template <typename T>
+class PackedPointerAndBool {
+private:
+ static_assert(alignof(T) >= 2, "alignof(T) must be at least 2 for the packing to be possible");
+
+ std::uintptr_t value;
+
+ static std::uintptr_t encode(T* p, bool b);
+ static T* decodePointer(std::uintptr_t value);
+ static bool decodeBool(std::uintptr_t value);
+
+public:
+ PackedPointerAndBool(T* p, bool b);
+
+ PackedPointerAndBool() = default;
+
+ PackedPointerAndBool(const PackedPointerAndBool&) = default;
+ PackedPointerAndBool(PackedPointerAndBool&&) = default;
+
+ PackedPointerAndBool& operator=(const PackedPointerAndBool&) = default;
+ PackedPointerAndBool& operator=(PackedPointerAndBool&&) = default;
+
+ T* getPointer() const;
+ void setPointer(T* p);
+
+ bool getBool() const;
+ void setBool(bool b);
+
+ bool operator==(const PackedPointerAndBool<T>& other) const;
+ bool operator!=(const PackedPointerAndBool<T>& other) const;
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/data_structures/packed_pointer_and_bool.defn.h>
+
+#endif // FRUIT_PACKED_POINTER_AND_BOOL_H
diff --git a/include/fruit/impl/data_structures/semistatic_graph.defn.h b/include/fruit/impl/data_structures/semistatic_graph.defn.h
new file mode 100644
index 0000000..76db0b7
--- /dev/null
+++ b/include/fruit/impl/data_structures/semistatic_graph.defn.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SEMISTATIC_GRAPH_DEFN_H
+#define SEMISTATIC_GRAPH_DEFN_H
+
+#include <fruit/impl/data_structures/semistatic_graph.h>
+
+namespace fruit {
+namespace impl {
+
+inline bool SemistaticGraphInternalNodeId::operator==(const SemistaticGraphInternalNodeId& x) const {
+ return id == x.id;
+}
+
+inline bool SemistaticGraphInternalNodeId::operator<(const SemistaticGraphInternalNodeId& x) const {
+ return id < x.id;
+}
+
+template <typename NodeId, typename Node>
+inline SemistaticGraph<NodeId, Node>::node_iterator::node_iterator(NodeData* itr) : itr(itr) {}
+
+template <typename NodeId, typename Node>
+inline Node& SemistaticGraph<NodeId, Node>::node_iterator::getNode() {
+ FruitAssert(itr->edges_begin != 1);
+ return itr->node;
+}
+
+template <typename NodeId, typename Node>
+inline bool SemistaticGraph<NodeId, Node>::node_iterator::isTerminal() {
+ FruitAssert(itr->edges_begin != 1);
+ return itr->edges_begin == 0;
+}
+
+template <typename NodeId, typename Node>
+inline void SemistaticGraph<NodeId, Node>::node_iterator::setTerminal() {
+ FruitAssert(itr->edges_begin != 1);
+ itr->edges_begin = 0;
+}
+
+template <typename NodeId, typename Node>
+inline bool SemistaticGraph<NodeId, Node>::node_iterator::operator==(const node_iterator& other) const {
+ return itr == other.itr;
+}
+
+template <typename NodeId, typename Node>
+inline SemistaticGraph<NodeId, Node>::const_node_iterator::const_node_iterator(const NodeData* itr) : itr(itr) {}
+
+template<typename NodeId, typename Node>
+inline SemistaticGraph<NodeId, Node>::const_node_iterator::const_node_iterator(node_iterator itr) : itr(itr.itr) {}
+
+template <typename NodeId, typename Node>
+inline const Node& SemistaticGraph<NodeId, Node>::const_node_iterator::getNode() {
+ FruitAssert(itr->edges_begin != 1);
+ return itr->node;
+}
+
+template <typename NodeId, typename Node>
+inline bool SemistaticGraph<NodeId, Node>::const_node_iterator::isTerminal() {
+ FruitAssert(itr->edges_begin != 1);
+ return itr->edges_begin == 0;
+}
+
+template <typename NodeId, typename Node>
+inline bool SemistaticGraph<NodeId, Node>::const_node_iterator::operator==(const const_node_iterator& other) const {
+ return itr == other.itr;
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::edge_iterator
+SemistaticGraph<NodeId, Node>::node_iterator::neighborsBegin() {
+ FruitAssert(itr->edges_begin != 0);
+ FruitAssert(itr->edges_begin != 1);
+ return edge_iterator{reinterpret_cast<InternalNodeId*>(itr->edges_begin)};
+}
+
+template <typename NodeId, typename Node>
+inline SemistaticGraph<NodeId, Node>::edge_iterator::edge_iterator(InternalNodeId* itr) : itr(itr) {}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::node_iterator
+SemistaticGraph<NodeId, Node>::edge_iterator::getNodeIterator(node_iterator nodes_begin) {
+ return node_iterator{nodeAtId(nodes_begin.itr, *itr)};
+}
+
+template <typename NodeId, typename Node>
+inline void SemistaticGraph<NodeId, Node>::edge_iterator::operator++() {
+ ++itr;
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::node_iterator
+SemistaticGraph<NodeId, Node>::edge_iterator::getNodeIterator(std::size_t i, node_iterator nodes_begin) {
+ itr += i;
+ return getNodeIterator(nodes_begin);
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::node_iterator SemistaticGraph<NodeId, Node>::begin() {
+ return node_iterator{nodes.begin()};
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::node_iterator SemistaticGraph<NodeId, Node>::end() {
+ return node_iterator{nodes.end()};
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::const_node_iterator SemistaticGraph<NodeId, Node>::end() const {
+ return const_node_iterator{nodes.end()};
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::node_iterator SemistaticGraph<NodeId, Node>::at(NodeId nodeId) {
+ InternalNodeId internalNodeId = node_index_map.at(nodeId);
+ return node_iterator{nodeAtId(internalNodeId)};
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::const_node_iterator
+SemistaticGraph<NodeId, Node>::find(NodeId nodeId) const {
+ const InternalNodeId* internalNodeIdPtr = node_index_map.find(nodeId);
+ if (internalNodeIdPtr == nullptr) {
+ return const_node_iterator{nodes.end()};
+ } else {
+ const NodeData* p = nodeAtId(*internalNodeIdPtr);
+ if (p->edges_begin == 1) {
+ return const_node_iterator{nodes.end()};
+ }
+ return const_node_iterator{p};
+ }
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::node_iterator SemistaticGraph<NodeId, Node>::find(NodeId nodeId) {
+ const InternalNodeId* internalNodeIdPtr = node_index_map.find(nodeId);
+ if (internalNodeIdPtr == nullptr) {
+ return node_iterator{nodes.end()};
+ } else {
+ NodeData* p = nodeAtId(*internalNodeIdPtr);
+ if (p->edges_begin == 1) {
+ return node_iterator{nodes.end()};
+ }
+ return node_iterator{p};
+ }
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::NodeData*
+SemistaticGraph<NodeId, Node>::nodeAtId(InternalNodeId internalNodeId) {
+ return nodeAtId(nodes.data(), internalNodeId);
+}
+
+template <typename NodeId, typename Node>
+inline const typename SemistaticGraph<NodeId, Node>::NodeData*
+SemistaticGraph<NodeId, Node>::nodeAtId(InternalNodeId internalNodeId) const {
+ return nodeAtId(nodes.data(), internalNodeId);
+}
+
+template <typename NodeId, typename Node>
+inline typename SemistaticGraph<NodeId, Node>::NodeData*
+SemistaticGraph<NodeId, Node>::nodeAtId(NodeData* nodes_begin, InternalNodeId internalNodeId) {
+ FruitAssert(internalNodeId.id % sizeof(NodeData) == 0);
+ NodeData* p = reinterpret_cast<NodeData*>(reinterpret_cast<char*>(nodes_begin) + internalNodeId.id);
+ // The code above is faster (the compiler doesn't have to worry about internalNodeId.id%sizeof(NodeData), that we know
+ // to be 0).
+ FruitAssert(p == nodes_begin + internalNodeId.id / sizeof(NodeData));
+ return p;
+}
+
+template <typename NodeId, typename Node>
+inline const typename SemistaticGraph<NodeId, Node>::NodeData*
+SemistaticGraph<NodeId, Node>::nodeAtId(const NodeData* nodes_begin, InternalNodeId internalNodeId) {
+ FruitAssert(internalNodeId.id % sizeof(NodeData) == 0);
+ const NodeData* p = reinterpret_cast<const NodeData*>(reinterpret_cast<const char*>(nodes_begin) + internalNodeId.id);
+ // The code above is faster (the compiler doesn't have to worry about internalNodeId.id%sizeof(NodeData), that we know
+ // to be 0).
+ FruitAssert(p == nodes_begin + internalNodeId.id / sizeof(NodeData));
+ return p;
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // SEMISTATIC_GRAPH_INLINES_H
diff --git a/include/fruit/impl/data_structures/semistatic_graph.h b/include/fruit/impl/data_structures/semistatic_graph.h
new file mode 100644
index 0000000..b48f1cf
--- /dev/null
+++ b/include/fruit/impl/data_structures/semistatic_graph.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SEMISTATIC_GRAPH_H
+#define SEMISTATIC_GRAPH_H
+
+#include "memory_pool.h"
+#include <fruit/impl/data_structures/semistatic_map.h>
+
+#ifdef FRUIT_EXTRA_DEBUG
+#include <iostream>
+#endif
+
+namespace fruit {
+namespace impl {
+
+// The alignas ensures that a SemistaticGraphInternalNodeId* always has 0 in the low-order bit.
+struct alignas(2) alignas(alignof(std::size_t)) SemistaticGraphInternalNodeId {
+ // This stores the index in the vector times sizeof(NodeData).
+ std::size_t id;
+
+ bool operator==(const SemistaticGraphInternalNodeId& x) const;
+ bool operator<(const SemistaticGraphInternalNodeId& x) const;
+};
+
+/**
+ * A direct graph implementation where most of the graph is fixed at construction time, but a few nodes and edges can be
+ * added
+ * later.
+ *
+ * Also, nodes can either be normal nodes or terminal nodes. Terminal nodes can't have outgoing edges. Note that a node
+ * with no
+ * outgoing edges may or may not be marked as terminal.
+ *
+ * While insertion of elements after construction is supported, inserting or changing the neighbors of more than O(1)
+ * nodes
+ * after construction will raise the cost of any further operations to more than O(1).
+ *
+ * Even though adding nodes/edges after construction is inefficient, it is efficient to turn non-terminal nodes into
+ * terminal ones
+ * (and therefore removing all the outgoing edges from the node) after construction.
+ *
+ * NodeId and Node must be default constructible and trivially copyable.
+ */
+template <typename NodeId, typename Node>
+class SemistaticGraph {
+private:
+ using InternalNodeId = SemistaticGraphInternalNodeId;
+
+ // The node data for nodeId is in nodes[node_index_map.at(nodeId)/sizeof(NodeData)].
+ // To avoid hash table lookups, the edges in edges_storage are stored as indexes of `nodes' instead of as NodeIds.
+ // node_index_map contains all known NodeIds, including ones known only due to an outgoing edge ending there from
+ // another node.
+ SemistaticMap<NodeId, InternalNodeId> node_index_map;
+
+ struct NodeData {
+#ifdef FRUIT_EXTRA_DEBUG
+ NodeId key;
+#endif
+
+ public:
+ // If edges_begin==0, this is a terminal node.
+ // If edges_begin==1, this node doesn't exist, it's just referenced by another node.
+ // Otherwise, reinterpret_cast<InternalNodeId*>(edges_begin) is the beginning of the edges range.
+ std::uintptr_t edges_begin;
+
+ // An explicit "public" specifier here prevents the compiler from reordering the fields.
+ // We want the edges_begin field to be stored first because that's what we're going to branch on.
+ public:
+ Node node;
+ };
+
+ std::size_t first_unused_index;
+
+ FixedSizeVector<NodeData> nodes;
+
+ // Stores vectors of edges as contiguous chunks of node IDs.
+ // The NodeData elements in `nodes' contain indexes into this vector (stored as already multiplied by
+ // sizeof(NodeData)).
+ // The first element is unused.
+ FixedSizeVector<InternalNodeId> edges_storage;
+
+#ifdef FRUIT_EXTRA_DEBUG
+ template <typename NodeIter>
+ void printGraph(NodeIter first, NodeIter last);
+#endif
+
+ NodeData* nodeAtId(InternalNodeId internalNodeId);
+ const NodeData* nodeAtId(InternalNodeId internalNodeId) const;
+
+ static NodeData* nodeAtId(NodeData* nodes_begin, InternalNodeId internalNodeId);
+ static const NodeData* nodeAtId(const NodeData* nodes_begin, InternalNodeId internalNodeId);
+
+public:
+ class edge_iterator;
+
+ class node_iterator {
+ private:
+ NodeData* itr;
+
+ friend class SemistaticGraph<NodeId, Node>;
+
+ node_iterator(NodeData* itr);
+
+ public:
+ Node& getNode();
+
+ bool isTerminal();
+
+ // Turns the node into a terminal node, also removing all the deps.
+ void setTerminal();
+
+ // Assumes !isTerminal().
+ // neighborsEnd() is NOT provided/stored for efficiency, the client code is expected to know the number of
+ // neighbors.
+ edge_iterator neighborsBegin();
+
+ bool operator==(const node_iterator&) const;
+ };
+
+ class const_node_iterator {
+ private:
+ const NodeData* itr;
+
+ friend class SemistaticGraph<NodeId, Node>;
+
+ const_node_iterator(const NodeData* itr);
+
+ public:
+ const_node_iterator(node_iterator itr);
+
+ const Node& getNode();
+
+ bool isTerminal();
+
+ bool operator==(const const_node_iterator&) const;
+ };
+
+
+ class edge_iterator {
+ private:
+ // Iterator on edges_storage.
+ InternalNodeId* itr;
+
+ friend class SemistaticGraph<NodeId, Node>;
+ friend class SemistaticGraph<NodeId, Node>::node_iterator;
+
+ edge_iterator(InternalNodeId* itr);
+
+ public:
+ // getNodeIterator(graph.nodes.begin()) returns the first neighbor.
+ node_iterator getNodeIterator(node_iterator nodes_begin);
+
+ void operator++();
+
+ // Equivalent to i times operator++ followed by getNodeIterator(nodes_begin).
+ node_iterator getNodeIterator(std::size_t i, node_iterator nodes_begin);
+ };
+
+ // Constructs an *invalid* graph (as if this graph was just moved from).
+ SemistaticGraph() = default;
+
+ /**
+ * A value x obtained dereferencing a NodeIter::value_type must support the following operations:
+ * - x.getId(), returning a NodeId
+ * - x.getValue(), returning a Node
+ * - x.isTerminal(), returning a bool
+ * - x.getEdgesBegin() and x.getEdgesEnd(), that if !x.isTerminal() define a range of values of type NodeId
+ * (the outgoing edges).
+ *
+ * This constructor is *not* defined in semistatic_graph.templates.h, but only in semistatic_graph.cc.
+ * All instantiations must have a matching instantiation in semistatic_graph.cc.
+ *
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ template <typename NodeIter>
+ SemistaticGraph(NodeIter first, NodeIter last, MemoryPool& memory_pool);
+
+ SemistaticGraph(SemistaticGraph&&) = default;
+ SemistaticGraph(const SemistaticGraph&) = delete;
+
+ /**
+ * Creates a copy of x with the additional nodes in [first, last). The requirements on NodeIter as the same as for the
+ * 2-arg
+ * constructor.
+ * The nodes in [first, last) must NOT be already in x, but can be neighbors of nodes in x.
+ * The new graph will share data with `x', so must be destroyed before `x' is destroyed.
+ * Also, after this is called, `x' must not be modified until this object has been destroyed.
+ *
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ template <typename NodeIter>
+ SemistaticGraph(const SemistaticGraph& x, NodeIter first, NodeIter last, MemoryPool& memory_pool);
+
+ ~SemistaticGraph();
+
+ SemistaticGraph& operator=(const SemistaticGraph&) = delete;
+ SemistaticGraph& operator=(SemistaticGraph&&) = default;
+
+ // The result is unspecified. The only guarantee is that it's the right value to pass to edge_iterator's
+ // getNodeIterator() methods.
+ node_iterator begin();
+
+ node_iterator end();
+ const_node_iterator end() const;
+
+ // Precondition: `nodeId' must exist in the graph.
+ // Unlike std::map::at(), this yields undefined behavior if the precondition isn't satisfied (instead of throwing).
+ node_iterator at(NodeId nodeId);
+
+ // Prefer using at() when possible, this is slightly slower.
+ // Returns end() if the node ID was not found.
+ node_iterator find(NodeId nodeId);
+ const_node_iterator find(NodeId nodeId) const;
+
+#ifdef FRUIT_EXTRA_DEBUG
+ // Emits a runtime error if some node was not created but there is an edge pointing to it.
+ void checkFullyConstructed();
+#endif
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/data_structures/semistatic_graph.defn.h>
+
+// semistatic_graph.templates.h is not included here to limit the transitive includes. Include it explicitly (in .cpp
+// files).
+
+#endif // SEMISTATIC_GRAPH_H
diff --git a/include/fruit/impl/data_structures/semistatic_graph.templates.h b/include/fruit/impl/data_structures/semistatic_graph.templates.h
new file mode 100644
index 0000000..e55be4c
--- /dev/null
+++ b/include/fruit/impl/data_structures/semistatic_graph.templates.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SEMISTATIC_GRAPH_TEMPLATES_H
+#define SEMISTATIC_GRAPH_TEMPLATES_H
+
+#ifndef IN_FRUIT_CPP_FILE
+#error "Fruit .template.h file included in non-cpp file."
+#endif
+
+#include <fruit/impl/data_structures/arena_allocator.h>
+#include <fruit/impl/data_structures/fixed_size_vector.templates.h>
+#include <fruit/impl/data_structures/memory_pool.h>
+#include <fruit/impl/data_structures/semistatic_graph.h>
+#include <fruit/impl/data_structures/semistatic_map.templates.h>
+#include <fruit/impl/util/hash_helpers.h>
+
+#ifdef FRUIT_EXTRA_DEBUG
+#include <iostream>
+#endif
+
+namespace fruit {
+namespace impl {
+
+template <typename Iter, std::size_t index_increment>
+struct indexing_iterator {
+ Iter iter;
+ std::size_t index;
+
+ void operator++() {
+ ++iter;
+ index += index_increment;
+ }
+
+ auto operator*() -> decltype(std::make_pair(*iter, SemistaticGraphInternalNodeId{index})) {
+ return std::make_pair(*iter, SemistaticGraphInternalNodeId{index});
+ }
+};
+
+#ifdef FRUIT_EXTRA_DEBUG
+template <typename NodeId, typename Node>
+template <typename NodeIter>
+void SemistaticGraph<NodeId, Node>::printGraph(NodeIter first, NodeIter last) {
+ std::cerr << "Constructed injection graph with nodes:" << std::endl;
+ for (NodeIter i = first; i != last; ++i) {
+ std::cerr << i->getId() << " -> ";
+ if (i->isTerminal()) {
+ std::cerr << "(none, terminal)";
+ } else {
+ std::cerr << "{";
+ for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) {
+ if (j != i->getEdgesBegin()) {
+ std::cerr << ", ";
+ }
+ std::cerr << *j;
+ }
+ std::cerr << "}";
+ }
+ std::cerr << std::endl;
+ }
+ std::cerr << std::endl;
+}
+#endif // FRUIT_EXTRA_DEBUG
+
+template <typename NodeId, typename Node>
+template <typename NodeIter>
+SemistaticGraph<NodeId, Node>::SemistaticGraph(NodeIter first, NodeIter last, MemoryPool& memory_pool) {
+ std::size_t num_edges = 0;
+ // Step 1: assign IDs to all nodes, fill node_index_map and set first_unused_index.
+ HashSetWithArenaAllocator<NodeId> node_ids = createHashSetWithArenaAllocator<NodeId>(last - first, memory_pool);
+ for (NodeIter i = first; i != last; ++i) {
+ node_ids.insert(i->getId());
+ if (!i->isTerminal()) {
+ for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) {
+ node_ids.insert(*j);
+ ++num_edges;
+ }
+ }
+ }
+
+ using itr_t = typename HashSetWithArenaAllocator<NodeId>::iterator;
+ node_index_map = SemistaticMap<NodeId, InternalNodeId>(
+ indexing_iterator<itr_t, sizeof(NodeData)>{node_ids.begin(), 0}, node_ids.size(), memory_pool);
+
+ first_unused_index = node_ids.size();
+
+ // Step 2: fill `nodes' and edges_storage.
+
+ // Note that not all of these will be assigned in the loop below.
+ nodes = FixedSizeVector<NodeData>(first_unused_index, NodeData{
+#ifdef FRUIT_EXTRA_DEBUG
+ NodeId(),
+#endif
+ 1, Node()});
+
+ // edges_storage[0] is unused, that's the reason for the +1
+ edges_storage = FixedSizeVector<InternalNodeId>(num_edges + 1);
+ edges_storage.push_back(InternalNodeId());
+
+ for (NodeIter i = first; i != last; ++i) {
+ NodeData& nodeData = *nodeAtId(node_index_map.at(i->getId()));
+ nodeData.node = i->getValue();
+ if (i->isTerminal()) {
+ nodeData.edges_begin = 0;
+ } else {
+ nodeData.edges_begin = reinterpret_cast<std::uintptr_t>(edges_storage.data() + edges_storage.size());
+ for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) {
+ InternalNodeId other_node_id = node_index_map.at(*j);
+ edges_storage.push_back(other_node_id);
+ }
+ }
+ }
+
+#ifdef FRUIT_EXTRA_DEBUG
+ printGraph(first, last);
+#endif
+}
+
+template <typename NodeId, typename Node>
+template <typename NodeIter>
+SemistaticGraph<NodeId, Node>::SemistaticGraph(const SemistaticGraph& x, NodeIter first, NodeIter last,
+ MemoryPool& memory_pool)
+ : first_unused_index(x.first_unused_index) {
+
+ // TODO: The code below is very similar to the other constructor, extract the common parts in separate functions.
+
+ std::size_t num_new_edges = 0;
+
+ // Step 1: assign IDs to new nodes, fill `node_index_map' and update `first_unused_index'.
+
+ // Step 1a: collect all new node IDs.
+ using node_ids_elem_t = std::pair<NodeId, InternalNodeId>;
+ using node_ids_t = std::vector<node_ids_elem_t, ArenaAllocator<node_ids_elem_t>>;
+ node_ids_t node_ids = node_ids_t(ArenaAllocator<node_ids_elem_t>(memory_pool));
+ for (NodeIter i = first; i != last; ++i) {
+ if (x.node_index_map.find(i->getId()) == nullptr) {
+ node_ids.push_back(std::make_pair(i->getId(), InternalNodeId()));
+ }
+ if (!i->isTerminal()) {
+ for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) {
+ if (x.node_index_map.find(*j) == nullptr) {
+ node_ids.push_back(std::make_pair(*j, InternalNodeId()));
+ }
+ ++num_new_edges;
+ }
+ }
+ }
+
+ // Step 1b: remove duplicates.
+ std::sort(node_ids.begin(), node_ids.end());
+ node_ids.erase(std::unique(node_ids.begin(), node_ids.end()), node_ids.end());
+
+ // Step 1c: assign new IDs.
+ for (auto& p : node_ids) {
+ p.second = InternalNodeId{first_unused_index * sizeof(NodeData)};
+ ++first_unused_index;
+ }
+
+ // Step 1d: actually populate node_index_map.
+ node_index_map = SemistaticMap<NodeId, InternalNodeId>(x.node_index_map, std::move(node_ids));
+
+ // Step 2: fill `nodes' and `edges_storage'
+ nodes = FixedSizeVector<NodeData>(x.nodes, first_unused_index);
+ // Note that the loop below does not necessarily assign all of these.
+ for (std::size_t i = x.nodes.size(); i < first_unused_index; ++i) {
+ nodes.push_back(NodeData{
+#ifdef FRUIT_EXTRA_DEBUG
+ NodeId(),
+#endif
+ 1, Node()});
+ }
+
+ // edges_storage[0] is unused, that's the reason for the +1
+ edges_storage = FixedSizeVector<InternalNodeId>(num_new_edges + 1);
+ edges_storage.push_back(InternalNodeId());
+
+ for (NodeIter i = first; i != last; ++i) {
+ NodeData& nodeData = *nodeAtId(node_index_map.at(i->getId()));
+ nodeData.node = i->getValue();
+ if (i->isTerminal()) {
+ nodeData.edges_begin = 0;
+ } else {
+ nodeData.edges_begin = reinterpret_cast<std::uintptr_t>(edges_storage.data() + edges_storage.size());
+ for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) {
+ InternalNodeId otherNodeId = node_index_map.at(*j);
+ edges_storage.push_back(otherNodeId);
+ }
+ }
+ }
+
+#ifdef FRUIT_EXTRA_DEBUG
+ printGraph(first, last);
+#endif
+}
+
+#ifdef FRUIT_EXTRA_DEBUG
+template <typename NodeId, typename Node>
+void SemistaticGraph<NodeId, Node>::checkFullyConstructed() {
+ for (NodeData& data : nodes) {
+ if (data.edges_begin == 1) {
+ std::cerr << "Fruit bug: the dependency graph was not fully constructed." << std::endl;
+ abort();
+ }
+ }
+}
+#endif // !NDEBUG
+
+// This is here so that we don't have to include fixed_size_vector.templates.h in fruit.h.
+template <typename NodeId, typename Node>
+SemistaticGraph<NodeId, Node>::~SemistaticGraph() {}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // SEMISTATIC_GRAPH_TEMPLATES_H
diff --git a/include/fruit/impl/data_structures/semistatic_map.defn.h b/include/fruit/impl/data_structures/semistatic_map.defn.h
new file mode 100644
index 0000000..7170f23
--- /dev/null
+++ b/include/fruit/impl/data_structures/semistatic_map.defn.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SEMISTATIC_MAP_DEFN_H
+#define SEMISTATIC_MAP_DEFN_H
+
+#include <fruit/impl/data_structures/semistatic_map.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename Key, typename Value>
+inline SemistaticMap<Key, Value>::HashFunction::HashFunction() : a(0), shift(0) {}
+
+template <typename Key, typename Value>
+inline typename SemistaticMap<Key, Value>::Unsigned SemistaticMap<Key, Value>::HashFunction::hash(Unsigned x) const {
+ return (Unsigned)(a * x) >> shift;
+}
+
+template <typename Key, typename Value>
+inline typename SemistaticMap<Key, Value>::Unsigned SemistaticMap<Key, Value>::hash(const Key& key) const {
+ return hash_function.hash(std::hash<typename std::remove_cv<Key>::type>()(key));
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // SEMISTATIC_MAP_DEFN_H
diff --git a/include/fruit/impl/data_structures/semistatic_map.h b/include/fruit/impl/data_structures/semistatic_map.h
new file mode 100644
index 0000000..aa5bf3a
--- /dev/null
+++ b/include/fruit/impl/data_structures/semistatic_map.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SEMISTATIC_MAP_H
+#define SEMISTATIC_MAP_H
+
+#include <fruit/impl/data_structures/fixed_size_vector.h>
+
+#include "arena_allocator.h"
+#include "memory_pool.h"
+#include <climits>
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * Provides a subset of the interface of std::map, and also has these additional assumptions:
+ * - Key must be default constructible and trivially copyable
+ * - Value must be default constructible and trivially copyable
+ *
+ * Also, while insertion of elements after construction is supported, inserting more than O(1) elements
+ * after construction will raise the cost of any further lookups to more than O(1).
+ */
+template <typename Key, typename Value>
+class SemistaticMap {
+private:
+ using Unsigned = std::uintptr_t;
+ using NumBits = unsigned char;
+ using value_type = std::pair<Key, Value>;
+
+ static constexpr unsigned char beta = 4;
+
+ static_assert(
+ std::numeric_limits<NumBits>::max() >= sizeof(Unsigned) * CHAR_BIT,
+ "An unsigned char is not enough to contain the number of bits in your platform. Please report this issue.");
+
+ struct HashFunction {
+ Unsigned a;
+ NumBits shift; // shift==(sizeof(Unsigned)*CHAR_BIT - num_bits)
+
+ HashFunction();
+
+ Unsigned hash(Unsigned x) const;
+ };
+
+ static NumBits pickNumBits(std::size_t n);
+
+ struct CandidateValuesRange {
+ value_type* begin;
+ value_type* end;
+ };
+
+ HashFunction hash_function;
+ // Given a key x, if p=lookup_table[hash_function.hash(x)] the candidate places for x are [p.first, p.second). These
+ // pointers
+ // point to the values[] vector, but it might be either the one of this object or the one of an object that was
+ // shallow-copied
+ // into this one.
+ FixedSizeVector<CandidateValuesRange> lookup_table;
+ FixedSizeVector<value_type> values;
+
+ Unsigned hash(const Key& key) const;
+
+ // Inserts a range [elems_begin, elems_end) of new (key,value) pairs with hash h. The keys must not exist in the map.
+ // Before calling this, ensure that the capacity of `values' is sufficient to contain the new values without
+ // re-allocating.
+ void insert(std::size_t h, const value_type* elems_begin, const value_type* elems_end);
+
+public:
+ // Constructs an *invalid* map (as if this map was just moved from).
+ SemistaticMap() = default;
+
+ /**
+ * Iter must be a forward iterator with value type std::pair<Key, Value>.
+ *
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ template <typename Iter>
+ SemistaticMap(Iter begin, std::size_t num_values, MemoryPool& memory_pool);
+
+ // Creates a shallow copy of `map' with the additional elements in new_elements.
+ // The keys in new_elements must be unique and must not be present in `map'.
+ // The new map will share data with `map', so must be destroyed before `map' is destroyed.
+ // NOTE: If more than O(1) elements are added, calls to at() and find() on the result will *not* be O(1).
+ // This is O(new_elements.size()*log(new_elements.size())).
+ SemistaticMap(const SemistaticMap<Key, Value>& map,
+ std::vector<value_type, ArenaAllocator<value_type>>&& new_elements);
+
+ SemistaticMap(SemistaticMap&&) = default;
+ SemistaticMap(const SemistaticMap&) = delete;
+
+ ~SemistaticMap();
+
+ SemistaticMap& operator=(SemistaticMap&&) = default;
+ SemistaticMap& operator=(const SemistaticMap&) = delete;
+
+ // Precondition: `key' must exist in the map.
+ // Unlike std::map::at(), this yields undefined behavior if the precondition isn't satisfied (instead of throwing).
+ const Value& at(Key key) const;
+
+ // Prefer using at() when possible, this is slightly slower.
+ // Returns nullptr if the key was not found.
+ const Value* find(Key key) const;
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/data_structures/semistatic_map.defn.h>
+
+// semistatic_map.templates.h is NOT included here to reduce the transitive includes. Include it when needed (in .cpp
+// files).
+
+#endif // SEMISTATIC_MAP_H
diff --git a/include/fruit/impl/data_structures/semistatic_map.templates.h b/include/fruit/impl/data_structures/semistatic_map.templates.h
new file mode 100644
index 0000000..f9034c3
--- /dev/null
+++ b/include/fruit/impl/data_structures/semistatic_map.templates.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SEMISTATIC_MAP_TEMPLATES_H
+#define SEMISTATIC_MAP_TEMPLATES_H
+
+#ifndef IN_FRUIT_CPP_FILE
+#error "Fruit .template.h file included in non-cpp file."
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <random>
+#include <utility>
+// This include is not necessary for GCC/Clang, but it's necessary for MSVC.
+#include <numeric>
+
+#include <fruit/impl/data_structures/semistatic_map.h>
+
+#include <fruit/impl/data_structures/arena_allocator.h>
+#include <fruit/impl/data_structures/fixed_size_vector.templates.h>
+#include <fruit/impl/fruit_assert.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename Key, typename Value>
+template <typename Iter>
+SemistaticMap<Key, Value>::SemistaticMap(Iter values_begin, std::size_t num_values, MemoryPool& memory_pool) {
+ NumBits num_bits = pickNumBits(num_values);
+ std::size_t num_buckets = size_t(1) << num_bits;
+
+ FixedSizeVector<Unsigned, ArenaAllocator<Unsigned>> count(num_buckets, 0, ArenaAllocator<Unsigned>(memory_pool));
+
+ hash_function.shift = (sizeof(Unsigned) * CHAR_BIT - num_bits);
+
+ // The cast is a no-op in some systems (e.g. GCC and Clang under Linux 64bit) but it's needed in other systems (e.g.
+ // MSVC).
+ unsigned seed = (unsigned)std::chrono::system_clock::now().time_since_epoch().count();
+ std::default_random_engine random_generator(seed);
+ std::uniform_int_distribution<Unsigned> random_distribution;
+
+ while (1) {
+ hash_function.a = random_distribution(random_generator);
+
+ Iter itr = values_begin;
+ for (std::size_t i = 0; i < num_values; ++i, ++itr) {
+ Unsigned& this_count = count[hash((*itr).first)];
+ ++this_count;
+ if (this_count == beta) {
+ goto pick_another;
+ }
+ }
+ break;
+
+ pick_another:
+ for (std::size_t i = 0; i < num_buckets; ++i) {
+ count[i] = 0;
+ }
+ }
+
+ values = FixedSizeVector<value_type>(num_values, value_type());
+
+ std::partial_sum(count.begin(), count.end(), count.begin());
+ lookup_table = FixedSizeVector<CandidateValuesRange>(count.size());
+ for (Unsigned n : count) {
+ lookup_table.push_back(CandidateValuesRange{values.data() + n, values.data() + n});
+ }
+
+ // At this point lookup_table[h] is the number of keys in [first, last) that have a hash <=h.
+ // Note that even though we ensure this after construction, it is not maintained by insert() so it's not an invariant.
+
+ Iter itr = values_begin;
+ for (std::size_t i = 0; i < num_values; ++i, ++itr) {
+ value_type*& first_value_ptr = lookup_table[hash((*itr).first)].begin;
+ --first_value_ptr;
+ FruitAssert(values.data() <= first_value_ptr);
+ FruitAssert(first_value_ptr < values.data() + values.size());
+ *first_value_ptr = *itr;
+ }
+}
+
+template <typename Key, typename Value>
+SemistaticMap<Key, Value>::SemistaticMap(const SemistaticMap<Key, Value>& map,
+ std::vector<value_type, ArenaAllocator<value_type>>&& new_elements)
+ : hash_function(map.hash_function), lookup_table(map.lookup_table, map.lookup_table.size()) {
+
+ // Sort by hash.
+ std::sort(new_elements.begin(), new_elements.end(),
+ [this](const value_type& x, const value_type& y) { return hash(x.first) < hash(y.first); });
+
+ std::size_t num_additional_values = new_elements.size();
+ // Add the space needed to store copies of the old buckets.
+ for (auto itr = new_elements.begin(), itr_end = new_elements.end(); itr != itr_end; /* no increment */) {
+ Unsigned h = hash(itr->first);
+ auto p = map.lookup_table[h];
+ num_additional_values += (p.end - p.begin);
+ for (; itr != itr_end && hash(itr->first) == h; ++itr) {
+ }
+ }
+
+ values = FixedSizeVector<value_type>(num_additional_values);
+
+ // Now actually perform the insertions.
+
+ if (new_elements.empty()) {
+ // This is to workaround a bug in the STL shipped with GCC <4.8.2, where calling data() on an
+ // empty vector causes undefined behavior (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59829).
+ return;
+ }
+ for (value_type *itr = new_elements.data(), *itr_end = new_elements.data() + new_elements.size(); itr != itr_end;
+ /* no increment */) {
+ Unsigned h = hash(itr->first);
+ auto p = map.lookup_table[h];
+ num_additional_values += (p.end - p.begin);
+ value_type* first = itr;
+ for (; itr != itr_end && hash(itr->first) == h; ++itr) {
+ }
+ value_type* last = itr;
+ insert(h, first, last);
+ }
+}
+
+template <typename Key, typename Value>
+void SemistaticMap<Key, Value>::insert(std::size_t h, const value_type* elems_begin, const value_type* elems_end) {
+
+ value_type* old_bucket_begin = lookup_table[h].begin;
+ value_type* old_bucket_end = lookup_table[h].end;
+
+ lookup_table[h].begin = values.data() + values.size();
+
+ // Step 1: re-insert all keys with the same hash at the end (if any).
+ for (value_type* p = old_bucket_begin; p != old_bucket_end; ++p) {
+ values.push_back(*p);
+ }
+
+ // Step 2: also insert the new keys and values
+ for (auto itr = elems_begin; itr != elems_end; ++itr) {
+ values.push_back(*itr);
+ }
+
+ lookup_table[h].end = values.data() + values.size();
+
+ // The old sequence is no longer pointed to by any index in the lookup table, but recompacting the vectors would be
+ // too slow.
+}
+
+template <typename Key, typename Value>
+const Value& SemistaticMap<Key, Value>::at(Key key) const {
+ Unsigned h = hash(key);
+ for (const value_type* p = lookup_table[h].begin; /* p!=lookup_table[h].end but no need to check */; ++p) {
+ FruitAssert(p != lookup_table[h].end);
+ if (p->first == key) {
+ return p->second;
+ }
+ }
+}
+
+template <typename Key, typename Value>
+const Value* SemistaticMap<Key, Value>::find(Key key) const {
+ Unsigned h = hash(key);
+ for (const value_type *p = lookup_table[h].begin, *p_end = lookup_table[h].end; p != p_end; ++p) {
+ if (p->first == key) {
+ return &(p->second);
+ }
+ }
+ return nullptr;
+}
+
+template <typename Key, typename Value>
+typename SemistaticMap<Key, Value>::NumBits SemistaticMap<Key, Value>::pickNumBits(std::size_t n) {
+ NumBits result = 1;
+ while ((std::size_t(1) << result) < n) {
+ ++result;
+ }
+ return result;
+}
+
+// This is here so that we don't have to include fixed_size_vector.templates.h in fruit.h.
+template <typename Key, typename Value>
+SemistaticMap<Key, Value>::~SemistaticMap() {}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // SEMISTATIC_MAP_TEMPLATES_H
diff --git a/include/fruit/impl/fruit-config.h b/include/fruit/impl/fruit-config.h
new file mode 100644
index 0000000..f9c3b28
--- /dev/null
+++ b/include/fruit/impl/fruit-config.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_CONFIG_H
+#define FRUIT_CONFIG_H
+
+#include <fruit/impl/fruit-config-base.h>
+
+#if FRUIT_HAS_STD_IS_TRIVIALLY_COPYABLE
+#if FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) \
+ (std::is_trivially_copyable<T>::value || (std::is_empty<T>::value && std::is_trivially_copy_constructible<T>::value))
+#else // !FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) (std::is_trivially_copyable<T>::value)
+#endif // FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#elif FRUIT_HAS_IS_TRIVIALLY_COPYABLE
+#if FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) \
+ (__is_trivially_copyable(T) || (std::is_empty<T>::value && std::is_trivially_copy_constructible<T>::value))
+#else // !FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) (__is_trivially_copyable(T))
+#endif // FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#elif FRUIT_HAS_HAS_TRIVIAL_COPY
+// The compiler doesn't support __is_trivially_copyable (nor is std::is_trivially_copyable
+// supported by the library). We use this check as a proxy, but it's not exactly the same thing.
+#if FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) \
+ (__has_trivial_copy(T) || (std::is_empty<T>::value && std::is_trivially_copy_constructible<T>::value))
+#else // !FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) (__has_trivial_copy(T))
+#endif // FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#else
+// We use the standard one, but most likely it won't work.
+#if FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) \
+ (std::is_trivially_copyable<T>::value || (std::is_empty<T>::value && std::is_trivially_copy_constructible<T>::value))
+#else // !FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#define FRUIT_IS_TRIVIALLY_COPYABLE(T) (std::is_trivially_copyable<T>::value)
+#endif // FRUIT_HAS_STD_IS_TRIVIALLY_COPY_CONSTRUCTIBLE
+#endif
+
+#if FRUIT_HAS_ALWAYS_INLINE_ATTRIBUTE
+#define FRUIT_ALWAYS_INLINE __attribute__((always_inline))
+#elif FRUIT_HAS_FORCEINLINE
+#define FRUIT_ALWAYS_INLINE __forceinline
+#else
+#define FRUIT_ALWAYS_INLINE
+#endif
+
+#if FRUIT_HAS_GCC_ATTRIBUTE_DEPRECATED
+#define FRUIT_DEPRECATED_DECLARATION(...) __VA_ARGS__ __attribute__((deprecated))
+// Marking the declaration is enough.
+#define FRUIT_DEPRECATED_DEFINITION(...) __VA_ARGS__
+#elif FRUIT_HAS_DECLSPEC_DEPRECATED
+#define FRUIT_DEPRECATED_DECLARATION(...) __declspec(deprecated) __VA_ARGS__
+#define FRUIT_DEPRECATED_DEFINITION(...) __declspec(deprecated) __VA_ARGS__
+// We use this only if the above two are not supported, because some compilers "support" this syntax (i.e., it compiles)
+// but they just ignore the attribute.
+#elif FRUIT_HAS_ATTRIBUTE_DEPRECATED
+#define FRUIT_DEPRECATED_DECLARATION(...) [[deprecated]] __VA_ARGS__
+#define FRUIT_DEPRECATED_DEFINITION(...) [[deprecated]] __VA_ARGS__
+#else
+#define FRUIT_DEPRECATED_DECLARATION(...) __VA_ARGS__
+#define FRUIT_DEPRECATED_DEFINITION(...) __VA_ARGS__
+#endif
+
+#if FRUIT_HAS_MSVC_ASSUME
+#define FRUIT_UNREACHABLE \
+ FruitAssert(false); \
+ __assume(0)
+#elif FRUIT_HAS_BUILTIN_UNREACHABLE
+#define FRUIT_UNREACHABLE \
+ FruitAssert(false); \
+ __builtin_unreachable()
+#endif
+
+#endif // FRUIT_CONFIG_H
diff --git a/include/fruit/impl/fruit_assert.h b/include/fruit/impl/fruit_assert.h
new file mode 100644
index 0000000..b2b4b0d
--- /dev/null
+++ b/include/fruit/impl/fruit_assert.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_ASSERT_H
+#define FRUIT_ASSERT_H
+
+#ifdef FRUIT_EXTRA_DEBUG
+#include <cassert>
+// Usage: FruitStaticAssert(MetaExpr)
+#define FruitStaticAssert(...) static_assert(fruit::impl::meta::Eval<__VA_ARGS__>::value, "")
+#define FruitAssert(...) assert(__VA_ARGS__)
+
+#else
+// We still define this, otherwise some compilers (e.g. Clang 3.9) would complain that there's a stray ';' when this is
+// used inside a struct/class definition.
+#define FruitStaticAssert(...) static_assert(true, "")
+#define FruitAssert(...)
+
+#endif
+
+#define FruitDelegateCheck(...) static_assert(true || sizeof(fruit::impl::meta::Eval<__VA_ARGS__>), "")
+#define FruitDisplayErrorForType(...) static_assert(false && sizeof(fruit::impl::meta::Eval<__VA_ARGS__>), "")
+
+#endif // FRUIT_ASSERT_H
diff --git a/include/fruit/impl/fruit_internal_forward_decls.h b/include/fruit/impl/fruit_internal_forward_decls.h
new file mode 100644
index 0000000..4883470
--- /dev/null
+++ b/include/fruit/impl/fruit_internal_forward_decls.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_FRUIT_INTERNAL_FORWARD_DECLS_H
+#define FRUIT_FRUIT_INTERNAL_FORWARD_DECLS_H
+
+#include <memory>
+#include <vector>
+
+namespace fruit {
+namespace impl {
+
+class ComponentStorage;
+class NormalizedComponentStorage;
+class InjectorStorage;
+struct TypeId;
+struct ComponentStorageEntry;
+struct NormalizedBinding;
+struct NormalizedMultibinding;
+struct NormalizedMultibindingSet;
+struct InjectorAccessorForTests;
+
+template <typename Component, typename... Args>
+class LazyComponentImpl;
+
+namespace meta {
+template <typename... PreviousBindings>
+struct OpForComponent;
+}
+
+} // namespace impl
+
+} // namespace fruit
+
+#endif // FRUIT_FRUIT_INTERNAL_FORWARD_DECLS_H
diff --git a/include/fruit/impl/injection_debug_errors.h b/include/fruit/impl/injection_debug_errors.h
new file mode 100644
index 0000000..3efdc5e
--- /dev/null
+++ b/include/fruit/impl/injection_debug_errors.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTION_DEBUG_ERRORS
+#define FRUIT_INJECTION_DEBUG_ERRORS
+
+#include <fruit/impl/injection_errors.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+template <typename... MissingProvides>
+struct ComponentDoesNotEntailDueToProvidesError {
+ static_assert(AlwaysFalse<MissingProvides...>::value, "");
+};
+
+struct ComponentDoesNotEntailDueToProvidesErrorTag {
+ template <typename... MissingProvides>
+ using apply = ComponentDoesNotEntailDueToProvidesError<MissingProvides...>;
+};
+
+template <typename... MissingInterfaceBindings>
+struct ComponentDoesNotEntailDueToInterfaceBindingsError {
+ static_assert(AlwaysFalse<MissingInterfaceBindings...>::value, "");
+};
+
+struct ComponentDoesNotEntailDueToInterfaceBindingsErrorTag {
+ template <typename... MissingInterfaceBindings>
+ using apply = ComponentDoesNotEntailDueToInterfaceBindingsError<MissingInterfaceBindings...>;
+};
+
+template <typename... AdditionalRequirements>
+struct ComponentDoesNotEntailDueToRequirementsError {
+ static_assert(AlwaysFalse<AdditionalRequirements...>::value, "");
+};
+
+struct ComponentDoesNotEntailDueToRequirementsErrorTag {
+ template <typename... AdditionalRequirements>
+ using apply = ComponentDoesNotEntailDueToProvidesError<AdditionalRequirements...>;
+};
+
+template <typename Deps, typename CandidateEntailedDeps>
+struct ComponentDoesNotEntailDueToIncompatibleDepsError {
+ static_assert(AlwaysFalse<Deps>::value, "");
+};
+
+struct ComponentDoesNotEntailDueToIncompatibleDepsErrorTag {
+ template <typename Deps, typename CandidateEntailedDeps>
+ using apply = ComponentDoesNotEntailDueToIncompatibleDepsError<Deps, CandidateEntailedDeps>;
+};
+
+template <typename... RequirementsWithConstMismatch>
+struct ComponentDoesNotEntailDueToDifferentConstnessOfRequirementsError {
+ static_assert(AlwaysFalse<RequirementsWithConstMismatch...>::value, "");
+};
+
+struct ComponentDoesNotEntailDueToDifferentConstnessOfRequirementsErrorTag {
+ template <typename... RequirementsWithConstMismatch>
+ using apply = ComponentDoesNotEntailDueToDifferentConstnessOfRequirementsError<RequirementsWithConstMismatch...>;
+};
+
+template <typename... ProvidesWithConstMismatch>
+struct ComponentDoesNotEntailDueToDifferentConstnessOfProvidesError {
+ static_assert(AlwaysFalse<ProvidesWithConstMismatch...>::value, "");
+};
+
+struct ComponentDoesNotEntailDueToDifferentConstnessOfProvidesErrorTag {
+ template <typename... ProvidesWithConstMismatch>
+ using apply = ComponentDoesNotEntailDueToDifferentConstnessOfProvidesError<ProvidesWithConstMismatch...>;
+};
+
+template <typename ProofTh, typename ForestThs>
+struct ProofNotEntailedByForestBecauseThNotFoundError {
+ static_assert(AlwaysFalse<ProofTh>::value, "");
+};
+
+struct ProofNotEntailedByForestBecauseThNotFoundErrorTag {
+ template <typename ProofTh, typename ForestThs>
+ using apply = ProofNotEntailedByForestBecauseThNotFoundError<ProofTh, ForestThs>;
+};
+
+template <typename ForestHps, typename ProofHps, typename Difference>
+struct ProofNotEntailedByForestBecauseHpsNotASubsetError {
+ static_assert(AlwaysFalse<ForestHps>::value, "");
+};
+
+struct ProofNotEntailedByForestBecauseHpsNotASubsetErrorTag {
+ template <typename ForestHps, typename ProofHps, typename Difference>
+ using apply = ProofNotEntailedByForestBecauseHpsNotASubsetError<ForestHps, ProofHps, Difference>;
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_INJECTION_DEBUG_ERRORS
diff --git a/include/fruit/impl/injection_errors.h b/include/fruit/impl/injection_errors.h
new file mode 100644
index 0000000..06a0344
--- /dev/null
+++ b/include/fruit/impl/injection_errors.h
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTION_ERRORS
+#define FRUIT_INJECTION_ERRORS
+
+#include <fruit/impl/fruit_assert.h>
+#include <fruit/impl/meta/set.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename... Ts>
+struct AlwaysFalse {
+ static constexpr bool value = false;
+};
+
+template <typename T>
+struct NoBindingFoundError {
+ static_assert(AlwaysFalse<T>::value, "No explicit binding nor C::Inject definition was found for T.");
+};
+
+template <typename T, typename C>
+struct NoBindingFoundForAbstractClassError {
+ static_assert(AlwaysFalse<T>::value,
+ "No explicit binding was found for T, and note that C is an abstract class (so Fruit can't auto-inject "
+ "this type, "
+ "even if it has an Inject typedef or an INJECT annotation that will be ignored).");
+};
+
+template <typename... Ts>
+struct RepeatedTypesError {
+ static_assert(AlwaysFalse<Ts...>::value,
+ "A type was specified more than once. Requirements and provided types should be unique.");
+};
+
+template <typename... TypesInLoop>
+struct SelfLoopError {
+ static_assert(AlwaysFalse<TypesInLoop...>::value,
+ "Found a loop in the dependencies! The types in TypesInLoop all depend on the next, and the "
+ "last one depends on the first.");
+};
+
+template <typename T, typename C>
+struct NonClassTypeError {
+ static_assert(AlwaysFalse<T>::value, "A non-class type T was specified. Use C instead.");
+};
+
+template <typename AnnotatedT, typename T>
+struct AnnotatedTypeError {
+ static_assert(AlwaysFalse<T>::value, "An annotated type was specified where a non-annotated type was expected.");
+};
+
+template <typename C>
+struct TypeAlreadyBoundError {
+ static_assert(AlwaysFalse<C>::value, "Trying to bind C but it is already bound.");
+};
+
+template <typename RequiredSignature, typename SignatureInInjectTypedef>
+struct RequiredFactoryWithDifferentSignatureError {
+ static_assert(AlwaysFalse<RequiredSignature>::value,
+ "The required C factory doesn't have the same signature as the Inject annotation in C.");
+};
+
+template <typename Signature, typename SignatureInLambda>
+struct AnnotatedSignatureDifferentFromLambdaSignatureError {
+ static_assert(AlwaysFalse<Signature>::value,
+ "The annotated signature specified is not the same as the lambda's signature (after removing "
+ "annotations).");
+};
+
+template <typename... DuplicatedTypes>
+struct DuplicateTypesInComponentError {
+ static_assert(AlwaysFalse<DuplicatedTypes...>::value,
+ "The installed component provides some types that are already provided by the current "
+ "component.");
+};
+
+template <typename... Requirements>
+struct InjectorWithRequirementsError {
+ static_assert(AlwaysFalse<Requirements...>::value,
+ "Injectors can't have requirements. If you want Fruit to try auto-resolving the requirements "
+ "in the injector's scope, cast the component to a component with no requirements before "
+ "constructing the injector with it.");
+};
+
+template <typename C, typename CandidateSignature>
+struct InjectTypedefNotASignatureError {
+ static_assert(AlwaysFalse<C>::value, "C::Inject should be a typedef to a signature, e.g. C(int)");
+};
+
+template <typename C, typename SignatureReturnType>
+struct InjectTypedefForWrongClassError {
+ static_assert(AlwaysFalse<C>::value,
+ "C::Inject is a signature, but does not return a C. Maybe the class C has no Inject typedef "
+ "and inherited the base class' one? If that's not the case, make sure it returns just C, not "
+ "C* or other types.");
+};
+
+template <typename C>
+struct InjectTypedefWithAnnotationError {
+ static_assert(AlwaysFalse<C>::value,
+ "C::Inject is a signature that returns an annotated type. The annotation must be removed, "
+ "Fruit will deduce the correct annotation based on how the required binding.");
+};
+
+template <typename CandidateSignature>
+struct NotASignatureError {
+ static_assert(AlwaysFalse<CandidateSignature>::value,
+ "CandidateSignature was specified as parameter, but it's not a signature. Signatures are of "
+ "the form MyClass(int, float).");
+};
+
+template <typename CandidateLambda>
+struct NotALambdaError {
+ static_assert(AlwaysFalse<CandidateLambda>::value,
+ "CandidateLambda was specified as parameter, but it's not a lambda.");
+};
+
+template <typename Signature>
+struct ConstructorDoesNotExistError {
+ static_assert(AlwaysFalse<Signature>::value, "The specified constructor does not exist.");
+};
+
+template <typename I, typename C>
+struct NotABaseClassOfError {
+ static_assert(AlwaysFalse<I>::value, "I is not a base class of C.");
+};
+
+template <typename ProviderType>
+struct FunctorUsedAsProviderError {
+ static_assert(AlwaysFalse<ProviderType>::value,
+ "A stateful lambda or a non-lambda functor was used as provider. Only functions and stateless "
+ "lambdas can be used as providers.");
+};
+
+template <typename... ComponentRequirements>
+struct ComponentWithRequirementsInInjectorError {
+ static_assert(AlwaysFalse<ComponentRequirements...>::value,
+ "When using the two-argument constructor of Injector, the component used as second parameter "
+ "must not have requirements (while the normalized component can), but the specified component "
+ "requires ComponentRequirements.");
+};
+
+template <typename... UnsatisfiedRequirements>
+struct UnsatisfiedRequirementsInNormalizedComponentError {
+ static_assert(AlwaysFalse<UnsatisfiedRequirements...>::value,
+ "The requirements in UnsatisfiedRequirements are required by the NormalizedComponent but are "
+ "not provided by the Component (second parameter of the Injector constructor).");
+};
+
+template <typename... TypesNotProvided>
+struct TypesInInjectorNotProvidedError {
+ static_assert(AlwaysFalse<TypesNotProvided...>::value,
+ "The types in TypesNotProvided are declared as provided by the injector, but none of the two "
+ "components passed to the Injector constructor provides them.");
+};
+
+template <typename... TypesProvidedAsConstOnly>
+struct TypesInInjectorProvidedAsConstOnlyError {
+ static_assert(
+ AlwaysFalse<TypesProvidedAsConstOnly...>::value,
+ "The types in TypesProvidedAsConstOnly are declared as non-const provided types by the injector, but the "
+ "components passed to the Injector constructor provide them as const only. You should mark them as const in the "
+ "injector (e.g., switching from Injector<T> to Injector<const T>) or mark them as non-const in the "
+ "Component/NormalizedComponent (e.g. switching from [Normalized]Component<const T> to "
+ "[Normalized]Component<T>).");
+};
+
+template <typename T>
+struct TypeNotProvidedError {
+ static_assert(AlwaysFalse<T>::value,
+ "Trying to get an instance of T, but it is not provided by this Provider/Injector.");
+};
+
+template <typename T>
+struct TypeProvidedAsConstOnlyError {
+ static_assert(
+ AlwaysFalse<T>::value,
+ "Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector and a non-const "
+ "pointer/reference/Provider was requested. You should either switch to injecting a const value (e.g. switching "
+ "from"
+ " injecting T*, T&, std::unique_ptr<T> or Provider<T> to injecting a T, const T*, const T& or Provider<const T>) "
+ "or get the value from an Injector/Provider that provides it as a non-const type (e.g. switching from calling "
+ "get "
+ "on an Injector<const T> or on a Provider<const T> to calling get on an Injector<T> or a Provider<T>).");
+};
+
+template <typename T>
+struct NonConstBindingRequiredButConstBindingProvidedError {
+ static_assert(
+ AlwaysFalse<T>::value,
+ "The type T was provided as constant, however one of the constructors/providers/factories in this component "
+ "requires it as a non-constant (or this Component declares it as a non-const provided/required type). "
+ "If you want to only have a const binding for this type, you should change the places that use the type to "
+ "inject "
+ "a constant value (e.g. T, const T*, const T& and Provider<const T> are ok while you should avoid injecting T*, "
+ "T&,"
+ " std::unique_ptr<T> and Provider<T>) and if the type is in Component<...> make sure that it's marked as const "
+ "there"
+ " (e.g. Component<const T> and Component<Required<const T>> are ok while Component<T> and Component<Required<T>> "
+ "are "
+ "not. "
+ "On the other hand, if you want to have a non-const binding for this type, you should switch to a non-const "
+ "bindInstance (if you're binding an instance) or changing any installed component functions to declare the type "
+ "as "
+ "non-const, e.g. Component<T> or Component<Required<T>> instead of Component<const T> and "
+ "Component<Required<const T>>.");
+};
+
+template <typename C, typename InjectSignature>
+struct NoConstructorMatchingInjectSignatureError {
+ static_assert(AlwaysFalse<C>::value,
+ "C contains an Inject typedef but it's not constructible with the specified types");
+};
+
+template <typename ExpectedSignature, typename FunctorSignature>
+struct FunctorSignatureDoesNotMatchError {
+ static_assert(AlwaysFalse<ExpectedSignature>::value,
+ "Unexpected functor signature (it should be the same as ExpectedSignature minus any Assisted "
+ "types).");
+};
+
+template <typename Signature>
+struct FactoryReturningPointerError {
+ static_assert(AlwaysFalse<Signature>::value,
+ "The specified factory returns a pointer. This is not supported; return a value or an "
+ "std::unique_ptr instead.");
+};
+
+template <typename Lambda>
+struct LambdaWithCapturesError {
+ // It's not guaranteed by the standard, but it's reasonable to expect lambdas with no captures
+ // to be empty. This is always the case in GCC and Clang, but is not guaranteed to work in all
+ // conforming C++ compilers. If this error happens for a lambda with no captures, please file a
+ // bug at https://github.com/google/fruit/issues and indicate the compiler (with version) that
+ // you're using.
+ static_assert(AlwaysFalse<Lambda>::value, "Only lambdas with no captures are supported.");
+};
+
+template <typename Lambda>
+struct NonTriviallyCopyableLambdaError {
+ // It's not guaranteed by the standard, but it's reasonable to expect lambdas with no captures
+ // to be trivially copyable. This is always the case in GCC and Clang, but is not guaranteed to
+ // work in all conforming C++ compilers. If this error happens for a lambda with no captures,
+ // please file a bug at https://github.com/google/fruit/issues and indicate the compiler (with
+ // version) that you're using.
+ static_assert(AlwaysFalse<Lambda>::value,
+ "Only trivially copyable lambdas are supported. Make sure that your lambda has no captures.");
+};
+
+template <typename C>
+struct CannotConstructAbstractClassError {
+ static_assert(AlwaysFalse<C>::value, "The specified class can't be constructed because it's an abstract class.");
+};
+
+template <typename C>
+struct InterfaceBindingToSelfError {
+ static_assert(AlwaysFalse<C>::value,
+ "The type C was bound to itself. If this was intentional, to \"tell Fruit to inject the type"
+ " C\", this binding is unnecessary, just remove it. bind<I,C>() is to tell Fruit about"
+ " base-derived class relationships.");
+};
+
+template <typename TypeParameter, typename TypeOfValue>
+struct TypeMismatchInBindInstanceError {
+ static_assert(AlwaysFalse<TypeParameter>::value,
+ "A type parameter was specified in bindInstance() but it doesn't match the value type"
+ " (even after removing the fruit::Annotation<>, if any). Please change the type parameter"
+ " to be the same as the type of the value (or a subclass).");
+};
+
+template <typename RequiredType>
+struct RequiredTypesInComponentArgumentsError {
+ static_assert(AlwaysFalse<RequiredType>::value,
+ "A Required<...> type was passed as a non-first template parameter to fruit::Component or "
+ "fruit::NormalizedComponent. "
+ "All required types (if any) should be passed together as a single Required<> type passed as the first "
+ "type argument of fruit::Component (and fruit::NormalizedComponent). For example, write "
+ "fruit::Component<fruit::Required<Foo, Bar>, Baz> instead of "
+ "fruit::Component<fruit::Required<Foo>, fruit::Required<Bar>, Baz>.");
+};
+
+template <typename T>
+struct NonInjectableTypeError {
+ static_assert(
+ AlwaysFalse<T>::value,
+ "The type T is not injectable. Injectable types are of the form X, X*, X&, const X, const X*, const X&, "
+ "std::shared_ptr<X>, or Provider<X> where X is a fundamental type (excluding void), a class, a struct or "
+ "an enum.");
+};
+
+template <typename T>
+struct ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError {
+ static_assert(
+ AlwaysFalse<T>::value,
+ "The type T was declared as a const Required type in the returned Component, however a non-const binding is "
+ "required. You should either change all the usages of this type so that they no longer require a non-const "
+ "binding "
+ "(i.e., you shouldn't inject T*, T& or std::shared_ptr<T>) or you should remove the 'const' in the type of the "
+ "returned Component, e.g. changing fruit::Component<fruit::Required<const T, ...>, ...> to "
+ "fruit::Component<fruit::Required<T, ...>, ...>.");
+};
+
+template <typename T>
+struct ProviderReturningPointerToAbstractClassWithNoVirtualDestructorError {
+ static_assert(
+ AlwaysFalse<T>::value,
+ "registerProvider() was called with a lambda that returns a pointer to T, but T is an abstract class with no "
+ "virtual destructor so when the injector is deleted Fruit will be unable to call the right destructor (the one "
+ "of "
+ "the concrete class that was then casted to T). You must either add a virtual destructor to T or change the "
+ "registerProvider() call to return a pointer to the concrete class (and then add a bind<T, TImpl>() so that T is "
+ "bound).");
+};
+
+template <typename T>
+struct MultibindingProviderReturningPointerToAbstractClassWithNoVirtualDestructorError {
+ static_assert(
+ AlwaysFalse<T>::value,
+ "registerMultibindingProvider() was called with a lambda that returns a pointer to T, but T is an abstract class "
+ "with no virtual destructor so when the injector is deleted Fruit will be unable to call the right destructor "
+ "(the "
+ "one of the concrete class that was then casted to T). You must add a virtual destructor to T or replace the "
+ "registerMultibindingProvider() with a registerProvider() for the concrete class and an addMultibinding() for T. "
+ "Note that with the latter, if you end up with multiple addMultibinding() calls for the same concrete class, "
+ "there will be only one instance of the concrete class in the injector, not one per addMultibdinding() call; if "
+ "you want separate instances you might want to use annotated injection for the concrete class (so that there's "
+ "one "
+ "instance per annotation).");
+};
+
+template <typename T>
+struct RegisterFactoryForUniquePtrOfAbstractClassWithNoVirtualDestructorError {
+ static_assert(AlwaysFalse<T>::value,
+ "registerFactory() was called with a lambda that returns a std::unique_ptr<T>, but T is an abstract "
+ "class with no "
+ "virtual destructor so when the returned std::unique_ptr<T> object is deleted the wrong destructor "
+ "will be called "
+ "(T's destructor instead of the one of the concrete class that was then casted to T). You must add a "
+ "virtual destructor to T.");
+};
+
+template <typename BaseFactory, typename DerivedFactory>
+struct FactoryBindingForUniquePtrOfClassWithNoVirtualDestructorError {
+ static_assert(
+ AlwaysFalse<BaseFactory>::value,
+ "Fruit was trying to bind BaseFactory to DerivedFactory but the return type of BaseFactory is a std::unique_ptr "
+ "of "
+ "a class with no virtual destructor, so when the std::unique_ptr object is destroyed the wrong destructor would "
+ "be "
+ "called (the one in the base class instead of the derived class). To avoid this, you must add a virtual "
+ "destructor to the base class.");
+};
+
+struct LambdaWithCapturesErrorTag {
+ template <typename Lambda>
+ using apply = LambdaWithCapturesError<Lambda>;
+};
+
+struct NonTriviallyCopyableLambdaErrorTag {
+ template <typename Lambda>
+ using apply = NonTriviallyCopyableLambdaError<Lambda>;
+};
+
+struct FactoryReturningPointerErrorTag {
+ template <typename Signature>
+ using apply = FactoryReturningPointerError<Signature>;
+};
+
+struct NoBindingFoundErrorTag {
+ template <typename T>
+ using apply = NoBindingFoundError<T>;
+};
+
+struct RepeatedTypesErrorTag {
+ template <typename... Ts>
+ using apply = RepeatedTypesError<Ts...>;
+};
+
+struct SelfLoopErrorTag {
+ template <typename... TypesInLoop>
+ using apply = SelfLoopError<TypesInLoop...>;
+};
+
+struct NonClassTypeErrorTag {
+ template <typename T, typename C>
+ using apply = NonClassTypeError<T, C>;
+};
+
+struct AnnotatedTypeErrorTag {
+ template <typename T, typename C>
+ using apply = AnnotatedTypeError<T, C>;
+};
+
+struct TypeAlreadyBoundErrorTag {
+ template <typename C>
+ using apply = TypeAlreadyBoundError<C>;
+};
+
+struct RequiredFactoryWithDifferentSignatureErrorTag {
+ template <typename RequiredSignature, typename SignatureInInjectTypedef>
+ using apply = RequiredFactoryWithDifferentSignatureError<RequiredSignature, SignatureInInjectTypedef>;
+};
+
+struct AnnotatedSignatureDifferentFromLambdaSignatureErrorTag {
+ template <typename Signature, typename SignatureInLambda>
+ using apply = AnnotatedSignatureDifferentFromLambdaSignatureError<Signature, SignatureInLambda>;
+};
+
+struct DuplicateTypesInComponentErrorTag {
+ template <typename... DuplicatedTypes>
+ using apply = DuplicateTypesInComponentError<DuplicatedTypes...>;
+};
+
+struct InjectorWithRequirementsErrorTag {
+ template <typename... Requirements>
+ using apply = InjectorWithRequirementsError<Requirements...>;
+};
+
+struct ComponentWithRequirementsInInjectorErrorTag {
+ template <typename... ComponentRequirements>
+ using apply = ComponentWithRequirementsInInjectorError<ComponentRequirements...>;
+};
+
+struct InjectTypedefNotASignatureErrorTag {
+ template <typename C, typename TypeInInjectTypedef>
+ using apply = InjectTypedefNotASignatureError<C, TypeInInjectTypedef>;
+};
+
+struct InjectTypedefForWrongClassErrorTag {
+ template <typename C, typename ReturnTypeOfInjectTypedef>
+ using apply = InjectTypedefForWrongClassError<C, ReturnTypeOfInjectTypedef>;
+};
+
+struct InjectTypedefWithAnnotationErrorTag {
+ template <typename C>
+ using apply = InjectTypedefWithAnnotationError<C>;
+};
+
+struct UnsatisfiedRequirementsInNormalizedComponentErrorTag {
+ template <typename... UnsatisfiedRequirements>
+ using apply = UnsatisfiedRequirementsInNormalizedComponentError<UnsatisfiedRequirements...>;
+};
+
+struct TypesInInjectorNotProvidedErrorTag {
+ template <typename... TypesNotProvided>
+ using apply = TypesInInjectorNotProvidedError<TypesNotProvided...>;
+};
+
+struct TypesInInjectorProvidedAsConstOnlyErrorTag {
+ template <typename... TypesProvidedAsConstOnly>
+ using apply = TypesInInjectorProvidedAsConstOnlyError<TypesProvidedAsConstOnly...>;
+};
+
+struct FunctorUsedAsProviderErrorTag {
+ template <typename ProviderType>
+ using apply = FunctorUsedAsProviderError<ProviderType>;
+};
+
+struct ConstructorDoesNotExistErrorTag {
+ template <typename Signature>
+ using apply = ConstructorDoesNotExistError<Signature>;
+};
+
+struct NotABaseClassOfErrorTag {
+ template <typename I, typename C>
+ using apply = NotABaseClassOfError<I, C>;
+};
+
+struct NotASignatureErrorTag {
+ template <typename CandidateSignature>
+ using apply = NotASignatureError<CandidateSignature>;
+};
+
+struct NotALambdaErrorTag {
+ template <typename CandidateLambda>
+ using apply = NotALambdaError<CandidateLambda>;
+};
+
+struct TypeNotProvidedErrorTag {
+ template <typename T>
+ using apply = TypeNotProvidedError<T>;
+};
+
+struct TypeProvidedAsConstOnlyErrorTag {
+ template <typename T>
+ using apply = TypeProvidedAsConstOnlyError<T>;
+};
+
+struct NonConstBindingRequiredButConstBindingProvidedErrorTag {
+ template <typename T>
+ using apply = NonConstBindingRequiredButConstBindingProvidedError<T>;
+};
+
+struct NoConstructorMatchingInjectSignatureErrorTag {
+ template <typename C, typename InjectSignature>
+ using apply = NoConstructorMatchingInjectSignatureError<C, InjectSignature>;
+};
+
+struct FunctorSignatureDoesNotMatchErrorTag {
+ template <typename ExpectedSignature, typename FunctorSignature>
+ using apply = FunctorSignatureDoesNotMatchError<ExpectedSignature, FunctorSignature>;
+};
+
+struct CannotConstructAbstractClassErrorTag {
+ template <typename C>
+ using apply = CannotConstructAbstractClassError<C>;
+};
+
+struct NoBindingFoundForAbstractClassErrorTag {
+ template <typename T, typename C>
+ using apply = NoBindingFoundForAbstractClassError<T, C>;
+};
+
+struct InterfaceBindingToSelfErrorTag {
+ template <typename C>
+ using apply = InterfaceBindingToSelfError<C>;
+};
+
+struct TypeMismatchInBindInstanceErrorTag {
+ template <typename TypeParameter, typename TypeOfValue>
+ using apply = TypeMismatchInBindInstanceError<TypeParameter, TypeOfValue>;
+};
+
+struct RequiredTypesInComponentArgumentsErrorTag {
+ template <typename RequiredType>
+ using apply = RequiredTypesInComponentArgumentsError<RequiredType>;
+};
+
+struct NonInjectableTypeErrorTag {
+ template <typename T>
+ using apply = NonInjectableTypeError<T>;
+};
+
+struct ConstBindingDeclaredAsRequiredButNonConstBindingRequiredErrorTag {
+ template <typename T>
+ using apply = ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<T>;
+};
+
+struct ProviderReturningPointerToAbstractClassWithNoVirtualDestructorErrorTag {
+ template <typename T>
+ using apply = ProviderReturningPointerToAbstractClassWithNoVirtualDestructorError<T>;
+};
+
+struct MultibindingProviderReturningPointerToAbstractClassWithNoVirtualDestructorErrorTag {
+ template <typename T>
+ using apply = MultibindingProviderReturningPointerToAbstractClassWithNoVirtualDestructorError<T>;
+};
+
+struct RegisterFactoryForUniquePtrOfAbstractClassWithNoVirtualDestructorErrorTag {
+ template <typename T>
+ using apply = RegisterFactoryForUniquePtrOfAbstractClassWithNoVirtualDestructorError<T>;
+};
+
+struct FactoryBindingForUniquePtrOfClassWithNoVirtualDestructorErrorTag {
+ template <typename BaseFactory, typename DerivedFactory>
+ using apply = FactoryBindingForUniquePtrOfClassWithNoVirtualDestructorError<BaseFactory, DerivedFactory>;
+};
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_INJECTION_ERRORS
diff --git a/include/fruit/impl/injector.defn.h b/include/fruit/impl/injector.defn.h
new file mode 100644
index 0000000..2072db5
--- /dev/null
+++ b/include/fruit/impl/injector.defn.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTOR_DEFN_H
+#define FRUIT_INJECTOR_DEFN_H
+
+#include <fruit/component.h>
+
+// Redundant, but makes KDevelop happy.
+#include <fruit/injector.h>
+
+namespace fruit {
+
+template <typename... P>
+template <typename... FormalArgs, typename... Args>
+inline Injector<P...>::Injector(Component<P...> (*getComponent)(FormalArgs...), Args&&... args) {
+ Component<P...> component = fruit::createComponent().install(getComponent, std::forward<Args>(args)...);
+
+ fruit::impl::MemoryPool memory_pool;
+ using exposed_types_t = std::vector<fruit::impl::TypeId, fruit::impl::ArenaAllocator<fruit::impl::TypeId>>;
+ exposed_types_t exposed_types =
+ exposed_types_t(std::initializer_list<fruit::impl::TypeId>{fruit::impl::getTypeId<P>()...},
+ fruit::impl::ArenaAllocator<fruit::impl::TypeId>(memory_pool));
+ storage = std::unique_ptr<fruit::impl::InjectorStorage>(
+ new fruit::impl::InjectorStorage(std::move(component.storage), exposed_types, memory_pool));
+}
+
+namespace impl {
+namespace meta {
+
+template <typename... P>
+struct InjectorImplHelper {
+
+ // This performs all checks needed in the constructor of Injector that takes NormalizedComponent.
+ template <typename NormalizedComp, typename Comp>
+ struct CheckConstructionFromNormalizedComponent {
+ using Op = InstallComponent(Comp, NormalizedComp);
+
+ // The calculation of MergedComp will also do some checks, e.g. multiple bindings for the same type.
+ using MergedComp = GetResult(Op);
+
+ using TypesNotProvided = SetDifference(RemoveConstFromTypes(Vector<Type<P>...>), GetComponentPs(MergedComp));
+ using MergedCompRs = SetDifference(GetComponentRsSuperset(MergedComp), GetComponentPs(MergedComp));
+
+ using type = Eval<If(
+ Not(IsEmptySet(GetComponentRsSuperset(Comp))),
+ ConstructErrorWithArgVector(ComponentWithRequirementsInInjectorErrorTag,
+ SetToVector(GetComponentRsSuperset(Comp))),
+ If(Not(IsEmptySet(MergedCompRs)),
+ ConstructErrorWithArgVector(UnsatisfiedRequirementsInNormalizedComponentErrorTag, SetToVector(MergedCompRs)),
+ If(Not(IsContained(VectorToSetUnchecked(RemoveConstFromTypes(Vector<Type<P>...>)),
+ GetComponentPs(MergedComp))),
+ ConstructErrorWithArgVector(TypesInInjectorNotProvidedErrorTag, SetToVector(TypesNotProvided)),
+ If(Not(IsContained(VectorToSetUnchecked(RemoveConstTypes(Vector<Type<P>...>)),
+ GetComponentNonConstRsPs(MergedComp))),
+ ConstructErrorWithArgVector(
+ TypesInInjectorProvidedAsConstOnlyErrorTag,
+ SetToVector(SetDifference(VectorToSetUnchecked(RemoveConstTypes(Vector<Type<P>...>)),
+ GetComponentNonConstRsPs(MergedComp)))),
+ None))))>;
+ };
+
+ template <typename T>
+ struct CheckGet {
+ using Comp = ConstructComponentImpl(Type<P>...);
+
+ using type = Eval<PropagateError(CheckInjectableType(RemoveAnnotations(Type<T>)),
+ If(Not(IsInSet(NormalizeType(Type<T>), GetComponentPs(Comp))),
+ ConstructError(TypeNotProvidedErrorTag, Type<T>),
+ If(And(TypeInjectionRequiresNonConstBinding(Type<T>),
+ Not(IsInSet(NormalizeType(Type<T>), GetComponentNonConstRsPs(Comp)))),
+ ConstructError(TypeProvidedAsConstOnlyErrorTag, Type<T>), None)))>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+
+template <typename... P>
+template <typename... NormalizedComponentParams, typename... ComponentParams, typename... FormalArgs, typename... Args>
+inline Injector<P...>::Injector(const NormalizedComponent<NormalizedComponentParams...>& normalized_component,
+ Component<ComponentParams...> (*getComponent)(FormalArgs...), Args&&... args) {
+ Component<ComponentParams...> component = fruit::createComponent().install(getComponent, std::forward<Args>(args)...);
+
+ fruit::impl::MemoryPool memory_pool;
+ storage = std::unique_ptr<fruit::impl::InjectorStorage>(new fruit::impl::InjectorStorage(
+ *(normalized_component.storage.storage), std::move(component.storage), memory_pool));
+
+ using NormalizedComp =
+ fruit::impl::meta::ConstructComponentImpl(fruit::impl::meta::Type<NormalizedComponentParams>...);
+ using Comp1 = fruit::impl::meta::ConstructComponentImpl(fruit::impl::meta::Type<ComponentParams>...);
+ // We don't check whether the construction of NormalizedComp or Comp resulted in errors here; if they did, the
+ // instantiation
+ // of NormalizedComponent<NormalizedComponentParams...> or Component<ComponentParams...> would have resulted in an
+ // error already.
+
+ using E = typename fruit::impl::meta::InjectorImplHelper<P...>::template CheckConstructionFromNormalizedComponent<
+ NormalizedComp, Comp1>::type;
+ (void)typename fruit::impl::meta::CheckIfError<E>::type();
+}
+
+template <typename... P>
+template <typename T>
+inline typename Injector<P...>::template RemoveAnnotations<T> Injector<P...>::get() {
+
+ using E = typename fruit::impl::meta::InjectorImplHelper<P...>::template CheckGet<T>::type;
+ (void)typename fruit::impl::meta::CheckIfError<E>::type();
+ return storage->template get<T>();
+}
+
+template <typename... P>
+template <typename T>
+inline Injector<P...>::operator T() {
+ return get<T>();
+}
+
+template <typename... P>
+template <typename AnnotatedC>
+inline const std::vector<typename fruit::Injector<P...>::template RemoveAnnotationsHelper<AnnotatedC>::type*>&
+Injector<P...>::getMultibindings() {
+
+ using Op = fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
+ fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>)>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+ return storage->template getMultibindings<AnnotatedC>();
+}
+
+template <typename... P>
+FRUIT_DEPRECATED_DEFINITION(inline void Injector<P...>::eagerlyInjectAll()) {
+ // Eagerly inject normal bindings.
+ void* unused[] = {reinterpret_cast<void*>(
+ storage->template get<fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::AddPointerInAnnotatedType(fruit::impl::meta::Type<P>)>>>())...};
+ (void)unused;
+
+ storage->eagerlyInjectMultibindings();
+}
+
+} // namespace fruit
+
+#endif // FRUIT_INJECTOR_DEFN_H
diff --git a/include/fruit/impl/injector/injector_accessor_for_tests.defn.h b/include/fruit/impl/injector/injector_accessor_for_tests.defn.h
new file mode 100644
index 0000000..c74c930
--- /dev/null
+++ b/include/fruit/impl/injector/injector_accessor_for_tests.defn.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTOR_ACCESSOR_FOR_TESTS_DEFN_H
+#define FRUIT_INJECTOR_ACCESSOR_FOR_TESTS_DEFN_H
+
+#include <fruit/impl/injector/injector_accessor_for_tests.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename AnnotatedC, typename... Params>
+const typename fruit::Injector<Params...>::template RemoveAnnotations<AnnotatedC>*
+InjectorAccessorForTests::unsafeGet(fruit::Injector<Params...>& injector) {
+ using Op = fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
+ fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>)>;
+ (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+ return injector.storage->template unsafeGet<AnnotatedC>();
+}
+}
+}
+
+#endif // FRUIT_INJECTOR_ACCESSOR_FOR_TESTS_DEFN_H
diff --git a/include/fruit/impl/injector/injector_accessor_for_tests.h b/include/fruit/impl/injector/injector_accessor_for_tests.h
new file mode 100644
index 0000000..b2c106c
--- /dev/null
+++ b/include/fruit/impl/injector/injector_accessor_for_tests.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTOR_ACCESSOR_FOR_TESTS_H
+#define FRUIT_INJECTOR_ACCESSOR_FOR_TESTS_H
+
+#include <fruit/fruit.h>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * A class used to access Injector's internals in Fruit's own tests. Note that this is *not* meant to be used outside
+ * of Fruit.
+ */
+struct InjectorAccessorForTests {
+
+ /**
+ * If C was bound (directly or indirectly) in the component used to create this injector, returns a pointer to the
+ * instance of C (constructing it if necessary). Otherwise returns nullptr.
+ *
+ * This supports annotated injection, just use Annotated<Annotation, C> instead of just C.
+ * With a non-annotated parameter C, this returns a C*.
+ * With an annotated parameter C=Annotated<Annotation, SomeClass>, this returns a const SomeClass*.
+ *
+ * Note that this doesn't trigger auto-bindings: so even if the constructor of C was visible to some get*Component
+ * function (or to the place where unsafeGet is called), in order to successfully get an instance with this method
+ * you need all the following to be true:
+ * * C was explicitly bound in a component, or C was a dependency (direct or indirect) of a type that was explicitly
+ * bound
+ * * C was not bound to any interface (note however that if C was bound to I, you can do unsafeGet<I>() instead).
+ *
+ * Otherwise this method will return nullptr.
+ */
+ template <typename C, typename... Params>
+ static const typename fruit::Injector<Params...>::template RemoveAnnotations<C>*
+ unsafeGet(fruit::Injector<Params...>& injector);
+};
+}
+}
+
+#include <fruit/impl/injector/injector_accessor_for_tests.defn.h>
+
+#endif // FRUIT_INJECTOR_ACCESSOR_FOR_TESTS_H
diff --git a/include/fruit/impl/injector/injector_storage.defn.h b/include/fruit/impl/injector/injector_storage.defn.h
new file mode 100644
index 0000000..b5de693
--- /dev/null
+++ b/include/fruit/impl/injector/injector_storage.defn.h
@@ -0,0 +1,850 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTOR_STORAGE_DEFN_H
+#define FRUIT_INJECTOR_STORAGE_DEFN_H
+
+#include <fruit/impl/component_storage/component_storage_entry.h>
+#include <fruit/impl/fruit_assert.h>
+#include <fruit/impl/meta/component.h>
+#include <fruit/impl/meta/vector.h>
+#include <fruit/impl/util/demangle_type_name.h>
+#include <fruit/impl/util/lambda_invoker.h>
+#include <fruit/impl/util/type_info.h>
+
+#include <cassert>
+
+// Redundant, but makes KDevelop happy.
+#include <fruit/impl/injector/injector_storage.h>
+
+namespace fruit {
+namespace impl {
+
+inline InjectorStorage::BindingDataNodeIter* InjectorStorage::BindingDataNodeIter::operator->() {
+ return this;
+}
+
+inline void InjectorStorage::BindingDataNodeIter::operator++() {
+ ++itr;
+}
+
+inline bool InjectorStorage::BindingDataNodeIter::operator==(const BindingDataNodeIter& other) const {
+ return itr == other.itr;
+}
+
+inline bool InjectorStorage::BindingDataNodeIter::operator!=(const BindingDataNodeIter& other) const {
+ return itr != other.itr;
+}
+
+inline std::ptrdiff_t InjectorStorage::BindingDataNodeIter::operator-(BindingDataNodeIter other) const {
+ return itr - other.itr;
+}
+
+inline TypeId InjectorStorage::BindingDataNodeIter::getId() {
+ // For these kinds the type_id has a different meaning, but we never need to call this method for those.
+ FruitAssert(itr->kind != ComponentStorageEntry::Kind::COMPRESSED_BINDING);
+ FruitAssert(itr->kind != ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS);
+ FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS);
+ FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS);
+ FruitAssert(itr->kind != ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS);
+ FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS);
+ FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS);
+ return itr->type_id;
+}
+
+inline NormalizedBinding InjectorStorage::BindingDataNodeIter::getValue() {
+ return NormalizedBinding(*itr);
+}
+
+inline bool InjectorStorage::BindingDataNodeIter::isTerminal() {
+#ifdef FRUIT_EXTRA_DEBUG
+ if (itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT &&
+ itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION &&
+ itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION &&
+ itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION) {
+ std::cerr << "Unexpected binding kind: " << (std::size_t)itr->kind << std::endl;
+ FruitAssert(false);
+ }
+#endif
+ return itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT;
+}
+
+inline const TypeId* InjectorStorage::BindingDataNodeIter::getEdgesBegin() {
+ FruitAssert(itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION ||
+ itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION);
+ return itr->binding_for_object_to_construct.deps->deps;
+}
+
+inline const TypeId* InjectorStorage::BindingDataNodeIter::getEdgesEnd() {
+ FruitAssert(itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION ||
+ itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION);
+ return itr->binding_for_object_to_construct.deps->deps + itr->binding_for_object_to_construct.deps->num_deps;
+}
+
+template <typename AnnotatedT>
+struct GetFirstStage;
+
+// General case, value.
+template <typename C>
+struct GetFirstStage {
+ const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ return injector.getPtr<C>(node_itr);
+ }
+};
+
+template <typename C>
+struct GetFirstStage<const C> {
+ const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ return injector.getPtr<C>(node_itr);
+ }
+};
+
+template <typename C>
+struct GetFirstStage<std::shared_ptr<C>> {
+ // This method is covered by tests, even though lcov doesn't detect that.
+ C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ FruitAssert(node_itr.getNode().is_nonconst);
+ return const_cast<C*>(injector.getPtr<C>(node_itr));
+ }
+};
+
+template <typename C>
+struct GetFirstStage<C*> {
+ C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ FruitAssert(node_itr.getNode().is_nonconst);
+ return const_cast<C*>(injector.getPtr<C>(node_itr));
+ }
+};
+
+template <typename C>
+struct GetFirstStage<const C*> {
+ const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ return injector.getPtr<C>(node_itr);
+ }
+};
+
+template <typename C>
+struct GetFirstStage<C&> {
+ C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ FruitAssert(node_itr.getNode().is_nonconst);
+ return const_cast<C*>(injector.getPtr<C>(node_itr));
+ }
+};
+
+template <typename C>
+struct GetFirstStage<const C&> {
+ // This method is covered by tests, even though lcov doesn't detect that.
+ const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ return injector.getPtr<C>(node_itr);
+ }
+};
+
+template <typename C>
+struct GetFirstStage<Provider<C>> {
+ Provider<C> operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) {
+ return Provider<C>(&injector, node_itr);
+ }
+};
+
+template <typename Annotation, typename T>
+struct GetFirstStage<fruit::Annotated<Annotation, T>> : public GetFirstStage<T> {};
+
+template <typename AnnotatedT>
+struct GetSecondStage;
+
+// General case, value.
+template <typename C>
+struct GetSecondStage {
+ C operator()(const C* p) {
+ return *p;
+ }
+};
+
+template <typename C>
+struct GetSecondStage<const C> {
+ const C operator()(const C* p) {
+ return *p;
+ }
+};
+
+template <typename C>
+struct GetSecondStage<std::shared_ptr<C>> {
+ // This method is covered by tests, even though lcov doesn't detect that.
+ std::shared_ptr<C> operator()(C* p) {
+ return std::shared_ptr<C>(std::shared_ptr<char>(), p);
+ }
+};
+
+template <typename C>
+struct GetSecondStage<C*> {
+ C* operator()(C* p) {
+ return p;
+ }
+};
+
+template <typename C>
+struct GetSecondStage<const C*> {
+ // This method is covered by tests, even though lcov doesn't detect that.
+ const C* operator()(const C* p) {
+ return p;
+ }
+};
+
+template <typename C>
+struct GetSecondStage<C&> {
+ C& operator()(C* p) {
+ return *p;
+ }
+};
+
+template <typename C>
+struct GetSecondStage<const C&> {
+ const C& operator()(const C* p) {
+ return *p;
+ }
+};
+
+template <typename C>
+struct GetSecondStage<Provider<C>> {
+ Provider<C> operator()(Provider<C> p) {
+ return p;
+ }
+};
+
+template <typename Annotation, typename T>
+struct GetSecondStage<fruit::Annotated<Annotation, T>> : public GetSecondStage<T> {};
+
+template <typename AnnotatedT>
+inline InjectorStorage::RemoveAnnotations<AnnotatedT> InjectorStorage::get() {
+ std::lock_guard<std::recursive_mutex> lock(mutex);
+ return GetSecondStage<AnnotatedT>()(GetFirstStage<AnnotatedT>()(*this, lazyGetPtr<NormalizeType<AnnotatedT>>()));
+}
+
+template <typename T>
+inline T InjectorStorage::get(InjectorStorage::Graph::node_iterator node_iterator) {
+ FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<T>,
+ fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<T>)));
+ std::lock_guard<std::recursive_mutex> lock(mutex);
+ return GetSecondStage<T>()(GetFirstStage<T>()(*this, node_iterator));
+}
+
+template <typename AnnotatedC>
+inline InjectorStorage::Graph::node_iterator InjectorStorage::lazyGetPtr() {
+ return lazyGetPtr(getTypeId<AnnotatedC>());
+}
+
+template <typename AnnotatedC>
+inline InjectorStorage::Graph::node_iterator
+InjectorStorage::lazyGetPtr(Graph::edge_iterator deps, std::size_t dep_index, Graph::node_iterator bindings_begin)
+ const {
+ // Here we (intentionally) do not lock `mutex', since this is a read-only method that only accesses immutable data.
+ Graph::node_iterator itr = deps.getNodeIterator(dep_index, bindings_begin);
+ FruitAssert(bindings.find(getTypeId<AnnotatedC>()) == Graph::const_node_iterator(itr));
+ FruitAssert(!(bindings.end() == Graph::const_node_iterator(itr)));
+ return itr;
+}
+
+template <typename C>
+inline const C* InjectorStorage::getPtr(Graph::node_iterator itr) {
+ FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<C>,
+ fruit::impl::meta::NormalizeType(fruit::impl::meta::Type<C>)));
+ const void* p = getPtrInternal(itr);
+ return reinterpret_cast<const C*>(p);
+}
+
+template <typename AnnotatedC>
+inline const InjectorStorage::RemoveAnnotations<AnnotatedC>* InjectorStorage::unsafeGet() {
+ std::lock_guard<std::recursive_mutex> lock(mutex);
+ using C = RemoveAnnotations<AnnotatedC>;
+ const void* p = unsafeGetPtr(getTypeId<AnnotatedC>());
+ return reinterpret_cast<const C*>(p);
+}
+
+inline InjectorStorage::Graph::node_iterator InjectorStorage::lazyGetPtr(TypeId type) {
+ return bindings.at(type);
+}
+
+inline const void* InjectorStorage::unsafeGetPtr(TypeId type) {
+ Graph::node_iterator itr = bindings.find(type);
+ if (itr == bindings.end()) {
+ return nullptr;
+ }
+ return getPtrInternal(itr);
+}
+
+template <typename AnnotatedC>
+inline const std::vector<InjectorStorage::RemoveAnnotations<AnnotatedC>*>& InjectorStorage::getMultibindings() {
+ std::lock_guard<std::recursive_mutex> lock(mutex);
+ using C = RemoveAnnotations<AnnotatedC>;
+ void* p = getMultibindings(getTypeId<AnnotatedC>());
+ if (p == nullptr) {
+ static std::vector<C*> empty_vector;
+ return empty_vector;
+ } else {
+ return *reinterpret_cast<std::vector<C*>*>(p);
+ }
+}
+
+inline const void* InjectorStorage::getPtrInternal(Graph::node_iterator node_itr) {
+ NormalizedBinding& normalized_binding = node_itr.getNode();
+ if (!node_itr.isTerminal()) {
+ normalized_binding.object = normalized_binding.create(*this, node_itr);
+ FruitAssert(node_itr.isTerminal());
+ }
+ return normalized_binding.object;
+}
+
+inline NormalizedMultibindingSet* InjectorStorage::getNormalizedMultibindingSet(TypeId type) {
+ auto itr = multibindings.find(type);
+ if (itr != multibindings.end())
+ return &(itr->second);
+ else
+ return nullptr;
+}
+
+template <typename AnnotatedC>
+inline std::shared_ptr<char> InjectorStorage::createMultibindingVector(InjectorStorage& storage) {
+ using C = RemoveAnnotations<AnnotatedC>;
+ TypeId type = getTypeId<AnnotatedC>();
+ NormalizedMultibindingSet* multibinding_set = storage.getNormalizedMultibindingSet(type);
+
+ // This method is only called if there was at least 1 multibinding (otherwise the would-be caller would have returned
+ // nullptr
+ // instead of calling this).
+ FruitAssert(multibinding_set != nullptr);
+
+ if (multibinding_set->v.get() != nullptr) {
+ // Result cached, return early.
+ return multibinding_set->v;
+ }
+
+ storage.ensureConstructedMultibinding(*multibinding_set);
+
+ std::vector<C*> s;
+ s.reserve(multibinding_set->elems.size());
+ for (const NormalizedMultibinding& multibinding : multibinding_set->elems) {
+ FruitAssert(multibinding.is_constructed);
+ s.push_back(reinterpret_cast<C*>(multibinding.object));
+ }
+
+ std::shared_ptr<std::vector<C*>> vector_ptr = std::make_shared<std::vector<C*>>(std::move(s));
+ std::shared_ptr<char> result(vector_ptr, reinterpret_cast<char*>(vector_ptr.get()));
+
+ multibinding_set->v = result;
+
+ return result;
+}
+
+template <typename I, typename C, typename AnnotatedC>
+InjectorStorage::const_object_ptr_t
+InjectorStorage::createInjectedObjectForBind(InjectorStorage& injector,
+ InjectorStorage::Graph::node_iterator node_itr) {
+
+ InjectorStorage::Graph::node_iterator bindings_begin = injector.bindings.begin();
+ const C* cPtr = injector.get<const C*>(injector.lazyGetPtr<AnnotatedC>(node_itr.neighborsBegin(), 0, bindings_begin));
+ node_itr.setTerminal();
+ // This step is needed when the cast C->I changes the pointer
+ // (e.g. for multiple inheritance).
+ const I* iPtr = static_cast<const I*>(cPtr);
+ return reinterpret_cast<const_object_ptr_t>(iPtr);
+}
+
+// I, C must not be pointers.
+template <typename AnnotatedI, typename AnnotatedC>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBind() {
+ using I = RemoveAnnotations<AnnotatedI>;
+ using C = RemoveAnnotations<AnnotatedC>;
+ FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<I>)));
+ FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<C>)));
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION;
+ result.type_id = getTypeId<AnnotatedI>();
+ ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct;
+ binding.create = createInjectedObjectForBind<I, C, AnnotatedC>;
+ binding.deps = getBindingDeps<fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>>();
+#ifdef FRUIT_EXTRA_DEBUG
+ binding.is_nonconst = true;
+#endif
+ return result;
+}
+
+// I, C must not be pointers.
+template <typename AnnotatedI, typename AnnotatedC>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForConstBind() {
+ using I = RemoveAnnotations<AnnotatedI>;
+ using C = RemoveAnnotations<AnnotatedC>;
+ FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<I>)));
+ FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<C>)));
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION;
+ result.type_id = getTypeId<AnnotatedI>();
+ ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct;
+ binding.create = createInjectedObjectForBind<I, C, AnnotatedC>;
+ binding.deps = getBindingDeps<fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>>();
+#ifdef FRUIT_EXTRA_DEBUG
+ binding.is_nonconst = false;
+#endif
+ return result;
+}
+
+template <typename AnnotatedC, typename C>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBindInstance(C& instance) {
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT;
+ result.type_id = getTypeId<AnnotatedC>();
+ ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object;
+ binding.object_ptr = &instance;
+#ifdef FRUIT_EXTRA_DEBUG
+ binding.is_nonconst = true;
+#endif
+ return result;
+}
+
+template <typename AnnotatedC, typename C>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBindConstInstance(const C& instance) {
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT;
+ result.type_id = getTypeId<AnnotatedC>();
+ ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object;
+ binding.object_ptr = &instance;
+#ifdef FRUIT_EXTRA_DEBUG
+ binding.is_nonconst = false;
+#endif
+ return result;
+}
+
+// The inner operator() takes an InjectorStorage& and a Graph::edge_iterator (the type's deps) and
+// returns the injected object as a C*.
+// This takes care of move-constructing a C into the injector's own allocator if needed.
+template <typename AnnotatedSignature, typename Lambda, bool lambda_returns_pointer,
+ typename AnnotatedT = InjectorStorage::SignatureType<AnnotatedSignature>,
+ typename AnnotatedArgVector =
+ fruit::impl::meta::Eval<fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<AnnotatedSignature>)>,
+ typename Indexes =
+ fruit::impl::meta::Eval<fruit::impl::meta::GenerateIntSequence(fruit::impl::meta::VectorSize(
+ fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<AnnotatedSignature>)))>>
+struct InvokeLambdaWithInjectedArgVector;
+
+// AnnotatedT is of the form C* or Annotated<Annotation, C*>
+template <typename AnnotatedSignature, typename Lambda, typename AnnotatedT, typename... AnnotatedArgs,
+ typename... Indexes>
+struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, true /* lambda_returns_pointer */, AnnotatedT,
+ fruit::impl::meta::Vector<AnnotatedArgs...>,
+ fruit::impl::meta::Vector<Indexes...>> {
+ using CPtr = InjectorStorage::RemoveAnnotations<AnnotatedT>;
+ using AnnotatedC = InjectorStorage::NormalizeType<AnnotatedT>;
+
+ FRUIT_ALWAYS_INLINE
+ CPtr operator()(InjectorStorage& injector, FixedSizeAllocator& allocator) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ CPtr cPtr =
+ LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover<
+ typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type...>(
+ injector.get<fruit::impl::meta::UnwrapType<AnnotatedArgs>>()...);
+
+ allocator.registerExternallyAllocatedObject(cPtr);
+
+ // This can happen if the user-supplied provider returns nullptr.
+ if (cPtr == nullptr) {
+ InjectorStorage::fatal("attempting to get an instance for the type " + std::string(getTypeId<AnnotatedC>()) +
+ " but the provider returned nullptr");
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ return cPtr;
+ }
+
+ // This is not inlined in outerConstructHelper so that when get<> needs to construct an object more complex than a
+ // pointer
+ // (e.g. a shared_ptr), that happens as late as possible so that it's easier for the optimizer to optimize out some
+ // operations (e.g. the increment/decrement/check of shared_ptr's reference count).
+ template <typename... GetFirstStageResults>
+ FRUIT_ALWAYS_INLINE CPtr innerConstructHelper(InjectorStorage& injector,
+ GetFirstStageResults... getFirstStageResults) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ return LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover<
+ typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type...>(
+ GetSecondStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()(
+ getFirstStageResults)...);
+ }
+
+ // This is not inlined in operator() so that all the lazyGetPtr() calls happen first (instead of being interleaved
+ // with the get() calls). The lazyGetPtr() calls don't branch, while the get() calls branch on the result of the
+ // lazyGetPtr()s, so it's faster to execute them in this order.
+ template <typename... NodeItrs>
+ FRUIT_ALWAYS_INLINE CPtr outerConstructHelper(InjectorStorage& injector, NodeItrs... nodeItrs) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ return innerConstructHelper(
+ injector, GetFirstStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()(
+ injector, nodeItrs)...);
+ }
+
+ FRUIT_ALWAYS_INLINE
+ CPtr operator()(InjectorStorage& injector, SemistaticGraph<TypeId, NormalizedBinding>& bindings,
+ FixedSizeAllocator& allocator, InjectorStorage::Graph::edge_iterator deps) {
+ // `deps' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)deps;
+
+ InjectorStorage::Graph::node_iterator bindings_begin = bindings.begin();
+ // `bindings_begin' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)bindings_begin;
+ CPtr cPtr = outerConstructHelper(
+ injector,
+ injector.lazyGetPtr<InjectorStorage::NormalizeType<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>(
+ deps, fruit::impl::meta::getIntValue<Indexes>(), bindings_begin)...);
+ allocator.registerExternallyAllocatedObject(cPtr);
+
+ // This can happen if the user-supplied provider returns nullptr.
+ if (cPtr == nullptr) {
+ InjectorStorage::fatal("attempting to get an instance for the type " + std::string(getTypeId<AnnotatedC>()) +
+ " but the provider returned nullptr");
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ return cPtr;
+ }
+};
+
+template <typename AnnotatedSignature, typename Lambda, typename AnnotatedC, typename... AnnotatedArgs,
+ typename... Indexes>
+struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, false /* lambda_returns_pointer */, AnnotatedC,
+ fruit::impl::meta::Vector<AnnotatedArgs...>,
+ fruit::impl::meta::Vector<Indexes...>> {
+ using C = InjectorStorage::RemoveAnnotations<AnnotatedC>;
+
+ FRUIT_ALWAYS_INLINE
+ C* operator()(InjectorStorage& injector, FixedSizeAllocator& allocator) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ return allocator.constructObject<AnnotatedC, C&&>(
+ LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover<
+ typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type&&...>(
+ injector.get<typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>()...));
+ }
+
+ // This is not inlined in outerConstructHelper so that when get<> needs to construct an object more complex than a
+ // pointer
+ // (e.g. a shared_ptr), that happens as late as possible so that it's easier for the optimizer to optimize out some
+ // operations (e.g. the increment/decrement/check of shared_ptr's reference count).
+ template <typename... GetFirstStageResults>
+ FRUIT_ALWAYS_INLINE C* innerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator,
+ GetFirstStageResults... getFirstStageResults) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ return allocator.constructObject<AnnotatedC, C&&>(
+ LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover<
+ typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type...>(
+ GetSecondStage<typename InjectorStorage::AnnotationRemover<
+ typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type>()(getFirstStageResults)...));
+ }
+
+ // This is not inlined in operator() so that all the lazyGetPtr() calls happen first (instead of being interleaved
+ // with the get() calls). The lazyGetPtr() calls don't branch, while the get() calls branch on the result of the
+ // lazyGetPtr()s, so it's faster to execute them in this order.
+ template <typename... NodeItrs>
+ FRUIT_ALWAYS_INLINE C* outerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator,
+ NodeItrs... nodeItrs) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ return innerConstructHelper(
+ injector, allocator,
+ GetFirstStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()(injector,
+ nodeItrs)...);
+ }
+
+ C* operator()(InjectorStorage& injector, SemistaticGraph<TypeId, NormalizedBinding>& bindings,
+ FixedSizeAllocator& allocator, InjectorStorage::Graph::edge_iterator deps) {
+ InjectorStorage::Graph::node_iterator bindings_begin = bindings.begin();
+ // `bindings_begin' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)bindings_begin;
+
+ // `deps' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)deps;
+
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ C* p = outerConstructHelper(
+ injector, allocator,
+ injector.lazyGetPtr<InjectorStorage::NormalizeType<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>(
+ deps, fruit::impl::meta::getIntValue<Indexes>(), bindings_begin)...);
+ return p;
+ }
+};
+
+template <typename C, typename T, typename AnnotatedSignature, typename Lambda>
+InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForProvider(InjectorStorage& injector,
+ Graph::node_iterator node_itr) {
+ C* cPtr = InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, std::is_pointer<T>::value>()(
+ injector, injector.bindings, injector.allocator, node_itr.neighborsBegin());
+ node_itr.setTerminal();
+ return reinterpret_cast<const_object_ptr_t>(cPtr);
+}
+
+template <typename AnnotatedSignature, typename Lambda>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForProvider() {
+#ifdef FRUIT_EXTRA_DEBUG
+ using Signature =
+ fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature(
+ fruit::impl::meta::Type<AnnotatedSignature>)>>;
+ FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<Signature>,
+ fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type<Lambda>)));
+#endif
+ using AnnotatedT = SignatureType<AnnotatedSignature>;
+ using AnnotatedC = NormalizeType<AnnotatedT>;
+ // T is either C or C*.
+ using T = RemoveAnnotations<AnnotatedT>;
+ using C = NormalizeType<T>;
+ ComponentStorageEntry result;
+ constexpr bool needs_allocation = !std::is_pointer<T>::value;
+ result.kind = needs_allocation
+ ? ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION
+ : ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION;
+ result.type_id = getTypeId<AnnotatedC>();
+ ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct;
+ binding.create = createInjectedObjectForProvider<C, T, AnnotatedSignature, Lambda>;
+ binding.deps = getBindingDeps<NormalizedSignatureArgs<AnnotatedSignature>>();
+#ifdef FRUIT_EXTRA_DEBUG
+ binding.is_nonconst = true;
+#endif
+ return result;
+}
+
+template <typename I, typename C, typename T, typename AnnotatedSignature, typename Lambda>
+InjectorStorage::const_object_ptr_t
+InjectorStorage::createInjectedObjectForCompressedProvider(InjectorStorage& injector, Graph::node_iterator node_itr) {
+ C* cPtr = InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, std::is_pointer<T>::value>()(
+ injector, injector.bindings, injector.allocator, node_itr.neighborsBegin());
+ node_itr.setTerminal();
+ I* iPtr = static_cast<I*>(cPtr);
+ return reinterpret_cast<object_ptr_t>(iPtr);
+}
+
+template <typename AnnotatedSignature, typename Lambda, typename AnnotatedI>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForCompressedProvider() {
+#ifdef FRUIT_EXTRA_DEBUG
+ using Signature =
+ fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature(
+ fruit::impl::meta::Type<AnnotatedSignature>)>>;
+ FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<Signature>,
+ fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type<Lambda>)));
+#endif
+ using AnnotatedT = SignatureType<AnnotatedSignature>;
+ using AnnotatedC = NormalizeType<AnnotatedT>;
+ // T is either C or C*.
+ using T = RemoveAnnotations<AnnotatedT>;
+ using C = NormalizeType<T>;
+ using I = RemoveAnnotations<AnnotatedI>;
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::COMPRESSED_BINDING;
+ result.type_id = getTypeId<AnnotatedI>();
+ ComponentStorageEntry::CompressedBinding& binding = result.compressed_binding;
+ binding.c_type_id = getTypeId<AnnotatedC>();
+ binding.create = createInjectedObjectForCompressedProvider<I, C, T, AnnotatedSignature, Lambda>;
+ return result;
+}
+
+// The inner operator() takes an InjectorStorage& and a Graph::edge_iterator (the type's deps) and
+// returns the injected object as a C*.
+// This takes care of allocating the required space into the injector's allocator.
+template <typename AnnotatedSignature,
+ typename Indexes =
+ fruit::impl::meta::Eval<fruit::impl::meta::GenerateIntSequence(fruit::impl::meta::VectorSize(
+ fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<AnnotatedSignature>)))>>
+struct InvokeConstructorWithInjectedArgVector;
+
+template <typename AnnotatedC, typename... AnnotatedArgs, typename... Indexes>
+struct InvokeConstructorWithInjectedArgVector<AnnotatedC(AnnotatedArgs...), fruit::impl::meta::Vector<Indexes...>> {
+ using C = InjectorStorage::RemoveAnnotations<AnnotatedC>;
+
+ // This is not inlined in outerConstructHelper so that when get<> needs to construct an object more complex than a
+ // pointer
+ // (e.g. a shared_ptr), that happens as late as possible so that it's easier for the optimizer to optimize out some
+ // operations (e.g. the increment/decrement/check of shared_ptr's reference count).
+ template <typename... GetFirstStageResults>
+ FRUIT_ALWAYS_INLINE C* innerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator,
+ GetFirstStageResults... getFirstStageResults) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ return allocator.constructObject<AnnotatedC, typename InjectorStorage::AnnotationRemover<AnnotatedArgs>::type&&...>(
+ GetSecondStage<InjectorStorage::RemoveAnnotations<AnnotatedArgs>>()(getFirstStageResults)...);
+ }
+
+ // This is not inlined in operator() so that all the lazyGetPtr() calls happen first (instead of being interleaved
+ // with the get() calls). The lazyGetPtr() calls don't branch, while the get() calls branch on the result of the
+ // lazyGetPtr()s, so it's faster to execute them in this order.
+ template <typename... NodeItrs>
+ FRUIT_ALWAYS_INLINE C* outerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator,
+ NodeItrs... nodeItrs) {
+ // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused.
+ (void)injector;
+ return innerConstructHelper(
+ injector, allocator, GetFirstStage<InjectorStorage::RemoveAnnotations<AnnotatedArgs>>()(injector, nodeItrs)...);
+ }
+
+ FRUIT_ALWAYS_INLINE
+ C* operator()(InjectorStorage& injector, SemistaticGraph<TypeId, NormalizedBinding>& bindings,
+ FixedSizeAllocator& allocator, InjectorStorage::Graph::edge_iterator deps) {
+
+ // `deps' *is* used below, but when there are no Args some compilers report it as unused.
+ (void)deps;
+
+ InjectorStorage::Graph::node_iterator bindings_begin = bindings.begin();
+ // `bindings_begin' *is* used below, but when there are no Args some compilers report it as unused.
+ (void)bindings_begin;
+ C* p = outerConstructHelper(injector, allocator,
+ injector.lazyGetPtr<typename InjectorStorage::TypeNormalizer<AnnotatedArgs>::type>(
+ deps, fruit::impl::meta::getIntValue<Indexes>(), bindings_begin)...);
+ return p;
+ }
+};
+
+template <typename C, typename AnnotatedSignature>
+InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForConstructor(InjectorStorage& injector,
+ Graph::node_iterator node_itr) {
+ C* cPtr = InvokeConstructorWithInjectedArgVector<AnnotatedSignature>()(injector, injector.bindings,
+ injector.allocator, node_itr.neighborsBegin());
+ node_itr.setTerminal();
+ return reinterpret_cast<InjectorStorage::object_ptr_t>(cPtr);
+}
+
+template <typename AnnotatedSignature>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForConstructor() {
+ using AnnotatedC = SignatureType<AnnotatedSignature>;
+ using C = RemoveAnnotations<AnnotatedC>;
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION;
+ result.type_id = getTypeId<AnnotatedC>();
+ ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct;
+ binding.create = createInjectedObjectForConstructor<C, AnnotatedSignature>;
+ binding.deps = getBindingDeps<NormalizedSignatureArgs<AnnotatedSignature>>();
+#ifdef FRUIT_EXTRA_DEBUG
+ binding.is_nonconst = true;
+#endif
+ return result;
+}
+
+template <typename I, typename C, typename AnnotatedSignature>
+InjectorStorage::const_object_ptr_t
+InjectorStorage::createInjectedObjectForCompressedConstructor(InjectorStorage& injector,
+ Graph::node_iterator node_itr) {
+ C* cPtr = InvokeConstructorWithInjectedArgVector<AnnotatedSignature>()(injector, injector.bindings,
+ injector.allocator, node_itr.neighborsBegin());
+ node_itr.setTerminal();
+ I* iPtr = static_cast<I*>(cPtr);
+ return reinterpret_cast<object_ptr_t>(iPtr);
+}
+
+template <typename AnnotatedSignature, typename AnnotatedI>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForCompressedConstructor() {
+ using AnnotatedC = SignatureType<AnnotatedSignature>;
+ using C = RemoveAnnotations<AnnotatedC>;
+ using I = RemoveAnnotations<AnnotatedI>;
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::COMPRESSED_BINDING;
+ result.type_id = getTypeId<AnnotatedI>();
+ ComponentStorageEntry::CompressedBinding& binding = result.compressed_binding;
+ binding.c_type_id = getTypeId<AnnotatedC>();
+ binding.create = createInjectedObjectForCompressedConstructor<I, C, AnnotatedSignature>;
+ return result;
+}
+
+template <typename AnnotatedT>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator() {
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR;
+ result.type_id = getTypeId<AnnotatedT>();
+ ComponentStorageEntry::MultibindingVectorCreator& binding = result.multibinding_vector_creator;
+ binding.get_multibindings_vector = createMultibindingVector<AnnotatedT>;
+ return result;
+}
+
+template <typename I, typename C, typename AnnotatedCPtr>
+InjectorStorage::object_ptr_t InjectorStorage::createInjectedObjectForMultibinding(InjectorStorage& m) {
+ C* cPtr = m.get<AnnotatedCPtr>();
+ // This step is needed when the cast C->I changes the pointer
+ // (e.g. for multiple inheritance).
+ I* iPtr = static_cast<I*>(cPtr);
+ return reinterpret_cast<InjectorStorage::object_ptr_t>(iPtr);
+}
+
+template <typename AnnotatedI, typename AnnotatedC>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibinding() {
+ using AnnotatedCPtr = fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::AddPointerInAnnotatedType(fruit::impl::meta::Type<AnnotatedC>)>>;
+ using I = RemoveAnnotations<AnnotatedI>;
+ using C = RemoveAnnotations<AnnotatedC>;
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION;
+ result.type_id = getTypeId<AnnotatedI>();
+ ComponentStorageEntry::MultibindingForObjectToConstruct& binding = result.multibinding_for_object_to_construct;
+ binding.create = createInjectedObjectForMultibinding<I, C, AnnotatedCPtr>;
+ binding.deps = getBindingDeps<fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>>();
+ return result;
+}
+
+template <typename AnnotatedC, typename C>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForInstanceMultibinding(C& instance) {
+ ComponentStorageEntry result;
+ result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT;
+ result.type_id = getTypeId<AnnotatedC>();
+ ComponentStorageEntry::MultibindingForConstructedObject& binding = result.multibinding_for_constructed_object;
+ binding.object_ptr = &instance;
+ return result;
+}
+
+template <typename C, typename T, typename AnnotatedSignature, typename Lambda>
+InjectorStorage::object_ptr_t InjectorStorage::createInjectedObjectForMultibindingProvider(InjectorStorage& injector) {
+ C* cPtr = InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, std::is_pointer<T>::value>()(
+ injector, injector.allocator);
+ return reinterpret_cast<object_ptr_t>(cPtr);
+}
+
+template <typename AnnotatedSignature, typename Lambda>
+inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibindingProvider() {
+#ifdef FRUIT_EXTRA_DEBUG
+ using Signature =
+ fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature(
+ fruit::impl::meta::Type<AnnotatedSignature>)>>;
+ FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<Signature>,
+ fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type<Lambda>)));
+#endif
+
+ using AnnotatedT = SignatureType<AnnotatedSignature>;
+ using AnnotatedC = NormalizeType<AnnotatedT>;
+ using T = RemoveAnnotations<AnnotatedT>;
+ using C = NormalizeType<T>;
+ ComponentStorageEntry result;
+ bool needs_allocation = !std::is_pointer<T>::value;
+ if (needs_allocation)
+ result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION;
+ else
+ result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION;
+ result.type_id = getTypeId<AnnotatedC>();
+ ComponentStorageEntry::MultibindingForObjectToConstruct& binding = result.multibinding_for_object_to_construct;
+ binding.create = createInjectedObjectForMultibindingProvider<C, T, AnnotatedSignature, Lambda>;
+ binding.deps = getBindingDeps<NormalizedSignatureArgs<AnnotatedSignature>>();
+ return result;
+}
+
+} // namespace fruit
+} // namespace impl
+
+#endif // FRUIT_INJECTOR_STORAGE_DEFN_H
diff --git a/include/fruit/impl/injector/injector_storage.h b/include/fruit/impl/injector/injector_storage.h
new file mode 100644
index 0000000..6faa5f9
--- /dev/null
+++ b/include/fruit/impl/injector/injector_storage.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTOR_STORAGE_H
+#define FRUIT_INJECTOR_STORAGE_H
+
+#include <fruit/fruit_forward_decls.h>
+#include <fruit/impl/data_structures/fixed_size_allocator.h>
+#include <fruit/impl/meta/component.h>
+#include <fruit/impl/normalized_component_storage/normalized_bindings.h>
+
+#include <unordered_map>
+#include <vector>
+#include <mutex>
+
+namespace fruit {
+namespace impl {
+
+template <typename T>
+struct GetHelper;
+
+/**
+ * A component where all types have to be explicitly registered, and all checks are at runtime.
+ * Used to implement Component<>, don't use directly.
+ *
+ * This class handles the creation of types of the forms:
+ * - shared_ptr<C>, [const] C*, [const] C&, C (where C is an atomic type)
+ * - Injector<T1, ..., Tk> (with T1, ..., Tk of the above forms).
+ */
+class InjectorStorage {
+public:
+ // TODO: remove.
+ // using BindingVectors = std::pair<std::vector<std::pair<TypeId, BindingData>>,
+ // std::vector<std::pair<TypeId, MultibindingData>>>;
+ using Graph = SemistaticGraph<TypeId, NormalizedBinding>;
+
+ template <typename AnnotatedT>
+ using RemoveAnnotations = fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<AnnotatedT>)>>;
+
+ // MSVC 14 has trouble specializing alias templates using expanded pack elements.
+ // This is a known issue:
+ // https://stackoverflow.com/questions/43411542/metaprogramming-failed-to-specialize-alias-template
+ // The workaround is just to use a struct directly.
+ template <typename AnnotatedT>
+ struct AnnotationRemover {
+ using type = RemoveAnnotations<AnnotatedT>;
+ };
+
+ template <typename T>
+ using NormalizeType = fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::NormalizeType(fruit::impl::meta::Type<T>)>>;
+
+ template <typename T>
+ struct TypeNormalizer {
+ using type = NormalizeType<T>;
+ };
+
+ template <typename Signature>
+ using SignatureType = fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::SignatureType(fruit::impl::meta::Type<Signature>)>>;
+
+ template <typename Signature>
+ using NormalizedSignatureArgs = fruit::impl::meta::Eval<fruit::impl::meta::NormalizeTypeVector(
+ fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<Signature>))>;
+
+ // Prints the specified error and calls exit(1).
+ static void fatal(const std::string& error);
+
+ template <typename AnnotatedI, typename AnnotatedC>
+ static ComponentStorageEntry createComponentStorageEntryForBind();
+
+ template <typename AnnotatedI, typename AnnotatedC>
+ static ComponentStorageEntry createComponentStorageEntryForConstBind();
+
+ template <typename AnnotatedC, typename C>
+ static ComponentStorageEntry createComponentStorageEntryForBindInstance(C& instance);
+
+ template <typename AnnotatedC, typename C>
+ static ComponentStorageEntry createComponentStorageEntryForBindConstInstance(const C& instance);
+
+ template <typename AnnotatedSignature, typename Lambda>
+ static ComponentStorageEntry createComponentStorageEntryForProvider();
+
+ template <typename AnnotatedSignature, typename Lambda, typename AnnotatedI>
+ static ComponentStorageEntry createComponentStorageEntryForCompressedProvider();
+
+ template <typename AnnotatedSignature>
+ static ComponentStorageEntry createComponentStorageEntryForConstructor();
+
+ template <typename AnnotatedSignature, typename AnnotatedI>
+ static ComponentStorageEntry createComponentStorageEntryForCompressedConstructor();
+
+ template <typename AnnotatedT>
+ static ComponentStorageEntry createComponentStorageEntryForMultibindingVectorCreator();
+
+ template <typename AnnotatedI, typename AnnotatedC>
+ static ComponentStorageEntry createComponentStorageEntryForMultibinding();
+
+ template <typename AnnotatedC, typename C>
+ static ComponentStorageEntry createComponentStorageEntryForInstanceMultibinding(C& instance);
+
+ template <typename AnnotatedSignature, typename Lambda>
+ static ComponentStorageEntry createComponentStorageEntryForMultibindingProvider();
+
+private:
+ // The NormalizedComponentStorage owned by this object (if any).
+ // Only used for the 1-argument constructor, otherwise it's nullptr.
+ std::unique_ptr<NormalizedComponentStorage> normalized_component_storage_ptr;
+
+ FixedSizeAllocator allocator;
+
+ // A graph with injected types as nodes (each node stores the NormalizedBindingData for the type) and dependencies as
+ // edges.
+ // For types that have a constructed object already, the corresponding node is stored as terminal node.
+ SemistaticGraph<TypeId, NormalizedBinding> bindings;
+
+ // Maps the type index of a type T to the corresponding NormalizedMultibindingSet object (that stores all
+ // multibindings).
+ std::unordered_map<TypeId, NormalizedMultibindingSet> multibindings;
+
+ // This mutex is used to synchronize concurrent accesses to this InjectorStorage object.
+ std::recursive_mutex mutex;
+
+private:
+ template <typename AnnotatedC>
+ static std::shared_ptr<char> createMultibindingVector(InjectorStorage& storage);
+
+ // If not bound, returns nullptr.
+ NormalizedMultibindingSet* getNormalizedMultibindingSet(TypeId type);
+
+ // Looks up the location where the type is (or will be) stored, but does not construct the class.
+ template <typename AnnotatedC>
+ Graph::node_iterator lazyGetPtr();
+
+ // getPtr() is equivalent to getPtrInternal(lazyGetPtr())
+ template <typename C>
+ const C* getPtr(Graph::node_iterator itr);
+
+ // Similar to the previous, but takes a node_iterator. Use this when the node_iterator is known, it's faster.
+ const void* getPtrInternal(Graph::node_iterator itr);
+
+ // getPtr(typeInfo) is equivalent to getPtr(lazyGetPtr(typeInfo)).
+ Graph::node_iterator lazyGetPtr(TypeId type);
+
+ // getPtr(deps, index) is equivalent to getPtr(lazyGetPtr(deps, index)).
+ Graph::node_iterator lazyGetPtr(Graph::edge_iterator deps, std::size_t dep_index);
+
+ // Similar to getPtr, but the binding might not exist. Returns nullptr if it doesn't.
+ const void* unsafeGetPtr(TypeId type);
+
+ void* getPtrForMultibinding(TypeId type);
+
+ // Returns a std::vector<T*>*, or nullptr if there are no multibindings.
+ void* getMultibindings(TypeId type);
+
+ // Constructs any necessary instances, but NOT the instance set.
+ void ensureConstructedMultibinding(NormalizedMultibindingSet& multibinding_set);
+
+ template <typename T>
+ friend struct GetFirstStage;
+
+ template <typename T>
+ friend class fruit::Provider;
+
+ using object_ptr_t = void*;
+ using const_object_ptr_t = const void*;
+
+ template <typename I, typename C, typename AnnotatedC>
+ static const_object_ptr_t createInjectedObjectForBind(InjectorStorage& injector,
+ InjectorStorage::Graph::node_iterator node_itr);
+
+ template <typename C, typename T, typename AnnotatedSignature, typename Lambda>
+ static const_object_ptr_t createInjectedObjectForProvider(InjectorStorage& injector, Graph::node_iterator node_itr);
+
+ template <typename I, typename C, typename T, typename AnnotatedSignature, typename Lambda>
+ static const_object_ptr_t createInjectedObjectForCompressedProvider(InjectorStorage& injector,
+ Graph::node_iterator node_itr);
+
+ template <typename C, typename AnnotatedSignature>
+ static const_object_ptr_t createInjectedObjectForConstructor(InjectorStorage& injector,
+ Graph::node_iterator node_itr);
+
+ template <typename I, typename C, typename AnnotatedSignature>
+ static const_object_ptr_t createInjectedObjectForCompressedConstructor(InjectorStorage& injector,
+ Graph::node_iterator node_itr);
+
+ template <typename I, typename C, typename AnnotatedCPtr>
+ static object_ptr_t createInjectedObjectForMultibinding(InjectorStorage& m);
+
+ template <typename C, typename T, typename AnnotatedSignature, typename Lambda>
+ static object_ptr_t createInjectedObjectForMultibindingProvider(InjectorStorage& injector);
+
+public:
+ // Wraps a std::vector<ComponentStorageEntry>::iterator as an iterator on tuples
+ // (typeId, normalizedBindingData, isTerminal, edgesBegin, edgesEnd)
+ struct BindingDataNodeIter {
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>::iterator itr;
+
+ BindingDataNodeIter* operator->();
+
+ void operator++();
+
+ bool operator==(const BindingDataNodeIter& other) const;
+ bool operator!=(const BindingDataNodeIter& other) const;
+
+ std::ptrdiff_t operator-(BindingDataNodeIter other) const;
+
+ TypeId getId();
+ NormalizedBinding getValue();
+ bool isTerminal();
+ const TypeId* getEdgesBegin();
+ const TypeId* getEdgesEnd();
+ };
+
+ /**
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ InjectorStorage(ComponentStorage&& storage, const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ MemoryPool& memory_pool);
+
+ /**
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ InjectorStorage(const NormalizedComponentStorage& normalized_storage, ComponentStorage&& storage,
+ MemoryPool& memory_pool);
+
+ // This is just the default destructor, but we declare it here to avoid including
+ // normalized_component_storage.h in fruit.h.
+ ~InjectorStorage();
+
+ InjectorStorage(InjectorStorage&&) = delete;
+ InjectorStorage& operator=(InjectorStorage&&) = delete;
+
+ InjectorStorage(const InjectorStorage& other) = delete;
+ InjectorStorage& operator=(const InjectorStorage& other) = delete;
+
+ // Usually get<T>() returns a T.
+ // However, get<Annotated<Annotation1, T>>() returns a T, not an Annotated<Annotation1, T>.
+ template <typename AnnotatedT>
+ RemoveAnnotations<AnnotatedT> get();
+
+ // Similar to the above, but specifying the node_iterator of the type. Use this together with lazyGetPtr when the
+ // node_iterator is known, it's faster.
+ // Note that T should *not* be annotated.
+ template <typename T>
+ T get(InjectorStorage::Graph::node_iterator node_iterator);
+
+ // Looks up the location where the type is (or will be) stored, but does not construct the class.
+ // get<AnnotatedT>() is equivalent to get<AnnotatedT>(lazyGetPtr<Apply<NormalizeType, AnnotatedT>>(deps, dep_index))
+ // and also to get<T> (lazyGetPtr<Apply<NormalizeType, AnnotatedT>>(deps, dep_index))
+ // dep_index is the index of the dep in `deps'.
+ template <typename AnnotatedC>
+ Graph::node_iterator lazyGetPtr(Graph::edge_iterator deps, std::size_t dep_index,
+ Graph::node_iterator bindings_begin) const;
+
+ // Returns nullptr if AnnotatedC was not bound.
+ template <typename AnnotatedC>
+ const RemoveAnnotations<AnnotatedC>* unsafeGet();
+
+ template <typename AnnotatedC>
+ const std::vector<RemoveAnnotations<AnnotatedC>*>& getMultibindings();
+
+ void eagerlyInjectMultibindings();
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/injector/injector_storage.defn.h>
+
+#endif // FRUIT_INJECTOR_STORAGE_H
diff --git a/include/fruit/impl/meta/algos.h b/include/fruit/impl/meta/algos.h
new file mode 100644
index 0000000..a5a1996
--- /dev/null
+++ b/include/fruit/impl/meta/algos.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_ALGOS_H
+#define FRUIT_META_ALGOS_H
+
+#include <fruit/impl/fruit-config.h>
+#include <fruit/impl/meta/immutable_map.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// We need a different (slower) implementation to workaround a Clang bug:
+// https://llvm.org/bugs/show_bug.cgi?id=25669
+// TODO: remove this once that bug is fixed (for the appropriate Clang versions).
+#if FRUIT_HAS_CLANG_ARBITRARY_OVERLOAD_RESOLUTION_BUG
+
+struct HasDuplicatesHelper {
+ template <typename... Types>
+ struct apply {
+ using type = Bool<false>;
+ };
+
+ template <typename Type, typename... Types>
+ struct apply<Type, Types...> {
+ using type = Or(StaticOr<std::is_same<Type, Types>::value...>, Id<HasDuplicatesHelper(Types...)>);
+ };
+};
+
+struct HasDuplicates {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Types...>> {
+ using type = HasDuplicatesHelper(Types...);
+ };
+};
+
+#else // !FRUIT_HAS_CLANG_ARBITRARY_OVERLOAD_RESOLUTION_BUG
+
+// Checks if the given Vector has duplicated types.
+struct HasDuplicates {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Types...>> {
+ using M = VectorsToImmutableMap(Vector<Types...>, GenerateIntSequence(Int<sizeof...(Types)>));
+ using type = Not(StaticAnd<Eval<ImmutableMapContainsKey(M, Types)>::value...>);
+ };
+};
+
+#endif // FRUIT_HAS_CLANG_ARBITRARY_OVERLOAD_RESOLUTION_BUG
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_ALGOS_H
diff --git a/include/fruit/impl/meta/basics.h b/include/fruit/impl/meta/basics.h
new file mode 100644
index 0000000..8cb4604
--- /dev/null
+++ b/include/fruit/impl/meta/basics.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_BASICS_H
+#define FRUIT_META_BASICS_H
+
+#include <functional>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+template <typename T>
+struct Type {
+ using type = T;
+};
+
+template <bool b>
+struct Bool {
+ static constexpr bool value = b;
+};
+
+template <int n>
+struct Int {
+ static constexpr int value = n;
+};
+
+// This was added to workaround a bug in MSVC 2017 15.5, that crashes when expanding Indexes::value... in some cases
+// (where Indexes is a template parameter pack of Int<...> types).
+// TODO: Remove this once MSVC 2017 is fixed and the fix has been out for some time.
+template <typename N>
+constexpr int getIntValue() {
+ return N::value;
+}
+
+// None is used as "the nullptr of metaprogramming". E.g. when a function has no meaningful value to
+// return, it can return None instead.
+struct None {};
+
+struct If {};
+
+// PropagateError(E, X) evaluates E then X. The result is X's result, but if E returns an error,
+// that's the result instead.
+struct PropagateError {};
+
+// Used to propagate an ErrorTag::apply<ErrorArgs...> up the instantiation chain, but without instantiating it right
+// away, to allow shorter error stacktraces.
+// Instantiating ErrorTag::apply<ErrorArgs...> must result in a static_assert error.
+template <typename ErrorTag, typename... ErrorArgs>
+struct Error {};
+
+// Use as Catch(ExpressionThatMightThrow, ErrorTag, Handler)
+// Handler(Error<ErrorTag, ErrorArgs...>) is called if ExpressionThatMightThrow throws ErrorTag.
+struct Catch {};
+
+// Use as CatchAll(ExpressionThatMightThrow, Handler)
+// Handler(Error<ErrorTag, ErrorArgs...>) is called if ExpressionThatMightThrow throws any error.
+struct CatchAll {};
+
+// Call(F, Args...) is equivalent to F(Args...) in a metaexpression, except that Call(F, Args...)
+// also works when F is a metaexpression.
+struct Call {
+ template <typename F, typename... Args>
+ struct apply : public F::template apply<Args...> {};
+};
+
+// UnwrapType<Type<T>> is T.
+template <typename WrappedType>
+using UnwrapType = typename WrappedType::type;
+
+// MSVC 14 has trouble specializing alias templates using expanded pack elements.
+// This is a known issue:
+// https://stackoverflow.com/questions/43411542/metaprogramming-failed-to-specialize-alias-template
+// The workaround is just to use a struct directly.
+// typename TypeUnwrapper<Type<T>>::type is T.
+template <typename WrappedType>
+struct TypeUnwrapper {
+ using type = UnwrapType<WrappedType>;
+};
+
+// Logical And with short-circuit evaluation.
+struct And {
+ template <typename... MetaExprs>
+ struct apply {
+ using type = Bool<true>;
+ };
+
+ template <typename MetaExpr>
+ struct apply<MetaExpr> {
+ using type = MetaExpr;
+ };
+
+ template <typename MetaExpr, typename MetaExpr2>
+ struct apply<MetaExpr, MetaExpr2> {
+ using type = If(MetaExpr, MetaExpr2, Bool<false>);
+ };
+
+ template <typename MetaExpr, typename MetaExpr2, typename... MetaExprs>
+ struct apply<MetaExpr, MetaExpr2, MetaExprs...> {
+ using type = If(MetaExpr, If(MetaExpr2, And(MetaExprs...), Bool<false>), Bool<false>);
+ };
+};
+
+// Logical Or with short-circuit evaluation.
+struct Or {
+ template <typename... MetaExprs>
+ struct apply {
+ using type = Bool<false>;
+ };
+
+ template <typename MetaExpr>
+ struct apply<MetaExpr> {
+ using type = MetaExpr;
+ };
+
+ template <typename MetaExpr, typename MetaExpr2>
+ struct apply<MetaExpr, MetaExpr2> {
+ using type = If(MetaExpr, Bool<true>, MetaExpr2);
+ };
+
+ template <typename MetaExpr, typename MetaExpr2, typename... MetaExprs>
+ struct apply<MetaExpr, MetaExpr2, MetaExprs...> {
+ using type = If(MetaExpr, Bool<true>, If(MetaExpr2, Bool<true>, Or(MetaExprs...)));
+ };
+};
+
+// Call(Call(DeferArgs(F), Args...), MoreArgs...)
+//
+// is equivalent to:
+// Result = F(Args..., MoreArgs...)
+//
+// Note that you can't write:
+// DeferArgs(F)(Args...)(MoreArgs...)
+//
+// Because Call must be used to call metafunctions that are metaexpressions.
+struct DeferArgs {
+ template <typename F>
+ struct apply {
+ struct type {
+ template <typename... Args>
+ struct apply {
+ struct type {
+ template <typename... MoreArgs>
+ struct apply {
+ using type = F(Args..., MoreArgs...);
+ };
+ };
+ };
+ };
+ };
+};
+
+// Call(PartialCall(F, Args...), MoreArgs...)
+//
+// is equivalent to:
+// Result = F(Args..., MoreArgs...)
+//
+// Note that you can't write:
+// PartialCall(F, Args...)(MoreArgs...)
+//
+// Because Call must be used to call metafunctions that are metaexpressions.
+struct PartialCall {
+ template <typename F, typename... Args>
+ struct apply {
+ struct type {
+ template <typename... MoreArgs>
+ struct apply {
+ using type = F(Args..., MoreArgs...);
+ };
+ };
+ };
+};
+
+struct IsSame {
+ template <typename T, typename U>
+ struct apply {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<T, T> {
+ using type = Bool<true>;
+ };
+};
+
+struct Not {
+ template <typename B>
+ struct apply {
+ using type = Bool<!B::value>;
+ };
+};
+
+struct IsNone {
+ template <typename T>
+ struct apply {
+ using type = Bool<false>;
+ };
+};
+
+template <>
+struct IsNone::apply<None> {
+ using type = Bool<true>;
+};
+
+template <typename T>
+using Id = T;
+
+struct Identity {
+ template <typename T>
+ struct apply {
+ using type = T;
+ };
+};
+
+template <typename T>
+struct DebugTypeHelper {
+ static_assert(sizeof(T*) * 0 != 0, "");
+ using type = T;
+};
+
+template <typename T>
+using DebugType = typename DebugTypeHelper<T>::type;
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_BASICS_H
diff --git a/include/fruit/impl/meta/component.h b/include/fruit/impl/meta/component.h
new file mode 100644
index 0000000..54aaf40
--- /dev/null
+++ b/include/fruit/impl/meta/component.h
@@ -0,0 +1,1012 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_COMPONENT_H
+#define FRUIT_META_COMPONENT_H
+
+#include <fruit/fruit_forward_decls.h>
+#include <fruit/impl/fruit_internal_forward_decls.h>
+#include <fruit/impl/injection_debug_errors.h>
+#include <fruit/impl/meta/algos.h>
+#include <fruit/impl/meta/errors.h>
+#include <fruit/impl/meta/list.h>
+#include <fruit/impl/meta/map.h>
+#include <fruit/impl/meta/metaprogramming.h>
+#include <fruit/impl/meta/numeric_operations.h>
+#include <fruit/impl/meta/proof_trees.h>
+#include <fruit/impl/meta/set.h>
+#include <fruit/impl/meta/signatures.h>
+#include <fruit/impl/meta/wrappers.h>
+
+#include <memory>
+#include <type_traits>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+//********************************************************************************************************************************
+// Part 1: Simple type functors (no ConsComp involved).
+//********************************************************************************************************************************
+
+// Given a type T, returns the class that should be injected to ensure that T is provided at runtime (if any).
+struct GetClassForType {
+ // General case, if none of the following apply.
+ // When adding a specialization here, make sure that the ComponentStorage
+ // can actually get<> the specified type when the class was registered.
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<T*>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<T&>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T*>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T&>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<std::shared_ptr<T>>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<Assisted<T>>> {
+ using type = None;
+ };
+
+ template <typename T>
+ struct apply<Type<Provider<T>>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<Provider<const T>>> {
+ using type = Type<T>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, T>>> {
+ using type = Type<T>;
+ };
+};
+
+struct GetClassForTypeVector {
+ template <typename V>
+ struct apply {
+ using type = TransformVector(V, GetClassForType);
+ };
+};
+
+// Given a type T, returns the type in the injection graph that corresponds to T.
+struct NormalizeType {
+ // When adding a specialization here, make sure that the ComponentStorage
+ // can actually get<> the specified type when the class was registered.
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<T*>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<T&>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T*>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T&>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<std::shared_ptr<T>>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<Assisted<T>>> {
+ using type = None;
+ };
+
+ template <typename T>
+ struct apply<Type<Provider<T>>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<Provider<const T>>> {
+ using type = Type<T>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, T>>> {
+ using type = Type<fruit::Annotated<Annotation, UnwrapType<Eval<NormalizeType(Type<T>)>>>>;
+ };
+};
+
+struct NormalizeUntilStable {
+ template <typename T>
+ struct apply {
+ using type = If(IsSame(NormalizeType(T), T), T, NormalizeUntilStable(NormalizeType(T)));
+ };
+};
+
+struct NormalizeTypeVector {
+ template <typename V>
+ struct apply {
+ using type = TransformVector(V, NormalizeType);
+ };
+};
+
+struct TypeInjectionRequiresNonConstBinding {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T>> {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<T*>> {
+ using type = Bool<true>;
+ };
+
+ template <typename T>
+ struct apply<Type<T&>> {
+ using type = Bool<true>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T*>> {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T&>> {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<std::shared_ptr<T>>> {
+ using type = Bool<true>;
+ };
+
+ template <typename T>
+ struct apply<Type<Assisted<T>>> {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<Provider<T>>> {
+ using type = Bool<true>;
+ };
+
+ template <typename T>
+ struct apply<Type<Provider<const T>>> {
+ using type = Bool<false>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, T>>> {
+ using type = TypeInjectionRequiresNonConstBinding(Type<T>);
+ };
+};
+
+// Returns U wrapped in the same annotations in AnnotatedT (if any).
+struct CopyAnnotation {
+ template <typename AnnotatedT, typename U>
+ struct apply;
+
+ template <typename T, typename U>
+ struct apply {
+ using type = U;
+ };
+
+ template <typename Annotation, typename T, typename U>
+ struct apply<Type<fruit::Annotated<Annotation, T>>, Type<U>> {
+ using type = Type<fruit::Annotated<Annotation, U>>;
+ };
+};
+
+struct IsValidSignature {
+ template <typename Signature>
+ struct apply {
+ using type = Bool<false>;
+ };
+
+ template <typename T, typename... Args>
+ struct apply<Type<T(Args...)>> {
+ using type = Bool<true>;
+ };
+};
+
+// Removes the Annotation (if any) wrapping a type T.
+struct RemoveAnnotations {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, T>>> {
+ using type = Type<T>;
+ };
+};
+
+// Removes the Annotation(s) (if any) wrapping the types in AnnotatedSignature.
+struct RemoveAnnotationsFromSignature {
+ template <typename AnnotatedSignature>
+ struct apply {
+ using type = ConstructError(NotASignatureErrorTag, AnnotatedSignature);
+ };
+
+ template <typename AnnotatedT, typename... AnnotatedArgs>
+ struct apply<Type<AnnotatedT(AnnotatedArgs...)>> {
+ using type = ConsSignature(RemoveAnnotations(Type<AnnotatedT>), Id<RemoveAnnotations(Type<AnnotatedArgs>)>...);
+ };
+};
+
+// Removes the Annotation(s) (if any) wrapping the types in the Vector V.
+struct RemoveAnnotationsFromVector {
+ template <typename V>
+ struct apply {
+ using type = TransformVector(V, RemoveAnnotations);
+ };
+};
+
+// Maps T->T* in a possibly-annotated type.
+struct AddPointerInAnnotatedType {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T*>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, T>>> {
+ using type = Type<fruit::Annotated<Annotation, T*>>;
+ };
+};
+
+// TODO: This also does UnlabelAssisted<>. Consider renaming and/or removing that logic (and
+// letting callers do the unlabeling when desired).
+struct RemoveNonAssisted {
+ template <typename V>
+ struct apply {
+ struct Helper {
+ // Non-assisted case
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = CurrentResult;
+ };
+
+ template <typename CurrentResult, typename T>
+ struct apply<CurrentResult, Type<Assisted<T>>> {
+ using type = PushBack(CurrentResult, Type<T>);
+ };
+ };
+
+ using type = FoldVector(V, Helper, Vector<>);
+ };
+};
+
+struct RemoveAssisted {
+ template <typename V>
+ struct apply {
+ struct Helper {
+ // Non-assisted case
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = PushBack(CurrentResult, T);
+ };
+
+ // Assisted case
+ template <typename CurrentResult, typename T>
+ struct apply<CurrentResult, Type<Assisted<T>>> {
+ using type = CurrentResult;
+ };
+ };
+
+ using type = FoldVector(V, Helper, Vector<>);
+ };
+};
+
+struct UnlabelAssistedSingleType {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<Assisted<T>>> {
+ using type = Type<T>;
+ };
+};
+
+struct UnlabelAssisted {
+ template <typename V>
+ struct apply {
+ using type = TransformVector(V, UnlabelAssistedSingleType);
+ };
+};
+
+struct RequiredLambdaArgsForAssistedFactory {
+ template <typename AnnotatedSignature>
+ struct apply {
+ using type = RemoveAnnotationsFromVector(UnlabelAssisted(SignatureArgs(AnnotatedSignature)));
+ };
+};
+
+struct RequiredLambdaSignatureForAssistedFactory {
+ template <typename AnnotatedSignature>
+ struct apply {
+ using type = ConsSignatureWithVector(RemoveAnnotations(SignatureType(AnnotatedSignature)),
+ RequiredLambdaArgsForAssistedFactory(AnnotatedSignature));
+ };
+};
+
+struct InjectedFunctionArgsForAssistedFactory {
+ template <typename AnnotatedSignature>
+ struct apply {
+ using type = RemoveNonAssisted(SignatureArgs(AnnotatedSignature));
+ };
+};
+
+struct InjectedSignatureForAssistedFactory {
+ template <typename AnnotatedSignature>
+ struct apply {
+ using type = ConsSignatureWithVector(RemoveAnnotations(SignatureType(AnnotatedSignature)),
+ InjectedFunctionArgsForAssistedFactory(AnnotatedSignature));
+ };
+};
+
+struct IsAssisted {
+ template <typename T>
+ struct apply {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<Assisted<T>>> {
+ using type = Bool<true>;
+ };
+};
+
+struct NumAssisted {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Types...>> {
+ using type = SumAll(typename IsAssisted::apply<Types>::type...);
+ };
+};
+
+// Counts the number of Assisted<> types in V before the given index.
+struct NumAssistedBefore {
+ template <typename Index, typename V>
+ struct apply;
+
+ template <typename V>
+ struct apply<Int<0>, V> {
+ using type = Int<0>;
+ };
+
+ template <int n, typename V>
+ struct apply<Int<n>, V> {
+ using N = Int<n>;
+ using type = Minus(NumAssisted(V), NumAssisted(VectorRemoveFirstN(V, N)));
+ };
+};
+
+// Checks whether C is auto-injectable thanks to an Inject typedef.
+struct HasInjectAnnotation {
+ template <typename C>
+ struct apply;
+
+ template <typename C>
+ struct apply<Type<C>> {
+ template <typename C1>
+ static Bool<true> test(typename C1::Inject*);
+
+ template <typename>
+ static Bool<false> test(...);
+
+ using type = decltype(test<C>(nullptr));
+ };
+};
+
+struct DoGetInjectAnnotation {
+ template <typename C>
+ struct apply;
+
+ template <typename C>
+ struct apply<Type<C>> {
+ using type = Type<typename C::Inject>;
+ };
+};
+
+struct GetInjectAnnotation {
+ template <typename AnnotatedC>
+ struct apply {
+ using C = RemoveAnnotations(AnnotatedC);
+ using DecoratedS = DoGetInjectAnnotation(C);
+ using SResult = SignatureType(DecoratedS);
+ using AnnotatedSArgs = SignatureArgs(DecoratedS);
+ using SArgs = RemoveAnnotationsFromVector(UnlabelAssisted(AnnotatedSArgs));
+ // We replace the non-annotated return type with the potentially-annotated AnnotatedC.
+ using AnnotatedDecoratedS = ConsSignatureWithVector(AnnotatedC, AnnotatedSArgs);
+ using type = If(IsAbstract(C), ConstructError(CannotConstructAbstractClassErrorTag, C),
+ If(Not(IsValidSignature(DecoratedS)),
+ ConstructError(InjectTypedefNotASignatureErrorTag, C, DecoratedS),
+ If(Not(IsSame(SResult, RemoveAnnotations(SResult))),
+ ConstructError(InjectTypedefWithAnnotationErrorTag, C),
+ If(Not(IsSame(C, SResult)), ConstructError(InjectTypedefForWrongClassErrorTag, C, SResult),
+ If(Not(IsConstructibleWithVector(C, SArgs)),
+ ConstructError(NoConstructorMatchingInjectSignatureErrorTag, C,
+ ConsSignatureWithVector(SResult, SArgs)),
+ AnnotatedDecoratedS)))));
+ };
+};
+
+//********************************************************************************************************************************
+// Part 2: Type functors involving at least one ConsComp.
+//********************************************************************************************************************************
+
+template <typename RsSupersetParam, typename PsParam, typename NonConstRsPsParam,
+#ifndef FRUIT_NO_LOOP_CHECK
+ typename DepsParam,
+#endif
+ typename InterfaceBindingsParam, typename DeferredBindingFunctorsParam>
+struct Comp {
+ // The actual set of requirements is SetDifference(RsSuperset, Ps)
+ // We don't store Rs explicitly because we'd need to remove elements very often (and that's slow).
+ using RsSuperset = RsSupersetParam;
+
+ using Ps = PsParam;
+ // This is a set of normalized types.
+ // - If a type is in SetDifference(RsSuperset, Ps) and not here: it's required as const only
+ // - If a type is in SetDifference(RsSuperset, Ps) and also here: it's required as non-const
+ // - If a type is in Ps and not here: it's provided as const only
+ // - If a type is in Ps and also here: it's provided as non-const
+ using NonConstRsPs = NonConstRsPsParam;
+#ifndef FRUIT_NO_LOOP_CHECK
+ using Deps = DepsParam;
+#endif
+ using InterfaceBindings = InterfaceBindingsParam;
+ using DeferredBindingFunctors = DeferredBindingFunctorsParam;
+
+ // Invariants:
+ // * all types appearing as arguments of Deps are in Rs
+ // * all types in Ps are at the head of one (and only one) Dep.
+ // (note that the types in Rs can appear in deps any number of times, 0 is also ok)
+ // * Deps is of the form Vector<Dep...> with each Dep of the form T(Args...) and where Vector<Args...> is a set (no
+ // repetitions).
+ // * Bindings is a proof tree forest, with injected classes as formulas.
+ // * Each element X of the list DeferredBindingFunctors has:
+ // - a default-constructible X::apply<Comp> type
+ // - a void X::apply<Comp>::operator(ComponentStorage&)
+ // - an X::apply<Comp>::Result type
+ // * Each element of NonConstRsPs is in RsSuperset or in Ps (or both)
+};
+
+// Using ConsComp instead of Comp<...> in a meta-expression allows the types to be evaluated.
+// See ConsVector for more details.
+struct ConsComp {
+ template <typename RsSupersetParam, typename PsParam, typename NonConstRsPsParam,
+#ifndef FRUIT_NO_LOOP_CHECK
+ typename DepsParam,
+#endif
+ typename InterfaceBindingsParam, typename DeferredBindingFunctorsParam>
+ struct apply {
+ using type = Comp<RsSupersetParam, PsParam, NonConstRsPsParam,
+#ifndef FRUIT_NO_LOOP_CHECK
+ DepsParam,
+#endif
+ InterfaceBindingsParam, DeferredBindingFunctorsParam>;
+ };
+};
+
+struct GetComponentDeps {
+ template <typename Comp>
+ struct apply {
+ using type = typename Comp::Deps;
+ };
+};
+
+struct GetComponentPs {
+ template <typename Comp>
+ struct apply {
+ using type = typename Comp::Ps;
+ };
+};
+
+struct GetComponentRsSuperset {
+ template <typename Comp>
+ struct apply {
+ using type = typename Comp::RsSuperset;
+ };
+};
+
+struct GetComponentNonConstRsPs {
+ template <typename Comp>
+ struct apply {
+ using type = typename Comp::NonConstRsPs;
+ };
+};
+
+struct IsInjectableBareType {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Bool<std::is_arithmetic<T>::value || std::is_class<T>::value || std::is_enum<T>::value>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, T>>> {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<std::shared_ptr<T>>> {
+ using type = Bool<false>;
+ };
+};
+
+// Checks if T is a (non-annotated) injectable type.
+struct IsInjectableType {
+ template <typename T>
+ struct apply {
+ using type = IsInjectableBareType(NormalizeType(T));
+ };
+};
+
+// Checks that T is a (non-annotated) injectable type. If it isn't this returns an error, otherwise it returns None.
+struct CheckInjectableType {
+ template <typename T>
+ struct apply {
+ using type = If(Not(IsInjectableType(T)), ConstructError(NonInjectableTypeErrorTag, T), None);
+ };
+};
+
+// Checks that Types... are (non-annotated) injectable types. If they have an annotation or they are not injectable it
+// an appropriate error is returned.
+// Otherwise this returns None.
+struct CheckInjectableTypeVector {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = PropagateError(CheckInjectableType(T), CurrentResult);
+ };
+ };
+
+ template <typename V>
+ struct apply {
+ using type = FoldVector(V, Helper, None);
+ };
+};
+
+// Checks that Types... are normalized and injectable types. If not it returns an appropriate error.
+// If they are all normalized types this returns Result.
+struct CheckNormalizedTypes {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Type<Types>...>> {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using NormalizedType = NormalizeType(T);
+ using type = PropagateError(CheckInjectableType(RemoveAnnotations(NormalizeUntilStable(T))),
+ If(Not(IsSame(NormalizeType(T), T)),
+ ConstructError(NonClassTypeErrorTag, RemoveAnnotations(T),
+ RemoveAnnotations(NormalizeUntilStable(T))),
+ CurrentResult));
+ };
+ };
+
+ using type = Fold(Helper, None, Type<Types>...);
+ };
+};
+
+// Checks that Types... are not annotated types. If they have an annotation it returns an appropriate error.
+// If none of them is annotated, this returns None.
+struct CheckNotAnnotatedTypes {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Type<Types>...>> {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using TypeWithoutAnnotations = RemoveAnnotations(T);
+ using type = If(Not(IsSame(TypeWithoutAnnotations, T)),
+ ConstructError(AnnotatedTypeErrorTag, T, TypeWithoutAnnotations), CurrentResult);
+ };
+ };
+
+ using type = Fold(Helper, None, Type<Types>...);
+ };
+};
+
+// Check that there are no fruit::Required<> types in Component/NormalizedComponent's arguments.
+// If there aren't any, this returns None.
+struct CheckNoRequiredTypesInComponentArguments {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Types...>> {
+ using type = None;
+ };
+
+ template <typename T, typename... OtherTypes>
+ struct apply<Vector<Type<T>, OtherTypes...>> {
+ using type = CheckNoRequiredTypesInComponentArguments(Vector<OtherTypes...>);
+ };
+
+ template <typename... RequiredArgs, typename... OtherTypes>
+ struct apply<Vector<Type<fruit::Required<RequiredArgs...>>, OtherTypes...>> {
+ using type = ConstructError(RequiredTypesInComponentArgumentsErrorTag, Type<fruit::Required<RequiredArgs...>>);
+ };
+};
+
+// Check that there are no fruit::Required<> types in Injector's arguments.
+// If there aren't any, this returns None.
+struct CheckNoRequiredTypesInInjectorArguments {
+ template <typename... Types>
+ struct apply {
+ using type = None;
+ };
+
+ template <typename T, typename... Types>
+ struct apply<T, Types...> {
+ using type = CheckNoRequiredTypesInInjectorArguments(Types...);
+ };
+
+ template <typename... RequiredArgs, typename... Types>
+ struct apply<Type<fruit::Required<RequiredArgs...>>, Types...> {
+ using type = ConstructError(InjectorWithRequirementsErrorTag, Type<RequiredArgs>...);
+ };
+};
+
+// Checks that there are no repetitions in Types. If there are, it returns an appropriate error.
+// If there are no repetitions it returns None.
+struct CheckNoRepeatedTypes {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Types...>> {
+ using type = If(HasDuplicates(Vector<Types...>), ConstructError(RepeatedTypesErrorTag, Types...), None);
+ };
+};
+
+struct RemoveConstFromType {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T>;
+ };
+
+ template <typename T>
+ struct apply<Type<const T>> {
+ using type = Type<T>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, T>>> {
+ using type = Type<fruit::Annotated<Annotation, T>>;
+ };
+
+ template <typename Annotation, typename T>
+ struct apply<Type<fruit::Annotated<Annotation, const T>>> {
+ using type = Type<fruit::Annotated<Annotation, T>>;
+ };
+};
+
+struct RemoveConstFromTypes {
+ template <typename V>
+ struct apply;
+
+ template <typename... Types>
+ struct apply<Vector<Types...>> {
+ using type = ConsVector(Id<RemoveConstFromType(Types)>...);
+ };
+};
+
+struct RemoveConstTypes {
+ struct Helper {
+ template <typename Acc, typename T>
+ struct apply;
+
+ template <typename... AccContent, typename T>
+ struct apply<Vector<AccContent...>, Type<const T>> {
+ using type = Vector<AccContent...>;
+ };
+
+ template <typename... AccContent, typename T>
+ struct apply<Vector<AccContent...>, Type<T>> {
+ using type = Vector<AccContent..., Type<T>>;
+ };
+
+ template <typename... AccContent, typename Annotation, typename T>
+ struct apply<Vector<AccContent...>, Type<fruit::Annotated<Annotation, const T>>> {
+ using type = Vector<AccContent...>;
+ };
+
+ template <typename... AccContent, typename Annotation, typename T>
+ struct apply<Vector<AccContent...>, Type<fruit::Annotated<Annotation, T>>> {
+ using type = Vector<AccContent..., Type<fruit::Annotated<Annotation, T>>>;
+ };
+ };
+
+ template <typename V>
+ struct apply {
+ using type = FoldVector(V, Helper, Vector<>);
+ };
+};
+
+// From a vector of injected types, this filters out the types that only require const bindings and then normalizes
+// the types in the result.
+struct NormalizedNonConstTypesIn {
+ struct Helper {
+ template <typename Acc, typename T>
+ struct apply {
+ using type = If(TypeInjectionRequiresNonConstBinding(T), PushBack(Acc, NormalizeType(T)), Acc);
+ };
+ };
+
+ template <typename V>
+ struct apply {
+ using type = FoldVector(V, Helper, Vector<>);
+ };
+};
+
+struct ConstructComponentImpl {
+ // Non-specialized case: no requirements.
+ template <typename... Ps>
+ struct apply {
+ using type = PropagateError(
+ CheckNoRepeatedTypes(RemoveConstFromTypes(Vector<Ps...>)),
+ PropagateError(CheckNormalizedTypes(RemoveConstFromTypes(Vector<Ps...>)),
+ PropagateError(CheckNoRequiredTypesInComponentArguments(Vector<Ps...>),
+ ConsComp(EmptySet, VectorToSetUnchecked(RemoveConstFromTypes(Vector<Ps...>)),
+ RemoveConstTypes(Vector<Ps...>),
+#ifndef FRUIT_NO_LOOP_CHECK
+ Vector<Pair<Ps, Vector<>>...>,
+#endif
+ Vector<>, EmptyList))));
+ };
+
+ // With requirements.
+ template <typename... Rs, typename... Ps>
+ struct apply<Type<Required<Rs...>>, Ps...> {
+ using type1 = PropagateError(
+ CheckNoRepeatedTypes(RemoveConstFromTypes(Vector<Type<Rs>..., Ps...>)),
+ PropagateError(CheckNormalizedTypes(RemoveConstFromTypes(Vector<Type<Rs>..., Ps...>)),
+ PropagateError(CheckNoRequiredTypesInComponentArguments(Vector<Ps...>),
+ ConsComp(VectorToSetUnchecked(RemoveConstFromTypes(Vector<Type<Rs>...>)),
+ VectorToSetUnchecked(RemoveConstFromTypes(Vector<Ps...>)),
+ RemoveConstTypes(Vector<Type<Rs>..., Ps...>),
+#ifndef FRUIT_NO_LOOP_CHECK
+ Vector<Pair<Ps, Vector<Type<Rs>...>>...>,
+#endif
+ Vector<>, EmptyList))));
+
+#if !defined(FRUIT_NO_LOOP_CHECK) && defined(FRUIT_EXTRA_DEBUG)
+ using Loop = ProofForestFindLoop(GetComponentDeps(type1));
+ using type = If(IsNone(Loop), type1, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop));
+#else // defined(FRUIT_NO_LOOP_CHECK) || !defined(FRUIT_EXTRA_DEBUG)
+ using type = type1;
+#endif // defined(FRUIT_NO_LOOP_CHECK) || !defined(FRUIT_EXTRA_DEBUG)
+ };
+};
+
+struct CheckTypesNotProvidedAsConst {
+ template <typename Comp, typename V>
+ struct apply {
+ struct Helper {
+ template <typename Acc, typename T>
+ struct apply {
+ using type = If(And(IsInSet(T, typename Comp::Ps), Not(IsInSet(T, typename Comp::NonConstRsPs))),
+ ConstructError(NonConstBindingRequiredButConstBindingProvidedErrorTag, T), Acc);
+ };
+ };
+
+ using type = FoldVector(V, Helper, None);
+ };
+};
+
+// Adds the types in NewRequirementsVector to the requirements (unless they are already provided/required).
+// The caller must convert the types to the corresponding class type and expand any Provider<>s.
+struct AddRequirements {
+ template <typename Comp, typename NewRequirementsVector, typename NewNonConstRequirementsVector>
+ struct apply {
+ using Comp1 = ConsComp(FoldVector(NewRequirementsVector, AddToSet, typename Comp::RsSuperset), typename Comp::Ps,
+ FoldVector(NewNonConstRequirementsVector, AddToSet, typename Comp::NonConstRsPs),
+#ifndef FRUIT_NO_LOOP_CHECK
+ typename Comp::Deps,
+#endif
+ typename Comp::InterfaceBindings, typename Comp::DeferredBindingFunctors);
+ using type = PropagateError(CheckTypesNotProvidedAsConst(Comp, NewNonConstRequirementsVector), Comp1);
+ };
+};
+
+// Similar to AddProvidedType, but doesn't report an error if a Bind<C, CImpl> was present.
+struct AddProvidedTypeIgnoringInterfaceBindings {
+ template <typename Comp, typename C, typename IsNonConst, typename CRequirements, typename CNonConstRequirements>
+ struct apply {
+ using Comp1 = ConsComp(
+ FoldVector(CRequirements, AddToSet, typename Comp::RsSuperset), AddToSetUnchecked(typename Comp::Ps, C),
+ If(IsNonConst, AddToSetUnchecked(FoldVector(CNonConstRequirements, AddToSet, typename Comp::NonConstRsPs), C),
+ FoldVector(CNonConstRequirements, AddToSet, typename Comp::NonConstRsPs)),
+#ifndef FRUIT_NO_LOOP_CHECK
+ PushFront(typename Comp::Deps, Pair<C, CRequirements>),
+#endif
+ typename Comp::InterfaceBindings, typename Comp::DeferredBindingFunctors);
+ using type = If(IsInSet(C, typename Comp::Ps), ConstructError(TypeAlreadyBoundErrorTag, C),
+ PropagateError(CheckTypesNotProvidedAsConst(Comp, CNonConstRequirements), Comp1));
+ };
+};
+
+// Adds C to the provides and removes it from the requirements (if it was there at all).
+// Also checks that it wasn't already provided.
+// Moreover, adds the requirements of C to the requirements, unless they were already provided/required.
+// The caller must convert the types to the corresponding class type and expand any Provider<>s.
+struct AddProvidedType {
+ template <typename Comp, typename C, typename IsNonConst, typename CRequirements, typename CNonConstRequirements>
+ struct apply {
+ using type = If(Not(IsNone(FindInMap(typename Comp::InterfaceBindings, C))),
+ ConstructError(TypeAlreadyBoundErrorTag, C),
+ AddProvidedTypeIgnoringInterfaceBindings(Comp, C, IsNonConst, CRequirements,
+ CNonConstRequirements));
+ };
+};
+
+struct AddDeferredBinding {
+ template <typename Comp, typename DeferredBinding>
+ struct apply {
+ using new_DeferredBindingFunctors = Cons<DeferredBinding, typename Comp::DeferredBindingFunctors>;
+ using type = ConsComp(typename Comp::RsSuperset, typename Comp::Ps, typename Comp::NonConstRsPs,
+#ifndef FRUIT_NO_LOOP_CHECK
+ typename Comp::Deps,
+#endif
+ typename Comp::InterfaceBindings, new_DeferredBindingFunctors);
+ };
+};
+
+struct CheckNoLoopInDeps {
+ template <typename Comp>
+ struct apply {
+ using Loop = ProofForestFindLoop(typename Comp::Deps);
+ using type = If(IsNone(Loop), Bool<true>, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop));
+ };
+};
+
+#if defined(FRUIT_EXTRA_DEBUG) || defined(FRUIT_IN_META_TEST)
+struct CheckComponentEntails {
+ template <typename Comp, typename EntailedComp>
+ struct apply {
+ using CompRs = SetDifference(typename Comp::RsSuperset, typename Comp::Ps);
+ using EntailedCompRs = SetDifference(typename EntailedComp::RsSuperset, typename EntailedComp::Ps);
+ using CommonRs = SetIntersection(CompRs, EntailedCompRs);
+ using CommonPs = SetIntersection(typename Comp::Ps, typename EntailedComp::Ps);
+ using type =
+ If(Not(IsContained(typename EntailedComp::Ps, typename Comp::Ps)),
+ ConstructErrorWithArgVector(ComponentDoesNotEntailDueToProvidesErrorTag,
+ SetToVector(SetDifference(typename EntailedComp::Ps, typename Comp::Ps))),
+ If(Not(IsVectorContained(typename EntailedComp::InterfaceBindings, typename Comp::InterfaceBindings)),
+ ConstructErrorWithArgVector(ComponentDoesNotEntailDueToInterfaceBindingsErrorTag,
+ SetToVector(SetDifference(typename EntailedComp::InterfaceBindings,
+ typename Comp::InterfaceBindings))),
+ If(Not(IsContained(CompRs, EntailedCompRs)),
+ ConstructErrorWithArgVector(ComponentDoesNotEntailDueToRequirementsErrorTag,
+ SetToVector(SetDifference(CompRs, EntailedCompRs))),
+ If(Not(IsContained(SetIntersection(CommonRs, typename Comp::NonConstRsPs),
+ typename EntailedComp::NonConstRsPs)),
+ ConstructErrorWithArgVector(ComponentDoesNotEntailDueToDifferentConstnessOfRequirementsErrorTag,
+ SetToVector(SetDifference(SetIntersection(CommonRs,
+ typename Comp::NonConstRsPs),
+ typename EntailedComp::NonConstRsPs))),
+ If(Not(IsContained(SetIntersection(CommonPs, typename EntailedComp::NonConstRsPs),
+ typename Comp::NonConstRsPs)),
+ ConstructErrorWithArgVector(
+ ComponentDoesNotEntailDueToDifferentConstnessOfProvidesErrorTag,
+ SetToVector(SetDifference(SetIntersection(CommonPs, typename EntailedComp::NonConstRsPs),
+ typename Comp::NonConstRsPs))),
+ Bool<true>)))));
+ static_assert(true || sizeof(typename CheckIfError<Eval<type>>::type), "");
+ };
+};
+#endif // defined(FRUIT_EXTRA_DEBUG) || defined(FRUIT_IN_META_TEST)
+
+// This calls ConstructError(NoBindingFoundErrorTag, ...) or
+// ConstructError(NoBindingFoundForAbstractClassErrorTag, ...) as appropriate.
+// Call this when we're unable to auto-inject a type AnnotatedC and we're giving up.
+struct ConstructNoBindingFoundError {
+ template <typename AnnotatedC>
+ struct apply {
+ using type = If(IsAbstract(RemoveAnnotations(AnnotatedC)),
+ ConstructError(NoBindingFoundForAbstractClassErrorTag, AnnotatedC, RemoveAnnotations(AnnotatedC)),
+ ConstructError(NoBindingFoundErrorTag, AnnotatedC));
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_COMPONENT_H
diff --git a/include/fruit/impl/meta/errors.h b/include/fruit/impl/meta/errors.h
new file mode 100644
index 0000000..6f9de8a
--- /dev/null
+++ b/include/fruit/impl/meta/errors.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_ERRORS_H
+#define FRUIT_META_ERRORS_H
+
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/logical_operations.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+template <typename T>
+struct CheckIfError {
+ using type = T;
+};
+
+template <typename ErrorTag, typename... ErrorArgs>
+struct CheckIfError<Error<ErrorTag, ErrorArgs...>> {
+ using type = typename ErrorTag::template apply<ErrorArgs...>;
+};
+
+// ConstructError(ErrorTag, Args...) returns Error<ErrorTag, Args...>.
+// Never construct an Error<...> directly, using this metafunction makes debugging easier.
+struct ConstructError {
+ template <typename ErrorTag, typename... Args>
+ struct apply {
+#ifdef FRUIT_DEEP_TEMPLATE_INSTANTIATION_STACKTRACES_FOR_ERRORS
+ static_assert(true || sizeof(typename CheckIfError<Error<ErrorTag, UnwrapType<Args>...>>::type), "");
+#endif
+ using type = Error<ErrorTag, typename TypeUnwrapper<Args>::type...>;
+ };
+};
+
+// Extracts the first error in the given types.
+struct ExtractFirstError {
+ template <typename... Types>
+ struct apply;
+
+ template <typename Type, typename... Types>
+ struct apply<Type, Types...> : public apply<Types...> {};
+
+ template <typename ErrorTag, typename... ErrorParams, typename... Types>
+ struct apply<Error<ErrorTag, ErrorParams...>, Types...> {
+ using type = Error<ErrorTag, ErrorParams...>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_ERRORS_H
diff --git a/include/fruit/impl/meta/eval.h b/include/fruit/impl/meta/eval.h
new file mode 100644
index 0000000..268b7dd
--- /dev/null
+++ b/include/fruit/impl/meta/eval.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_EVAL_H
+#define FRUIT_META_EVAL_H
+
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/errors.h>
+#include <fruit/impl/meta/logical_operations.h>
+
+#include <functional>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+template <typename MetaExpr>
+struct DoEval;
+
+// General case, meta-constant.
+template <typename MetaExpr>
+struct DoEval {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = MetaExpr;
+};
+
+template <typename Type>
+struct SimpleIsError {
+ static constexpr bool value = false;
+};
+template <typename ErrorTag, typename... ErrorArgs>
+struct SimpleIsError<Error<ErrorTag, ErrorArgs...>> {
+ static constexpr bool value = true;
+};
+
+#ifdef FRUIT_EXTRA_DEBUG
+
+// For debugging, we use a separate DoEvalFun so that we get longer (and more informative)
+// instantiation traces.
+
+template <typename MetaFun, typename... Params>
+struct DoEvalFun {
+ using type =
+ typename DoEval<typename std::conditional<StaticOr<SimpleIsError<Params>::value...>::value, ExtractFirstError,
+ MetaFun>::type::template apply<Params...>::type>::type;
+};
+
+template <typename MetaFun, typename... MetaExprs>
+struct DoEval<MetaFun(MetaExprs...)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename DoEvalFun<MetaFun, typename DoEval<MetaExprs>::type...>::type;
+};
+
+// Similar to the previous specialization, but this will be selected when the function signature
+// became a function pointer (this happens when a signature parameter is itself a signature).
+template <typename MetaFun, typename... MetaExprs>
+struct DoEval<MetaFun (*)(MetaExprs...)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename DoEvalFun<MetaFun, typename DoEval<MetaExprs>::type...>::type;
+};
+
+#else // FRUIT_EXTRA_DEBUG
+
+template <typename MetaFun, typename... MetaExprs>
+struct DoEval<MetaFun(MetaExprs...)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename DoEval<typename std::conditional<
+ StaticOr<SimpleIsError<typename DoEval<MetaExprs>::type>::value...>::value, ExtractFirstError,
+ MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
+};
+
+// Similar to the previous specialization, but this will be selected when the function signature
+// became a function pointer (this happens when a signature parameter is itself a signature).
+template <typename MetaFun, typename... MetaExprs>
+struct DoEval<MetaFun (*)(MetaExprs...)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename DoEval<typename std::conditional<
+ StaticOr<SimpleIsError<typename DoEval<MetaExprs>::type>::value...>::value, ExtractFirstError,
+ MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
+};
+
+#endif // FRUIT_EXTRA_DEBUG
+
+template <typename ExprResult, typename ErrorTag, typename Handler>
+struct EvalCatch {
+ using type = ExprResult;
+};
+
+template <typename CaughtErrorTag, typename... ErrorArgs, typename Handler>
+struct EvalCatch<Error<CaughtErrorTag, ErrorArgs...>, CaughtErrorTag, Handler> {
+ using type =
+ typename DoEval<typename DoEval<Handler>::type::template apply<Error<CaughtErrorTag, ErrorArgs...>>::type>::type;
+};
+
+template <typename ExprResult, typename Handler>
+struct EvalCatchAll {
+ using type = ExprResult;
+};
+
+template <typename CaughtErrorTag, typename... ErrorArgs, typename Handler>
+struct EvalCatchAll<Error<CaughtErrorTag, ErrorArgs...>, Handler> {
+ using type =
+ typename DoEval<typename DoEval<Handler>::type::template apply<Error<CaughtErrorTag, ErrorArgs...>>::type>::type;
+};
+
+template <typename Expr, typename ErrorTag, typename Handler>
+struct DoEval<Catch(Expr, ErrorTag, Handler)> {
+ using type = typename EvalCatch<typename DoEval<Expr>::type, typename DoEval<ErrorTag>::type, Handler>::type;
+};
+
+template <typename Expr, typename ErrorTag, typename Handler>
+struct DoEval<Catch (*)(Expr, ErrorTag, Handler)> {
+ using type = typename EvalCatch<typename DoEval<Expr>::type, typename DoEval<ErrorTag>::type, Handler>::type;
+};
+
+template <typename Expr, typename Handler>
+struct DoEval<CatchAll(Expr, Handler)> {
+ using type = typename EvalCatchAll<typename DoEval<Expr>::type, Handler>::type;
+};
+
+template <typename Expr, typename Handler>
+struct DoEval<CatchAll (*)(Expr, Handler)> {
+ using type = typename EvalCatchAll<typename DoEval<Expr>::type, Handler>::type;
+};
+
+template <typename MetaBool, typename ThenMetaExpr, typename ElseMetaExpr>
+struct EvalIf;
+
+template <typename ErrorTag, typename... ErrorArgs, typename ThenMetaExpr, typename ElseMetaExpr>
+struct EvalIf<Error<ErrorTag, ErrorArgs...>, ThenMetaExpr, ElseMetaExpr> {
+ using type = Error<ErrorTag, ErrorArgs...>;
+};
+
+template <typename ThenMetaExpr, typename ElseMetaExpr>
+struct EvalIf<Bool<true>, ThenMetaExpr, ElseMetaExpr> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename DoEval<ThenMetaExpr>::type;
+};
+
+template <typename ThenMetaExpr, typename ElseMetaExpr>
+struct EvalIf<Bool<false>, ThenMetaExpr, ElseMetaExpr> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename DoEval<ElseMetaExpr>::type;
+};
+
+template <typename CondMetaExpr, typename ThenMetaExpr, typename ElseMetaExpr>
+struct DoEval<If(CondMetaExpr, ThenMetaExpr, ElseMetaExpr)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename EvalIf<typename DoEval<CondMetaExpr>::type, ThenMetaExpr, ElseMetaExpr>::type;
+};
+
+// Similar to the previous specialization, but this will be selected when the function signature
+// became a function pointer (this happens when a signature parameter is itself a signature).
+template <typename CondMetaExpr, typename ThenMetaExpr, typename ElseMetaExpr>
+struct DoEval<If (*)(CondMetaExpr, ThenMetaExpr, ElseMetaExpr)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename EvalIf<typename DoEval<CondMetaExpr>::type, ThenMetaExpr, ElseMetaExpr>::type;
+};
+
+template <typename T, typename ElseMetaExpr>
+struct EvalPropagateError {
+ using type = typename DoEval<ElseMetaExpr>::type;
+};
+
+template <typename ErrorTag, typename... ErrorArgs, typename ElseMetaExpr>
+struct EvalPropagateError<Error<ErrorTag, ErrorArgs...>, ElseMetaExpr> {
+ using type = Error<ErrorTag, ErrorArgs...>;
+};
+
+template <typename MaybeErrorMetaExpr, typename ElseMetaExpr>
+struct DoEval<PropagateError(MaybeErrorMetaExpr, ElseMetaExpr)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename EvalPropagateError<typename DoEval<MaybeErrorMetaExpr>::type, ElseMetaExpr>::type;
+};
+
+// Similar to the previous specialization, but this will be selected when the function signature
+// became a function pointer (this happens when a signature parameter is itself a signature).
+template <typename MaybeErrorMetaExpr, typename ElseMetaExpr>
+struct DoEval<PropagateError (*)(MaybeErrorMetaExpr, ElseMetaExpr)> {
+#ifdef FRUIT_TRACE_INSTANTIATIONS
+ constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
+ return true;
+ }
+ static_assert(static_warning(), "");
+#endif
+ using type = typename EvalPropagateError<typename DoEval<MaybeErrorMetaExpr>::type, ElseMetaExpr>::type;
+};
+
+template <typename MetaExpr>
+using Eval = typename DoEval<MetaExpr>::type;
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_EVAL_H
diff --git a/include/fruit/impl/meta/fold.h b/include/fruit/impl/meta/fold.h
new file mode 100644
index 0000000..e8c86f5
--- /dev/null
+++ b/include/fruit/impl/meta/fold.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_FOLD_H
+#define FRUIT_META_FOLD_H
+
+#include <functional>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+struct Fold {
+ template <typename F, typename InitialValue, typename... Types>
+ struct apply;
+
+ template <typename F, typename InitialValue>
+ struct apply<F, InitialValue> {
+ using type = InitialValue;
+ };
+
+ template <typename F, typename InitialValue, typename T0>
+ struct apply<F, InitialValue, T0> {
+ using type = typename F::template apply<InitialValue, T0>::type;
+ };
+
+ template <typename F, typename InitialValue, typename T0, typename T1>
+ struct apply<F, InitialValue, T0, T1> {
+ using type =
+ typename F::template apply<typename DoEval<typename F::template apply<InitialValue, T0>::type>::type, T1>::type;
+ };
+
+ template <typename F, typename InitialValue, typename T0, typename T1, typename T2>
+ struct apply<F, InitialValue, T0, T1, T2> {
+ using type = typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<InitialValue, T0>::type>::type, T1>::type>::type,
+ T2>::type;
+ };
+
+ template <typename F, typename InitialValue, typename T0, typename T1, typename T2, typename T3>
+ struct apply<F, InitialValue, T0, T1, T2, T3> {
+ using type = typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<InitialValue, T0>::type>::type, T1>::type>::type,
+ T2>::type>::type,
+ T3>::type;
+ };
+
+ template <typename F, typename InitialValue, typename T0, typename T1, typename T2, typename T3, typename T4>
+ struct apply<F, InitialValue, T0, T1, T2, T3, T4> {
+ using type = typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<InitialValue, T0>::type>::type, T1>::type>::type,
+ T2>::type>::type,
+ T3>::type>::type,
+ T4>::type;
+ };
+
+ template <typename F, typename InitialValue, typename T0, typename T1, typename T2, typename T3, typename T4,
+ typename... Types>
+ struct apply<F, InitialValue, T0, T1, T2, T3, T4, Types...> {
+ using type = Fold(
+ F, typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<InitialValue, T0>::type>::type, T1>::type>::type,
+ T2>::type>::type,
+ T3>::type>::type,
+ T4>::type,
+ Types...);
+ };
+
+ // Optimized specialization, processing 10 values at a time.
+ template <typename F, typename InitialValue, typename T0, typename T1, typename T2, typename T3, typename T4,
+ typename T5, typename T6, typename T7, typename T8, typename T9, typename... Types>
+ struct apply<F, InitialValue, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, Types...> {
+ using type = Fold(
+ F,
+ typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<
+ typename DoEval<typename F::template apply<InitialValue, T0>::type>::type,
+ T1>::type>::type,
+ T2>::type>::type,
+ T3>::type>::type,
+ T4>::type>::type,
+ T5>::type>::type,
+ T6>::type>::type,
+ T7>::type>::type,
+ T8>::type>::type,
+ T9>::type,
+ Types...);
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_FOLD_H
diff --git a/include/fruit/impl/meta/graph.h b/include/fruit/impl/meta/graph.h
new file mode 100644
index 0000000..ca8dece
--- /dev/null
+++ b/include/fruit/impl/meta/graph.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_GRAPH_H
+#define FRUIT_META_GRAPH_H
+
+#include <fruit/impl/meta/immutable_map.h>
+#include <fruit/impl/meta/map.h>
+#include <fruit/impl/meta/set.h>
+#include <fruit/impl/meta/triplet.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// A Graph is a Map from each node to a set containing its neighbors.
+
+using GetGraphNodes = GetImmutableMapKeys;
+
+using GraphFindNeighbors = FindInImmutableMap;
+
+using GraphContainsNode = ImmutableMapContainsKey;
+
+// Returns a loop in the given graph as a Vector<N1, ..., Nk> such that the graph contains a loop
+// N1->...->Nk->N1, or None if there are no loops.
+struct GraphFindLoop {
+ template <typename G>
+ struct apply {
+ using ImmutableG = VectorToImmutableMap(G);
+
+ // DfsVisit(VisitedSet, VisitingSet, Node) does a DFS visit starting at Node and returns a
+ // Triplet<NewVisitedSet, Loop, IsLoopComplete>, where Loop is the Vector representing the part
+ // of the loop starting at Node (if any loop was found) or Null otherwise.
+ struct DfsVisit {
+ template <typename VisitedSet, typename VisitingSet, typename Node>
+ struct apply {
+ using NewVisitingSet = AddToSetUnchecked(VisitingSet, Node);
+
+ struct VisitSingleNeighbor {
+ // CurrentResult is a Triplet<VisitedSet, Loop, IsLoopComplete> (where IsLoopComplete
+ // is only meaningful when Loop is not None).
+ template <typename CurrentResult, typename Neighbor>
+ struct apply {
+ using type = If(IsNone(typename CurrentResult::Second),
+ // Go ahead, no loop found yet.
+ DfsVisit(typename CurrentResult::First, NewVisitingSet, Neighbor),
+ // Found a loop in another neighbor of the same node, we don't need to
+ // visit this neighbor.
+ CurrentResult);
+ };
+ };
+
+ using NewVisitedSet = AddToSet(VisitedSet, Node);
+ using Neighbors = GraphFindNeighbors(ImmutableG, Node);
+ using Result = FoldVector(Neighbors, VisitSingleNeighbor, ConsTriplet(NewVisitedSet, None, Bool<false>));
+ using type = If(IsNone(Neighbors),
+ // No neighbors.
+ ConsTriplet(NewVisitedSet, None, Bool<false>),
+ If(IsInSet(Node, VisitingSet),
+ // We've just found a loop, since Node is another node that we're currently
+ // visiting
+ Triplet<VisitedSet, Vector<Node>, Bool<false>>,
+ If(IsNone(GetSecond(Result)),
+ // No loop found.
+ Result,
+ // Found a loop
+ If(GetThird(Result) /* IsLoopComplete */, Result,
+ If(VectorEndsWith(GetSecond(Result) /* Loop */, Node),
+ // The loop is complete now.
+ ConsTriplet(GetFirst(Result), GetSecond(Result), Bool<true>),
+ // Loop still not complete, add the current node.
+ ConsTriplet(GetFirst(Result), PushFront(GetSecond(Result), Node), Bool<false>))))));
+ };
+ };
+
+ struct VisitStartingAtNode {
+ // CurrentResult is a Pair<CurrentVisitedSet, Loop>
+ template <typename CurrentResult, typename Node>
+ struct apply {
+ using DfsResult = DfsVisit(GetFirst(CurrentResult), EmptySet, Node);
+ using type = If(IsNone(GetSecond(CurrentResult)),
+ // No loop found yet.
+ MakePair(GetFirst(DfsResult), GetSecond(DfsResult)),
+ // Found a loop, return early
+ CurrentResult);
+ };
+ };
+
+ using type = GetSecond(FoldVector(GetMapKeys(G), VisitStartingAtNode, Pair<EmptySet, None>));
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_GRAPH_H
diff --git a/include/fruit/impl/meta/immutable_map.h b/include/fruit/impl/meta/immutable_map.h
new file mode 100644
index 0000000..898cc6e
--- /dev/null
+++ b/include/fruit/impl/meta/immutable_map.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_IMMUTABLE_MAP_H
+#define FRUIT_META_IMMUTABLE_MAP_H
+
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/immutable_set.h>
+#include <fruit/impl/meta/pair.h>
+#include <fruit/impl/meta/set.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// ImmutableMap ::= ImmutableSet<Pair<Key1, Value1>, ..., Pair<KeyN, ValueN>>
+
+struct VectorsToImmutableMap {
+ template <typename KeyVector, typename ValueVector>
+ struct apply;
+
+ template <typename... Keys, typename... Values>
+ struct apply<Vector<Keys...>, Vector<Values...>> {
+ using type = ConsImmutableSet<Pair<Keys, Values>...>;
+ };
+};
+
+struct VectorToImmutableMap {
+ template <typename PairVector>
+ struct apply;
+
+ template <typename... Pairs>
+ struct apply<Vector<Pairs...>> {
+ using type = ConsImmutableSet<Pairs...>;
+ };
+};
+
+struct IsInImmutableMap {
+ template <typename S, typename T>
+ struct apply {
+ using type = Bool<std::is_base_of<T, S>::value>;
+ };
+};
+
+struct FindInImmutableMap {
+ template <typename M, typename T>
+ struct apply {
+ template <typename Value>
+ static Value f(Pair<T, Value>*);
+
+ static None f(void*);
+
+ using type = decltype(f((M*)nullptr));
+ };
+};
+
+struct ImmutableMapContainsKey {
+ template <typename M, typename T>
+ struct apply {
+ using type = Not(IsNone(FindInImmutableMap(M, T)));
+ };
+};
+
+struct GetImmutableMapKeys {
+ template <typename M>
+ struct apply;
+
+ template <typename... Pairs>
+ struct apply<ConsImmutableSet<Pairs...>> {
+ using type = Vector<typename Pairs::First...>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_IMMUTABLE_MAP_H
diff --git a/include/fruit/impl/meta/immutable_set.h b/include/fruit/impl/meta/immutable_set.h
new file mode 100644
index 0000000..5e6998a
--- /dev/null
+++ b/include/fruit/impl/meta/immutable_set.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_IMMUTABLE_SET_H
+#define FRUIT_META_IMMUTABLE_SET_H
+
+#include <fruit/impl/fruit_assert.h>
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/vector.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// ImmutableSet ::= ConsImmutableSet<Ts...>
+
+template <typename... Ts>
+struct ConsImmutableSet : public Ts... {};
+
+struct VectorToImmutableSet {
+ template <typename V>
+ struct apply;
+
+ template <typename... Ts>
+ struct apply<Vector<Ts...>> {
+ using type = ConsImmutableSet<Ts...>;
+ };
+};
+
+struct IsInImmutableSet {
+ template <typename S, typename T>
+ struct apply {
+ using type = Bool<std::is_base_of<T, S>::value>;
+ };
+};
+
+struct SizeOfImmutableSet {
+ template <typename IS>
+ struct apply;
+
+ template <typename... Ts>
+ struct apply<ConsImmutableSet<Ts...>> {
+ using type = Int<sizeof...(Ts)>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_IMMUTABLE_SET_H
diff --git a/include/fruit/impl/meta/list.h b/include/fruit/impl/meta/list.h
new file mode 100644
index 0000000..b0bb695
--- /dev/null
+++ b/include/fruit/impl/meta/list.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_LIST_H
+#define FRUIT_META_LIST_H
+
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/logical_operations.h>
+#include <fruit/impl/meta/numeric_operations.h>
+#include <functional>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// List ::= EmptyList | Cons<T, List>
+
+struct EmptyList {};
+
+template <typename T, typename Tail>
+struct Cons {};
+
+// TODO: Consider inlining to improve performance.
+// If L is a list containing T1,...,Tn this calculates F(InitialValue, F(T1, F(..., F(Tn) ...))).
+struct FoldList {
+ template <typename L, typename F, typename InitialValue>
+ struct apply;
+
+ template <typename F, typename InitialValue>
+ struct apply<EmptyList, F, InitialValue> {
+ using type = InitialValue;
+ };
+
+ template <typename Head, typename Tail, typename F, typename InitialValue>
+ struct apply<Cons<Head, Tail>, F, InitialValue> {
+ using type = FoldList(Tail, F, F(InitialValue, Head));
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_LIST_H
diff --git a/include/fruit/impl/meta/logical_operations.h b/include/fruit/impl/meta/logical_operations.h
new file mode 100644
index 0000000..e35ebeb
--- /dev/null
+++ b/include/fruit/impl/meta/logical_operations.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_LOGICAL_OPERATIONS_H
+#define FRUIT_META_LOGICAL_OPERATIONS_H
+
+#include <fruit/impl/meta/basics.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+template <bool... bs>
+struct BoolVector {};
+
+template <bool... bs>
+using StaticAnd = Bool<std::is_same<BoolVector<bs...>, BoolVector<(true || bs)...>>::value>;
+
+template <bool... bs>
+using StaticOr = Bool<!std::is_same<BoolVector<bs...>, BoolVector<(false && bs)...>>::value>;
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_LOGICAL_OPERATIONS_H
diff --git a/include/fruit/impl/meta/map.h b/include/fruit/impl/meta/map.h
new file mode 100644
index 0000000..6291a59
--- /dev/null
+++ b/include/fruit/impl/meta/map.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_MAP_H
+#define FRUIT_META_MAP_H
+
+#include <fruit/impl/meta/set.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// A Map is a Set whose elements have the form Pair<Key, Value>
+
+struct GetMapKeys {
+ template <typename M>
+ struct apply;
+
+ template <typename... Pairs>
+ struct apply<Vector<Pairs...>> {
+ using type = Vector<typename Pairs::First...>;
+ };
+};
+
+// TODO: Consider implementing this by finding the position.
+struct MapContainsKey {
+ template <typename TToFind>
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = CurrentResult;
+ };
+ template <typename CurrentResult, typename Value>
+ struct apply<CurrentResult, Pair<TToFind, Value>> {
+ using type = Bool<true>;
+ };
+ };
+
+ template <typename M, typename TToFind>
+ struct apply {
+ using type = FoldVector(M, Helper<TToFind>, Bool<false>);
+ };
+};
+
+// TODO: Consider implementing this by finding the position first, then calling VectorRemoveFirstN
+// and getting the first element.
+struct FindInMap {
+ template <typename TToFind>
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = CurrentResult;
+ };
+ template <typename CurrentResult, typename Value>
+ struct apply<CurrentResult, Pair<TToFind, Value>> {
+ using type = Value;
+ };
+ };
+
+ template <typename M, typename TToFind>
+ struct apply {
+ using type = FoldVector(M, Helper<TToFind>, None);
+ };
+};
+
+// TODO: Consider implementing this by finding the position first, then calling VectorRemoveFirstN
+// and getting the first element.
+struct FindValueInMap {
+ template <typename TToFind>
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = CurrentResult;
+ };
+ template <typename CurrentResult, typename Value>
+ struct apply<CurrentResult, Pair<Value, TToFind>> {
+ using type = Value;
+ };
+ };
+
+ template <typename M, typename TToFind>
+ struct apply {
+ using type = FoldVector(M, Helper<TToFind>, None);
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_MAP_H
diff --git a/include/fruit/impl/meta/metaprogramming.h b/include/fruit/impl/meta/metaprogramming.h
new file mode 100644
index 0000000..c6190a0
--- /dev/null
+++ b/include/fruit/impl/meta/metaprogramming.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_METAPROGRAMMING_H
+#define FRUIT_META_METAPROGRAMMING_H
+
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/vector.h>
+
+#include <fruit/impl/fruit_assert.h>
+#include <fruit/impl/fruit_internal_forward_decls.h>
+#include <fruit/impl/injection_errors.h>
+#include <fruit/impl/meta/errors.h>
+
+#include <memory>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+struct IsConstructible {
+ template <typename C, typename... Args>
+ struct apply;
+
+ template <typename C, typename... Args>
+ struct apply<Type<C>, Type<Args>...> {
+ using type = Bool<std::is_constructible<C, Args...>::value>;
+ };
+};
+
+struct IsConstructibleWithVector {
+ template <typename C, typename V>
+ struct apply;
+
+ template <typename C, typename... Types>
+ struct apply<Type<C>, Vector<Type<Types>...>> {
+ using type = Bool<std::is_constructible<C, Types...>::value>;
+ };
+};
+
+struct AddPointer {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T*>;
+ };
+};
+
+struct IsCallable {
+ template <typename T>
+ struct apply;
+
+ template <typename C>
+ struct apply<Type<C>> {
+ template <typename C1>
+ static Bool<true> test(decltype(&C1::operator()));
+
+ template <typename>
+ static Bool<false> test(...);
+
+ using type = decltype(test<C>(nullptr));
+ };
+};
+
+struct GetCallOperatorSignature {
+ template <typename T>
+ struct apply;
+
+ template <typename C>
+ struct apply<Type<C>> {
+ using type = Type<decltype(&C::operator())>;
+ };
+};
+
+struct AddPointerToVector {
+ template <typename V>
+ struct apply;
+
+ template <typename... Ts>
+ struct apply<Vector<Type<Ts>...>> {
+ using type = Vector<Type<Ts*>...>;
+ };
+};
+
+struct GetNthTypeHelper {
+ template <typename N, typename... Ts>
+ struct apply;
+
+ template <typename T, typename... Ts>
+ struct apply<Int<0>, T, Ts...> {
+ using type = T;
+ };
+
+ template <int n, typename T, typename... Ts>
+ struct apply<Int<n>, T, Ts...> {
+ using type = GetNthTypeHelper(Int<n - 1>, Ts...);
+ };
+};
+
+struct GetNthType {
+ template <typename N, typename V>
+ struct apply;
+
+ template <typename N, typename... Ts>
+ struct apply<N, Vector<Ts...>> {
+ using type = GetNthTypeHelper(N, Ts...);
+ };
+};
+
+struct FunctorResultHelper {
+ template <typename MethodSignature>
+ struct apply;
+
+ template <typename Result, typename Functor, typename... Args>
+ struct apply<Type<Result (Functor::*)(Args...)>> {
+ using type = Type<Result>;
+ };
+};
+
+struct FunctorResult {
+ template <typename F>
+ struct apply;
+
+ template <typename F>
+ struct apply<Type<F>> {
+ using type = FunctorResultHelper(Type<decltype(&F::operator())>);
+ };
+};
+
+struct FunctionSignatureHelper {
+ template <typename LambdaMethod>
+ struct apply;
+
+ template <typename Result, typename LambdaObject, typename... Args>
+ struct apply<Type<Result (LambdaObject::*)(Args...) const>> {
+ using type = Type<Result(Args...)>;
+ };
+};
+
+// Function is either a plain function type of the form T(*)(Args...) or a lambda.
+struct FunctionSignature {
+ template <typename Function>
+ struct apply;
+
+ template <typename Function>
+ struct apply<Type<Function>> {
+ using CandidateSignature = FunctionSignatureHelper(GetCallOperatorSignature(Type<Function>));
+ using type = If(Not(IsCallable(Type<Function>)), ConstructError(NotALambdaErrorTag, Type<Function>),
+ If(Not(IsConstructible(AddPointer(CandidateSignature), Type<Function>)),
+ ConstructError(FunctorUsedAsProviderErrorTag, Type<Function>), CandidateSignature));
+ };
+
+ template <typename Result, typename... Args>
+ struct apply<Type<Result(Args...)>> {
+ using type = Type<Result(Args...)>;
+ };
+
+ template <typename Result, typename... Args>
+ struct apply<Type<Result (*)(Args...)>> {
+ using type = Type<Result(Args...)>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_METAPROGRAMMING_H
diff --git a/include/fruit/impl/meta/numeric_operations.h b/include/fruit/impl/meta/numeric_operations.h
new file mode 100644
index 0000000..be362c1
--- /dev/null
+++ b/include/fruit/impl/meta/numeric_operations.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_NUMERIC_OPERATIONS_H
+#define FRUIT_META_NUMERIC_OPERATIONS_H
+
+#include <fruit/impl/meta/basics.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+struct Sum {
+ template <typename... Ints>
+ struct apply;
+
+ template <int n, int m>
+ struct apply<Int<n>, Int<m>> {
+ using type = Int<n + m>;
+ };
+};
+
+struct SumAll {
+ template <typename... Ints>
+ struct apply {
+ using type = Int<0>;
+ };
+
+ template <typename N1, typename... Ints>
+ struct apply<N1, Ints...> {
+ using type = Int<N1::value + apply<Ints...>::type::value>;
+ };
+
+ // Optimization, not required for correctness.
+ template <typename N0, typename N1, typename N2, typename N3, typename N4, typename... Ints>
+ struct apply<N0, N1, N2, N3, N4, Ints...> {
+ using type = Int<N0::value + N1::value + N2::value + N3::value + N4::value + apply<Ints...>::type::value>;
+ };
+};
+
+struct Minus {
+ template <typename N, typename M>
+ struct apply;
+
+ template <int n, int m>
+ struct apply<Int<n>, Int<m>> {
+ using type = Int<n - m>;
+ };
+};
+
+struct GreaterThan {
+ template <typename N, typename M>
+ struct apply;
+
+ template <int n, int m>
+ struct apply<Int<n>, Int<m>> {
+ using type = Bool<(n > m)>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_NUMERIC_OPERATIONS_H
diff --git a/include/fruit/impl/meta/pair.h b/include/fruit/impl/meta/pair.h
new file mode 100644
index 0000000..10d8a3e
--- /dev/null
+++ b/include/fruit/impl/meta/pair.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_PAIR_H
+#define FRUIT_META_PAIR_H
+
+#include <fruit/impl/meta/basics.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+template <typename First1, typename Second1>
+struct Pair {
+ using First = First1;
+ using Second = Second1;
+};
+
+struct MakePair {
+ template <typename First, typename Second>
+ struct apply {
+ using type = Pair<First, Second>;
+ };
+};
+
+struct GetFirst {
+ template <typename P>
+ struct apply {
+ using type = typename P::First;
+ };
+};
+
+struct GetSecond {
+ template <typename P>
+ struct apply {
+ using type = typename P::Second;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_PAIR_H
diff --git a/include/fruit/impl/meta/proof_tree_comparison.h b/include/fruit/impl/meta/proof_tree_comparison.h
new file mode 100644
index 0000000..a8d1f91
--- /dev/null
+++ b/include/fruit/impl/meta/proof_tree_comparison.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_PROOF_TREE_COMPARISON_H
+#define FRUIT_PROOF_TREE_COMPARISON_H
+
+#if !defined(FRUIT_EXTRA_DEBUG) && !defined(FRUIT_IN_META_TEST)
+#error "This file should only be included in debug mode or in tests."
+#endif
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// Checks whether Proof is entailed by Forest, i.e. whether there is a corresponding Proof1 in Forest with the same
+// thesis
+// and with the same hypotheses as Proof (or a subset).
+struct IsProofEntailedByForest {
+ template <typename ProofTh, typename ProofHps, typename Forest>
+ struct apply {
+ using ForestHps = FindInMap(Forest, ProofTh);
+ using type = If(IsNone(ForestHps), Bool<false>, IsContained(ForestHps, ProofHps));
+ };
+};
+
+struct IsForestEntailedByForest {
+ template <typename EntailedForest, typename Forest>
+ struct apply {
+ struct Helper {
+ template <typename CurrentResult, typename EntailedProof>
+ struct apply;
+
+ template <typename CurrentResult, typename EntailedProofTh, typename EntailedProofHps>
+ struct apply<CurrentResult, Pair<EntailedProofTh, EntailedProofHps>> {
+ using type = And(CurrentResult, IsProofEntailedByForest(EntailedProofTh, EntailedProofHps, Forest));
+ };
+ };
+
+ using type = FoldVector(EntailedForest, Helper, Bool<true>);
+ };
+};
+
+// Given two proof trees, check if they are equal.
+// Only for debugging.
+struct IsProofTreeEqualTo {
+ template <typename Proof1, typename Proof2>
+ struct apply {
+ using type = And(IsSame(typename Proof1::First, typename Proof2::First),
+ IsSameSet(typename Proof1::Second, typename Proof2::Second));
+ };
+};
+
+// Given two proofs forests, check if they are equal.
+// This is not very efficient, consider re-implementing if it will be used often.
+// Only for debugging.
+struct IsForestEqualTo {
+ template <typename Forest1, typename Forest2>
+ struct apply {
+ using type = And(IsForestEntailedByForest(Forest1, Forest2), IsForestEntailedByForest(Forest2, Forest1));
+ };
+};
+
+// Only for debugging, similar to checking IsProofEntailedByForest but gives a detailed error.
+// Only for debugging.
+struct CheckProofEntailedByForest {
+ template <typename ProofTh, typename ProofHps, typename Forest>
+ struct apply {
+ using ForestHps = FindInMap(Forest, ProofTh);
+ using type = If(IsNone(ForestHps),
+ ConstructError(ProofNotEntailedByForestBecauseThNotFoundErrorTag, ProofTh, GetMapKeys(Forest)),
+ If(IsContained(ForestHps, ProofHps), Bool<true>,
+ ConstructError(ProofNotEntailedByForestBecauseHpsNotASubsetErrorTag, ForestHps, ProofHps,
+ SetDifference(ForestHps, ProofHps))));
+ };
+};
+
+// Only for debugging, similar to checking IsProofEntailedByForest but gives a detailed error.
+// Only for debugging.
+struct CheckForestEntailedByForest {
+ template <typename EntailedForest, typename Forest>
+ struct apply {
+ struct Helper {
+ template <typename CurrentResult, typename EntailedProof>
+ struct apply;
+
+ template <typename CurrentResult, typename EntailedProofTh, typename EntailedProofHps>
+ struct apply<CurrentResult, Pair<EntailedProofTh, EntailedProofHps>> {
+ using type = PropagateError(CurrentResult,
+ CheckProofEntailedByForest(EntailedProofTh, EntailedProofHps, Forest));
+ };
+ };
+
+ using type = FoldVector(EntailedForest, Helper, Bool<true>);
+ };
+};
+
+// Given two proofs forests, check if they are equal.
+// This is not very efficient, consider re-implementing if it will be used often.
+// Only for debugging.
+struct CheckForestEqualTo {
+ template <typename Forest1, typename Forest2>
+ struct apply {
+ using type = PropagateError(CheckForestEntailedByForest(Forest1, Forest2),
+ CheckForestEntailedByForest(Forest2, Forest1));
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_PROOF_TREE_COMPARISON_H
diff --git a/include/fruit/impl/meta/proof_trees.h b/include/fruit/impl/meta/proof_trees.h
new file mode 100644
index 0000000..ad6795f
--- /dev/null
+++ b/include/fruit/impl/meta/proof_trees.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_PROOF_TREES_H
+#define FRUIT_META_PROOF_TREES_H
+
+#include <fruit/impl/fruit_assert.h>
+#include <fruit/impl/injection_debug_errors.h>
+#include <fruit/impl/meta/errors.h>
+#include <fruit/impl/meta/graph.h>
+#include <fruit/impl/meta/set.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// Given a set of formulas Hps=Set<Hp1, ... Hp(n)> and a formula Th, Pair<Th, Hps> represents the
+// following proof tree:
+//
+// Hp1 ... Hp(n)
+// -------------
+// Th
+//
+// Hp1, ... Hp(n) must be distinct.
+// Formulas are atomic, any type can be used as formula (except None).
+//
+// A proof forest is a map (i.e. a Set of Pair(s)) from each Th to the corresponding set of Hps.
+// Note that a proof forest doesn't need to have any additional property (for example, a proof tree
+// might contain the thesis as hypotheses, or there might be a longer loop e.g A=>B, B=>A.
+using EmptyProofForest = EmptySet;
+
+#ifndef FRUIT_NO_LOOP_CHECK
+
+using ProofForestFindHps = GraphFindNeighbors;
+
+// ProofForestFindLoop(F) returns a loop in the given forest as a Vector<Th1, ..., Thk> such that:
+// IsInSet(Th1, ProofForestFindHps(Th2)), IsInSet(Th2, ProofForestFindHps(Th3)), ...,
+// IsInSet(Th{k-1}, ProofForestFindHps(Thk)), IsInSet(Thk, ProofForestFindHps(Th1))
+// if there is no such loop, returns None.
+using ProofForestFindLoop = GraphFindLoop;
+
+#else // FRUIT_NO_LOOP_CHECK
+
+struct ProofForestFindLoop {
+ template <typename F>
+ struct apply {
+ using type = None;
+ };
+};
+
+struct AddProofTreeToForest {
+ template <typename Forest, typename Proof>
+ struct apply {
+ using type = Vector<>;
+ };
+};
+
+struct AddProofTreeSetToForest {
+ template <typename Proofs, typename Forest>
+ struct apply {
+ using type = Vector<>;
+ };
+};
+
+#endif // FRUIT_NO_LOOP_CHECK
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_PROOF_TREES_H
diff --git a/include/fruit/impl/meta/set.h b/include/fruit/impl/meta/set.h
new file mode 100644
index 0000000..7a39d43
--- /dev/null
+++ b/include/fruit/impl/meta/set.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_SET_H
+#define FRUIT_META_SET_H
+
+#include <fruit/impl/fruit_assert.h>
+#include <fruit/impl/meta/immutable_set.h>
+#include <fruit/impl/meta/pair.h>
+#include <fruit/impl/meta/vector.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// Set ::= Vector<Ts...>, with no duplicates.
+
+using EmptySet = Vector<>;
+
+template <typename T>
+using ToSet1 = Vector<T>;
+
+template <typename T, typename U>
+using ToSet2 = Vector<T, U>;
+
+using IsInSet = IsInVector;
+
+// If S is a set with elements (T1, ..., Tn) this calculates
+// F(InitialValue, F(T1, F(..., F(Tn) ...))).
+// If S is EmptySet this returns InitialValue.
+using FoldSet = FoldVector;
+
+// If S is a set with elements (T1, ..., Tn) this calculates
+// Combine(F(T1), Combine(F(T2),..., F(Tn) ...)).
+//
+// `Combine' must be associative, and CombineIdentity must be an identity value wrt Combine.
+// Use this instead of FoldSet when possible, it shares more sub-instances when invoked multiple
+// times with similar sets.
+struct FoldSetWithCombine {
+ template <typename S, typename F, typename Combine, typename CombineIdentity>
+ struct apply {
+ using type = FoldVector(TransformVector(S, F), Combine, CombineIdentity);
+ };
+};
+
+// Converts a set (T1,...,Tn) to a set (F(T1), ..., F(Tn)).
+// F(T1), ..., F(Tn) must be distinct.
+using TransformSet = TransformVector;
+
+using SetSize = VectorSize;
+
+using AddToSetUnchecked = PushFront;
+
+struct AddToSet {
+ template <typename S, typename T>
+ struct apply {
+ using type = If(IsInSet(T, S), S, AddToSetUnchecked(S, T));
+ };
+};
+
+// Checks if S1 is contained in S2.
+struct IsContained {
+ template <typename S1, typename S2>
+ struct apply {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = And(CurrentResult, IsInSet(T, S2));
+ };
+ };
+
+ using type = FoldVector(S1, Helper, Bool<true>);
+ };
+};
+
+// Checks if S1 is disjoint from S2.
+struct IsDisjoint {
+ template <typename S1, typename S2>
+ struct apply {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = Or(CurrentResult, IsInSet(T, S2));
+ };
+ };
+
+ using type = Not(FoldVector(S1, Helper, Bool<false>));
+ };
+};
+
+struct IsEmptySet {
+ template <typename S>
+ struct apply {
+ using type = Bool<false>;
+ };
+};
+
+template <>
+struct IsEmptySet::apply<Vector<>> {
+ using type = Bool<true>;
+};
+
+using SetToVector = Identity;
+
+// The vector must have no duplicates.
+using VectorToSetUnchecked = Identity;
+
+struct SetDifference {
+ template <typename S1, typename S2>
+ struct apply {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = If(IsInSet(T, S2), CurrentResult, AddToSetUnchecked(CurrentResult, T));
+ };
+ };
+
+ using type = FoldSet(S1, Helper, EmptySet);
+ };
+};
+
+struct SetIntersection {
+ template <typename S1, typename S2>
+ struct apply {
+ struct Helper {
+ template <typename CurrentResult, typename T>
+ struct apply {
+ using type = If(IsInSet(T, S2), AddToSetUnchecked(CurrentResult, T), CurrentResult);
+ };
+ };
+
+ using type = If(GreaterThan(SetSize(S1), SetSize(S2)), SetIntersection(S2, S1), FoldSet(S1, Helper, EmptySet));
+ };
+};
+
+struct SetUnion {
+ template <typename S1, typename S2>
+ struct apply {
+ using type = If(GreaterThan(SetSize(S1), SetSize(S2)), SetUnion(S2, S1),
+ FoldSet(SetDifference(S1, S2), AddToSetUnchecked, S2));
+ };
+};
+
+using SetUncheckedUnion = ConcatVectors;
+
+struct IsSameSet {
+ template <typename S1, typename S2>
+ struct apply {
+ using type = And(IsContained(S1, S2), IsContained(S2, S1));
+ };
+};
+
+// Returns an arbitrary element from the given set (that must be non-empty).
+struct GetArbitrarySetElement {
+ template <typename S>
+ struct apply;
+
+ template <typename T, typename... Ts>
+ struct apply<Vector<T, Ts...>> {
+ using type = T;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_SET_H
diff --git a/include/fruit/impl/meta/signatures.h b/include/fruit/impl/meta/signatures.h
new file mode 100644
index 0000000..69e5ac7
--- /dev/null
+++ b/include/fruit/impl/meta/signatures.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_SIGNATURES_H
+#define FRUIT_META_SIGNATURES_H
+
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/vector.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// Similar to ConsSignature, but takes a Vector of args instead of individual args.
+struct ConsSignatureWithVector {
+ template <typename ReturnType, typename ArgVector>
+ struct apply;
+
+ template <typename ReturnType, typename... Args>
+ struct apply<Type<ReturnType>, Vector<Type<Args>...>> {
+ using type = Type<ReturnType(Args...)>;
+ };
+};
+
+struct SignatureType {
+ template <typename Signature>
+ struct apply;
+
+ template <typename T, typename... Types>
+ struct apply<Type<T(Types...)>> {
+ using type = Type<T>;
+ };
+};
+
+struct SignatureArgs {
+ template <typename Signature>
+ struct apply;
+
+ template <typename T, typename... Types>
+ struct apply<Type<T(Types...)>> {
+ using type = Vector<Type<Types>...>;
+ };
+};
+
+struct IsSignature {
+ template <typename Signature>
+ struct apply {
+ using type = Bool<false>;
+ };
+
+ template <typename C, typename... Args>
+ struct apply<Type<C(Args...)>> {
+ using type = Bool<true>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_SIGNATURES_H
diff --git a/include/fruit/impl/meta/triplet.h b/include/fruit/impl/meta/triplet.h
new file mode 100644
index 0000000..832a8cf
--- /dev/null
+++ b/include/fruit/impl/meta/triplet.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_TRIPLET_H
+#define FRUIT_META_TRIPLET_H
+
+#include <fruit/impl/meta/basics.h>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+template <typename First1, typename Second1, typename Third1>
+struct Triplet {
+ using First = First1;
+ using Second = Second1;
+ using Third = Third1;
+};
+
+struct ConsTriplet {
+ template <typename First, typename Second, typename Third>
+ struct apply {
+ using type = Triplet<First, Second, Third>;
+ };
+};
+
+struct GetThird {
+ template <typename T>
+ struct apply {
+ using type = typename T::Third;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_TRIPLET_H
diff --git a/include/fruit/impl/meta/vector.h b/include/fruit/impl/meta/vector.h
new file mode 100644
index 0000000..a94a21d
--- /dev/null
+++ b/include/fruit/impl/meta/vector.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_VECTOR_H
+#define FRUIT_META_VECTOR_H
+
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/eval.h>
+#include <fruit/impl/meta/fold.h>
+#include <fruit/impl/meta/logical_operations.h>
+#include <fruit/impl/meta/numeric_operations.h>
+#include <functional>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+// Used to pass around a Vector<Types...>, no meaning per se.
+template <typename... Types>
+struct Vector {};
+
+// Using ConsVector(MetaExpr...) instead of Vector<MetaExpr...> in a meta-expression allows the
+// types to be evaluated. Avoid using Vector<...> directly in a meta-expression, unless you're sure
+// that the arguments have already been evaluated (e.g. if Args... are arguments of a metafunction,
+// Vector<Args...> is ok but Vector<MyFunction(Args)...> is wrong.
+struct ConsVector {
+ template <typename... Types>
+ struct apply {
+ using type = Vector<Types...>;
+ };
+};
+
+struct GenerateIntSequenceEvenHelper {
+ template <typename Half>
+ struct apply;
+
+ template <int... ns>
+ struct apply<Vector<Int<ns>...>> {
+ using type = Vector<Int<ns>..., Int<sizeof...(ns) + ns>...>;
+ };
+};
+
+struct GenerateIntSequenceOddHelper {
+ template <typename Half>
+ struct apply;
+
+ template <int... ns>
+ struct apply<Vector<Int<ns>...>> {
+ using type = Vector<Int<ns>..., Int<sizeof...(ns)>, Int<sizeof...(ns) + 1 + ns>...>;
+ };
+};
+
+struct GenerateIntSequence {
+ template <typename N>
+ struct apply {
+ using type = If(Bool<(N::value % 2) == 0>, GenerateIntSequenceEvenHelper(GenerateIntSequence(Int<N::value / 2>)),
+ GenerateIntSequenceOddHelper(GenerateIntSequence(Int<N::value / 2>)));
+ };
+};
+
+template <>
+struct GenerateIntSequence::apply<Int<0>> {
+ using type = Vector<>;
+};
+
+template <>
+struct GenerateIntSequence::apply<Int<1>> {
+ using type = Vector<Int<0>>;
+};
+
+struct IsInVector {
+ template <typename T>
+ struct AlwaysFalseBool {
+ constexpr static bool value = false;
+ };
+
+ template <bool... bs>
+ struct BoolVector;
+
+ template <typename T, typename V>
+ struct apply;
+
+ template <typename T, typename... Ts>
+ struct apply<T, Vector<Ts...>> {
+ using type = Bool<
+ !std::is_same<BoolVector<AlwaysFalseBool<Ts>::value...>, BoolVector<std::is_same<T, Ts>::value...>>::value>;
+ };
+};
+
+struct IsVectorContained {
+ template <typename V1, typename V2>
+ struct apply;
+
+ template <typename T>
+ struct AlwaysTrueBool {
+ constexpr static bool value = true;
+ };
+
+ template <bool... bs>
+ struct BoolVector;
+
+ template <typename... Ts, typename V2>
+ struct apply<Vector<Ts...>, V2> {
+ using type = Bool<std::is_same<BoolVector<AlwaysTrueBool<Ts>::value...>,
+ BoolVector<Id<typename IsInVector::template apply<Ts, V2>::type>::value...>>::value>;
+ };
+};
+
+struct VectorSize {
+ template <typename V>
+ struct apply;
+
+ template <typename... Ts>
+ struct apply<Vector<Ts...>> {
+ using type = Int<sizeof...(Ts)>;
+ };
+};
+
+struct PushFront {
+ template <typename V, typename T>
+ struct apply;
+
+ template <typename T, typename... Ts>
+ struct apply<Vector<Ts...>, T> {
+ using type = Vector<T, Ts...>;
+ };
+};
+
+struct PushBack {
+ template <typename V, typename T>
+ struct apply;
+
+ template <typename T, typename... Ts>
+ struct apply<Vector<Ts...>, T> {
+ using type = Vector<Ts..., T>;
+ };
+};
+
+struct ConcatVectors {
+ template <typename V1, typename V2>
+ struct apply;
+
+ template <typename... Ts, typename... Us>
+ struct apply<Vector<Ts...>, Vector<Us...>> {
+ using type = Vector<Ts..., Us...>;
+ };
+};
+
+struct TransformVector {
+ template <typename V, typename F>
+ struct apply;
+
+ template <typename... Ts, typename F>
+ struct apply<Vector<Ts...>, F> {
+ using type = Vector<Eval<typename F::template apply<Ts>::type>...>;
+ };
+};
+
+struct ReplaceInVectorHelper {
+ template <typename ToReplace, typename NewElem, typename T>
+ struct apply {
+ using type = T;
+ };
+
+ template <typename ToReplace, typename NewElem>
+ struct apply<ToReplace, NewElem, ToReplace> {
+ using type = NewElem;
+ };
+};
+
+struct ReplaceInVector {
+ template <typename V, typename ToReplace, typename NewElem>
+ struct apply {
+ using type = TransformVector(V, PartialCall(ReplaceInVectorHelper, ToReplace, NewElem));
+ };
+};
+
+// If V is Vector<T1, ..., Tn> this calculates F(InitialValue, F(T1, F(..., F(Tn) ...))).
+// If V is Vector<> this returns InitialValue.
+struct FoldVector {
+ template <typename V, typename F, typename InitialValue>
+ struct apply;
+
+ template <typename... Ts, typename F, typename InitialValue>
+ struct apply<Vector<Ts...>, F, InitialValue> {
+ using type = Fold(F, InitialValue, Ts...);
+ };
+};
+
+template <typename Unused>
+using AlwaysVoidPtr = void*;
+
+// Returns a copy of V but without the first N elements.
+// N must be at least 0 and at most VectorSize(V).
+struct VectorRemoveFirstN {
+ template <typename V, typename N, typename Indexes = Eval<GenerateIntSequence(N)>>
+ struct apply;
+
+ template <typename... Types, typename N, typename... Indexes>
+ struct apply<Vector<Types...>, N, Vector<Indexes...>> {
+ template <typename... RemainingTypes>
+ static Vector<RemainingTypes...> f(AlwaysVoidPtr<Indexes>..., RemainingTypes*...);
+
+ using type = decltype(f((Types*)nullptr...));
+ };
+};
+
+struct VectorEndsWith {
+ template <typename V, typename T>
+ struct apply {
+ using N = Int<Eval<VectorSize(V)>::value - 1>;
+ using type = IsSame(VectorRemoveFirstN(V, N), Vector<T>);
+ };
+
+ template <typename T>
+ struct apply<Vector<>, T> {
+ using type = Bool<false>;
+ };
+};
+
+// Removes all None elements from the vector.
+// O(n) instantiations.
+struct VectorRemoveNone {
+ template <typename V>
+ struct apply {
+ using type = Vector<>;
+ };
+
+ template <typename T, typename... Ts>
+ struct apply<Vector<T, Ts...>> {
+ using type = PushFront(VectorRemoveNone(Vector<Ts...>), T);
+ };
+
+ template <typename... Ts>
+ struct apply<Vector<None, Ts...>> {
+ using type = VectorRemoveNone(Vector<Ts...>);
+ };
+};
+
+struct ConstructErrorWithArgVectorHelper {
+ template <typename ErrorTag, typename ArgsVector, typename... OtherArgs>
+ struct apply;
+
+ template <typename ErrorTag, typename... Args, typename... OtherArgs>
+ struct apply<ErrorTag, Vector<Args...>, OtherArgs...> {
+ using type = ConstructError(ErrorTag, OtherArgs..., Args...);
+ };
+};
+
+struct ConstructErrorWithArgVector {
+ template <typename ErrorTag, typename ArgsVector, typename... OtherArgs>
+ struct apply {
+ using type = ConstructErrorWithArgVectorHelper(ErrorTag, VectorRemoveNone(ArgsVector), OtherArgs...);
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_VECTOR_H
diff --git a/include/fruit/impl/meta/wrappers.h b/include/fruit/impl/meta/wrappers.h
new file mode 100644
index 0000000..53dd05c
--- /dev/null
+++ b/include/fruit/impl/meta/wrappers.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_META_WRAPPERS_H
+#define FRUIT_META_WRAPPERS_H
+
+#include <fruit/impl/fruit-config.h>
+
+#include <memory>
+
+namespace fruit {
+namespace impl {
+namespace meta {
+
+struct ConsSignature {
+ template <typename ReturnType, typename... Args>
+ struct apply;
+
+ template <typename ReturnType, typename... Args>
+ struct apply<Type<ReturnType>, Type<Args>...> {
+ using type = Type<ReturnType(Args...)>;
+ };
+};
+
+struct ConsStdFunction {
+ template <typename Signature>
+ struct apply;
+
+ template <typename Signature>
+ struct apply<Type<Signature>> {
+ using type = Type<std::function<Signature>>;
+ };
+};
+
+struct ConsUniquePtr {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<std::unique_ptr<T>>;
+ };
+};
+
+struct RemoveUniquePtr {
+ template <typename T>
+ struct apply {
+ using type = T;
+ };
+
+ template <typename T>
+ struct apply<Type<std::unique_ptr<T>>> {
+ using type = Type<T>;
+ };
+};
+
+struct RemovePointer {
+ template <typename T>
+ struct apply {
+ using type = T;
+ };
+
+ template <typename T>
+ struct apply<Type<T*>> {
+ using type = Type<T>;
+ };
+};
+
+struct ConsReference {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<T&>;
+ };
+};
+
+struct ConsConstReference {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Type<const T&>;
+ };
+};
+
+struct IsEmpty {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Bool<std::is_empty<T>::value>;
+ };
+};
+
+struct IsTriviallyCopyable {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Bool<FRUIT_IS_TRIVIALLY_COPYABLE(T)>;
+ };
+};
+
+struct IsPointer {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Bool<std::is_pointer<T>::value>;
+ };
+};
+
+struct IsUniquePtr {
+ template <typename T>
+ struct apply {
+ using type = Bool<false>;
+ };
+
+ template <typename T>
+ struct apply<Type<std::unique_ptr<T>>> {
+ using type = Bool<true>;
+ };
+};
+
+struct IsAbstract {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Bool<std::is_abstract<T>::value>;
+ };
+};
+
+struct IsBaseOf {
+ template <typename I, typename C>
+ struct apply;
+
+ template <typename I, typename C>
+ struct apply<Type<I>, Type<C>> {
+ using type = Bool<std::is_base_of<I, C>::value>;
+ };
+};
+
+struct HasVirtualDestructor {
+ template <typename T>
+ struct apply;
+
+ template <typename T>
+ struct apply<Type<T>> {
+ using type = Bool<std::has_virtual_destructor<T>::value>;
+ };
+};
+
+} // namespace meta
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_WRAPPERS_H
diff --git a/include/fruit/impl/normalized_component.defn.h b/include/fruit/impl/normalized_component.defn.h
new file mode 100644
index 0000000..3aad35f
--- /dev/null
+++ b/include/fruit/impl/normalized_component.defn.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_NORMALIZED_COMPONENT_INLINES_H
+#define FRUIT_NORMALIZED_COMPONENT_INLINES_H
+
+#include <fruit/normalized_component.h>
+
+#include <fruit/component.h>
+#include <fruit/impl/util/type_info.h>
+
+namespace fruit {
+
+template <typename... Params>
+template <typename... FormalArgs, typename... Args>
+inline NormalizedComponent<Params...>::NormalizedComponent(Component<Params...> (*getComponent)(FormalArgs...),
+ Args&&... args)
+ : NormalizedComponent(std::move(fruit::Component<Params...>(
+ fruit::createComponent().install(getComponent, std::forward<Args>(args)...))
+ .storage),
+ fruit::impl::MemoryPool()) {}
+
+template <typename... Params>
+inline NormalizedComponent<Params...>::NormalizedComponent(fruit::impl::ComponentStorage&& storage,
+ fruit::impl::MemoryPool memory_pool)
+ : storage(std::move(storage),
+ fruit::impl::getTypeIdsForList<typename fruit::impl::meta::Eval<fruit::impl::meta::SetToVector(
+ typename fruit::impl::meta::Eval<fruit::impl::meta::ConstructComponentImpl(
+ fruit::impl::meta::Type<Params>...)>::Ps)>>(memory_pool),
+ memory_pool, fruit::impl::NormalizedComponentStorageHolder::WithUndoableCompression()) {}
+
+} // namespace fruit
+
+#endif // FRUIT_NORMALIZED_COMPONENT_INLINES_H
diff --git a/include/fruit/impl/normalized_component_storage/binding_normalization.h b/include/fruit/impl/normalized_component_storage/binding_normalization.h
new file mode 100644
index 0000000..b5d395e
--- /dev/null
+++ b/include/fruit/impl/normalized_component_storage/binding_normalization.h
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_BINDING_NORMALIZATION_H
+#define FRUIT_BINDING_NORMALIZATION_H
+
+#ifndef IN_FRUIT_CPP_FILE
+// We don't want to include it in public headers to save some compile time.
+#error "binding_normalization.h included in non-cpp file."
+#endif
+
+#include <fruit/impl/component_storage/component_storage_entry.h>
+#include <fruit/impl/data_structures/arena_allocator.h>
+#include <fruit/impl/data_structures/fixed_size_allocator.h>
+#include <fruit/impl/normalized_component_storage/normalized_component_storage.h>
+#include <fruit/impl/util/hash_helpers.h>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * This struct contains helper functions used for binding normalization.
+ * They are wrapped in a struct so that Fruit classes can easily declare to be friend
+ * of all these.
+ */
+class BindingNormalization {
+public:
+ // Stores an element of the form (c_type_id, -> undo_info) for each binding compression that was
+ // performed.
+ // These are used to undo binding compression after applying it (if necessary).
+ using BindingCompressionInfoMap =
+ HashMapWithArenaAllocator<TypeId, NormalizedComponentStorage::CompressedBindingUndoInfo>;
+
+ using LazyComponentWithNoArgs = ComponentStorageEntry::LazyComponentWithNoArgs;
+ using LazyComponentWithArgs = ComponentStorageEntry::LazyComponentWithArgs;
+
+ using LazyComponentWithNoArgsSet = NormalizedComponentStorage::LazyComponentWithNoArgsSet;
+ using LazyComponentWithArgsSet = NormalizedComponentStorage::LazyComponentWithArgsSet;
+
+ using LazyComponentWithNoArgsReplacementMap = NormalizedComponentStorage::LazyComponentWithNoArgsReplacementMap;
+ using LazyComponentWithArgsReplacementMap = NormalizedComponentStorage::LazyComponentWithArgsReplacementMap;
+
+ /**
+ * Normalizes the toplevel entries and performs binding compression.
+ * This does *not* keep track of what binding compressions were performed, so they can't be undone. When we might need
+ * to undo the binding compression, use normalizeBindingsWithUndoableBindingCompression() instead.
+ */
+ static void normalizeBindingsWithPermanentBindingCompression(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings);
+
+ /**
+ * Normalizes the toplevel entries and performs binding compression, but keeps track of which compressions were
+ * performed so that we can later undo some of them if needed.
+ * This is more expensive than normalizeBindingsWithPermanentBindingCompression(), use that when it suffices.
+ */
+ static void normalizeBindingsWithUndoableBindingCompression(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
+ MemoryPool& memory_pool_for_fully_expanded_components_maps,
+ MemoryPool& memory_pool_for_component_replacements_maps,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings,
+ BindingCompressionInfoMap& bindingCompressionInfoMap,
+ LazyComponentWithNoArgsSet& fully_expanded_components_with_no_args,
+ LazyComponentWithArgsSet& fully_expanded_components_with_args,
+ LazyComponentWithNoArgsReplacementMap& component_with_no_args_replacements,
+ LazyComponentWithArgsReplacementMap& component_with_args_replacements);
+
+ static void normalizeBindingsAndAddTo(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries, MemoryPool& memory_pool,
+ const NormalizedComponentStorage& base_normalized_component,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& new_bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings);
+
+private:
+ using multibindings_vector_elem_t = std::pair<ComponentStorageEntry, ComponentStorageEntry>;
+ using multibindings_vector_t = std::vector<multibindings_vector_elem_t, ArenaAllocator<multibindings_vector_elem_t>>;
+
+ /**
+ * Adds the multibindings in multibindings_vector to the `multibindings' map.
+ * Each element of multibindings_vector is a pair, where the first element is the multibinding and the second is the
+ * corresponding MULTIBINDING_VECTOR_CREATOR entry.
+ */
+ static void addMultibindings(std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
+ const multibindings_vector_t& multibindings_vector);
+
+ static void printLazyComponentInstallationLoop(
+ const std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& entries_to_process,
+ const ComponentStorageEntry& last_entry);
+
+ /**
+ * Normalizes the toplevel entries (but doesn't perform binding compression).
+ */
+ template <typename... Functors>
+ static void normalizeBindings(FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
+ MemoryPool& memory_pool, MemoryPool& memory_pool_for_fully_expanded_components_maps,
+ MemoryPool& memory_pool_for_component_replacements_maps,
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>& binding_data_map,
+ Functors... functors);
+
+ struct BindingCompressionInfo {
+ TypeId i_type_id;
+ ComponentStorageEntry::BindingForObjectToConstruct::create_t create_i_with_compression;
+ };
+
+ /**
+ * Normalizes the toplevel entries and performs binding compression.
+ * - SaveCompressedBindingUndoInfo should have an operator()(TypeId, CompressedBindingUndoInfo) that will be called
+ * with (c_type_id, undo_info) for each binding compression that was applied (and that therefore might need to be
+ * undone later).
+ */
+ template <typename SaveCompressedBindingUndoInfo, typename SaveFullyExpandedComponentsWithNoArgs,
+ typename SaveFullyExpandedComponentsWithArgs, typename SaveComponentReplacementsWithNoArgs,
+ typename SaveComponentReplacementsWithArgs>
+ static void normalizeBindingsWithBindingCompression(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
+ MemoryPool& memory_pool_for_fully_expanded_components_maps,
+ MemoryPool& memory_pool_for_component_replacements_maps,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings,
+ SaveCompressedBindingUndoInfo save_compressed_binding_undo_info,
+ SaveFullyExpandedComponentsWithNoArgs save_fully_expanded_components_with_no_args,
+ SaveFullyExpandedComponentsWithArgs save_fully_expanded_components_with_args,
+ SaveComponentReplacementsWithNoArgs save_component_replacements_with_no_args,
+ SaveComponentReplacementsWithArgs save_component_replacements_with_args);
+
+ /**
+ * bindingCompressionInfoMap is an output parameter. This function will store information on all performed binding
+ * compressions in that map, to allow them to be undone later, if necessary.
+ * compressed_bindings_map is a map CtypeId -> (ItypeId, bindingData)
+ * - SaveCompressedBindingUndoInfo should have an operator()(TypeId, CompressedBindingUndoInfo) that will be called
+ * with (c_type_id, undo_info) for each binding compression that was applied (and that therefore might need to be
+ * undone later).
+ */
+ template <typename SaveCompressedBindingUndoInfo>
+ static std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>
+ performBindingCompression(HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>&& binding_data_map,
+ HashMapWithArenaAllocator<TypeId, BindingCompressionInfo>&& compressed_bindings_map,
+ MemoryPool& memory_pool, const multibindings_vector_t& multibindings_vector,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ SaveCompressedBindingUndoInfo save_compressed_binding_undo_info);
+
+ static void handlePreexistingLazyComponentWithArgsReplacement(ComponentStorageEntry& replaced_component_entry,
+ const ComponentStorageEntry& preexisting_replacement,
+ ComponentStorageEntry& new_replacement);
+
+ static void handlePreexistingLazyComponentWithNoArgsReplacement(ComponentStorageEntry& replaced_component_entry,
+ const ComponentStorageEntry& preexisting_replacement,
+ ComponentStorageEntry& new_replacement);
+
+ template <typename HandleCompressedBinding, typename HandleMultibinding, typename FindNormalizedBinding,
+ typename IsValidItr, typename IsNormalizedBindingItrForConstructedObject, typename GetObjectPtr,
+ typename GetCreate, typename IsComponentWithNoArgsAlreadyExpandedInNormalizedComponent,
+ typename IsComponentWithArgsAlreadyExpandedInNormalizedComponent,
+ typename SaveFullyExpandedComponentsWithNoArgs, typename SaveFullyExpandedComponentsWithArgs,
+ typename GetComponentWithNoArgsReplacementInNormalizedComponent,
+ typename GetComponentWithArgsReplacementInNormalizedComponent,
+ typename IsLazyComponentWithNoArgsIteratorValid, typename IsLazyComponentWithArgsIteratorValid,
+ typename DereferenceLazyComponentWithNoArgsIterator, typename DereferenceLazyComponentWithArgsIterator,
+ typename SaveComponentReplacementsWithNoArgs, typename SaveComponentReplacementsWithArgs>
+ struct BindingNormalizationFunctors {
+
+ /**
+ * This should have an operator()(ComponentStorageEntry&) that will be called for each COMPRESSED_BINDING entry.
+ */
+ HandleCompressedBinding handle_compressed_binding;
+
+ /**
+ * This should have an
+ * operator()(ComponentStorageEntry& multibinding_entry, ComponentStorageEntry& multibinding_vector_creator_entry)
+ * that will be called for each multibinding entry.
+ */
+ HandleMultibinding handle_multibinding;
+
+ /**
+ * This should have a
+ * NormalizedBindingItr operator()(TypeId)
+ * that returns a NormalizedBindingItr describing whether the binding is present in a base component (if any).
+ */
+ FindNormalizedBinding find_normalized_binding;
+
+ /**
+ * This should have a
+ * bool operator()(NormalizedBindingItr)
+ */
+ IsValidItr is_valid_itr;
+
+ /**
+ * This should have a
+ * bool operator()(NormalizedBindingItr)
+ * (that can only be used when IsValidItr returns true).
+ */
+ IsNormalizedBindingItrForConstructedObject is_normalized_binding_itr_for_constructed_object;
+
+ /**
+ * This should have a
+ * ComponentStorageEntry::BindingForConstructedObject::object_ptr_t operator()(NormalizedBindingItr)
+ * (that can only be used when IsNormalizedBindingItrForConstructedObject returns true).
+ */
+ GetObjectPtr get_object_ptr;
+
+ /**
+ * This should have a
+ * ComponentStorageEntry::BindingForObjectToConstruct::create_t operator()(NormalizedBindingItr)
+ * (that can only be used when IsNormalizedBindingItrForConstructedObject returns false).
+ */
+ GetCreate get_create;
+
+ IsComponentWithNoArgsAlreadyExpandedInNormalizedComponent
+ is_component_with_no_args_already_expanded_in_normalized_component;
+ IsComponentWithArgsAlreadyExpandedInNormalizedComponent
+ is_component_with_args_already_expanded_in_normalized_component;
+ SaveFullyExpandedComponentsWithNoArgs save_fully_expanded_components_with_no_args;
+ SaveFullyExpandedComponentsWithArgs save_fully_expanded_components_with_args;
+
+ /**
+ * Gets a LazyComponentWithNoArgsIterator pointing to the replacement for the given lazy component in the normalized
+ * component (if any).
+ */
+ GetComponentWithNoArgsReplacementInNormalizedComponent
+ get_component_with_no_args_replacement_in_normalized_component;
+
+ /**
+ * Gets a LazyComponentWithArgsIterator pointing to the replacement for the given lazy component in the normalized
+ * component (if any).
+ */
+ GetComponentWithArgsReplacementInNormalizedComponent get_component_with_args_replacement_in_normalized_component;
+
+ IsLazyComponentWithNoArgsIteratorValid is_lazy_component_with_no_args_iterator_valid;
+ IsLazyComponentWithArgsIteratorValid is_lazy_component_with_args_iterator_valid;
+
+ DereferenceLazyComponentWithNoArgsIterator dereference_lazy_component_with_no_args_iterator;
+ DereferenceLazyComponentWithArgsIterator dereference_lazy_component_with_args_iterator;
+
+ SaveComponentReplacementsWithNoArgs save_component_replacements_with_no_args;
+ SaveComponentReplacementsWithArgs save_component_replacements_with_args;
+ };
+
+ /**
+ * This struct groups all data structures available during binding normalization, to avoid mentioning them in all
+ * handle*Binding functions below.
+ */
+ template <typename... Functors>
+ struct BindingNormalizationContext {
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data;
+ MemoryPool& memory_pool;
+ MemoryPool& memory_pool_for_fully_expanded_components_maps;
+ MemoryPool& memory_pool_for_component_replacements_maps;
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>& binding_data_map;
+ BindingNormalizationFunctors<Functors...> functors;
+
+ // These are in reversed order (note that toplevel_entries must also be in reverse order).
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>> entries_to_process;
+
+ // These sets contain the lazy components whose expansion has already completed.
+ LazyComponentWithNoArgsSet fully_expanded_components_with_no_args =
+ NormalizedComponentStorage::createLazyComponentWithNoArgsSet(20 /* capacity */,
+ memory_pool_for_fully_expanded_components_maps);
+ LazyComponentWithArgsSet fully_expanded_components_with_args =
+ NormalizedComponentStorage::createLazyComponentWithArgsSet(20 /* capacity */,
+ memory_pool_for_fully_expanded_components_maps);
+
+ // These sets contain the elements with kind *_END_MARKER in entries_to_process.
+ // For component with args, these sets do *not* own the objects, entries_to_process does.
+ LazyComponentWithNoArgsSet components_with_no_args_with_expansion_in_progress =
+ NormalizedComponentStorage::createLazyComponentWithNoArgsSet(20 /* capacity */, memory_pool);
+ LazyComponentWithArgsSet components_with_args_with_expansion_in_progress =
+ NormalizedComponentStorage::createLazyComponentWithArgsSet(20 /* capacity */, memory_pool);
+
+ // These sets contain Component replacements, as mappings componentToReplace->replacementComponent.
+ LazyComponentWithNoArgsReplacementMap component_with_no_args_replacements =
+ NormalizedComponentStorage::createLazyComponentWithNoArgsReplacementMap(
+ 20 /* capacity */, memory_pool_for_component_replacements_maps);
+ LazyComponentWithArgsReplacementMap component_with_args_replacements =
+ NormalizedComponentStorage::createLazyComponentWithArgsReplacementMap(
+ 20 /* capacity */, memory_pool_for_component_replacements_maps);
+
+ BindingNormalizationContext(FixedSizeVector<ComponentStorageEntry>& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
+ MemoryPool& memory_pool, MemoryPool& memory_pool_for_fully_expanded_components_maps,
+ MemoryPool& memory_pool_for_component_replacements_maps,
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>& binding_data_map,
+ BindingNormalizationFunctors<Functors...> functors);
+
+ BindingNormalizationContext(const BindingNormalizationContext&) = delete;
+ BindingNormalizationContext(BindingNormalizationContext&&) = delete;
+
+ BindingNormalizationContext& operator=(const BindingNormalizationContext&) = delete;
+ BindingNormalizationContext& operator=(BindingNormalizationContext&&) = delete;
+
+ ~BindingNormalizationContext();
+ };
+
+ template <typename... Params>
+ static void handleBindingForConstructedObject(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleBindingForObjectToConstructThatNeedsAllocation(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleBindingForObjectToConstructThatNeedsNoAllocation(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleCompressedBinding(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleMultibinding(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleMultibindingVectorCreator(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleComponentWithoutArgsEndMarker(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleComponentWithArgsEndMarker(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleReplacedLazyComponentWithArgs(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleReplacedLazyComponentWithNoArgs(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleLazyComponentWithArgs(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void handleLazyComponentWithNoArgs(BindingNormalizationContext<Params...>& context);
+
+ template <typename... Params>
+ static void performComponentReplacement(BindingNormalizationContext<Params...>& context,
+ const ComponentStorageEntry& replacement);
+
+ static void printMultipleBindingsError(TypeId type);
+
+ static void printIncompatibleComponentReplacementsError(const ComponentStorageEntry& replaced_component_entry,
+ const ComponentStorageEntry& replacement_component_entry1,
+ const ComponentStorageEntry& replacement_component_entry2);
+
+ static void
+ printComponentReplacementFailedBecauseTargetAlreadyExpanded(const ComponentStorageEntry& replaced_component_entry,
+ const ComponentStorageEntry& replacement_component_entry);
+};
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_BINDING_NORMALIZATION_H
diff --git a/include/fruit/impl/normalized_component_storage/binding_normalization.templates.h b/include/fruit/impl/normalized_component_storage/binding_normalization.templates.h
new file mode 100644
index 0000000..85888d7
--- /dev/null
+++ b/include/fruit/impl/normalized_component_storage/binding_normalization.templates.h
@@ -0,0 +1,725 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
+#define FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
+
+#ifndef IN_FRUIT_CPP_FILE
+// We don't want to include it in public headers to save some compile time.
+#error "binding_normalization.templates.h included in non-cpp file."
+#endif
+
+#include <fruit/impl/component_storage/component_storage_entry.h>
+#include <fruit/impl/normalized_component_storage/binding_normalization.h>
+#include <fruit/impl/util/type_info.h>
+
+using namespace fruit::impl;
+
+namespace fruit {
+namespace impl {
+
+template <typename... Functors>
+BindingNormalization::BindingNormalizationContext<Functors...>::BindingNormalizationContext(
+ FixedSizeVector<ComponentStorageEntry>& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
+ MemoryPool& memory_pool_for_fully_expanded_components_maps, MemoryPool& memory_pool_for_component_replacements_maps,
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>& binding_data_map,
+ BindingNormalizationFunctors<Functors...> functors)
+ : fixed_size_allocator_data(fixed_size_allocator_data), memory_pool(memory_pool),
+ memory_pool_for_fully_expanded_components_maps(memory_pool_for_fully_expanded_components_maps),
+ memory_pool_for_component_replacements_maps(memory_pool_for_component_replacements_maps),
+ binding_data_map(binding_data_map), functors(functors),
+ entries_to_process(toplevel_entries.begin(), toplevel_entries.end(),
+ ArenaAllocator<ComponentStorageEntry>(memory_pool)) {
+
+ toplevel_entries.clear();
+}
+
+template <typename... Functors>
+BindingNormalization::BindingNormalizationContext<Functors...>::~BindingNormalizationContext() {
+ FruitAssert(components_with_no_args_with_expansion_in_progress.empty());
+ FruitAssert(components_with_args_with_expansion_in_progress.empty());
+
+ for (const ComponentStorageEntry::LazyComponentWithArgs& x : fully_expanded_components_with_args) {
+ x.destroy();
+ }
+
+ for (const auto& pair : component_with_args_replacements) {
+ const LazyComponentWithArgs& replaced_component = pair.first;
+ const ComponentStorageEntry& replacement_component = pair.second;
+ replaced_component.destroy();
+ replacement_component.destroy();
+ }
+
+ for (const auto& pair : component_with_no_args_replacements) {
+ const ComponentStorageEntry& replacement_component = pair.second;
+ replacement_component.destroy();
+ }
+}
+
+template <typename... Functors>
+void BindingNormalization::normalizeBindings(FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
+ MemoryPool& memory_pool,
+ MemoryPool& memory_pool_for_fully_expanded_components_maps,
+ MemoryPool& memory_pool_for_component_replacements_maps,
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>& binding_data_map,
+ Functors... functors) {
+
+ FruitAssert(binding_data_map.empty());
+
+ using Context = BindingNormalizationContext<Functors...>;
+
+ Context context(toplevel_entries, fixed_size_allocator_data, memory_pool,
+ memory_pool_for_fully_expanded_components_maps, memory_pool_for_component_replacements_maps,
+ binding_data_map, BindingNormalizationFunctors<Functors...>{functors...});
+
+ // When we expand a lazy component, instead of removing it from the stack we change its kind (in entries_to_process)
+ // to one of the *_END_MARKER kinds. This allows to keep track of the "call stack" for the expansion.
+
+ while (!context.entries_to_process.empty()) {
+ switch (context.entries_to_process.back().kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT:
+ handleBindingForConstructedObject(context);
+ break;
+
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION:
+ handleBindingForObjectToConstructThatNeedsAllocation(context);
+ break;
+
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION:
+ handleBindingForObjectToConstructThatNeedsNoAllocation(context);
+ break;
+
+ case ComponentStorageEntry::Kind::COMPRESSED_BINDING:
+ handleCompressedBinding(context);
+ break;
+
+ case ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT:
+ case ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION:
+ case ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION:
+ handleMultibinding(context);
+ break;
+
+ case ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR:
+ handleMultibindingVectorCreator(context);
+ break;
+
+ case ComponentStorageEntry::Kind::COMPONENT_WITHOUT_ARGS_END_MARKER:
+ handleComponentWithoutArgsEndMarker(context);
+ break;
+
+ case ComponentStorageEntry::Kind::COMPONENT_WITH_ARGS_END_MARKER:
+ handleComponentWithArgsEndMarker(context);
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS:
+ handleReplacedLazyComponentWithArgs(context);
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS:
+ handleReplacedLazyComponentWithNoArgs(context);
+ break;
+
+ case ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS:
+ handleLazyComponentWithArgs(context);
+ break;
+
+ case ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS:
+ handleLazyComponentWithNoArgs(context);
+ break;
+
+ default:
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cerr << "Unexpected kind: " << (std::size_t)context.entries_to_process.back().kind << std::endl;
+#endif
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+ }
+
+ context.functors.save_fully_expanded_components_with_no_args(context.fully_expanded_components_with_no_args);
+ context.functors.save_fully_expanded_components_with_args(context.fully_expanded_components_with_args);
+ context.functors.save_component_replacements_with_no_args(context.component_with_no_args_replacements);
+ context.functors.save_component_replacements_with_args(context.component_with_args_replacements);
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void
+BindingNormalization::handleBindingForConstructedObject(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT);
+
+ context.entries_to_process.pop_back();
+
+ auto itr = context.functors.find_normalized_binding(entry.type_id);
+ if (context.functors.is_valid_itr(itr)) {
+ if (!context.functors.is_normalized_binding_itr_for_constructed_object(itr) ||
+ context.functors.get_object_ptr(itr) != entry.binding_for_constructed_object.object_ptr) {
+ printMultipleBindingsError(entry.type_id);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+ // Otherwise ok, duplicate but consistent binding.
+ return;
+ }
+
+ ComponentStorageEntry& entry_in_map = context.binding_data_map[entry.type_id];
+ if (entry_in_map.type_id.type_info != nullptr) {
+ if (entry_in_map.kind != ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT ||
+ entry.binding_for_constructed_object.object_ptr != entry_in_map.binding_for_constructed_object.object_ptr) {
+ printMultipleBindingsError(entry.type_id);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+// Otherwise ok, duplicate but consistent binding.
+
+// This avoids assertion failures when injecting a non-const pointer and there is a const duplicate binding that
+// appears before the non-const one (so we'd otherwise ignore the non-const one).
+#ifdef FRUIT_EXTRA_DEBUG
+ entry_in_map.binding_for_constructed_object.is_nonconst |= entry.binding_for_constructed_object.is_nonconst;
+#endif
+ return;
+ }
+
+ // New binding, add it to the map.
+ entry_in_map = std::move(entry);
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void BindingNormalization::handleBindingForObjectToConstructThatNeedsAllocation(
+ BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION);
+ context.entries_to_process.pop_back();
+
+ auto itr = context.functors.find_normalized_binding(entry.type_id);
+ if (context.functors.is_valid_itr(itr)) {
+ if (context.functors.is_normalized_binding_itr_for_constructed_object(itr) ||
+ context.functors.get_create(itr) != entry.binding_for_object_to_construct.create) {
+ printMultipleBindingsError(entry.type_id);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+ // Otherwise ok, duplicate but consistent binding.
+ return;
+ }
+
+ ComponentStorageEntry& entry_in_map = context.binding_data_map[entry.type_id];
+ context.fixed_size_allocator_data.addType(entry.type_id);
+ if (entry_in_map.type_id.type_info != nullptr) {
+ if (entry_in_map.kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ entry.binding_for_object_to_construct.create != entry_in_map.binding_for_object_to_construct.create) {
+ printMultipleBindingsError(entry.type_id);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+ // Otherwise ok, duplicate but consistent binding.
+ return;
+ }
+
+ // New binding, add it to the map.
+ entry_in_map = std::move(entry);
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void BindingNormalization::handleBindingForObjectToConstructThatNeedsNoAllocation(
+ BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
+ context.entries_to_process.pop_back();
+
+ auto itr = context.functors.find_normalized_binding(entry.type_id);
+ if (context.functors.is_valid_itr(itr)) {
+ if (context.functors.is_normalized_binding_itr_for_constructed_object(itr) ||
+ context.functors.get_create(itr) != entry.binding_for_object_to_construct.create) {
+ printMultipleBindingsError(entry.type_id);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+ // Otherwise ok, duplicate but consistent binding.
+ return;
+ }
+
+ ComponentStorageEntry& entry_in_map = context.binding_data_map[entry.type_id];
+ context.fixed_size_allocator_data.addExternallyAllocatedType(entry.type_id);
+ if (entry_in_map.type_id.type_info != nullptr) {
+ if (entry_in_map.kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION ||
+ entry.binding_for_object_to_construct.create != entry_in_map.binding_for_object_to_construct.create) {
+ printMultipleBindingsError(entry.type_id);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+ // Otherwise ok, duplicate but consistent binding.
+ return;
+ }
+
+ // New binding, add it to the map.
+ entry_in_map = std::move(entry);
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void
+BindingNormalization::handleCompressedBinding(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::COMPRESSED_BINDING);
+ context.entries_to_process.pop_back();
+ context.functors.handle_compressed_binding(entry);
+}
+
+template <typename... Params>
+void BindingNormalization::handleMultibinding(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT ||
+ entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
+ context.entries_to_process.pop_back();
+ FruitAssert(!context.entries_to_process.empty());
+ ComponentStorageEntry vector_creator_entry = std::move(context.entries_to_process.back());
+ context.entries_to_process.pop_back();
+ FruitAssert(vector_creator_entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR);
+ context.functors.handle_multibinding(entry, vector_creator_entry);
+}
+
+template <typename... Params>
+void BindingNormalization::handleMultibindingVectorCreator(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR);
+ context.entries_to_process.pop_back();
+ FruitAssert(!context.entries_to_process.empty());
+ ComponentStorageEntry multibinding_entry = std::move(context.entries_to_process.back());
+ context.entries_to_process.pop_back();
+ FruitAssert(multibinding_entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT ||
+ multibinding_entry.kind ==
+ ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ multibinding_entry.kind ==
+ ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
+ context.functors.handle_multibinding(multibinding_entry, entry);
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void
+BindingNormalization::handleComponentWithoutArgsEndMarker(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::COMPONENT_WITHOUT_ARGS_END_MARKER);
+ context.entries_to_process.pop_back();
+ // A lazy component expansion has completed; we now move the component from
+ // components_with_*_with_expansion_in_progress to fully_expanded_components_*.
+
+ context.components_with_no_args_with_expansion_in_progress.erase(entry.lazy_component_with_no_args);
+ context.fully_expanded_components_with_no_args.insert(std::move(entry.lazy_component_with_no_args));
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void
+BindingNormalization::handleComponentWithArgsEndMarker(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::COMPONENT_WITH_ARGS_END_MARKER);
+ context.entries_to_process.pop_back();
+ // A lazy component expansion has completed; we now move the component from
+ // components_with_*_with_expansion_in_progress to fully_expanded_components_*.
+
+ context.components_with_args_with_expansion_in_progress.erase(entry.lazy_component_with_args);
+ context.fully_expanded_components_with_args.insert(std::move(entry.lazy_component_with_args));
+}
+
+template <typename... Params>
+void BindingNormalization::handleReplacedLazyComponentWithArgs(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS);
+ ComponentStorageEntry replaced_component_entry = std::move(entry);
+ context.entries_to_process.pop_back();
+ FruitAssert(!context.entries_to_process.empty());
+
+ ComponentStorageEntry replacement_component_entry = std::move(context.entries_to_process.back());
+ context.entries_to_process.pop_back();
+ FruitAssert(replacement_component_entry.kind ==
+ ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS ||
+ replacement_component_entry.kind == ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS);
+
+ if (context.components_with_args_with_expansion_in_progress.count(entry.lazy_component_with_args) != 0 ||
+ context.fully_expanded_components_with_args.count(entry.lazy_component_with_args) != 0 ||
+ context.functors.is_component_with_args_already_expanded_in_normalized_component(
+ entry.lazy_component_with_args)) {
+ printComponentReplacementFailedBecauseTargetAlreadyExpanded(replaced_component_entry, replacement_component_entry);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ auto replacement_in_normalized_component_itr =
+ context.functors.get_component_with_args_replacement_in_normalized_component(
+ replaced_component_entry.lazy_component_with_args);
+ if (context.functors.is_lazy_component_with_args_iterator_valid(replacement_in_normalized_component_itr)) {
+ handlePreexistingLazyComponentWithArgsReplacement(
+ replaced_component_entry,
+ context.functors.dereference_lazy_component_with_args_iterator(replacement_in_normalized_component_itr),
+ replacement_component_entry);
+ return;
+ }
+
+ ComponentStorageEntry& replacement_component_entry_in_map =
+ context.component_with_args_replacements[replaced_component_entry.lazy_component_with_args];
+ if (replacement_component_entry_in_map.type_id.type_info != nullptr) {
+ handlePreexistingLazyComponentWithArgsReplacement(replaced_component_entry, replacement_component_entry_in_map,
+ replacement_component_entry);
+ return;
+ }
+
+ // We just inserted replaced_component_entry.lazy_component_with_args in the map, so it's now owned by the
+ // map.
+ replacement_component_entry_in_map = replacement_component_entry;
+}
+
+template <typename... Params>
+void BindingNormalization::handleReplacedLazyComponentWithNoArgs(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS);
+ ComponentStorageEntry replaced_component_entry = std::move(entry);
+ context.entries_to_process.pop_back();
+ FruitAssert(!context.entries_to_process.empty());
+
+ ComponentStorageEntry replacement_component_entry = std::move(context.entries_to_process.back());
+ context.entries_to_process.pop_back();
+ FruitAssert(replacement_component_entry.kind ==
+ ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS ||
+ replacement_component_entry.kind == ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS);
+
+ if (context.components_with_no_args_with_expansion_in_progress.count(entry.lazy_component_with_no_args) != 0 ||
+ context.fully_expanded_components_with_no_args.count(entry.lazy_component_with_no_args) != 0 ||
+ context.functors.is_component_with_no_args_already_expanded_in_normalized_component(
+ entry.lazy_component_with_no_args)) {
+ printComponentReplacementFailedBecauseTargetAlreadyExpanded(replaced_component_entry, replacement_component_entry);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ auto replacement_in_normalized_component_itr =
+ context.functors.get_component_with_no_args_replacement_in_normalized_component(
+ replaced_component_entry.lazy_component_with_no_args);
+ if (context.functors.is_lazy_component_with_no_args_iterator_valid(replacement_in_normalized_component_itr)) {
+ handlePreexistingLazyComponentWithNoArgsReplacement(
+ replaced_component_entry,
+ context.functors.dereference_lazy_component_with_no_args_iterator(replacement_in_normalized_component_itr),
+ replacement_component_entry);
+ return;
+ }
+
+ ComponentStorageEntry& replacement_component_entry_in_map =
+ context.component_with_no_args_replacements[replaced_component_entry.lazy_component_with_no_args];
+ if (replacement_component_entry_in_map.type_id.type_info != nullptr) {
+ handlePreexistingLazyComponentWithNoArgsReplacement(replaced_component_entry, replacement_component_entry_in_map,
+ replacement_component_entry);
+ return;
+ }
+
+ // We just inserted replaced_component_entry.lazy_component_with_args in the map, so it's now owned by the
+ // map.
+ replacement_component_entry_in_map = replacement_component_entry;
+}
+
+template <typename... Params>
+void BindingNormalization::performComponentReplacement(BindingNormalizationContext<Params...>& context,
+ const ComponentStorageEntry& replacement) {
+
+ // Instead of removing the entry from context.entries_to_process, we just replace it with the new component entry.
+
+ ComponentStorageEntry& entry = context.entries_to_process.back();
+
+ switch (replacement.kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ entry.kind = ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS;
+ entry.type_id = replacement.type_id;
+ entry.lazy_component_with_args = replacement.lazy_component_with_args.copy();
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS:
+ entry.kind = ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS;
+ entry.type_id = replacement.type_id;
+ entry.lazy_component_with_no_args = replacement.lazy_component_with_no_args;
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void
+BindingNormalization::handleLazyComponentWithArgs(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS);
+ if (context.fully_expanded_components_with_args.count(entry.lazy_component_with_args) ||
+ context.functors.is_component_with_args_already_expanded_in_normalized_component(
+ entry.lazy_component_with_args)) {
+ // This lazy component was already inserted, skip it.
+ entry.lazy_component_with_args.destroy();
+ context.entries_to_process.pop_back();
+ return;
+ }
+
+ auto replacement_component_in_normalized_component_itr =
+ context.functors.get_component_with_args_replacement_in_normalized_component(entry.lazy_component_with_args);
+ if (context.functors.is_lazy_component_with_args_iterator_valid(replacement_component_in_normalized_component_itr)) {
+ entry.lazy_component_with_args.destroy();
+ performComponentReplacement(context,
+ context.functors.dereference_lazy_component_with_args_iterator(
+ replacement_component_in_normalized_component_itr));
+ return;
+ }
+
+ auto replacement_component_itr = context.component_with_args_replacements.find(entry.lazy_component_with_args);
+ if (replacement_component_itr != context.component_with_args_replacements.end()) {
+ entry.lazy_component_with_args.destroy();
+ performComponentReplacement(context, replacement_component_itr->second);
+ return;
+ }
+
+ bool actually_inserted =
+ context.components_with_args_with_expansion_in_progress.insert(entry.lazy_component_with_args).second;
+ if (!actually_inserted) {
+ printLazyComponentInstallationLoop(context.entries_to_process, entry);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "Expanding lazy component: " << entry.lazy_component_with_args.component->getFunTypeId() << std::endl;
+#endif
+
+ // Instead of removing the component from component.lazy_components, we just change its kind to the
+ // corresponding *_END_MARKER kind.
+ // When we pop this marker, this component's expansion will be complete.
+ context.entries_to_process.back().kind = ComponentStorageEntry::Kind::COMPONENT_WITH_ARGS_END_MARKER;
+
+ // Note that this can also add other lazy components, so the resulting bindings can have a non-intuitive
+ // (although deterministic) order.
+ context.entries_to_process.back().lazy_component_with_args.component->addBindings(context.entries_to_process);
+}
+
+template <typename... Params>
+FRUIT_ALWAYS_INLINE inline void
+BindingNormalization::handleLazyComponentWithNoArgs(BindingNormalizationContext<Params...>& context) {
+ ComponentStorageEntry entry = context.entries_to_process.back();
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS);
+
+ if (context.fully_expanded_components_with_no_args.count(entry.lazy_component_with_no_args) ||
+ context.functors.is_component_with_no_args_already_expanded_in_normalized_component(
+ entry.lazy_component_with_no_args)) {
+ // This lazy component was already inserted, skip it.
+ context.entries_to_process.pop_back();
+ return;
+ }
+
+ auto replacement_component_in_normalized_component_itr =
+ context.functors.get_component_with_no_args_replacement_in_normalized_component(
+ entry.lazy_component_with_no_args);
+ if (context.functors.is_lazy_component_with_no_args_iterator_valid(
+ replacement_component_in_normalized_component_itr)) {
+ performComponentReplacement(context,
+ context.functors.dereference_lazy_component_with_no_args_iterator(
+ replacement_component_in_normalized_component_itr));
+ return;
+ }
+
+ auto replacement_component_itr = context.component_with_no_args_replacements.find(entry.lazy_component_with_no_args);
+ if (replacement_component_itr != context.component_with_no_args_replacements.end()) {
+ performComponentReplacement(context, replacement_component_itr->second);
+ return;
+ }
+
+ bool actually_inserted =
+ context.components_with_no_args_with_expansion_in_progress.insert(entry.lazy_component_with_no_args).second;
+ if (!actually_inserted) {
+ printLazyComponentInstallationLoop(context.entries_to_process, entry);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "Expanding lazy component: " << entry.type_id << std::endl;
+#endif
+
+ // Instead of removing the component from component.lazy_components, we just change its kind to the
+ // corresponding *_END_MARKER kind.
+ // When we pop this marker, this component's expansion will be complete.
+ context.entries_to_process.back().kind = ComponentStorageEntry::Kind::COMPONENT_WITHOUT_ARGS_END_MARKER;
+
+ // Note that this can also add other lazy components, so the resulting bindings can have a non-intuitive
+ // (although deterministic) order.
+ context.entries_to_process.back().lazy_component_with_no_args.addBindings(context.entries_to_process);
+}
+
+template <typename SaveCompressedBindingUndoInfo>
+std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>
+BindingNormalization::performBindingCompression(
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>&& binding_data_map,
+ HashMapWithArenaAllocator<TypeId, BindingCompressionInfo>&& compressed_bindings_map, MemoryPool& memory_pool,
+ const multibindings_vector_t& multibindings_vector,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ SaveCompressedBindingUndoInfo save_compressed_binding_undo_info) {
+ using result_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
+ result_t result = result_t(ArenaAllocator<ComponentStorageEntry>(memory_pool));
+
+ // We can't compress the binding if C is a dep of a multibinding.
+ for (const std::pair<ComponentStorageEntry, ComponentStorageEntry>& multibinding_entry_pair : multibindings_vector) {
+ const ComponentStorageEntry& entry = multibinding_entry_pair.first;
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT ||
+ entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ entry.kind ==
+ ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
+ if (entry.kind != ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT) {
+ const BindingDeps* deps = entry.multibinding_for_object_to_construct.deps;
+ FruitAssert(deps != nullptr);
+ for (std::size_t i = 0; i < deps->num_deps; ++i) {
+ compressed_bindings_map.erase(deps->deps[i]);
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "InjectorStorage: ignoring compressed binding for " << deps->deps[i]
+ << " because it's a dep of a multibinding." << std::endl;
+#endif
+ }
+ }
+ }
+
+ // We can't compress the binding if C is an exposed type (but I is likely to be exposed instead).
+ for (TypeId type : exposed_types) {
+ compressed_bindings_map.erase(type);
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "InjectorStorage: ignoring compressed binding for " << type << " because it's an exposed type."
+ << std::endl;
+#endif
+ }
+
+ // We can't compress the binding if some type X depends on C and X!=I.
+ for (auto& binding_data_map_entry : binding_data_map) {
+ TypeId x_id = binding_data_map_entry.first;
+ ComponentStorageEntry entry = binding_data_map_entry.second;
+ FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT ||
+ entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
+
+ if (entry.kind != ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT) {
+ for (std::size_t i = 0; i < entry.binding_for_object_to_construct.deps->num_deps; ++i) {
+ TypeId c_id = entry.binding_for_object_to_construct.deps->deps[i];
+ auto itr = compressed_bindings_map.find(c_id);
+ if (itr != compressed_bindings_map.end() && itr->second.i_type_id != x_id) {
+ compressed_bindings_map.erase(itr);
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "InjectorStorage: ignoring compressed binding for " << c_id << " because the type " << x_id
+ << " depends on it." << std::endl;
+#endif
+ }
+ }
+ }
+ }
+
+ // Two pairs of compressible bindings (I->C) and (C->X) can not exist (the C of a compressible binding is always bound
+ // either
+ // using constructor binding or provider binding, it can't be a binding itself). So no need to check for that.
+
+ // Now perform the binding compression.
+ for (auto& entry : compressed_bindings_map) {
+ TypeId c_id = entry.first;
+ TypeId i_id = entry.second.i_type_id;
+ auto i_binding_data = binding_data_map.find(i_id);
+ auto c_binding_data = binding_data_map.find(c_id);
+ FruitAssert(i_binding_data != binding_data_map.end());
+ FruitAssert(c_binding_data != binding_data_map.end());
+ NormalizedComponentStorage::CompressedBindingUndoInfo undo_info;
+ undo_info.i_type_id = i_id;
+ FruitAssert(i_binding_data->second.kind ==
+ ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
+ undo_info.i_binding = i_binding_data->second.binding_for_object_to_construct;
+ FruitAssert(c_binding_data->second.kind ==
+ ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION ||
+ c_binding_data->second.kind ==
+ ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION);
+ undo_info.c_binding = c_binding_data->second.binding_for_object_to_construct;
+ save_compressed_binding_undo_info(c_id, undo_info);
+
+ // Note that even if I is the one that remains, C is the one that will be allocated, not I.
+
+ i_binding_data->second.kind = c_binding_data->second.kind;
+ i_binding_data->second.binding_for_object_to_construct.create = entry.second.create_i_with_compression;
+ i_binding_data->second.binding_for_object_to_construct.deps =
+ c_binding_data->second.binding_for_object_to_construct.deps;
+#ifdef FRUIT_EXTRA_DEBUG
+ i_binding_data->second.binding_for_object_to_construct.is_nonconst |=
+ c_binding_data->second.binding_for_object_to_construct.is_nonconst;
+#endif
+
+ binding_data_map.erase(c_binding_data);
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "InjectorStorage: performing binding compression for the edge " << i_id << "->" << c_id << std::endl;
+#endif
+ }
+
+ // Copy the normalized bindings into the result vector.
+ result.reserve(binding_data_map.size());
+ for (auto& p : binding_data_map) {
+ result.push_back(p.second);
+ }
+
+ return result;
+}
+
+template <typename SaveCompressedBindingUndoInfo, typename SaveFullyExpandedComponentsWithNoArgs,
+ typename SaveFullyExpandedComponentsWithArgs, typename SaveComponentReplacementsWithNoArgs,
+ typename SaveComponentReplacementsWithArgs>
+void BindingNormalization::normalizeBindingsWithBindingCompression(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
+ MemoryPool& memory_pool_for_fully_expanded_components_maps, MemoryPool& memory_pool_for_component_replacements_maps,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings,
+ SaveCompressedBindingUndoInfo save_compressed_binding_undo_info,
+ SaveFullyExpandedComponentsWithNoArgs save_fully_expanded_components_with_no_args,
+ SaveFullyExpandedComponentsWithArgs save_fully_expanded_components_with_args,
+ SaveComponentReplacementsWithNoArgs save_component_replacements_with_no_args,
+ SaveComponentReplacementsWithArgs save_component_replacements_with_args) {
+
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry> binding_data_map =
+ createHashMapWithArenaAllocator<TypeId, ComponentStorageEntry>(20 /* capacity */, memory_pool);
+ // CtypeId -> (ItypeId, bindingData)
+ HashMapWithArenaAllocator<TypeId, BindingNormalization::BindingCompressionInfo> compressed_bindings_map =
+ createHashMapWithArenaAllocator<TypeId, BindingCompressionInfo>(20 /* capacity */, memory_pool);
+
+ multibindings_vector_t multibindings_vector =
+ multibindings_vector_t(ArenaAllocator<multibindings_vector_elem_t>(memory_pool));
+
+ struct DummyIterator {};
+
+ normalizeBindings(
+ std::move(toplevel_entries), fixed_size_allocator_data, memory_pool,
+ memory_pool_for_fully_expanded_components_maps, memory_pool_for_component_replacements_maps, binding_data_map,
+ [&compressed_bindings_map](ComponentStorageEntry entry) {
+ BindingCompressionInfo& compression_info = compressed_bindings_map[entry.compressed_binding.c_type_id];
+ compression_info.i_type_id = entry.type_id;
+ compression_info.create_i_with_compression = entry.compressed_binding.create;
+ },
+ [&multibindings_vector](ComponentStorageEntry multibinding, ComponentStorageEntry multibinding_vector_creator) {
+ multibindings_vector.emplace_back(multibinding, multibinding_vector_creator);
+ },
+ [](TypeId) { return DummyIterator(); }, [](DummyIterator) { return false; }, [](DummyIterator) { return false; },
+ [](DummyIterator) { return nullptr; }, [](DummyIterator) { return nullptr; },
+ [](const LazyComponentWithNoArgs&) { return false; }, [](const LazyComponentWithArgs&) { return false; },
+ save_fully_expanded_components_with_no_args, save_fully_expanded_components_with_args,
+ [](const LazyComponentWithNoArgs&) { return (ComponentStorageEntry*)nullptr; },
+ [](const LazyComponentWithArgs&) { return (ComponentStorageEntry*)nullptr; },
+ [](ComponentStorageEntry*) { return false; }, [](ComponentStorageEntry*) { return false; },
+ [](ComponentStorageEntry* p) { return *p; }, [](ComponentStorageEntry* p) { return *p; },
+ save_component_replacements_with_no_args, save_component_replacements_with_args);
+
+ bindings_vector = BindingNormalization::performBindingCompression(
+ std::move(binding_data_map), std::move(compressed_bindings_map), memory_pool, multibindings_vector, exposed_types,
+ save_compressed_binding_undo_info);
+
+ addMultibindings(multibindings, fixed_size_allocator_data, multibindings_vector);
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
diff --git a/include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h b/include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h
new file mode 100644
index 0000000..07022c5
--- /dev/null
+++ b/include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_NORMALIZED_BINDINGS_DEFN_H
+#define FRUIT_NORMALIZED_BINDINGS_DEFN_H
+
+#include <fruit/impl/normalized_component_storage/normalized_bindings.h>
+
+namespace fruit {
+namespace impl {
+
+inline NormalizedBinding::NormalizedBinding(ComponentStorageEntry entry) {
+ switch (entry.kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT:
+ object = entry.binding_for_constructed_object.object_ptr;
+#ifdef FRUIT_EXTRA_DEBUG
+ is_nonconst = entry.binding_for_constructed_object.is_nonconst;
+#endif
+ break;
+
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION:
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION:
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION:
+ create = entry.binding_for_object_to_construct.create;
+#ifdef FRUIT_EXTRA_DEBUG
+ is_nonconst = entry.binding_for_object_to_construct.is_nonconst;
+#endif
+ break;
+
+ default:
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cerr << "Unexpected kind: " << (std::size_t)entry.kind << std::endl;
+#endif
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_NORMALIZED_BINDINGS_DEFN_H
diff --git a/include/fruit/impl/normalized_component_storage/normalized_bindings.h b/include/fruit/impl/normalized_component_storage/normalized_bindings.h
new file mode 100644
index 0000000..cf7876a
--- /dev/null
+++ b/include/fruit/impl/normalized_component_storage/normalized_bindings.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_NORMALIZED_BINDINGS_H
+#define FRUIT_NORMALIZED_BINDINGS_H
+
+#include <fruit/impl/component_storage/component_storage_entry.h>
+#include <memory>
+
+namespace fruit {
+namespace impl {
+
+/** A single normalized binding (not a multibinding). */
+struct NormalizedBinding {
+ union {
+ // Valid iff this is a terminal node (in the SemistaticGraph that contains this NormalizedBinding object).
+ ComponentStorageEntry::BindingForConstructedObject::object_ptr_t object;
+
+ // Valid iff this is not a terminal node (in the SemistaticGraph that contains this NormalizedBinding object).
+ ComponentStorageEntry::BindingForObjectToConstruct::create_t create;
+ };
+
+#ifdef FRUIT_EXTRA_DEBUG
+ bool is_nonconst;
+#endif
+
+ NormalizedBinding() = default;
+
+ // Converts a ComponentStorageEntry to a NormalizedBinding.
+ // This is only supported for entries with these kinds:
+ // * BINDING_FOR_CONSTRUCTED_OBJECT,
+ // * BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION,
+ // * BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION,
+ explicit NormalizedBinding(ComponentStorageEntry entry);
+};
+
+/** A single normalized multibinding. */
+struct NormalizedMultibinding {
+
+ bool is_constructed;
+
+ union {
+ // Valid iff is_constructed==true.
+ ComponentStorageEntry::MultibindingForConstructedObject::object_ptr_t object;
+
+ // Valid iff is_constructed==false.
+ ComponentStorageEntry::MultibindingForObjectToConstruct::create_t create;
+ };
+};
+
+/** This stores all multibindings for a given type_id. */
+struct NormalizedMultibindingSet {
+
+ // Can be empty, but only if v is present and non-empty.
+ std::vector<NormalizedMultibinding> elems;
+
+ // TODO: Check this comment.
+ // Returns the std::vector<T*> of instances, or nullptr if none.
+ // Caches the result in the `v' member.
+ ComponentStorageEntry::MultibindingVectorCreator::get_multibindings_vector_t get_multibindings_vector;
+
+ // A (casted) pointer to the std::vector<T*> of objects, or nullptr if the vector hasn't been constructed yet.
+ // Can't be empty.
+ std::shared_ptr<char> v;
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/normalized_component_storage/normalized_bindings.defn.h>
+
+#endif // FRUIT_NORMALIZED_BINDINGS_H
diff --git a/include/fruit/impl/normalized_component_storage/normalized_component_storage.defn.h b/include/fruit/impl/normalized_component_storage/normalized_component_storage.defn.h
new file mode 100644
index 0000000..a0442cf
--- /dev/null
+++ b/include/fruit/impl/normalized_component_storage/normalized_component_storage.defn.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_NORMALIZED_COMPONENT_STORAGE_DEFN_H
+#define FRUIT_NORMALIZED_COMPONENT_STORAGE_DEFN_H
+
+#include <fruit/impl/normalized_component_storage/normalized_component_storage.h>
+
+namespace fruit {
+namespace impl {
+
+inline NormalizedComponentStorage::LazyComponentWithNoArgsSet
+NormalizedComponentStorage::createLazyComponentWithNoArgsSet(size_t capacity, MemoryPool& memory_pool) {
+ return createHashSetWithArenaAllocatorAndCustomFunctors<LazyComponentWithNoArgs>(
+ capacity, memory_pool, NormalizedComponentStorage::HashLazyComponentWithNoArgs(),
+ std::equal_to<LazyComponentWithNoArgs>());
+}
+
+inline NormalizedComponentStorage::LazyComponentWithArgsSet
+NormalizedComponentStorage::createLazyComponentWithArgsSet(size_t capacity, MemoryPool& memory_pool) {
+ return createHashSetWithArenaAllocatorAndCustomFunctors<LazyComponentWithArgs>(
+ capacity, memory_pool, NormalizedComponentStorage::HashLazyComponentWithArgs(),
+ NormalizedComponentStorage::LazyComponentWithArgsEqualTo());
+}
+
+inline NormalizedComponentStorage::LazyComponentWithNoArgsReplacementMap
+NormalizedComponentStorage::createLazyComponentWithNoArgsReplacementMap(size_t capacity, MemoryPool& memory_pool) {
+ return createHashMapWithArenaAllocatorAndCustomFunctors<LazyComponentWithNoArgs, ComponentStorageEntry>(
+ capacity, memory_pool, NormalizedComponentStorage::HashLazyComponentWithNoArgs(),
+ std::equal_to<LazyComponentWithNoArgs>());
+}
+
+inline NormalizedComponentStorage::LazyComponentWithArgsReplacementMap
+NormalizedComponentStorage::createLazyComponentWithArgsReplacementMap(size_t capacity, MemoryPool& memory_pool) {
+ return createHashMapWithArenaAllocatorAndCustomFunctors<LazyComponentWithArgs, ComponentStorageEntry>(
+ capacity, memory_pool, NormalizedComponentStorage::HashLazyComponentWithArgs(),
+ NormalizedComponentStorage::LazyComponentWithArgsEqualTo());
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_NORMALIZED_COMPONENT_STORAGE_DEFN_H
diff --git a/include/fruit/impl/normalized_component_storage/normalized_component_storage.h b/include/fruit/impl/normalized_component_storage/normalized_component_storage.h
new file mode 100644
index 0000000..d580b59
--- /dev/null
+++ b/include/fruit/impl/normalized_component_storage/normalized_component_storage.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_NORMALIZED_COMPONENT_STORAGE_H
+#define FRUIT_NORMALIZED_COMPONENT_STORAGE_H
+
+#ifndef IN_FRUIT_CPP_FILE
+// We don't want to include it in public headers to save some compile time.
+#error "normalized_component_storage.h included in non-cpp file."
+#endif
+
+#include <fruit/impl/component_storage/component_storage_entry.h>
+#include <fruit/impl/data_structures/fixed_size_allocator.h>
+#include <fruit/impl/data_structures/semistatic_graph.h>
+#include <fruit/impl/data_structures/semistatic_map.h>
+#include <fruit/impl/fruit_internal_forward_decls.h>
+#include <fruit/impl/normalized_component_storage/normalized_bindings.h>
+#include <fruit/impl/util/hash_helpers.h>
+#include <fruit/impl/util/type_info.h>
+
+#include <memory>
+#include <unordered_map>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * Similar to ComponentStorage, but used a normalized representation to minimize the amount
+ * of work needed to turn this into an injector. However, adding bindings to a normalized
+ * component is slower than adding them to a simple component.
+ */
+class NormalizedComponentStorage {
+public:
+ struct CompressedBindingUndoInfo {
+ TypeId i_type_id;
+ ComponentStorageEntry::BindingForObjectToConstruct i_binding;
+ ComponentStorageEntry::BindingForObjectToConstruct c_binding;
+ };
+
+ // A map from c_type_id to the corresponding CompressedBindingUndoInfo (if binding compression was performed for
+ // c_type_id).
+ using BindingCompressionInfoMap = HashMapWithArenaAllocator<TypeId, CompressedBindingUndoInfo>;
+ using BindingCompressionInfoMapAllocator = BindingCompressionInfoMap::allocator_type;
+
+ using LazyComponentWithNoArgs = ComponentStorageEntry::LazyComponentWithNoArgs;
+ using LazyComponentWithArgs = ComponentStorageEntry::LazyComponentWithArgs;
+
+ struct HashLazyComponentWithNoArgs {
+ std::size_t operator()(const LazyComponentWithNoArgs& x) const {
+ return x.hashCode();
+ }
+ };
+
+ struct LazyComponentWithArgsEqualTo {
+ bool operator()(const LazyComponentWithArgs& x, const LazyComponentWithArgs& y) const {
+ return *x.component == *y.component;
+ }
+ };
+
+ struct HashLazyComponentWithArgs {
+ std::size_t operator()(const LazyComponentWithArgs& x) const {
+ return x.component->hashCode();
+ }
+ };
+
+ using LazyComponentWithNoArgsSet = HashSetWithArenaAllocator<LazyComponentWithNoArgs, HashLazyComponentWithNoArgs,
+ std::equal_to<LazyComponentWithNoArgs>>;
+ using LazyComponentWithArgsSet =
+ HashSetWithArenaAllocator<LazyComponentWithArgs, HashLazyComponentWithArgs, LazyComponentWithArgsEqualTo>;
+
+ using LazyComponentWithNoArgsReplacementMap =
+ HashMapWithArenaAllocator<LazyComponentWithNoArgs, ComponentStorageEntry,
+ NormalizedComponentStorage::HashLazyComponentWithNoArgs,
+ std::equal_to<LazyComponentWithNoArgs>>;
+ using LazyComponentWithArgsReplacementMap =
+ HashMapWithArenaAllocator<LazyComponentWithArgs, ComponentStorageEntry,
+ NormalizedComponentStorage::HashLazyComponentWithArgs,
+ NormalizedComponentStorage::LazyComponentWithArgsEqualTo>;
+
+ static LazyComponentWithNoArgsSet createLazyComponentWithNoArgsSet(size_t capacity, MemoryPool& memory_pool);
+ static LazyComponentWithArgsSet createLazyComponentWithArgsSet(size_t capacity, MemoryPool& memory_pool);
+
+ static LazyComponentWithNoArgsReplacementMap createLazyComponentWithNoArgsReplacementMap(size_t capacity,
+ MemoryPool& memory_pool);
+ static LazyComponentWithArgsReplacementMap createLazyComponentWithArgsReplacementMap(size_t capacity,
+ MemoryPool& memory_pool);
+
+private:
+ // A graph with types as nodes (each node stores the BindingData for the type) and dependencies as edges.
+ // For types that have a constructed object already, the corresponding node is stored as terminal node.
+ SemistaticGraph<TypeId, NormalizedBinding> bindings;
+
+ // Maps the type index of a type T to the corresponding NormalizedMultibindingSet.
+ std::unordered_map<TypeId, NormalizedMultibindingSet> multibindings;
+
+ // Contains data on the set of types that can be allocated using this component.
+ FixedSizeAllocator::FixedSizeAllocatorData fixed_size_allocator_data;
+
+ // The MemoryPool used to allocate bindingCompressionInfoMap, fully_expanded_components_with_no_args and
+ // fully_expanded_components_with_args.
+ MemoryPool normalized_component_memory_pool;
+
+ // Stores information on binding compression that was performed in bindings of this object.
+ // See also the documentation for BindingCompressionInfoMap.
+ BindingCompressionInfoMap binding_compression_info_map;
+
+ LazyComponentWithNoArgsSet fully_expanded_components_with_no_args;
+ LazyComponentWithArgsSet fully_expanded_components_with_args;
+
+ LazyComponentWithNoArgsReplacementMap component_with_no_args_replacements;
+ LazyComponentWithArgsReplacementMap component_with_args_replacements;
+
+ friend class InjectorStorage;
+ friend class BindingNormalization;
+
+public:
+ using Graph = SemistaticGraph<TypeId, NormalizedBinding>;
+
+ NormalizedComponentStorage() = delete;
+
+ NormalizedComponentStorage(NormalizedComponentStorage&&) = delete;
+ NormalizedComponentStorage(const NormalizedComponentStorage&) = delete;
+
+ NormalizedComponentStorage& operator=(NormalizedComponentStorage&&) = delete;
+ NormalizedComponentStorage& operator=(const NormalizedComponentStorage&) = delete;
+
+ // These are just used as tags to select the desired constructor.
+ struct WithUndoableCompression {};
+ struct WithPermanentCompression {};
+
+ /**
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ NormalizedComponentStorage(ComponentStorage&& component,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types, MemoryPool& memory_pool,
+ WithUndoableCompression);
+
+ /**
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ NormalizedComponentStorage(ComponentStorage&& component,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types, MemoryPool& memory_pool,
+ WithPermanentCompression);
+
+ // We don't use the default destructor because that will require the inclusion of
+ // the Boost's hashmap header. We define this in the cpp file instead.
+ ~NormalizedComponentStorage();
+};
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/normalized_component_storage/normalized_component_storage.defn.h>
+
+#endif // FRUIT_NORMALIZED_COMPONENT_STORAGE_H
diff --git a/include/fruit/impl/normalized_component_storage/normalized_component_storage_holder.h b/include/fruit/impl/normalized_component_storage/normalized_component_storage_holder.h
new file mode 100644
index 0000000..6b1b7e9
--- /dev/null
+++ b/include/fruit/impl/normalized_component_storage/normalized_component_storage_holder.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_NORMALIZED_COMPONENT_STORAGE_HOLDER_H
+#define FRUIT_NORMALIZED_COMPONENT_STORAGE_HOLDER_H
+
+#include <fruit/fruit_forward_decls.h>
+#include <fruit/impl/data_structures/arena_allocator.h>
+#include <fruit/impl/data_structures/memory_pool.h>
+#include <fruit/impl/fruit_internal_forward_decls.h>
+#include <memory>
+
+namespace fruit {
+namespace impl {
+
+/**
+ * A wrapper around NormalizedComponentStorage, holding the NormalizedComponentStorage
+ * through a unique_ptr so that we don't need to include NormalizedComponentStorage in
+ * fruit.h.
+ */
+class NormalizedComponentStorageHolder {
+private:
+ std::unique_ptr<NormalizedComponentStorage> storage;
+
+ friend class InjectorStorage;
+
+ template <typename... P>
+ friend class fruit::Injector;
+
+public:
+ // These are just used as tags to select the desired constructor.
+ struct WithUndoableCompression {};
+ struct WithPermanentCompression {};
+
+ NormalizedComponentStorageHolder() = default;
+
+ /**
+ * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
+ */
+ NormalizedComponentStorageHolder(ComponentStorage&& component,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ MemoryPool& memory_pool, WithUndoableCompression);
+
+ NormalizedComponentStorageHolder(NormalizedComponentStorage&&) = delete;
+ NormalizedComponentStorageHolder(const NormalizedComponentStorage&) = delete;
+
+ NormalizedComponentStorageHolder& operator=(NormalizedComponentStorageHolder&&) = delete;
+ NormalizedComponentStorageHolder& operator=(const NormalizedComponentStorageHolder&) = default;
+
+ // We don't use the default destructor because that would require the inclusion of
+ // normalized_component_storage.h. We define this in the cpp file instead.
+ ~NormalizedComponentStorageHolder();
+};
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_NORMALIZED_COMPONENT_STORAGE_HOLDER_H
diff --git a/include/fruit/impl/provider.defn.h b/include/fruit/impl/provider.defn.h
new file mode 100644
index 0000000..6f65bbf
--- /dev/null
+++ b/include/fruit/impl/provider.defn.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_PROVIDER_DEFN_H
+#define FRUIT_PROVIDER_DEFN_H
+
+#include <fruit/impl/injector/injector_storage.h>
+
+// Redundant, but makes KDevelop happy.
+#include <fruit/provider.h>
+
+namespace fruit {
+
+template <typename C>
+inline Provider<C>::Provider(fruit::impl::InjectorStorage* storage,
+ fruit::impl::InjectorStorage::Graph::node_iterator itr)
+ : storage(storage), itr(itr) {}
+
+template <typename C>
+inline C* Provider<C>::get() {
+ return get<C*>();
+}
+
+namespace impl {
+namespace meta {
+
+template <typename C>
+struct ProviderImplHelper {
+
+ template <typename T>
+ using CheckGet = Eval<PropagateError(
+ CheckInjectableType(RemoveAnnotations(Type<T>)),
+ If(Not(IsSame(GetClassForType(Type<T>), RemoveConstFromType(Type<C>))),
+ ConstructError(Id<TypeNotProvidedErrorTag>, Type<T>),
+ If(And(TypeInjectionRequiresNonConstBinding(Type<T>), Not(IsSame(Id<GetClassForType(Type<T>)>, Type<C>))),
+ ConstructError(TypeProvidedAsConstOnlyErrorTag, Type<T>), None)))>;
+};
+
+} // namespace meta
+} // namespace impl
+
+template <typename C>
+template <typename T>
+inline T Provider<C>::get() {
+ using E = typename fruit::impl::meta::ProviderImplHelper<C>::template CheckGet<T>;
+ (void)typename fruit::impl::meta::CheckIfError<E>::type();
+ return storage->template get<T>(itr);
+}
+
+template <typename C>
+template <typename T>
+inline Provider<C>::operator T() {
+ return get<T>();
+}
+
+} // namespace fruit
+
+#endif // FRUIT_PROVIDER_DEFN_H
diff --git a/include/fruit/impl/util/call_with_tuple.h b/include/fruit/impl/util/call_with_tuple.h
new file mode 100644
index 0000000..763e6a6
--- /dev/null
+++ b/include/fruit/impl/util/call_with_tuple.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_CALL_WITH_TUPLE_H
+#define FRUIT_CALL_WITH_TUPLE_H
+
+#include <fruit/impl/meta/vector.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename IntVector, typename Result, typename ArgsTuple>
+struct CallWithTupleHelper;
+
+template <typename... Ints, typename Result, typename... Args>
+struct CallWithTupleHelper<fruit::impl::meta::Vector<Ints...>, Result, std::tuple<Args...>> {
+ Result operator()(Result (*fun)(Args...), std::tuple<Args...> args) {
+ // This parameter *is* used, but when the tuple is empty some compilers report is as unused.
+ (void)args;
+ return fun(std::get<fruit::impl::meta::getIntValue<Ints>()>(args)...);
+ }
+};
+
+template <typename Result, typename... Args>
+inline Result callWithTuple(Result (*fun)(Args...), std::tuple<Args...> args) {
+ using IntVector =
+ fruit::impl::meta::Eval<fruit::impl::meta::GenerateIntSequence(fruit::impl::meta::Int<sizeof...(Args)>)>;
+ return CallWithTupleHelper<IntVector, Result, std::tuple<Args...>>()(fun, args);
+}
+}
+}
+
+#endif // FRUIT_CALL_WITH_TUPLE_H
diff --git a/include/fruit/impl/util/demangle_type_name.h b/include/fruit/impl/util/demangle_type_name.h
new file mode 100644
index 0000000..59687db
--- /dev/null
+++ b/include/fruit/impl/util/demangle_type_name.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_DEMANGLE_TYPE_NAME_H
+#define FRUIT_DEMANGLE_TYPE_NAME_H
+
+#include <string>
+
+std::string demangleTypeName(const char* name);
+
+#endif // FRUIT_DEMANGLE_TYPE_NAME_H
diff --git a/include/fruit/impl/util/hash_codes.defn.h b/include/fruit/impl/util/hash_codes.defn.h
new file mode 100644
index 0000000..a860e24
--- /dev/null
+++ b/include/fruit/impl/util/hash_codes.defn.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_HASH_CODES_DEFN_H
+#define FRUIT_HASH_CODES_DEFN_H
+
+#include <fruit/impl/util/hash_codes.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename Tuple, int index>
+inline std::size_t hashTupleElement(const Tuple& x) {
+ const auto& value = std::get<index>(x);
+ using T = typename std::remove_const<typename std::remove_reference<decltype(value)>::type>::type;
+ return std::hash<T>()(value);
+}
+
+template <typename Tuple, int last_index>
+struct HashTupleHelper;
+
+template <int last_index>
+struct HashTupleHelper<std::tuple<>, last_index> {
+ std::size_t operator()(const std::tuple<>&) {
+ return 0;
+ }
+};
+
+template <typename... Args>
+struct HashTupleHelper<std::tuple<Args...>, 1> {
+ std::size_t operator()(const std::tuple<Args...>& x) {
+ return hashTupleElement<std::tuple<Args...>, 0>(x);
+ }
+};
+
+template <typename... Args, int last_index>
+struct HashTupleHelper<std::tuple<Args...>, last_index> {
+ std::size_t operator()(const std::tuple<Args...>& x) {
+ return combineHashes(hashTupleElement<std::tuple<Args...>, last_index - 1>(x),
+ HashTupleHelper<std::tuple<Args...>, last_index - 1>()(x));
+ }
+};
+
+template <typename... Args>
+inline std::size_t hashTuple(const std::tuple<Args...>& x) {
+ return HashTupleHelper<std::tuple<Args...>, sizeof...(Args)>()(x);
+}
+
+inline std::size_t combineHashes(std::size_t h1, std::size_t h2) {
+ h1 ^= h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2);
+ return h1;
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_HASH_CODES_DEFN_H
diff --git a/include/fruit/impl/util/hash_codes.h b/include/fruit/impl/util/hash_codes.h
new file mode 100644
index 0000000..b9213db
--- /dev/null
+++ b/include/fruit/impl/util/hash_codes.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_HASH_CODES_H
+#define FRUIT_HASH_CODES_H
+
+#include <tuple>
+
+namespace fruit {
+namespace impl {
+
+template <typename... Args>
+std::size_t hashTuple(const std::tuple<Args...>& x);
+
+std::size_t combineHashes(std::size_t h1, std::size_t h2);
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/util/hash_codes.defn.h>
+
+#endif // FRUIT_HASH_CODES_H
diff --git a/include/fruit/impl/util/hash_helpers.defn.h b/include/fruit/impl/util/hash_helpers.defn.h
new file mode 100644
index 0000000..e75ec4a
--- /dev/null
+++ b/include/fruit/impl/util/hash_helpers.defn.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_HASH_HELPERS_DEFN_H
+#define FRUIT_HASH_HELPERS_DEFN_H
+
+#include <fruit/impl/meta/vector.h>
+#include <fruit/impl/util/hash_helpers.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename T>
+inline HashSet<T> createHashSet() {
+ return createHashSet<T>(10);
+}
+
+template <typename T>
+inline HashSet<T> createHashSet(size_t capacity) {
+ return HashSet<T>(capacity, std::hash<T>());
+}
+
+template <typename T>
+inline HashSetWithArenaAllocator<T> createHashSetWithArenaAllocator(size_t capacity, MemoryPool& memory_pool) {
+ return HashSetWithArenaAllocator<T>(capacity, std::hash<T>(), std::equal_to<T>(), ArenaAllocator<T>(memory_pool));
+}
+
+template <typename T, typename Hasher, typename EqualityComparator>
+inline HashSetWithArenaAllocator<T, Hasher, EqualityComparator>
+createHashSetWithArenaAllocatorAndCustomFunctors(size_t capacity, MemoryPool& memory_pool, Hasher hasher,
+ EqualityComparator equality_comparator) {
+ return HashSetWithArenaAllocator<T, Hasher, EqualityComparator>(capacity, hasher, equality_comparator,
+ ArenaAllocator<T>(memory_pool));
+}
+
+template <typename Key, typename Value>
+inline HashMap<Key, Value> createHashMap() {
+ return createHashMap<Key, Value>(10);
+}
+
+template <typename Key, typename Value>
+inline HashMap<Key, Value> createHashMap(size_t capacity) {
+ return HashMap<Key, Value>(capacity, std::hash<Key>());
+}
+
+template <typename Key, typename Value>
+inline HashMapWithArenaAllocator<Key, Value> createHashMapWithArenaAllocator(std::size_t capacity,
+ MemoryPool& memory_pool) {
+ return createHashMapWithArenaAllocatorAndCustomFunctors<Key, Value>(capacity, memory_pool, std::hash<Key>(),
+ std::equal_to<Key>());
+}
+
+template <typename Key, typename Value, typename Hasher, typename EqualityComparator>
+inline HashMapWithArenaAllocator<Key, Value, Hasher, EqualityComparator>
+createHashMapWithArenaAllocatorAndCustomFunctors(size_t capacity, MemoryPool& memory_pool, Hasher hasher,
+ EqualityComparator equality_comparator) {
+ return HashMapWithArenaAllocator<Key, Value, Hasher, EqualityComparator>(
+ capacity, hasher, equality_comparator, ArenaAllocator<std::pair<const Key, Value>>(memory_pool));
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_HASH_HELPERS_DEFN_H
diff --git a/include/fruit/impl/util/hash_helpers.h b/include/fruit/impl/util/hash_helpers.h
new file mode 100644
index 0000000..a060aac
--- /dev/null
+++ b/include/fruit/impl/util/hash_helpers.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_HASH_HELPERS_H
+#define FRUIT_HASH_HELPERS_H
+
+#include <fruit/impl/data_structures/arena_allocator.h>
+#include <fruit/impl/fruit-config.h>
+
+#ifndef IN_FRUIT_CPP_FILE
+// We don't want to include it in public headers to save some compile time.
+#error "hash_helpers included in non-cpp file."
+#endif
+
+#if FRUIT_USES_BOOST
+#include <boost/unordered_map.hpp>
+#include <boost/unordered_set.hpp>
+#else
+#include <unordered_map>
+#include <unordered_set>
+#endif
+
+namespace fruit {
+namespace impl {
+
+#if FRUIT_USES_BOOST
+template <typename T, typename Hasher = std::hash<T>, typename EqualityComparator = std::equal_to<T>>
+using HashSet = boost::unordered_set<T, Hasher, EqualityComparator>;
+
+template <typename T, typename Hasher = std::hash<T>, typename EqualityComparator = std::equal_to<T>>
+using HashSetWithArenaAllocator = boost::unordered_set<T, Hasher, EqualityComparator, ArenaAllocator<T>>;
+
+template <typename Key, typename Value, typename Hasher = std::hash<Key>>
+using HashMap = boost::unordered_map<Key, Value, Hasher>;
+
+template <typename Key, typename Value, typename Hasher = std::hash<Key>,
+ typename EqualityComparator = std::equal_to<Key>>
+using HashMapWithArenaAllocator =
+ boost::unordered_map<Key, Value, Hasher, EqualityComparator, ArenaAllocator<std::pair<const Key, Value>>>;
+
+#else
+template <typename T, typename Hasher = std::hash<T>, typename EqualityComparator = std::equal_to<T>>
+using HashSet = std::unordered_set<T, Hasher, EqualityComparator>;
+
+template <typename T, typename Hasher = std::hash<T>, typename EqualityComparator = std::equal_to<T>>
+using HashSetWithArenaAllocator = std::unordered_set<T, Hasher, EqualityComparator, ArenaAllocator<T>>;
+
+template <typename Key, typename Value, typename Hasher = std::hash<Key>>
+using HashMap = std::unordered_map<Key, Value, Hasher>;
+
+template <typename Key, typename Value, typename Hasher = std::hash<Key>,
+ typename EqualityComparator = std::equal_to<Key>>
+using HashMapWithArenaAllocator =
+ std::unordered_map<Key, Value, Hasher, EqualityComparator, ArenaAllocator<std::pair<const Key, Value>>>;
+
+#endif
+
+template <typename T>
+HashSet<T> createHashSet();
+
+template <typename T>
+HashSet<T> createHashSet(size_t capacity);
+
+template <typename T>
+HashSetWithArenaAllocator<T> createHashSetWithArenaAllocator(size_t capacity, MemoryPool& memory_pool);
+
+template <typename T, typename Hasher, typename EqualityComparator>
+HashSetWithArenaAllocator<T, Hasher, EqualityComparator>
+createHashSetWithArenaAllocatorAndCustomFunctors(size_t capacity, MemoryPool& memory_pool, Hasher, EqualityComparator);
+
+template <typename Key, typename Value>
+HashMap<Key, Value> createHashMap();
+
+template <typename Key, typename Value>
+HashMap<Key, Value> createHashMap(size_t capacity);
+
+template <typename Key, typename Value>
+HashMapWithArenaAllocator<Key, Value> createHashMapWithArenaAllocator(size_t capacity, MemoryPool& memory_pool);
+
+template <typename Key, typename Value, typename Hasher, typename EqualityComparator>
+HashMapWithArenaAllocator<Key, Value, Hasher, EqualityComparator>
+createHashMapWithArenaAllocatorAndCustomFunctors(size_t capacity, MemoryPool& memory_pool, Hasher, EqualityComparator);
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/util/hash_helpers.defn.h>
+
+#endif // FRUIT_HASH_HELPERS_H
diff --git a/include/fruit/impl/util/lambda_invoker.h b/include/fruit/impl/util/lambda_invoker.h
new file mode 100644
index 0000000..2c8d63e
--- /dev/null
+++ b/include/fruit/impl/util/lambda_invoker.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_LAMBDA_INVOKER_H
+#define FRUIT_LAMBDA_INVOKER_H
+
+#include <fruit/impl/fruit-config.h>
+#include <fruit/impl/injection_errors.h>
+#include <fruit/impl/meta/errors.h>
+#include <fruit/impl/meta/metaprogramming.h>
+#include <fruit/impl/meta/signatures.h>
+#include <fruit/impl/meta/wrappers.h>
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+
+namespace fruit {
+namespace impl {
+
+class LambdaInvoker {
+public:
+ template <typename F, typename... Args>
+ FRUIT_ALWAYS_INLINE static auto invoke(Args&&... args)
+ -> decltype(std::declval<const F&>()(std::declval<Args>()...)) {
+ // We reinterpret-cast a char[] to avoid de-referencing nullptr, which would technically be
+ // undefined behavior (even though we would not access any data there anyway).
+ // Sharing this buffer for different types F would also be undefined behavior since we'd break
+ // strict aliasing between those types.
+ alignas(alignof(F)) static char buf[1];
+
+ FruitStaticAssert(fruit::impl::meta::IsEmpty(fruit::impl::meta::Type<F>));
+ FruitStaticAssert(fruit::impl::meta::IsTriviallyCopyable(fruit::impl::meta::Type<F>));
+ // Since `F' is empty, a valid value of type F is already stored at the beginning of buf.
+ F* f = reinterpret_cast<F*>(buf);
+ return (*f)(std::forward<Args>(args)...);
+ }
+};
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_LAMBDA_INVOKER_H
diff --git a/include/fruit/impl/util/type_info.defn.h b/include/fruit/impl/util/type_info.defn.h
new file mode 100644
index 0000000..8b8df69
--- /dev/null
+++ b/include/fruit/impl/util/type_info.defn.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_TYPE_INFO_DEFN_H
+#define FRUIT_TYPE_INFO_DEFN_H
+
+#include <fruit/impl/util/type_info.h>
+
+#include <fruit/fruit_forward_decls.h>
+#include <fruit/impl/data_structures/arena_allocator.h>
+#include <fruit/impl/data_structures/memory_pool.h>
+#include <fruit/impl/fruit-config.h>
+#include <fruit/impl/fruit_assert.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename T, bool is_abstract = std::is_abstract<T>::value>
+struct GetConcreteTypeInfo {
+ constexpr TypeInfo::ConcreteTypeInfo operator()() const {
+ return TypeInfo::ConcreteTypeInfo{
+ sizeof(T), alignof(T), std::is_trivially_destructible<T>::value,
+#ifdef FRUIT_EXTRA_DEBUG
+ false /* is_abstract */,
+#endif
+ };
+ }
+};
+
+// For abstract types we don't need the real information.
+// Also, some compilers might report compile errors in this case, for example alignof(T) doesn't work in Visual Studio
+// when T is an abstract type.
+template <typename T>
+struct GetConcreteTypeInfo<T, true> {
+ constexpr TypeInfo::ConcreteTypeInfo operator()() const {
+ return TypeInfo::ConcreteTypeInfo{
+ 0 /* type_size */, 0 /* type_alignment */, false /* is_trivially_destructible */,
+#ifdef FRUIT_EXTRA_DEBUG
+ true /* is_abstract */,
+#endif
+ };
+ }
+};
+
+// This should only be used if RTTI is disabled. Use the other constructor if possible.
+inline constexpr TypeInfo::TypeInfo(ConcreteTypeInfo concrete_type_info)
+ : info(nullptr), concrete_type_info(concrete_type_info) {}
+
+inline constexpr TypeInfo::TypeInfo(const std::type_info& info, ConcreteTypeInfo concrete_type_info)
+ : info(&info), concrete_type_info(concrete_type_info) {}
+
+inline std::string TypeInfo::name() const {
+ if (info != nullptr) // LCOV_EXCL_BR_LINE
+ return demangleTypeName(info->name());
+ else
+ return "<unknown> (type name not accessible because RTTI is disabled)"; // LCOV_EXCL_LINE
+}
+
+inline size_t TypeInfo::size() const {
+#ifdef FRUIT_EXTRA_DEBUG
+ FruitAssert(!concrete_type_info.is_abstract);
+#endif
+ return concrete_type_info.type_size;
+}
+
+inline size_t TypeInfo::alignment() const {
+#ifdef FRUIT_EXTRA_DEBUG
+ FruitAssert(!concrete_type_info.is_abstract);
+#endif
+ return concrete_type_info.type_alignment;
+}
+
+inline bool TypeInfo::isTriviallyDestructible() const {
+#ifdef FRUIT_EXTRA_DEBUG
+ FruitAssert(!concrete_type_info.is_abstract);
+#endif
+ return concrete_type_info.is_trivially_destructible;
+}
+
+inline TypeId::operator std::string() const {
+ return type_info->name();
+}
+
+inline bool TypeId::operator==(TypeId x) const {
+ return type_info == x.type_info;
+}
+
+inline bool TypeId::operator!=(TypeId x) const {
+ return type_info != x.type_info;
+}
+
+inline bool TypeId::operator<(TypeId x) const {
+ return type_info < x.type_info;
+}
+
+template <typename T>
+struct GetTypeInfoForType {
+ constexpr TypeInfo operator()() const {
+#ifdef FRUIT_HAS_TYPEID
+ return TypeInfo(typeid(T), GetConcreteTypeInfo<T>()());
+#else
+ return TypeInfo(GetConcreteTypeInfo<T>()());
+#endif
+ };
+};
+
+template <typename Annotation, typename T>
+struct GetTypeInfoForType<fruit::Annotated<Annotation, T>> {
+ constexpr TypeInfo operator()() const {
+#ifdef FRUIT_HAS_TYPEID
+ return TypeInfo(typeid(fruit::Annotated<Annotation, T>), GetConcreteTypeInfo<T>()());
+#else
+ return TypeInfo(GetConcreteTypeInfo<T>()());
+#endif
+ };
+};
+
+template <typename T>
+inline TypeId getTypeId() {
+#if defined(FRUIT_HAS_TYPEID) && !defined(FRUIT_HAS_CONSTEXPR_TYPEID)
+ // We can't use constexpr here because TypeInfo contains a `const std::type_info&` and that's not constexpr with the
+ // current compiler/STL.
+ static TypeInfo info = GetTypeInfoForType<T>()();
+#else
+ // Usual case. The `constexpr' ensures compile-time evaluation.
+ static constexpr TypeInfo info = GetTypeInfoForType<T>()();
+#endif
+ return TypeId{&info};
+}
+
+template <typename L>
+struct GetTypeIdsForListHelper;
+
+template <typename... Ts>
+struct GetTypeIdsForListHelper<fruit::impl::meta::Vector<Ts...>> {
+ std::vector<TypeId, ArenaAllocator<TypeId>> operator()(MemoryPool& memory_pool) {
+ return std::vector<TypeId, ArenaAllocator<TypeId>>(std::initializer_list<TypeId>{getTypeId<Ts>()...}, memory_pool);
+ }
+};
+
+template <typename L>
+std::vector<TypeId, ArenaAllocator<TypeId>> getTypeIdsForList(MemoryPool& memory_pool) {
+ return GetTypeIdsForListHelper<L>()(memory_pool);
+}
+
+#ifdef FRUIT_EXTRA_DEBUG
+
+inline std::ostream& operator<<(std::ostream& os, TypeId type) {
+ return os << std::string(type);
+}
+
+#endif // FRUIT_EXTRA_DEBUG
+
+} // namespace impl
+} // namespace fruit
+
+namespace std {
+
+inline std::size_t hash<fruit::impl::TypeId>::operator()(fruit::impl::TypeId type) const {
+ return hash<const fruit::impl::TypeInfo*>()(type.type_info);
+}
+
+} // namespace std
+
+#endif // FRUIT_TYPE_INFO_DEFN_H
diff --git a/include/fruit/impl/util/type_info.h b/include/fruit/impl/util/type_info.h
new file mode 100644
index 0000000..7ffc8b3
--- /dev/null
+++ b/include/fruit/impl/util/type_info.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_TYPE_INFO_H
+#define FRUIT_TYPE_INFO_H
+
+#include <fruit/impl/meta/vector.h>
+#include <fruit/impl/util/demangle_type_name.h>
+#include <typeinfo>
+
+#include <vector>
+
+namespace fruit {
+namespace impl {
+
+// Similar to std::type_index, but with a constexpr constructor and also storing the type size and alignment.
+// Also guaranteed to be aligned, to allow storing a TypeInfo and 1 bit together in the size of a void*.
+struct alignas(1) alignas(void*) TypeInfo {
+
+ struct ConcreteTypeInfo {
+ // These fields are allowed to have dummy values for abstract types.
+ std::size_t type_size;
+ std::size_t type_alignment;
+ bool is_trivially_destructible;
+
+#ifdef FRUIT_EXTRA_DEBUG
+ bool is_abstract;
+#endif
+ };
+
+ // This should only be used if RTTI is disabled. Use the other constructor if possible.
+ constexpr TypeInfo(ConcreteTypeInfo concrete_type_info);
+
+ constexpr TypeInfo(const std::type_info& info, ConcreteTypeInfo concrete_type_info);
+
+ std::string name() const;
+
+ size_t size() const;
+
+ size_t alignment() const;
+
+ bool isTriviallyDestructible() const;
+
+private:
+ // The std::type_info struct associated with the type, or nullptr if RTTI is disabled.
+ // This is only used for the type name.
+ const std::type_info* info;
+ ConcreteTypeInfo concrete_type_info;
+};
+
+struct TypeId {
+ const TypeInfo* type_info;
+
+ operator std::string() const;
+
+ bool operator==(TypeId x) const;
+ bool operator!=(TypeId x) const;
+ bool operator<(TypeId x) const;
+};
+
+// Returns the TypeId for the type T.
+// Multiple invocations for the same type return the same value.
+// This has special support for types of the form Annotated<SomeAnnotation, SomeType>, it reports
+// data for SomeType (except the name, that is "Annotated<SomeAnnotation, SomeType>").
+template <typename T>
+TypeId getTypeId();
+
+// A convenience function that returns an std::vector of TypeId values for the given meta-vector of types.
+template <typename V>
+std::vector<TypeId> getTypeIdsForList();
+
+} // namespace impl
+} // namespace fruit
+
+#ifdef FRUIT_EXTRA_DEBUG
+
+#include <ostream>
+
+namespace fruit {
+namespace impl {
+
+inline std::ostream& operator<<(std::ostream& os, TypeId type);
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_EXTRA_DEBUG
+
+namespace std {
+
+template <>
+struct hash<fruit::impl::TypeId> {
+ std::size_t operator()(fruit::impl::TypeId type) const;
+};
+
+} // namespace std
+
+#include <fruit/impl/util/type_info.defn.h>
+
+#endif // FRUIT_TYPE_INFO_H
diff --git a/include/fruit/injector.h b/include/fruit/injector.h
new file mode 100644
index 0000000..8f8e5ae
--- /dev/null
+++ b/include/fruit/injector.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_INJECTOR_H
+#define FRUIT_INJECTOR_H
+
+// This include is not required here, but having it here shortens the include trace in error messages.
+#include <fruit/impl/injection_errors.h>
+
+#include <fruit/component.h>
+#include <fruit/normalized_component.h>
+#include <fruit/provider.h>
+
+namespace fruit {
+
+/**
+ * An injector is a class constructed from a component that performs the needed injections and manages the lifetime of
+ * the created objects.
+ * An injector does *not* need to specify all types bound in the component; you can only specify the "root" type(s) and
+ * the injector will also create and store the instances of classes that are needed (directly or indirectly) to inject
+ * the root types.
+ *
+ * Example usage:
+ *
+ * Component<Foo, Bar> getFooBarComponent() {
+ * ...
+ * }
+ *
+ * Injector<Foo, Bar> injector(getFooBarComponent);
+ * Foo* foo = injector.get<Foo*>();
+ * Bar* bar(injector); // Equivalent to: Bar* bar = injector.get<Bar*>();
+ */
+template <typename... P>
+class Injector {
+private:
+ template <typename T>
+ struct RemoveAnnotationsHelper {
+ using type = fruit::impl::meta::UnwrapType<
+ fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<T>)>>;
+ };
+
+ template <typename T>
+ using RemoveAnnotations = typename RemoveAnnotationsHelper<T>::type;
+
+public:
+ // Moving injectors is allowed.
+ Injector(Injector&&) = default;
+
+ // Copying injectors is forbidden.
+ Injector(const Injector&) = delete;
+
+ /**
+ * This creates an injector from a component function (that can optionally have parameters).
+ *
+ * Args and FormalArgs (if any) must be the same types; or to be precise, each type in Args must be convertible into
+ * the corresponding type in FormalArgs.
+ *
+ * Example usage:
+ *
+ * Component<Foo, Bar> getFooBarComponent() {
+ * ...
+ * }
+ *
+ * Injector<Foo, Bar> injector(getFooBarComponent);
+ * Foo* foo = injector.get<Foo*>();
+ * Bar* bar(injector); // Equivalent to: Bar* bar = injector.get<Bar*>();
+ *
+ * Example usage with arguments:
+ *
+ * Component<Foo, Bar> getFooBarComponent(int n, double d) {
+ * ...
+ * }
+ *
+ * Injector<Foo, Bar> injector(getFooBarComponent, 10, 3.14);
+ * Foo* foo = injector.get<Foo*>();
+ */
+ template <typename... FormalArgs, typename... Args>
+ Injector(Component<P...> (*)(FormalArgs...), Args&&... args);
+
+ /**
+ * This creates an injector from a normalized component and a component function.
+ * See the documentation of NormalizedComponent for more details.
+ *
+ * Args and FormalArgs (if any) must be the same types; or to be precise, each type in Args must be convertible into
+ * the corresponding type in FormalArgs.
+ *
+ * The NormalizedComponent can have requirements, but the Component can't.
+ * The NormalizedComponent must remain valid during the lifetime of any Injector object constructed with it.
+ *
+ * Example usage:
+ *
+ * // In the global scope.
+ * Component<Request> getRequestComponent(Request* request) {
+ * return fruit::createComponent()
+ * .bindInstance(*request);
+ * }
+ *
+ * // At startup (e.g. inside main()).
+ * NormalizedComponent<Required<Request>, Bar, Bar2> normalizedComponent = ...;
+ *
+ * ...
+ * for (...) {
+ * // For each request.
+ * Request request = ...;
+ *
+ * Injector<Foo, Bar> injector(normalizedComponent, getRequestComponent, &request);
+ * Foo* foo = injector.get<Foo*>();
+ * ...
+ * }
+ */
+ template <typename... NormalizedComponentParams, typename... ComponentParams, typename... FormalArgs,
+ typename... Args>
+ Injector(const NormalizedComponent<NormalizedComponentParams...>& normalized_component,
+ Component<ComponentParams...> (*)(FormalArgs...), Args&&... args);
+
+ /**
+ * Deleted constructor, to ensure that constructing an Injector from a temporary NormalizedComponent doesn't compile.
+ */
+ template <typename... NormalizedComponentParams, typename... ComponentParams, typename... FormalArgs,
+ typename... Args>
+ Injector(NormalizedComponent<NormalizedComponentParams...>&& normalized_component,
+ Component<ComponentParams...> (*)(FormalArgs...), Args&&... args) = delete;
+
+ /**
+ * Returns an instance of the specified type. For any class C in the Injector's template parameters, the following
+ * variations are allowed:
+ *
+ * get<C>()
+ * get<C*>()
+ * get<C&>()
+ * get<const C*>()
+ * get<const C&>()
+ * get<shared_ptr<C>>()
+ * get<Provider<C>>()
+ * get<Provider<const C>>()
+ * get<Annotated<Annotation, C>>() (for any type `Annotation')
+ * get<Annotated<Annotation, C*>>() (for any type `Annotation')
+ * get<Annotated<Annotation, C&>>() (for any type `Annotation')
+ * get<Annotated<Annotation, const C*>>() (for any type `Annotation')
+ * get<Annotated<Annotation, const C&>>() (for any type `Annotation')
+ * get<Annotated<Annotation, shared_ptr<C>>>() (for any type `Annotation')
+ * get<Annotated<Annotation, Provider<C>>>() (for any type `Annotation')
+ * get<Annotated<Annotation, Provider<const C>>>() (for any type `Annotation')
+ *
+ * For any "const C" in the Injector's template parameters, only a subset of those are allowed, specifically:
+ *
+ * get<C>()
+ * get<const C*>()
+ * get<const C&>()
+ * get<Provider<const C>>()
+ * get<Annotated<Annotation, C>>() (for any type `Annotation')
+ * get<Annotated<Annotation, const C*>>() (for any type `Annotation')
+ * get<Annotated<Annotation, const C&>>() (for any type `Annotation')
+ * get<Annotated<Annotation, Provider<const C>>>() (for any type `Annotation')
+ *
+ * With a non-annotated parameter T, this returns a T.
+ * With an annotated parameter AnnotatedT=Annotated<Annotation, T>, this returns a T.
+ * E.g. if you want to inject a pointer for an annotated type, you can use this as follows:
+ *
+ * T* instance = injector.get<Annotated<Annotation, T*>>();
+ *
+ * The shared_ptr versions come with a slight performance hit, prefer injecting pointers or references if possible.
+ * Calling get<> repeatedly for the same class with the same injector will return the same instance.
+ */
+ template <typename T>
+ typename Injector<P...>::template RemoveAnnotations<T> get();
+
+ /**
+ * This is a convenient way to call get(). E.g.:
+ *
+ * MyInterface* x(injector);
+ *
+ * is equivalent to:
+ *
+ * MyInterface* x = injector.get<MyInterface*>();
+ *
+ * Note that this can't be used to inject an annotated type, i.e. this does NOT work:
+ *
+ * fruit::Annotated<SomeAnnotation, SomeClass> foo(injector);
+ *
+ * Because foo would be of type fruit::Annotated, not of type SomeClass. In that case you must use get() instead,
+ * e.g.:
+ *
+ * SomeClass* foo = injector.get<fruit::Annotated<SomeAnnotation, SomeClass*>>();;
+ */
+ template <typename T>
+ explicit operator T();
+
+ /**
+ * Gets all multibindings for a type T.
+ *
+ * Multibindings are independent from bindings; so if there is a (normal) binding for T, that is not returned.
+ * This returns an empty vector if there are no multibindings.
+ *
+ * With a non-annotated parameter T, this returns a const std::vector<T*>&.
+ * With an annotated parameter AnnotatedT=Annotated<Annotation, T>, this returns a const std::vector<T*>&.
+ */
+ template <typename T>
+ const std::vector<RemoveAnnotations<T>*>& getMultibindings();
+
+ /**
+ * This method is deprecated since Fruit injectors can now be accessed concurrently by multiple threads. This will be
+ * removed in a future Fruit release.
+ *
+ * Eagerly injects all reachable bindings and multibindings of this injector.
+ * This only creates instances of the types that are either:
+ * - exposed by this Injector (i.e. in the Injector's type parameters)
+ * - bound by a multibinding
+ * - needed to inject one of the above (directly or indirectly)
+ *
+ * Unreachable bindings (i.e. bindings that are not exposed by this Injector, and that are not used by any reachable
+ * binding) are not processed. Bindings that are only used lazily, using a Provider, are NOT eagerly injected.
+ *
+ * Also note that this guarantee doesn't apply to Providers.
+ */
+ FRUIT_DEPRECATED_DECLARATION(void eagerlyInjectAll());
+
+private:
+ using Check1 = typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<
+ fruit::impl::meta::CheckNoRequiredTypesInInjectorArguments(fruit::impl::meta::Type<P>...)>>::type;
+ // Force instantiation of Check1.
+ static_assert(true || sizeof(Check1), "");
+
+ using Comp = fruit::impl::meta::Eval<fruit::impl::meta::ConstructComponentImpl(fruit::impl::meta::Type<P>...)>;
+
+ using Check2 = typename fruit::impl::meta::CheckIfError<Comp>::type;
+ using VoidType = fruit::impl::meta::Type<void>;
+ // Force instantiation of Check2.
+ static_assert(true || sizeof(Check2), "");
+ using Check3 = typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::If(
+ fruit::impl::meta::Not(fruit::impl::meta::IsEmptySet(typename Comp::RsSuperset)),
+ fruit::impl::meta::ConstructErrorWithArgVector(fruit::impl::InjectorWithRequirementsErrorTag,
+ fruit::impl::meta::SetToVector(typename Comp::RsSuperset)),
+ VoidType)>>::type;
+ // Force instantiation of Check3.
+ static_assert(true || sizeof(Check3), "");
+
+ friend struct fruit::impl::InjectorAccessorForTests;
+
+ std::unique_ptr<fruit::impl::InjectorStorage> storage;
+};
+
+} // namespace fruit
+
+#include <fruit/impl/injector.defn.h>
+
+#endif // FRUIT_INJECTOR_H
diff --git a/include/fruit/macro.h b/include/fruit/macro.h
new file mode 100644
index 0000000..6ce59c8
--- /dev/null
+++ b/include/fruit/macro.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_MACRO_H
+#define FRUIT_MACRO_H
+
+// This include is not required here, but having it here shortens the include trace in error messages.
+#include <fruit/impl/injection_errors.h>
+
+#include <fruit/fruit_forward_decls.h>
+
+/**
+ * A convenience macro to define the Inject typedef while declaring/defining the constructor that will be used for
+ * injection.
+ * It also supports assisted injection and injection of annotated types.
+ *
+ * Example usage:
+ *
+ * class MyClass {
+ * public:
+ * INJECT(MyClass(Foo* foo, Bar* bar)) {...}
+ * };
+ *
+ * is equivalent to:
+ *
+ * class MyClass {
+ * public:
+ * using Inject = MyClass(Foo*, Bar*);
+ *
+ * MyClass(Foo* foo, Bar* y) {...}
+ * };
+ *
+ * Example usage for assisted injection (see PartialComponent::registerFactory):
+ *
+ * class MyClass {
+ * public:
+ * INJECT(MyClass(Foo* foo, ASSISTED(int) n) {...}
+ * };
+ *
+ * is equivalent to:
+ *
+ * class MyClass {
+ * public:
+ * using Inject = MyClass(Foo*, Assisted<int>);
+ *
+ * MyClass(Foo* foo, int n) {...}
+ * };
+ *
+ * Example usage for annotated types:
+ *
+ * class MyClass {
+ * public:
+ * INJECT(MyClass(ANNOTATED(SomeAnnotation, Foo*) foo, Bar* bar)) {...}
+ * };
+ *
+ * ASSISTED and ANNOTATED *can* be used together in the same INJECT() annotation, but they can't both be used for a
+ * single parameter (as this wouldn't make sense, parameters that use assisted injection are user-supplied, they aren't
+ * injected from a binding).
+ *
+ * NOTE: This can't be used if the constructor is templated (the class can be templated, however), if there are any
+ * default arguments or if the constructor is marked `explicit'.
+ * In those cases, define the Inject annotation manually or use registerConstructor()/registerFactory() instead.
+ *
+ * NOTE: ASSISTED takes just one argument, but it's declared as variadic to make sure that the preprocessor doesn't
+ * choke on multi-argument templates like the map above, that the processor is unable to parse correctly.
+ *
+ * NOTE: ASSISTED takes just 2 arguments, but it's declared as variadic to make sure that the preprocessor doesn't choke
+ * on multi-argument templates, that the processor is unable to parse correctly.
+ *
+ * NOTE: In addition to the public Inject typedef, two typedefs (FruitAssistedTypedef and FruitAnnotatedTypedef) will be
+ * defined inside the class, make sure you don't define another typedef/field/method with the same name if you use the
+ * INJECT macro (unlikely but possible) these typedefs are an implementation detail of Fruit and should not be used.
+ *
+ * NOTE: The return type (MyClass in this case) should not be annotated. However an annotated
+ * MyClass (or MyClass factory) can be injected from any INJECT declaration.
+ */
+#define INJECT(Signature) \
+ using Inject = Signature; \
+ \
+ template <typename FruitAssistedDeclarationParam> \
+ using FruitAssistedTypedef = FruitAssistedDeclarationParam; \
+ template <typename Annotation, typename FruitAnnotatedDeclarationParam> \
+ using FruitAnnotatedTypedef = FruitAnnotatedDeclarationParam; \
+ \
+ Signature
+
+#define ASSISTED(...) FruitAssistedTypedef<__VA_ARGS__>
+#define ANNOTATED(Annotation, ...) FruitAnnotatedTypedef<Annotation, __VA_ARGS__>
+
+/**
+ * These are intentionally NOT in the fruit namespace, they can't be there for technical reasons.
+ *
+ * NOTE: don't use these directly, they're only used to implement the INJECT macro.
+ * Consider them part of fruit::impl.
+ */
+template <typename T>
+using FruitAssistedTypedef = fruit::Assisted<T>;
+template <typename Annotation, typename T>
+using FruitAnnotatedTypedef = fruit::Annotated<Annotation, T>;
+
+#endif // FRUIT_MACRO_H
diff --git a/include/fruit/normalized_component.h b/include/fruit/normalized_component.h
new file mode 100644
index 0000000..105edf8
--- /dev/null
+++ b/include/fruit/normalized_component.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_NORMALIZED_COMPONENT_H
+#define FRUIT_NORMALIZED_COMPONENT_H
+
+// This include is not required here, but having it here shortens the include trace in error messages.
+#include <fruit/impl/injection_errors.h>
+
+#include <fruit/fruit_forward_decls.h>
+#include <fruit/impl/fruit_internal_forward_decls.h>
+#include <fruit/impl/meta/component.h>
+#include <fruit/impl/normalized_component_storage/normalized_component_storage_holder.h>
+#include <memory>
+
+namespace fruit {
+
+/**
+ * This class allows for fast creation of multiple injectors that share most (or all) the bindings.
+ *
+ * This is an advanced feature of Fruit that allows to reduce injection time in some cases; if you're just starting to
+ * use Fruit you might want to ignore this for now (just construct an Injector from your root Component function).
+ *
+ * Using a NormalizedComponent only helps if:
+ *
+ * - You create multiple injectors during the lifetime of a process. E.g. if you only create one injector at startup you
+ * won't benefit from using NormalizedComponent.
+ * - Some of those injectors share all (or almost all) their bindings.
+ *
+ * When both of those requirements apply, you can switch to using NormalizedComponent in the "similar" injectors by
+ * first refactoring the injectors' root components to be of the form:
+ *
+ * fruit::Component<...> getRootComponent(...) {
+ * return fruit::createComponent()
+ * // This contains the bindings common to the group of similar injectors.
+ * .install(getSharedComponent, ...)
+ * // This contains the bindings specific to this injector.
+ * .install(getSpecificComponent, ...);
+ * }
+ *
+ * Then you can change your injector construction from:
+ *
+ * fruit::Injector<...> injector(getRootComponent, ...);
+ *
+ * To:
+ *
+ * fruit::NormalizedComponent<fruit::Required<...>, ...> normalized_component(getSharedComponent, ...);
+ * fruit::Injector<...> injector(normalized_component, getSpecificComponent, ...);
+ *
+ * This splits the work of constructing the Injector in two phases: normalization (where Fruit will call the Component
+ * functions to collect all the bindings and check for some classes of runtime errors) and the actual creation of the
+ * injector, during which Fruit will also collect/check the additional bindings specific to that injector.
+ *
+ * Then you can share the same normalized_component object across all those injectors (also in different threads,
+ * NormalizedComponent is thread-safe), so that the normalization step only occurs once (i.e., you should only construct
+ * NormalizedComponent from getSharedComponent once, otherwise you'd pay the normalization cost multiple times).
+ *
+ * Creating an Injector from a NormalizedComponent and injecting separate instances is very cheap, on the order of 2 us
+ * for an injection graph with 100 classes and 900 edges (for more details see the Benchmarks page of the Fruit wiki:
+ * https://github.com/google/fruit/wiki/benchmarks ).
+ * This might (depending of course on your performance requirements) allow you to create injectors where it would
+ * otherwise be unthinkable, e.g. creating a separate injector for each request in a server.
+ *
+ * Injectors that share the same NormalizedComponent are still independent; for example, if you call injector.get<Foo>()
+ * in two injectors, each injector will construct its own instance of Foo.
+ *
+ * Example usage in a server:
+ *
+ * // In the global scope.
+ * Component<Request> getRequestComponent(Request* request) {
+ * return fruit::createComponent()
+ * .bindInstance(*request);
+ * }
+ *
+ * // At startup (e.g. inside main()).
+ * NormalizedComponent<Required<Request>, Bar, Bar2> normalizedComponent = ...;
+ *
+ * ...
+ * for (...) {
+ * // For each request.
+ * Request request = ...;
+ *
+ * Injector<Foo, Bar> injector(normalizedComponent, getRequestComponent, &request);
+ * Foo* foo = injector.get<Foo*>();
+ * ...
+ * }
+ *
+ * See also the documentation for the Injector constructor that takes a NormalizedComponent.
+ */
+template <typename... Params>
+class NormalizedComponent {
+public:
+ /**
+ * The Component used as parameter can have (and usually has) unsatisfied requirements, so it's usually of the form
+ * Component<Required<...>, ...>.
+ *
+ * The given component function is called with the provided arguments to construct the root component.
+ * The constraints on the argument types (if there are any) are the same as the ones for PartialComponent::install().
+ */
+ template <typename... FormalArgs, typename... Args>
+ NormalizedComponent(Component<Params...> (*)(FormalArgs...), Args&&... args);
+
+ NormalizedComponent(NormalizedComponent&&) = default;
+ NormalizedComponent(const NormalizedComponent&) = delete;
+
+ NormalizedComponent& operator=(NormalizedComponent&&) = delete;
+ NormalizedComponent& operator=(const NormalizedComponent&) = delete;
+
+private:
+ NormalizedComponent(fruit::impl::ComponentStorage&& storage, fruit::impl::MemoryPool memory_pool);
+
+ // This is held via a unique_ptr to avoid including normalized_component_storage.h
+ // in fruit.h.
+ fruit::impl::NormalizedComponentStorageHolder storage;
+
+ template <typename... OtherParams>
+ friend class Injector;
+
+ using Comp = fruit::impl::meta::Eval<fruit::impl::meta::ConstructComponentImpl(fruit::impl::meta::Type<Params>...)>;
+
+ using Check1 = typename fruit::impl::meta::CheckIfError<Comp>::type;
+ // Force instantiation of Check1.
+ static_assert(true || sizeof(Check1), "");
+};
+
+} // namespace fruit
+
+#include <fruit/impl/normalized_component.defn.h>
+
+#endif // FRUIT_NORMALIZED_COMPONENT_H
diff --git a/include/fruit/provider.h b/include/fruit/provider.h
new file mode 100644
index 0000000..9284d0b
--- /dev/null
+++ b/include/fruit/provider.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_PROVIDER_H
+#define FRUIT_PROVIDER_H
+
+// This include is not required here, but having it here shortens the include trace in error messages.
+#include <fruit/impl/injection_errors.h>
+
+#include <fruit/component.h>
+
+namespace fruit {
+
+/**
+ * A Provider is a class that allows access to instances of the types used as parameters of the Provider template.
+ * It's possible to inject a Provider<MyClass> instead of MyClass itself, and this allows lazy injection.
+ * For example:
+ *
+ * class S {
+ * private:
+ * Bar* bar = nullptr;
+ *
+ * public:
+ * INJECT(S(Foo* foo, Provider<Bar> barProvider)) {
+ * if (foo->needsBar()) {
+ * bar = barProvider.get();
+ * }
+ * }
+ * };
+ *
+ * In the example above, Bar will only be created if get<Bar*> is called.
+ * This can be useful if Bar is expensive to create (or some other types that need to be injected when a Bar is injected
+ * are) or if there are other side effects of the Bar constructor that are undesirable when !foo->needsBar().
+ * It's also possible to store the Provider object in a field, and create the Bar instance when the first method that
+ * needs it is called:
+ *
+ * class S {
+ * private:
+ * Provider<Bar> barProvider;
+ *
+ * public:
+ * INJECT(S(Provider<Bar> barProvider))
+ * : barProvider(barProvider) {
+ * }
+ *
+ * void execute() {
+ * if (...) {
+ * Bar* bar = barProvider.get();
+ * ...
+ * }
+ * }
+ * };
+ *
+ * As usual, Fruit ensures that (at most) one instance is ever created in a given injector; so if the Bar object was
+ * already constructed, the get() will simply return it.
+ *
+ * Note that you can inject a Provider<Foo> whenever you could have injected a Foo.
+ * It doesn't matter if Foo was bound using PartialComponent::registerProvider() or not.
+ */
+template <typename C>
+class Provider {
+private:
+ using Check1 =
+ typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
+ fruit::impl::meta::RemoveConstFromTypes(fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>))>>::type;
+ // Force instantiation of Check1.
+ static_assert(true || sizeof(Check1), "");
+
+ using Check2 =
+ typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::CheckNotAnnotatedTypes(
+ fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>)>>::type;
+ // Force instantiation of Check2.
+ static_assert(true || sizeof(Check2), "");
+
+public:
+ /**
+ * Returns an instance of the specified type. The following variations are allowed:
+ *
+ * On a Provider<Foo>, you can call:
+ *
+ * - provider.get<Foo>()
+ * - provider.get<Foo*>()
+ * - provider.get<Foo&>()
+ * - provider.get<const Foo*>()
+ * - provider.get<const Foo&>()
+ * - provider.get<std::shared_ptr<Foo>>()
+ * - provider.get<Provider<Foo>>()
+ * - provider.get<Provider<const Foo>>()
+ *
+ * On a Provider<const Foo>, you can call:
+ *
+ * - provider.get<Foo>()
+ * - provider.get<const Foo*>()
+ * - provider.get<const Foo&>()
+ * - provider.get<Provider<const Foo>>()
+ *
+ * The shared_ptr version is slightly slower than the ones returning a reference/pointer, use those if possible.
+ *
+ * Calling get<> repeatedly for the same class with the same injector will return the same instance (except for the
+ * first variation above, that returns a value; in that case, another copy of the same instance will be returned).
+ */
+ template <typename T>
+ T get();
+
+ /**
+ * This is a convenient way to call get(). E.g.:
+ *
+ * C& x(provider);
+ *
+ * is equivalent to:
+ *
+ * C& x = provider.get<C&>();
+ */
+ template <typename T>
+ explicit operator T();
+
+ /**
+ * This is equivalent to get<C*>(), it's provided for convenience.
+ */
+ C* get();
+
+private:
+ // This is NOT owned by the provider object. It is not deleted on destruction.
+ // This is never nullptr.
+ fruit::impl::InjectorStorage* storage;
+ fruit::impl::InjectorStorage::Graph::node_iterator itr;
+
+ Provider(fruit::impl::InjectorStorage* storage, fruit::impl::InjectorStorage::Graph::node_iterator itr);
+
+ friend class fruit::impl::InjectorStorage;
+
+ template <typename T>
+ friend struct fruit::impl::GetFirstStage;
+
+ template <typename... OtherPs>
+ friend class Injector;
+};
+
+} // namespace fruit
+
+#include <fruit/impl/provider.defn.h>
+
+#endif // FRUIT_PROVIDER_H
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..66bf79f
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,26 @@
+
+set(FRUIT_SOURCES
+ memory_pool.cpp
+binding_normalization.cpp
+demangle_type_name.cpp
+component.cpp
+fixed_size_allocator.cpp
+injector_storage.cpp
+normalized_component_storage.cpp
+normalized_component_storage_holder.cpp
+semistatic_map.cpp
+semistatic_graph.cpp)
+
+if("${BUILD_SHARED_LIBS}")
+ add_library(fruit SHARED ${FRUIT_SOURCES})
+
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+ set_target_properties(fruit PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+ endif()
+else()
+ add_library(fruit STATIC ${FRUIT_SOURCES})
+endif()
+
+install(TARGETS fruit
+ ARCHIVE DESTINATION "${INSTALL_LIBRARY_DIR}"
+ LIBRARY DESTINATION "${INSTALL_LIBRARY_DIR}")
diff --git a/src/binding_normalization.cpp b/src/binding_normalization.cpp
new file mode 100644
index 0000000..5ffe8ef
--- /dev/null
+++ b/src/binding_normalization.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <algorithm>
+#include <cstdlib>
+#include <fruit/impl/util/type_info.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include <fruit/impl/data_structures/semistatic_graph.templates.h>
+#include <fruit/impl/injector/injector_storage.h>
+#include <fruit/impl/normalized_component_storage/binding_normalization.h>
+#include <fruit/impl/normalized_component_storage/binding_normalization.templates.h>
+#include <fruit/impl/normalized_component_storage/normalized_component_storage.h>
+
+using std::cout;
+using std::endl;
+
+using namespace fruit::impl;
+
+namespace fruit {
+namespace impl {
+
+void BindingNormalization::printLazyComponentInstallationLoop(
+ const std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& entries_to_process,
+ const ComponentStorageEntry& last_entry) {
+ std::cerr << "Found a loop while expanding components passed to PartialComponent::install()." << std::endl;
+ std::cerr << "Component installation trace (from top-level to the most deeply-nested):" << std::endl;
+ for (const ComponentStorageEntry& entry : entries_to_process) {
+ switch (entry.kind) {
+ case ComponentStorageEntry::Kind::COMPONENT_WITH_ARGS_END_MARKER:
+ if (entry.type_id == last_entry.type_id &&
+ last_entry.kind == ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS &&
+ *entry.lazy_component_with_args.component == *last_entry.lazy_component_with_args.component) {
+ std::cerr << "<-- The loop starts here" << std::endl;
+ }
+ std::cerr << std::string(entry.lazy_component_with_args.component->getFunTypeId()) << std::endl;
+ break;
+
+ case ComponentStorageEntry::Kind::COMPONENT_WITHOUT_ARGS_END_MARKER:
+ if (entry.type_id == last_entry.type_id &&
+ last_entry.kind == ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS &&
+ entry.lazy_component_with_no_args.erased_fun == last_entry.lazy_component_with_no_args.erased_fun) {
+ std::cerr << "<-- The loop starts here" << std::endl;
+ }
+ std::cerr << std::string(entry.type_id) << std::endl;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch (last_entry.kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS:
+ std::cerr << std::string(last_entry.lazy_component_with_args.component->getFunTypeId()) << std::endl;
+ break;
+
+ case ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS:
+ std::cerr << std::string(last_entry.type_id) << std::endl;
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ exit(1);
+}
+
+void BindingNormalization::printMultipleBindingsError(TypeId type) {
+ std::cerr << "Fatal injection error: the type " << type.type_info->name()
+ << " was provided more than once, with different bindings." << std::endl
+ << "This was not caught at compile time because at least one of the involved components bound this type "
+ << "but didn't expose it in the component signature." << std::endl
+ << "If the type has a default constructor or an Inject annotation, this problem may arise even if this "
+ << "type is bound/provided by only one component (and then hidden), if this type is auto-injected in "
+ << "another component." << std::endl
+ << "If the source of the problem is unclear, try exposing this type in all the component signatures where "
+ << "it's bound; if no component hides it this can't happen." << std::endl;
+ exit(1);
+}
+
+void BindingNormalization::printIncompatibleComponentReplacementsError(
+ const ComponentStorageEntry& replaced_component_entry, const ComponentStorageEntry& replacement_component_entry1,
+ const ComponentStorageEntry& replacement_component_entry2) {
+ using fun_t = void (*)();
+
+ fun_t replaced_fun_address;
+ switch (replaced_component_entry.kind) {
+ case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS:
+ replaced_fun_address = replaced_component_entry.lazy_component_with_args.component->erased_fun;
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS:
+ replaced_fun_address = replaced_component_entry.lazy_component_with_no_args.erased_fun;
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ fun_t replacement_fun_address1;
+ switch (replacement_component_entry1.kind) {
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ replacement_fun_address1 = replacement_component_entry1.lazy_component_with_args.component->erased_fun;
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS:
+ replacement_fun_address1 = replacement_component_entry1.lazy_component_with_no_args.erased_fun;
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ fun_t replacement_fun_address2;
+ switch (replacement_component_entry2.kind) {
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ replacement_fun_address2 = replacement_component_entry2.lazy_component_with_args.component->erased_fun;
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS:
+ replacement_fun_address2 = replacement_component_entry2.lazy_component_with_no_args.erased_fun;
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ constexpr static bool function_pointers_have_same_size = sizeof(void*) == sizeof(fun_t);
+ if (function_pointers_have_same_size) {
+ std::cerr << "Fatal injection error: the component function at " << reinterpret_cast<void*>(replaced_fun_address)
+ << " with signature " << std::string(replaced_component_entry.type_id)
+ << " was replaced (using .replace(...).with(...)) with both the component function at "
+ << reinterpret_cast<void*>(replacement_fun_address1) << " with signature "
+ << std::string(replacement_component_entry1.type_id) << " and the component function at "
+ << reinterpret_cast<void*>(replacement_fun_address2) << " with signature "
+ << std::string(replacement_component_entry2.type_id) << " ." << std::endl;
+ } else {
+ std::cerr << "Fatal injection error: a component function with signature "
+ << std::string(replaced_component_entry.type_id)
+ << " was replaced (using .replace(...).with(...)) with both a component function with signature "
+ << std::string(replacement_component_entry1.type_id) << " and another component function with signature "
+ << std::string(replacement_component_entry2.type_id) << " ." << std::endl;
+ }
+ exit(1);
+}
+
+void BindingNormalization::printComponentReplacementFailedBecauseTargetAlreadyExpanded(
+ const ComponentStorageEntry& replaced_component_entry, const ComponentStorageEntry& replacement_component_entry) {
+ using fun_t = void (*)();
+
+ fun_t replaced_fun_address;
+ switch (replaced_component_entry.kind) {
+ case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS:
+ replaced_fun_address = replaced_component_entry.lazy_component_with_args.component->erased_fun;
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS:
+ replaced_fun_address = replaced_component_entry.lazy_component_with_no_args.erased_fun;
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ fun_t replacement_fun_address1;
+ switch (replacement_component_entry.kind) {
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ replacement_fun_address1 = replacement_component_entry.lazy_component_with_args.component->erased_fun;
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS:
+ replacement_fun_address1 = replacement_component_entry.lazy_component_with_no_args.erased_fun;
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ constexpr static bool function_pointers_have_same_size = sizeof(void*) == sizeof(fun_t);
+ if (function_pointers_have_same_size) {
+ std::cerr << "Fatal injection error: unable to replace (using .replace(...).with(...)) the component function at "
+ << reinterpret_cast<void*>(replaced_fun_address) << " with signature "
+ << std::string(replaced_component_entry.type_id) << " with the component function at "
+ << reinterpret_cast<void*>(replacement_fun_address1) << " with signature "
+ << std::string(replacement_component_entry.type_id)
+ << " because the former component function was installed before the .replace(...).with(...)." << std::endl
+ << "You should change the order of installation of subcomponents so that .replace(...).with(...) is "
+ << "processed before the installation of the component to replace.";
+ } else {
+ std::cerr << "Fatal injection error: unable to replace (using .replace(...).with(...)) a component function with "
+ << "signature " << std::string(replaced_component_entry.type_id)
+ << " with a component function at with signature " << std::string(replacement_component_entry.type_id)
+ << " because the former component function was installed before the .replace(...).with(...)." << std::endl
+ << "You should change the order of installation of subcomponents so that .replace(...).with(...) is "
+ << "processed before the installation of the component to replace.";
+ }
+ exit(1);
+}
+
+void BindingNormalization::addMultibindings(std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
+ const multibindings_vector_t& multibindingsVector) {
+
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "InjectorStorage: adding multibindings:" << std::endl;
+#endif
+ // Now we must merge multiple bindings for the same type.
+ for (auto i = multibindingsVector.begin(); i != multibindingsVector.end(); ++i) {
+ const ComponentStorageEntry& multibinding_entry = i->first;
+ const ComponentStorageEntry& multibinding_vector_creator_entry = i->second;
+ FruitAssert(multibinding_entry.kind ==
+ ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION ||
+ multibinding_entry.kind ==
+ ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
+ multibinding_entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT);
+ FruitAssert(multibinding_vector_creator_entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR);
+ NormalizedMultibindingSet& b = multibindings[multibinding_entry.type_id];
+
+ // Might be set already, but we need to set it if there was no multibinding for this type.
+ b.get_multibindings_vector = multibinding_vector_creator_entry.multibinding_vector_creator.get_multibindings_vector;
+
+ switch (i->first.kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT: {
+ NormalizedMultibinding normalized_multibinding;
+ normalized_multibinding.is_constructed = true;
+ normalized_multibinding.object = i->first.multibinding_for_constructed_object.object_ptr;
+ b.elems.push_back(std::move(normalized_multibinding));
+ } break;
+
+ case ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION: {
+ fixed_size_allocator_data.addExternallyAllocatedType(i->first.type_id);
+ NormalizedMultibinding normalized_multibinding;
+ normalized_multibinding.is_constructed = false;
+ normalized_multibinding.create = i->first.multibinding_for_object_to_construct.create;
+ b.elems.push_back(std::move(normalized_multibinding));
+ } break;
+
+ case ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION: {
+ fixed_size_allocator_data.addType(i->first.type_id);
+ NormalizedMultibinding normalized_multibinding;
+ normalized_multibinding.is_constructed = false;
+ normalized_multibinding.create = i->first.multibinding_for_object_to_construct.create;
+ b.elems.push_back(std::move(normalized_multibinding));
+ } break;
+
+ default:
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cerr << "Unexpected kind: " << (std::size_t)i->first.kind << std::endl;
+#endif
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+ }
+}
+
+void BindingNormalization::normalizeBindingsWithUndoableBindingCompression(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
+ MemoryPool& memory_pool_for_fully_expanded_components_maps, MemoryPool& memory_pool_for_component_replacements_maps,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings,
+ BindingCompressionInfoMap& bindingCompressionInfoMap,
+ LazyComponentWithNoArgsSet& fully_expanded_components_with_no_args,
+ LazyComponentWithArgsSet& fully_expanded_components_with_args,
+ LazyComponentWithNoArgsReplacementMap& component_with_no_args_replacements,
+ LazyComponentWithArgsReplacementMap& component_with_args_replacements) {
+
+ FruitAssert(bindingCompressionInfoMap.empty());
+
+ normalizeBindingsWithBindingCompression(
+ std::move(toplevel_entries), fixed_size_allocator_data, memory_pool,
+ memory_pool_for_fully_expanded_components_maps, memory_pool_for_component_replacements_maps, exposed_types,
+ bindings_vector, multibindings,
+ [&bindingCompressionInfoMap](TypeId c_type_id, NormalizedComponentStorage::CompressedBindingUndoInfo undo_info) {
+ bindingCompressionInfoMap[c_type_id] = undo_info;
+ },
+ [&fully_expanded_components_with_no_args](LazyComponentWithNoArgsSet& fully_expanded_components) {
+ fully_expanded_components_with_no_args = std::move(fully_expanded_components);
+ fully_expanded_components.clear();
+ },
+ [&fully_expanded_components_with_args](LazyComponentWithArgsSet& fully_expanded_components) {
+ fully_expanded_components_with_args = std::move(fully_expanded_components);
+ fully_expanded_components.clear();
+ },
+ [&component_with_no_args_replacements](LazyComponentWithNoArgsReplacementMap& component_replacements) {
+ component_with_no_args_replacements = std::move(component_replacements);
+ component_replacements.clear();
+ },
+ [&component_with_args_replacements](LazyComponentWithArgsReplacementMap& component_replacements) {
+ component_with_args_replacements = std::move(component_replacements);
+ component_replacements.clear();
+ });
+}
+
+void BindingNormalization::normalizeBindingsWithPermanentBindingCompression(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings) {
+ normalizeBindingsWithBindingCompression(
+ std::move(toplevel_entries), fixed_size_allocator_data, memory_pool, memory_pool, memory_pool, exposed_types,
+ bindings_vector, multibindings, [](TypeId, NormalizedComponentStorage::CompressedBindingUndoInfo) {},
+ [](LazyComponentWithNoArgsSet&) {}, [](LazyComponentWithArgsSet&) {},
+ [](LazyComponentWithNoArgsReplacementMap&) {}, [](LazyComponentWithArgsReplacementMap&) {});
+}
+
+void BindingNormalization::normalizeBindingsAndAddTo(
+ FixedSizeVector<ComponentStorageEntry>&& toplevel_entries, MemoryPool& memory_pool,
+ const NormalizedComponentStorage& base_normalized_component,
+ FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
+ std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& new_bindings_vector,
+ std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings) {
+
+ multibindings = base_normalized_component.multibindings;
+
+ fixed_size_allocator_data = base_normalized_component.fixed_size_allocator_data;
+
+ multibindings_vector_t multibindings_vector =
+ multibindings_vector_t(ArenaAllocator<multibindings_vector_elem_t>(memory_pool));
+
+ HashMapWithArenaAllocator<TypeId, ComponentStorageEntry> binding_data_map =
+ createHashMapWithArenaAllocator<TypeId, ComponentStorageEntry>(20 /* capacity */, memory_pool);
+
+ using Graph = NormalizedComponentStorage::Graph;
+
+ normalizeBindings(
+ std::move(toplevel_entries), fixed_size_allocator_data, memory_pool, memory_pool, memory_pool, binding_data_map,
+ [](ComponentStorageEntry) {},
+ [&multibindings_vector](ComponentStorageEntry multibinding, ComponentStorageEntry multibinding_vector_creator) {
+ multibindings_vector.emplace_back(multibinding, multibinding_vector_creator);
+ },
+ [&base_normalized_component](TypeId type_id) { return base_normalized_component.bindings.find(type_id); },
+ [&base_normalized_component](Graph::const_node_iterator itr) {
+ return !(itr == base_normalized_component.bindings.end());
+ },
+ [](Graph::const_node_iterator itr) { return itr.isTerminal(); },
+ [](Graph::const_node_iterator itr) { return itr.getNode().object; },
+ [](Graph::const_node_iterator itr) { return itr.getNode().create; },
+ [&base_normalized_component](const LazyComponentWithNoArgs& lazy_component) {
+ return base_normalized_component.fully_expanded_components_with_no_args.count(lazy_component) != 0;
+ },
+ [&base_normalized_component](const LazyComponentWithArgs& lazy_component) {
+ return base_normalized_component.fully_expanded_components_with_args.count(lazy_component) != 0;
+ },
+ [](LazyComponentWithNoArgsSet&) {}, [](LazyComponentWithArgsSet&) {},
+ [&base_normalized_component](const LazyComponentWithNoArgs& lazy_component) {
+ return base_normalized_component.component_with_no_args_replacements.find(lazy_component);
+ },
+ [&base_normalized_component](const LazyComponentWithArgs& lazy_component) {
+ return base_normalized_component.component_with_args_replacements.find(lazy_component);
+ },
+ [&base_normalized_component](typename LazyComponentWithNoArgsReplacementMap::const_iterator itr) {
+ return itr != base_normalized_component.component_with_no_args_replacements.end();
+ },
+ [&base_normalized_component](typename LazyComponentWithArgsReplacementMap::const_iterator itr) {
+ return itr != base_normalized_component.component_with_args_replacements.end();
+ },
+ [](typename LazyComponentWithNoArgsReplacementMap::const_iterator itr) { return itr->second; },
+ [](typename LazyComponentWithArgsReplacementMap::const_iterator itr) { return itr->second; },
+ [](LazyComponentWithNoArgsReplacementMap&) {}, [](LazyComponentWithArgsReplacementMap&) {});
+
+ // Copy the normalized bindings into the result vector.
+ new_bindings_vector.clear();
+ new_bindings_vector.reserve(binding_data_map.size());
+ for (auto& p : binding_data_map) {
+ new_bindings_vector.push_back(p.second);
+ }
+
+ // Determine what binding compressions must be undone.
+
+ HashSetWithArenaAllocator<TypeId> binding_compressions_to_undo =
+ createHashSetWithArenaAllocator<TypeId>(20 /* capacity */, memory_pool);
+ for (const ComponentStorageEntry& entry : new_bindings_vector) {
+ switch (entry.kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT:
+ break;
+
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION:
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION:
+ case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION: {
+ const BindingDeps* entry_deps = entry.binding_for_object_to_construct.deps;
+ for (std::size_t i = 0; i < entry_deps->num_deps; ++i) {
+ auto binding_compression_itr = base_normalized_component.binding_compression_info_map.find(entry_deps->deps[i]);
+ if (binding_compression_itr != base_normalized_component.binding_compression_info_map.end() &&
+ binding_compression_itr->second.i_type_id != entry.type_id) {
+ // The binding compression for `p.second.getDeps()->deps[i]' must be undone because something
+ // different from binding_compression_itr->iTypeId is now bound to it.
+ binding_compressions_to_undo.insert(entry_deps->deps[i]);
+ }
+ }
+ } break;
+
+ default:
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cerr << "Unexpected kind: " << (std::size_t)entry.kind << std::endl;
+#endif
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ break;
+ }
+ }
+
+ // Step 3: undo any binding compressions that can no longer be applied.
+ for (TypeId cTypeId : binding_compressions_to_undo) {
+ auto binding_compression_itr = base_normalized_component.binding_compression_info_map.find(cTypeId);
+ FruitAssert(binding_compression_itr != base_normalized_component.binding_compression_info_map.end());
+ FruitAssert(!(base_normalized_component.bindings.find(binding_compression_itr->second.i_type_id) ==
+ base_normalized_component.bindings.end()));
+
+ ComponentStorageEntry c_binding;
+ c_binding.type_id = cTypeId;
+ c_binding.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION;
+ c_binding.binding_for_object_to_construct = binding_compression_itr->second.c_binding;
+
+ ComponentStorageEntry i_binding;
+ i_binding.type_id = binding_compression_itr->second.i_type_id;
+ i_binding.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION;
+ i_binding.binding_for_object_to_construct = binding_compression_itr->second.i_binding;
+
+ new_bindings_vector.push_back(std::move(c_binding));
+ // This TypeId is already in normalized_component.bindings, we overwrite it here.
+ new_bindings_vector.push_back(std::move(i_binding));
+
+#ifdef FRUIT_EXTRA_DEBUG
+ std::cout << "InjectorStorage: undoing binding compression for: " << binding_compression_itr->second.i_type_id
+ << "->" << cTypeId << std::endl;
+#endif
+ }
+
+ // Step 4: Add multibindings.
+ BindingNormalization::addMultibindings(multibindings, fixed_size_allocator_data, multibindings_vector);
+}
+
+void BindingNormalization::handlePreexistingLazyComponentWithArgsReplacement(
+ ComponentStorageEntry& replaced_component_entry, const ComponentStorageEntry& preexisting_replacement,
+ ComponentStorageEntry& new_replacement) {
+ switch (new_replacement.kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS:
+ if (preexisting_replacement.kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS ||
+ preexisting_replacement.lazy_component_with_no_args.erased_fun !=
+ new_replacement.lazy_component_with_no_args.erased_fun) {
+ printIncompatibleComponentReplacementsError(replaced_component_entry, new_replacement, preexisting_replacement);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ // Duplicate but consistent replacement, we'll ignore it.
+ replaced_component_entry.lazy_component_with_args.destroy();
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ if (preexisting_replacement.kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS ||
+ !(*preexisting_replacement.lazy_component_with_args.component ==
+ *new_replacement.lazy_component_with_args.component)) {
+ printIncompatibleComponentReplacementsError(replaced_component_entry, new_replacement, preexisting_replacement);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ // Duplicate but consistent replacement, we'll ignore it.
+ replaced_component_entry.lazy_component_with_args.destroy();
+ new_replacement.lazy_component_with_args.destroy();
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+}
+
+void BindingNormalization::handlePreexistingLazyComponentWithNoArgsReplacement(
+ ComponentStorageEntry& replaced_component_entry, const ComponentStorageEntry& preexisting_replacement,
+ ComponentStorageEntry& new_replacement) {
+ switch (new_replacement.kind) { // LCOV_EXCL_BR_LINE
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS:
+ if (preexisting_replacement.kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS ||
+ preexisting_replacement.lazy_component_with_no_args.erased_fun !=
+ new_replacement.lazy_component_with_no_args.erased_fun) {
+ printIncompatibleComponentReplacementsError(replaced_component_entry, new_replacement, preexisting_replacement);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ // Duplicate but consistent replacement, we'll ignore it.
+ break;
+
+ case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
+ if (new_replacement.kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS ||
+ !(*preexisting_replacement.lazy_component_with_args.component ==
+ *new_replacement.lazy_component_with_args.component)) {
+ printIncompatibleComponentReplacementsError(replaced_component_entry, new_replacement, preexisting_replacement);
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+
+ // Duplicate but consistent replacement, we'll ignore it.
+ new_replacement.lazy_component_with_args.destroy();
+ break;
+
+ default:
+ FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
+ }
+}
+
+} // namespace impl
+// We need a LCOV_EXCL_BR_LINE below because for some reason gcov/lcov think there's a branch there.
+} // namespace fruit LCOV_EXCL_BR_LINE
diff --git a/src/component.cpp b/src/component.cpp
new file mode 100644
index 0000000..69e971b
--- /dev/null
+++ b/src/component.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <fruit/component.h>
+
+#include <exception>
+#include <iostream>
+
+namespace fruit {
+
+// TODO: reimplement this check somehow.
+/*
+EmptyPartialComponent::~EmptyPartialComponent() {
+ // If the user of Fruit didn't cast the result of createComponent() (possibly after adding some bindings) to a
+Component<>, we abort
+ // because that's a misuse of the Fruit API. If we went ahead, there might be some PartialComponent<> instances that
+point
+ // to the ComponentStorage in this EmptyComponent, and any use of those would cause undefined behavior.
+ // If an exception is in flight, don't abort; that's likely to be an unexpected flow so we don't want to alert the
+user of Fruit,
+ // and there can't be any leftover instances of PartialComponent<> referring to this EmptyComponent anyway.
+ if (!already_converted_to_component && !std::uncaught_exception()) {
+ std::cerr << "The result of fruit::createComponent() was not converted to a Component before the end of the
+expression! "
+ << "This is a misuse of the Fruit API. This is likely to cause undefined behavior, aborting now to be safe." <<
+std::endl;
+ std::abort();
+ }
+}
+*/
+
+// We need a LCOV_EXCL_BR_LINE below because for some reason gcov/lcov think there's a branch there.
+} // namespace fruit LCOV_EXCL_BR_LINE
diff --git a/src/demangle_type_name.cpp b/src/demangle_type_name.cpp
new file mode 100644
index 0000000..d0a72dc
--- /dev/null
+++ b/src/demangle_type_name.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <fruit/impl/fruit-config.h>
+#include <fruit/impl/util/demangle_type_name.h>
+
+#if FRUIT_HAS_CXA_DEMANGLE
+
+#include <cstdlib>
+#include <cxxabi.h>
+#include <string>
+
+std::string demangleTypeName(const char* name) {
+ int status;
+ std::string result;
+ char* demangled_name = abi::__cxa_demangle(name, nullptr, nullptr, &status);
+ if (status == 0) { // LCOV_EXCL_BR_LINE
+ result = demangled_name;
+ std::free(demangled_name);
+ }
+ return result;
+}
+
+#else // !FRUIT_HAS_CXA_DEMANGLE
+
+// For other compilers, fall back on returning demangled names. This might not be the appropriate behavior,
+std::string demangleTypeName(const char* name) {
+ return std::string(name);
+}
+
+#endif // !FRUIT_HAS_CXA_DEMANGLE
diff --git a/src/fixed_size_allocator.cpp b/src/fixed_size_allocator.cpp
new file mode 100644
index 0000000..e1a89ab
--- /dev/null
+++ b/src/fixed_size_allocator.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <fruit/impl/data_structures/fixed_size_allocator.h>
+#include <fruit/impl/data_structures/fixed_size_vector.templates.h>
+
+using namespace fruit::impl;
+
+namespace fruit {
+namespace impl {
+
+FixedSizeAllocator::~FixedSizeAllocator() {
+ // Destroy all objects in reverse order.
+ std::pair<destroy_t, void*>* p = on_destruction.end();
+ while (p != on_destruction.begin()) {
+ --p;
+ p->first(p->second);
+ }
+ delete[] storage_begin;
+}
+
+} // namespace impl
+} // namespace fruit
diff --git a/src/injector_storage.cpp b/src/injector_storage.cpp
new file mode 100644
index 0000000..840416a
--- /dev/null
+++ b/src/injector_storage.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <algorithm>
+#include <cstdlib>
+#include <fruit/impl/util/type_info.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include <fruit/impl/component_storage/component_storage.h>
+#include <fruit/impl/data_structures/semistatic_graph.templates.h>
+#include <fruit/impl/injector/injector_storage.h>
+#include <fruit/impl/normalized_component_storage/binding_normalization.h>
+#include <fruit/impl/normalized_component_storage/binding_normalization.templates.h>
+
+using std::cout;
+using std::endl;
+
+using namespace fruit::impl;
+
+namespace fruit {
+namespace impl {
+
+void InjectorStorage::fatal(const std::string& error) {
+ std::cerr << "Fatal injection error: " << error << std::endl;
+ exit(1);
+}
+
+// LCOV_EXCL_START
+namespace {
+template <typename Id, typename Value>
+struct DummyNode {
+ Id getId() {
+ return Id();
+ }
+ bool isTerminal() {
+ return false;
+ }
+ Id* getEdgesBegin() {
+ return nullptr;
+ }
+ Id* getEdgesEnd() {
+ return nullptr;
+ }
+ Value getValue() {
+ return Value();
+ }
+};
+}
+// LCOV_EXCL_STOP
+
+InjectorStorage::InjectorStorage(ComponentStorage&& component,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ MemoryPool& memory_pool)
+ : normalized_component_storage_ptr(new NormalizedComponentStorage(
+ std::move(component), exposed_types, memory_pool, NormalizedComponentStorage::WithPermanentCompression())),
+ allocator(normalized_component_storage_ptr->fixed_size_allocator_data),
+ bindings(normalized_component_storage_ptr->bindings, (DummyNode<TypeId, NormalizedBinding>*)nullptr,
+ (DummyNode<TypeId, NormalizedBinding>*)nullptr, memory_pool),
+ multibindings(std::move(normalized_component_storage_ptr->multibindings)) {
+
+#ifdef FRUIT_EXTRA_DEBUG
+ bindings.checkFullyConstructed();
+#endif
+}
+
+InjectorStorage::InjectorStorage(const NormalizedComponentStorage& normalized_component, ComponentStorage&& component,
+ MemoryPool& memory_pool) {
+
+ FixedSizeAllocator::FixedSizeAllocatorData fixed_size_allocator_data;
+ using new_bindings_vector_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
+ new_bindings_vector_t new_bindings_vector = new_bindings_vector_t(ArenaAllocator<ComponentStorageEntry>(memory_pool));
+
+ BindingNormalization::normalizeBindingsAndAddTo(std::move(component).release(), memory_pool, normalized_component,
+ fixed_size_allocator_data, new_bindings_vector, multibindings);
+
+ allocator = FixedSizeAllocator(fixed_size_allocator_data);
+
+ bindings = Graph(normalized_component.bindings, BindingDataNodeIter{new_bindings_vector.begin()},
+ BindingDataNodeIter{new_bindings_vector.end()}, memory_pool);
+#ifdef FRUIT_EXTRA_DEBUG
+ bindings.checkFullyConstructed();
+#endif
+}
+
+InjectorStorage::~InjectorStorage() {}
+
+void InjectorStorage::ensureConstructedMultibinding(NormalizedMultibindingSet& multibinding_set) {
+ for (NormalizedMultibinding& multibinding : multibinding_set.elems) {
+ if (!multibinding.is_constructed) {
+ multibinding.object = multibinding.create(*this);
+ multibinding.is_constructed = true;
+ }
+ }
+}
+
+void* InjectorStorage::getMultibindings(TypeId typeInfo) {
+ NormalizedMultibindingSet* multibinding_set = getNormalizedMultibindingSet(typeInfo);
+ if (multibinding_set == nullptr) {
+ // Not registered.
+ return nullptr;
+ }
+ return multibinding_set->get_multibindings_vector(*this).get();
+}
+
+void InjectorStorage::eagerlyInjectMultibindings() {
+ std::lock_guard<std::recursive_mutex> lock(mutex);
+ for (auto& typeInfoInfoPair : multibindings) {
+ typeInfoInfoPair.second.get_multibindings_vector(*this);
+ }
+}
+
+} // namespace impl
+// We need a LCOV_EXCL_BR_LINE below because for some reason gcov/lcov think there's a branch there.
+} // namespace fruit LCOV_EXCL_BR_LINE
diff --git a/src/memory_pool.cpp b/src/memory_pool.cpp
new file mode 100644
index 0000000..c4105b2
--- /dev/null
+++ b/src/memory_pool.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <fruit/impl/data_structures/memory_pool.h>
+
+using namespace fruit::impl;
+
+void MemoryPool::destroy() {
+ for (void* p : allocated_chunks) {
+ operator delete(p);
+ }
+}
diff --git a/src/normalized_component_storage.cpp b/src/normalized_component_storage.cpp
new file mode 100644
index 0000000..b483326
--- /dev/null
+++ b/src/normalized_component_storage.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <algorithm>
+#include <cstdlib>
+#include <fruit/impl/util/type_info.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include <fruit/impl/normalized_component_storage/normalized_component_storage.h>
+
+#include <fruit/impl/component_storage/component_storage.h>
+#include <fruit/impl/data_structures/semistatic_graph.templates.h>
+#include <fruit/impl/data_structures/semistatic_map.templates.h>
+#include <fruit/impl/injector/injector_storage.h>
+#include <fruit/impl/normalized_component_storage/binding_normalization.h>
+
+using std::cout;
+using std::endl;
+
+using namespace fruit;
+using namespace fruit::impl;
+
+namespace fruit {
+namespace impl {
+
+NormalizedComponentStorage::NormalizedComponentStorage(ComponentStorage&& component,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ MemoryPool& memory_pool, WithPermanentCompression)
+ : normalized_component_memory_pool(),
+ binding_compression_info_map(createHashMapWithArenaAllocator<TypeId, CompressedBindingUndoInfo>(
+ 0 /* capacity */, normalized_component_memory_pool)),
+ fully_expanded_components_with_no_args(
+ createLazyComponentWithNoArgsSet(0 /* capacity */, normalized_component_memory_pool)),
+ fully_expanded_components_with_args(
+ createLazyComponentWithArgsSet(0 /* capacity */, normalized_component_memory_pool)),
+ component_with_no_args_replacements(
+ createLazyComponentWithNoArgsReplacementMap(0 /* capacity */, normalized_component_memory_pool)),
+ component_with_args_replacements(
+ createLazyComponentWithArgsReplacementMap(0 /* capacity */, normalized_component_memory_pool)) {
+
+ using bindings_vector_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
+ bindings_vector_t bindings_vector = bindings_vector_t(ArenaAllocator<ComponentStorageEntry>(memory_pool));
+ BindingNormalization::normalizeBindingsWithPermanentBindingCompression(std::move(component).release(),
+ fixed_size_allocator_data, memory_pool,
+ exposed_types, bindings_vector, multibindings);
+
+ bindings = SemistaticGraph<TypeId, NormalizedBinding>(InjectorStorage::BindingDataNodeIter{bindings_vector.begin()},
+ InjectorStorage::BindingDataNodeIter{bindings_vector.end()},
+ memory_pool);
+}
+
+NormalizedComponentStorage::NormalizedComponentStorage(ComponentStorage&& component,
+ const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ MemoryPool& memory_pool, WithUndoableCompression)
+ : normalized_component_memory_pool(),
+ binding_compression_info_map(createHashMapWithArenaAllocator<TypeId, CompressedBindingUndoInfo>(
+ 20 /* capacity */, normalized_component_memory_pool)),
+ fully_expanded_components_with_no_args(
+ createLazyComponentWithNoArgsSet(20 /* capacity */, normalized_component_memory_pool)),
+ fully_expanded_components_with_args(
+ createLazyComponentWithArgsSet(20 /* capacity */, normalized_component_memory_pool)),
+ component_with_no_args_replacements(
+ createLazyComponentWithNoArgsReplacementMap(20 /* capacity */, normalized_component_memory_pool)),
+ component_with_args_replacements(
+ createLazyComponentWithArgsReplacementMap(20 /* capacity */, normalized_component_memory_pool)) {
+
+ using bindings_vector_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
+ bindings_vector_t bindings_vector = bindings_vector_t(ArenaAllocator<ComponentStorageEntry>(memory_pool));
+ BindingNormalization::normalizeBindingsWithUndoableBindingCompression(
+ std::move(component).release(), fixed_size_allocator_data, memory_pool, normalized_component_memory_pool,
+ normalized_component_memory_pool, exposed_types, bindings_vector, multibindings, binding_compression_info_map,
+ fully_expanded_components_with_no_args, fully_expanded_components_with_args, component_with_no_args_replacements,
+ component_with_args_replacements);
+
+ bindings = SemistaticGraph<TypeId, NormalizedBinding>(InjectorStorage::BindingDataNodeIter{bindings_vector.begin()},
+ InjectorStorage::BindingDataNodeIter{bindings_vector.end()},
+ memory_pool);
+}
+
+NormalizedComponentStorage::~NormalizedComponentStorage() {
+ for (auto& x : fully_expanded_components_with_args) {
+ x.destroy();
+ }
+
+ for (const auto& pair : component_with_args_replacements) {
+ const LazyComponentWithArgs& replaced_component = pair.first;
+ const ComponentStorageEntry& replacement_component = pair.second;
+ replaced_component.destroy();
+ replacement_component.destroy();
+ }
+
+ for (const auto& pair : component_with_no_args_replacements) {
+ const ComponentStorageEntry& replacement_component = pair.second;
+ replacement_component.destroy();
+ }
+
+ // We must free all the memory in these before the normalized_component_memory_pool is destroyed.
+ binding_compression_info_map = createHashMapWithArenaAllocator<TypeId, CompressedBindingUndoInfo>(
+ 0 /* capacity */, normalized_component_memory_pool);
+ fully_expanded_components_with_no_args =
+ createLazyComponentWithNoArgsSet(0 /* capacity */, normalized_component_memory_pool);
+ fully_expanded_components_with_args =
+ createLazyComponentWithArgsSet(0 /* capacity */, normalized_component_memory_pool);
+ component_with_no_args_replacements =
+ createLazyComponentWithNoArgsReplacementMap(0 /* capacity */, normalized_component_memory_pool);
+ component_with_args_replacements =
+ createLazyComponentWithArgsReplacementMap(0 /* capacity */, normalized_component_memory_pool);
+}
+
+} // namespace impl
+// We need a LCOV_EXCL_BR_LINE below because for some reason gcov/lcov think there's a branch there.
+} // namespace fruit LCOV_EXCL_BR_LINE
diff --git a/src/normalized_component_storage_holder.cpp b/src/normalized_component_storage_holder.cpp
new file mode 100644
index 0000000..b138ebd
--- /dev/null
+++ b/src/normalized_component_storage_holder.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <fruit/impl/normalized_component_storage/normalized_component_storage.h>
+#include <fruit/impl/normalized_component_storage/normalized_component_storage_holder.h>
+
+using namespace fruit;
+using namespace fruit::impl;
+
+namespace fruit {
+namespace impl {
+
+NormalizedComponentStorageHolder::NormalizedComponentStorageHolder(
+ ComponentStorage&& component, const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
+ MemoryPool& memory_pool, WithUndoableCompression)
+ : storage(new NormalizedComponentStorage(std::move(component), exposed_types, memory_pool,
+ NormalizedComponentStorage::WithUndoableCompression())) {}
+
+NormalizedComponentStorageHolder::~NormalizedComponentStorageHolder() {}
+
+} // namespace impl
+} // namespace fruit
diff --git a/src/semistatic_graph.cpp b/src/semistatic_graph.cpp
new file mode 100644
index 0000000..c018bbd
--- /dev/null
+++ b/src/semistatic_graph.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <fruit/impl/data_structures/semistatic_graph.h>
+#include <fruit/impl/data_structures/semistatic_graph.templates.h>
+
+#include <fruit/impl/normalized_component_storage/normalized_bindings.h>
+#include <fruit/impl/util/type_info.h>
+
+using namespace fruit::impl;
+
+// Clang requires the following instantiation to be in its namespace.
+namespace fruit {
+namespace impl {
+
+template class SemistaticGraph<TypeId, NormalizedBinding>;
+
+} // namespace impl
+} // namespace fruit
diff --git a/src/semistatic_map.cpp b/src/semistatic_map.cpp
new file mode 100644
index 0000000..57811f1
--- /dev/null
+++ b/src/semistatic_map.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define IN_FRUIT_CPP_FILE
+
+#include <fruit/impl/data_structures/semistatic_graph.h>
+#include <fruit/impl/data_structures/semistatic_map.h>
+#include <fruit/impl/data_structures/semistatic_map.templates.h>
+
+#include <fruit/impl/util/type_info.h>
+
+using namespace fruit::impl;
+
+// Clang requires the following instantiation to be in its namespace.
+namespace fruit {
+namespace impl {
+
+template class SemistaticMap<TypeId, SemistaticGraphInternalNodeId>;
+
+} // namespace impl
+} // namespace fruit
diff --git a/tests/BUILD b/tests/BUILD
new file mode 100644
index 0000000..fa6349f
--- /dev/null
+++ b/tests/BUILD
@@ -0,0 +1,98 @@
+
+licenses(["notice"])
+
+TEST_HEADERS = [
+ "test_macros.h",
+ "test_common.h",
+ "class_construction_tracker.h",
+]
+
+filegroup(
+ name = "test_headers_filegroup",
+ srcs = TEST_HEADERS,
+ visibility = ["//third_party/fruit/tests:__subpackages__"],
+)
+
+cc_library(
+ name = "test_headers",
+ srcs = [],
+ hdrs = TEST_HEADERS,
+ visibility = ["//third_party/fruit/tests:__subpackages__"],
+ includes = ["."],
+)
+
+[cc_test(
+ name = filename[:-4],
+ srcs = [filename],
+ deps = [
+ ":test_headers",
+ "//third_party/fruit",
+ ]
+) for filename in glob(
+ ["*.cpp"],
+ exclude = ["include_test.cpp"])]
+
+FRUIT_PUBLIC_HEADERS = [
+ "component",
+ "fruit",
+ "fruit_forward_decls",
+ "injector",
+ "macro",
+ "normalized_component",
+ "provider",
+]
+
+genrule(
+ name = "fruit_test_config_genrule",
+ srcs = [
+ "//third_party/fruit",
+ "//third_party/fruit:fruit_headers",
+ ":test_headers_filegroup",
+ ],
+ # Here we copy libfruit.so to work around an issue with py_test where the outputs of a cc_library in the data
+ # attribute of a py_test are not taken into account.
+ outs = [
+ "fruit_test_config.py",
+ "libfruit.so"
+ ],
+ visibility = ["//third_party/fruit/tests:__subpackages__"],
+ cmd = ""
+ + "FRUIT_HEADERS_LOCATION=`for f in $(locations //third_party/fruit:fruit_headers); do echo \"$$f\"; done | fgrep configuration/bazel/ | head -n 1 | sed 's|configuration/bazel/.*|./|'`;"
+ + "TEST_HEADERS_LOCATION=`for f in $(locations :test_headers_filegroup); do echo \"$$f\"; done | fgrep test_macros.h | sed 's|test_macros.h|./|'`;"
+ + "LIBFRUIT_LOCATION=`for f in $(locations //third_party/fruit); do echo \"$$f\"; done | fgrep libfruit.so | head -n 1 | sed 's|libfruit.so|./|'`;"
+ + "cp $${LIBFRUIT_LOCATION}/libfruit.so $(location libfruit.so);"
+ # The removal of ".*/genfiles" from the location is a bit of a hack, but that's how the path will look like in the py_tests
+ # below.
+ + "LIBFRUIT_COPY_DIR_LOCATION=`dirname $(location libfruit.so) | sed 's|.*/genfiles/|./|'`;"
+ + "LIBFRUIT_COPY_LOCATION=`echo $(location libfruit.so) | sed 's|.*/genfiles/|./|'`;"
+ + "echo -e \""
+ + "CXX='g++'\n"
+ + "CXX_COMPILER_NAME='GNU'\n"
+ + "CXX_COMPILER_VERSION='5.0.0'\n"
+ + "FRUIT_COMPILE_FLAGS='$(CC_FLAGS) -std=c++0x -W -Wall -Wno-missing-braces -g -Werror'\n"
+ + "ADDITIONAL_INCLUDE_DIRS=''\n"
+ + "CMAKE_BUILD_TYPE=None\n"
+ + "PATH_TO_COMPILED_FRUIT='$${LIBFRUIT_COPY_DIR_LOCATION}'\n"
+ + "PATH_TO_COMPILED_FRUIT_LIB='$${LIBFRUIT_COPY_LOCATION}'\n"
+ + "PATH_TO_FRUIT_STATIC_HEADERS='$${FRUIT_HEADERS_LOCATION}/include'\n"
+ + "PATH_TO_FRUIT_GENERATED_HEADERS='$${FRUIT_HEADERS_LOCATION}/configuration/bazel'\n"
+ + "PATH_TO_FRUIT_TEST_HEADERS='$${TEST_HEADERS_LOCATION}'\n"
+ + "ADDITIONAL_LINKER_FLAGS=''\n"
+ + "RUN_TESTS_UNDER_VALGRIND='0'\n"
+ + "VALGRIND_FLAGS=''\n"
+ + "ENABLE_COVERAGE=False\n"
+ + "\" > $(location fruit_test_config.py)",
+)
+
+py_library(
+ name = "fruit_test_common",
+ srcs = ["fruit_test_common.py", "fruit_test_config.py"],
+ imports = ["."],
+ visibility = ["//third_party/fruit/tests:__subpackages__"],
+)
+
+load("//third_party/fruit/tests:build_defs.bzl", "fruit_py_tests")
+
+fruit_py_tests(
+ srcs = glob(["test_*.py"]),
+)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..b5cbfdb
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,109 @@
+
+include(CMakeParseArguments)
+
+if("${WIN32}")
+ # This defaults to OFF on Windows, since we don't support PCHs there.
+ option(FRUIT_TESTS_USE_PRECOMPILED_HEADERS "Whether to use pre-compiled headers (PCHs) in Fruit tests." OFF)
+
+ if ("${FRUIT_TESTS_USE_PRECOMPILED_HEADERS}")
+ # TODO: consider adding support for PCHs on Windows (at least when using MinGW).
+ message(FATAL_ERROR "Please rerun CMake without -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS, precompiled headers are not supported on Windows.")
+ endif()
+else()
+ option(FRUIT_TESTS_USE_PRECOMPILED_HEADERS "Whether to use pre-compiled headers (PCHs) in Fruit tests." ON)
+endif()
+
+if("${WIN32}")
+ # No timeout on windows, the `timeout' executable has a different syntax.
+ set(TIMEOUT_COMMAND_PREFIX "")
+ set(TIMEOUT_COMMAND_PREFIX_STR "")
+else()
+ set(TIMEOUT_COMMAND_PREFIX "timeout" "30")
+ set(TIMEOUT_COMMAND_PREFIX_STR "timeout 30")
+endif()
+
+set(VALGRIND_FLAGS
+ --leak-check=full --malloc-fill=AA --track-origins=yes --read-var-info=yes --num-callers=50 --error-exitcode=1 --gen-suppressions=all --suppressions=${CMAKE_SOURCE_DIR}/tests/valgrind_suppressions.supp)
+string(REPLACE ";" " " VALGRIND_FLAGS_STR "${VALGRIND_FLAGS}")
+
+if(NOT "${WIN32}")
+ function(check_all_python_tests_listed DIRNAME)
+ # Join the list with " ".
+ string(REPLACE ";" " " STR "${ARGN}")
+ add_test(NAME check-all-python-tests-listed-${DIRNAME}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMAND bash -c -x "pwd; for f in \$(ls test_*.py); do echo \" ${STR} \" | fgrep -q \" \$f \" || { echo \"\$f not listed.\" && exit 1; }; done")
+ endfunction(check_all_python_tests_listed)
+endif()
+
+if ("${FRUIT_TESTS_USE_PRECOMPILED_HEADERS}")
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ add_custom_command(
+ OUTPUT test_common-precompiled.h.gch
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS test_common.h fruit
+ COMMAND bash -c "${CMAKE_CXX_COMPILER} -x c++-header ${FRUIT_COMPILE_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/../include -I${CMAKE_CURRENT_SOURCE_DIR} -I${CMAKE_CURRENT_BINARY_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/test_common.h -o test_common-precompiled.h.gch")
+ add_custom_target(test-common-precompiled-header ALL DEPENDS test_common-precompiled.h.gch)
+ # Note that the "test_common-precompiled.h" header doesn't exist, but it's ok because GCC looks for
+ # test_common-precompiled.h.gch first. We don't call the precompiled header test_common.h.gch so that if GCC doesn't
+ # find it it reports an error instead of using the normal header.
+ set(FRUIT_TESTONLY_CXXFLAGS "-include${CMAKE_CURRENT_BINARY_DIR}/test_common-precompiled.h")
+
+ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(Clang|AppleClang)$")
+ add_custom_command(
+ OUTPUT test_common.pch
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS test_common.h fruit
+ COMMAND bash -c "${CMAKE_CXX_COMPILER} -x c++-header ${FRUIT_COMPILE_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/../include -I${CMAKE_CURRENT_SOURCE_DIR} -I${CMAKE_CURRENT_BINARY_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/test_common.h -o test_common.pch")
+ add_custom_target(test-common-precompiled-header ALL DEPENDS test_common.pch)
+ set(FRUIT_TESTONLY_CXXFLAGS "-include-pch ${CMAKE_CURRENT_BINARY_DIR}/test_common.pch")
+ else()
+
+ message(ERROR "Using pre-compiled headers in tests is only supported with GCC and Clang. Please add -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF to your cmake invocation and try again.")
+ endif()
+else()
+ set(FRUIT_TESTONLY_CXXFLAGS "")
+endif()
+
+if("${FRUIT_ENABLE_COVERAGE}")
+ set(FRUIT_ENABLE_COVERAGE_PYTHON_BOOL "True")
+else()
+ set(FRUIT_ENABLE_COVERAGE_PYTHON_BOOL "False")
+endif()
+
+if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(MSVC)$")
+ # These warnings are disabled for tests only, since they can only be produced when using fruit as a client. Also, they cannot be disabled via pragma pushes/pops,
+ # so we leave it up to clients to disable them if desired.
+ # The warning C4702 is disabled because if MSVC optimizes the call to InvokeLambdaWithInjectedArgVector::operator() when cPtr is null, it will inline
+ # a FRUIT_UNREACHABLE statement, which makes all statements succeeding the operator() call unreachable.
+ # The warning C4503 is disabled because some of the test_class_destruction.py tests suchs as "test_injector_creation_and_injection"
+ # produce extremely long decorator names. This has no effect on the actual results of the test.
+ set(FRUIT_TESTONLY_CXXFLAGS "${FRUIT_TESTONLY_CXXFLAGS} /wd4702 /wd4503")
+endif()
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/fruit_test_config.py"
+ CONTENT "
+CXX='${CMAKE_CXX_COMPILER}'
+CXX_COMPILER_NAME='${CMAKE_CXX_COMPILER_ID}'
+CXX_COMPILER_VERSION='${CMAKE_CXX_COMPILER_VERSION}'
+FRUIT_COMPILE_FLAGS='${FRUIT_COMPILE_FLAGS} ${FRUIT_TESTONLY_CXXFLAGS}'
+ADDITIONAL_INCLUDE_DIRS='${BOOST_DIR}'
+ADDITIONAL_LINKER_FLAGS='${CMAKE_EXE_LINKER_FLAGS}'
+RUN_TESTS_UNDER_VALGRIND='${RUN_TESTS_UNDER_VALGRIND_FLAG}'
+VALGRIND_FLAGS='${VALGRIND_FLAGS_STR}'
+CMAKE_BUILD_TYPE='${CMAKE_BUILD_TYPE}'
+
+PATH_TO_COMPILED_FRUIT='$<TARGET_FILE_DIR:fruit>'
+PATH_TO_COMPILED_FRUIT_LIB='$<TARGET_FILE:fruit>'
+PATH_TO_FRUIT_STATIC_HEADERS='${CMAKE_CURRENT_SOURCE_DIR}/../include'
+PATH_TO_FRUIT_GENERATED_HEADERS='${CMAKE_CURRENT_BINARY_DIR}/../include'
+PATH_TO_FRUIT_TEST_HEADERS='${CMAKE_CURRENT_SOURCE_DIR}'
+ENABLE_COVERAGE=${FRUIT_ENABLE_COVERAGE_PYTHON_BOOL}
+")
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pytest.ini"
+ CONTENT "
+[pytest]
+testpaths = \"${CMAKE_CURRENT_SOURCE_DIR}\"
+addopts = -r a
+")
diff --git a/tests/build_defs.bzl b/tests/build_defs.bzl
new file mode 100644
index 0000000..6cc71c2
--- /dev/null
+++ b/tests/build_defs.bzl
@@ -0,0 +1,22 @@
+
+def fruit_py_tests(srcs, data=[]):
+ for filename in srcs:
+ native.py_test(
+ name = filename[:-3],
+ srcs = [filename],
+ imports = ["."],
+ deps = [
+ "//third_party/fruit/tests:fruit_test_common",
+ ],
+ data = data + [
+ "//third_party/fruit:fruit_headers",
+ "//third_party/fruit/tests:libfruit.so",
+ "//third_party/fruit/tests:test_headers_filegroup",
+ ],
+ args = [
+ "-p",
+ "no:cacheprovider",
+ "-n",
+ "4",
+ ],
+ )
diff --git a/tests/class_construction_tracker.h b/tests/class_construction_tracker.h
new file mode 100644
index 0000000..7bf1bfd
--- /dev/null
+++ b/tests/class_construction_tracker.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_CLASS_CONSTRUCTION_TRACKER_H
+#define FRUIT_CLASS_CONSTRUCTION_TRACKER_H
+
+#include <cstddef>
+
+/**
+ * This class is useful to keep track of how many instances of a given type are created during the entire program
+ * execution.
+ *
+ * Example use:
+ * class Foo : public ConstructionTracker<Foo> {
+ * ...
+ * };
+ *
+ * int main() {
+ * ...
+ * assert(Foo::num_objects_constructed == 3);
+ * }
+ */
+template <typename T>
+struct ConstructionTracker {
+ static std::size_t num_objects_constructed;
+
+ ConstructionTracker() {
+ ++num_objects_constructed;
+ }
+};
+
+template <typename T>
+std::size_t ConstructionTracker<T>::num_objects_constructed = 0;
+
+#endif // FRUIT_CLASS_CONSTRUCTION_TRACKER_H
diff --git a/tests/data_structures/BUILD b/tests/data_structures/BUILD
new file mode 100644
index 0000000..764f877
--- /dev/null
+++ b/tests/data_structures/BUILD
@@ -0,0 +1,8 @@
+
+licenses(["notice"])
+
+load("//third_party/fruit/tests:build_defs.bzl", "fruit_py_tests")
+
+fruit_py_tests(
+ srcs = glob(["test_*.py"]),
+)
diff --git a/tests/data_structures/test_fixed_size_allocator.py b/tests/data_structures/test_fixed_size_allocator.py
new file mode 100644
index 0000000..7f73f1c
--- /dev/null
+++ b/tests/data_structures/test_fixed_size_allocator.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ #define IN_FRUIT_CPP_FILE
+ #include <fruit/impl/data_structures/fixed_size_vector.templates.h>
+
+ using namespace std;
+ using namespace fruit::impl;
+
+ struct X {
+ int y;
+
+ static int num_instances;
+
+ X(int y) : y(y) {
+ Assert(std::uintptr_t(this) % alignof(X) == 0);
+ ++num_instances;
+ }
+
+ ~X() {
+ --num_instances;
+ }
+ };
+
+ int X::num_instances = 0;
+
+ struct Y {
+ static int num_instances;
+
+ Y() {
+ Assert(std::uintptr_t(this) % alignof(Y) == 0);
+ ++num_instances;
+ }
+
+ ~Y() {
+ --num_instances;
+ }
+ };
+
+ int Y::num_instances = 0;
+
+ template <int n>
+ struct alignas(n) TypeWithAlignment {
+ TypeWithAlignment() {
+ Assert(std::uintptr_t(this) % n == 0);
+ }
+ };
+ '''
+
+def test_empty_allocator():
+ source = '''
+ int main() {
+ FixedSizeAllocator allocator;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_2_types():
+ source = '''
+ int main() {
+ {
+ FixedSizeAllocator::FixedSizeAllocatorData allocator_data;
+ allocator_data.addType(getTypeId<X>());
+ allocator_data.addType(getTypeId<Y>());
+ FixedSizeAllocator allocator(allocator_data);
+ allocator.constructObject<X>(15);
+ allocator.constructObject<Y>();
+ Assert(X::num_instances == 1);
+ Assert(Y::num_instances == 1);
+ }
+ Assert(X::num_instances == 0);
+ Assert(Y::num_instances == 0);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_externally_allocated_only():
+ source = '''
+ int main() {
+ {
+ FixedSizeAllocator::FixedSizeAllocatorData allocator_data;
+ allocator_data.addExternallyAllocatedType(getTypeId<X>());
+ FixedSizeAllocator allocator(allocator_data);
+ allocator.registerExternallyAllocatedObject(new X(15));
+ // The allocator takes ownership. Valgrind will report an error if X is not deleted.
+ Assert(X::num_instances == 1);
+ }
+ Assert(X::num_instances == 0);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_mix():
+ source = '''
+ int main() {
+ {
+ FixedSizeAllocator::FixedSizeAllocatorData allocator_data;
+ allocator_data.addExternallyAllocatedType(getTypeId<X>());
+ allocator_data.addType(getTypeId<Y>());
+ FixedSizeAllocator allocator(allocator_data);
+ allocator.registerExternallyAllocatedObject(new X(15));
+ // The allocator takes ownership. Valgrind will report an error if X is not deleted.
+ allocator.constructObject<Y>();
+ Assert(X::num_instances == 1);
+ Assert(Y::num_instances == 1);
+ }
+ Assert(X::num_instances == 0);
+ Assert(Y::num_instances == 0);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_alignment():
+ source = '''
+ int main() {
+ FixedSizeAllocator::FixedSizeAllocatorData allocator_data;
+ allocator_data.addType(getTypeId<TypeWithAlignment<1>>());
+ allocator_data.addType(getTypeId<TypeWithAlignment<8>>());
+ allocator_data.addType(getTypeId<TypeWithAlignment<2>>());
+ allocator_data.addType(getTypeId<TypeWithAlignment<128>>());
+ allocator_data.addType(getTypeId<TypeWithAlignment<2>>());
+ allocator_data.addType(getTypeId<TypeWithAlignment<8>>());
+ allocator_data.addType(getTypeId<TypeWithAlignment<1>>());
+ FixedSizeAllocator allocator(allocator_data);
+ // TypeWithLargeAlignment::TypeWithLargeAlignment() will assert that the alignment is correct.
+ allocator.constructObject<TypeWithAlignment<2>>();
+ allocator.constructObject<TypeWithAlignment<8>>();
+ allocator.constructObject<TypeWithAlignment<1>>();
+ allocator.constructObject<TypeWithAlignment<128>>();
+ allocator.constructObject<TypeWithAlignment<1>>();
+ allocator.constructObject<TypeWithAlignment<8>>();
+ allocator.constructObject<TypeWithAlignment<2>>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_move_constructor():
+ source = '''
+ int main() {
+ {
+ FixedSizeAllocator::FixedSizeAllocatorData allocator_data;
+ allocator_data.addType(getTypeId<X>());
+ allocator_data.addType(getTypeId<Y>());
+ FixedSizeAllocator allocator(allocator_data);
+ allocator.constructObject<X>(15);
+ FixedSizeAllocator allocator2(std::move(allocator));
+ allocator2.constructObject<Y>();
+ Assert(X::num_instances == 1);
+ Assert(Y::num_instances == 1);
+ }
+ Assert(X::num_instances == 0);
+ Assert(Y::num_instances == 0);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/data_structures/test_fixed_size_vector.py b/tests/data_structures/test_fixed_size_vector.py
new file mode 100644
index 0000000..721598a
--- /dev/null
+++ b/tests/data_structures/test_fixed_size_vector.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ #define IN_FRUIT_CPP_FILE
+ #include <fruit/impl/data_structures/fixed_size_vector.templates.h>
+
+ using namespace std;
+ using namespace fruit::impl;
+ '''
+
+def test_empty_capacity_0():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v;
+ const FixedSizeVector<int>& const_v = v;
+ Assert(v.size() == 0);
+ Assert(v.data() == nullptr);
+ Assert(v.begin() == v.end());
+ Assert(const_v.data() == nullptr);
+ Assert(const_v.begin() == const_v.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_empty_capacity_nonzero():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v(15);
+ const FixedSizeVector<int>& const_v = v;
+ Assert(v.size() == 0);
+ Assert(v.data() != nullptr);
+ Assert(v.begin() == v.end());
+ Assert(const_v.data() != nullptr);
+ Assert(const_v.begin() == const_v.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_push_back():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v(2);
+ v.push_back(1000);
+ v.push_back(2000);
+ const FixedSizeVector<int>& const_v = v;
+ Assert(v.size() == 2);
+ Assert(v.data() == &(v[0]));
+ Assert(v.end() - v.begin() == 2);
+ Assert(&*(v.begin()) == &(v[0]));
+ Assert(&*(v.begin() + 1) == &(v[1]));
+ Assert(const_v.data() != nullptr);
+ Assert(const_v.begin() == &(const_v[0]));
+ Assert(const_v.end() - const_v.begin() == 2);
+ Assert(&*(const_v.begin()) == &(const_v[0]));
+ Assert(&*(const_v.begin() + 1) == &(const_v[1]));
+ Assert(v[0] == 1000);
+ Assert(v[1] == 2000);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_2arg_constructor():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v(2, 1000);
+ const FixedSizeVector<int>& const_v = v;
+ Assert(v.size() == 2);
+ Assert(v.data() == &(v[0]));
+ Assert(v.end() - v.begin() == 2);
+ Assert(&*(v.begin()) == &(v[0]));
+ Assert(&*(v.begin() + 1) == &(v[1]));
+ Assert(const_v.data() != nullptr);
+ Assert(const_v.begin() == &(const_v[0]));
+ Assert(const_v.end() - const_v.begin() == 2);
+ Assert(&*(const_v.begin()) == &(const_v[0]));
+ Assert(&*(const_v.begin() + 1) == &(const_v[1]));
+ Assert(v[0] == 1000);
+ Assert(v[1] == 1000);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_move_constructor():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v1(2);
+ v1.push_back(1000);
+ v1.push_back(2000);
+ FixedSizeVector<int> v = std::move(v1);
+ Assert(v.size() == 2);
+ Assert(v.data() == &(v[0]));
+ Assert(v.end() - v.begin() == 2);
+ Assert(&*(v.begin()) == &(v[0]));
+ Assert(&*(v.begin() + 1) == &(v[1]));
+ Assert(v[0] == 1000);
+ Assert(v[1] == 2000);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_copy_constructor():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v1(2);
+ v1.push_back(1000);
+ v1.push_back(2000);
+ FixedSizeVector<int> v(v1, 3);
+ Assert(v.size() == 2);
+ Assert(v.data() == &(v[0]));
+ Assert(v.end() - v.begin() == 2);
+ Assert(&*(v.begin()) == &(v[0]));
+ Assert(&*(v.begin() + 1) == &(v[1]));
+ Assert(v[0] == 1000);
+ Assert(v[1] == 2000);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_move_assignment():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v1(2);
+ v1.push_back(1000);
+ v1.push_back(2000);
+ FixedSizeVector<int> v;
+ v = std::move(v1);
+ Assert(v.size() == 2);
+ Assert(v.data() == &(v[0]));
+ Assert(v.end() - v.begin() == 2);
+ Assert(&*(v.begin()) == &(v[0]));
+ Assert(&*(v.begin() + 1) == &(v[1]));
+ Assert(v[0] == 1000);
+ Assert(v[1] == 2000);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_swap():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v1(2);
+ v1.push_back(1000);
+ v1.push_back(2000);
+ FixedSizeVector<int> v2(1);
+ v2.push_back(3000);
+ std::swap(v1, v2);
+ Assert(v1.size() == 1);
+ Assert(v1[0] == 3000);
+ Assert(v2.size() == 2);
+ Assert(v2[0] == 1000);
+ Assert(v2[1] == 2000);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_clear():
+ source = '''
+ int main() {
+ FixedSizeVector<int> v(2);
+ v.push_back(1000);
+ v.push_back(2000);
+ v.clear();
+ Assert(v.size() == 0);
+ Assert(v.data() != nullptr);
+ Assert(v.begin() == v.end());
+ // This must not blow up, clear() must preserve the capacity.
+ v.push_back(1000);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/data_structures/test_semistatic_graph.py b/tests/data_structures/test_semistatic_graph.py
new file mode 100644
index 0000000..e51577a
--- /dev/null
+++ b/tests/data_structures/test_semistatic_graph.py
@@ -0,0 +1,375 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ #define IN_FRUIT_CPP_FILE
+ #include <fruit/impl/data_structures/semistatic_graph.templates.h>
+
+ using namespace std;
+ using namespace fruit::impl;
+
+ using Graph = SemistaticGraph<int, const char*>;
+ using node_iterator = Graph::node_iterator;
+ using edge_iterator = Graph::edge_iterator;
+
+ vector<int> no_neighbors{};
+
+ struct SimpleNode {
+ int id;
+ const char* value;
+ const vector<int>* neighbors;
+ bool is_terminal;
+
+ int getId() { return id; }
+ const char* getValue() { return value; }
+ bool isTerminal() { return is_terminal; }
+ vector<int>::const_iterator getEdgesBegin() { return neighbors->begin(); }
+ vector<int>::const_iterator getEdgesEnd() { return neighbors->end(); }
+ };
+ '''
+
+def test_empty():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<SimpleNode> values{};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ Assert(graph.find(0) == graph.end());
+ Assert(graph.find(2) == graph.end());
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(cgraph.find(2) == cgraph.end());
+ Assert(cgraph.find(5) == cgraph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_1_node_no_edges():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<SimpleNode> values{{2, "foo", &no_neighbors, false}};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(!(cgraph.find(2) == cgraph.end()));
+ Assert(cgraph.find(2).getNode() == string("foo"));
+ Assert(cgraph.find(2).isTerminal() == false);
+ Assert(cgraph.find(5) == cgraph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_1_node_no_edges_terminal():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<SimpleNode> values{{2, "foo", &no_neighbors, true}};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == true);
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(!(cgraph.find(2) == cgraph.end()));
+ Assert(cgraph.find(2).getNode() == string("foo"));
+ Assert(cgraph.find(2).isTerminal() == true);
+ Assert(cgraph.find(5) == cgraph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_1_node_self_edge():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<int> neighbors = {2};
+ vector<SimpleNode> values{{2, "foo", &neighbors, false}};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ edge_iterator itr = graph.at(2).neighborsBegin();
+ (void)itr;
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("foo"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == false);
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(!(cgraph.find(2) == cgraph.end()));
+ Assert(cgraph.find(2).getNode() == string("foo"));
+ Assert(cgraph.find(2).isTerminal() == false);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_2_nodes_one_edge():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<int> neighbors = {2};
+ vector<SimpleNode> values{{2, "foo", &no_neighbors, false}, {3, "bar", &neighbors, false}};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ Assert(graph.at(3).getNode() == string("bar"));
+ Assert(graph.at(3).isTerminal() == false);
+ edge_iterator itr = graph.at(3).neighborsBegin();
+ (void)itr;
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("foo"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == false);
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(!(cgraph.find(2) == cgraph.end()));
+ Assert(cgraph.find(2).getNode() == string("foo"));
+ Assert(cgraph.find(2).isTerminal() == false);
+ Assert(cgraph.find(3).getNode() == string("bar"));
+ Assert(cgraph.find(3).isTerminal() == false);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_3_nodes_two_edges():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<int> neighbors = {2, 4};
+ vector<SimpleNode> values{{2, "foo", &no_neighbors, false}, {3, "bar", &neighbors, false}, {4, "baz", &no_neighbors, true}};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ Assert(graph.at(3).getNode() == string("bar"));
+ Assert(graph.at(3).isTerminal() == false);
+ edge_iterator itr = graph.at(3).neighborsBegin();
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("foo"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == false);
+ ++itr;
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("baz"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == true);
+ Assert(graph.at(4).getNode() == string("baz"));
+ Assert(graph.at(4).isTerminal() == true);
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(!(cgraph.find(2) == cgraph.end()));
+ Assert(cgraph.find(2).getNode() == string("foo"));
+ Assert(cgraph.find(2).isTerminal() == false);
+ Assert(cgraph.find(3).getNode() == string("bar"));
+ Assert(cgraph.find(3).isTerminal() == false);
+ Assert(cgraph.find(4).getNode() == string("baz"));
+ Assert(cgraph.find(4).isTerminal() == true);
+ Assert(cgraph.find(5) == cgraph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_add_node():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<SimpleNode> old_values{{2, "foo", &no_neighbors, false}, {4, "baz", &no_neighbors, true}};
+
+ Graph old_graph(old_values.begin(), old_values.end(), memory_pool);
+ vector<int> neighbors = {2, 4};
+ vector<SimpleNode> new_values{{3, "bar", &neighbors, false}};
+
+ Graph graph(old_graph, new_values.begin(), new_values.end(), memory_pool);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ Assert(graph.at(3).getNode() == string("bar"));
+ Assert(graph.at(3).isTerminal() == false);
+ edge_iterator itr = graph.at(3).neighborsBegin();
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("foo"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == false);
+ ++itr;
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("baz"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == true);
+ Assert(graph.at(4).getNode() == string("baz"));
+ Assert(graph.at(4).isTerminal() == true);
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(!(cgraph.find(2) == cgraph.end()));
+ Assert(cgraph.find(2).getNode() == string("foo"));
+ Assert(cgraph.find(2).isTerminal() == false);
+ Assert(cgraph.find(3).getNode() == string("bar"));
+ Assert(cgraph.find(3).isTerminal() == false);
+ Assert(cgraph.find(4).getNode() == string("baz"));
+ Assert(cgraph.find(4).isTerminal() == true);
+ Assert(cgraph.find(5) == cgraph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_set_terminal():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<int> neighbors = {2, 4};
+ vector<SimpleNode> values{{2, "foo", &no_neighbors, false}, {3, "bar", &neighbors, false}, {4, "baz", &no_neighbors, true}};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ graph.find(3).setTerminal();
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ Assert(graph.at(3).getNode() == string("bar"));
+ Assert(graph.at(3).isTerminal() == true);
+ Assert(graph.at(4).getNode() == string("baz"));
+ Assert(graph.at(4).isTerminal() == true);
+ Assert(graph.find(5) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(cgraph.find(0) == cgraph.end());
+ Assert(!(cgraph.find(2) == cgraph.end()));
+ Assert(cgraph.find(2).getNode() == string("foo"));
+ Assert(cgraph.find(3).getNode() == string("bar"));
+ Assert(cgraph.find(4).getNode() == string("baz"));
+ Assert(cgraph.find(5) == cgraph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_move_constructor():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<int> neighbors = {2};
+ vector<SimpleNode> values{{2, "foo", &no_neighbors, false}, {3, "bar", &neighbors, false}};
+
+ Graph graph1(values.begin(), values.end(), memory_pool);
+ Graph graph = std::move(graph1);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ Assert(graph.at(3).getNode() == string("bar"));
+ Assert(graph.at(3).isTerminal() == false);
+ edge_iterator itr = graph.at(3).neighborsBegin();
+ (void)itr;
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("foo"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == false);
+ Assert(graph.find(5) == graph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_move_assignment():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<int> neighbors = {2};
+ vector<SimpleNode> values{{2, "foo", &no_neighbors, false}, {3, "bar", &neighbors, false}};
+
+ Graph graph1(values.begin(), values.end(), memory_pool);
+ Graph graph;
+ graph = std::move(graph1);
+ Assert(graph.find(0) == graph.end());
+ Assert(!(graph.find(2) == graph.end()));
+ Assert(graph.at(2).getNode() == string("foo"));
+ Assert(graph.at(2).isTerminal() == false);
+ Assert(graph.at(3).getNode() == string("bar"));
+ Assert(graph.at(3).isTerminal() == false);
+ edge_iterator itr = graph.at(3).neighborsBegin();
+ (void)itr;
+ Assert(itr.getNodeIterator(graph.begin()).getNode() == string("foo"));
+ Assert(itr.getNodeIterator(graph.begin()).isTerminal() == false);
+ Assert(graph.find(5) == graph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_incomplete_graph():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<int> neighbors = {2};
+ vector<SimpleNode> values{{1, "foo", &neighbors, false}};
+
+ Graph graph(values.begin(), values.end(), memory_pool);
+ Assert(!(graph.find(1) == graph.end()));
+ Assert(graph.at(1).getNode() == string("foo"));
+ Assert(graph.at(1).isTerminal() == false);
+ Assert(graph.find(2) == graph.end());
+ const Graph& cgraph = graph;
+ Assert(!(cgraph.find(1) == cgraph.end()));
+ Assert(cgraph.find(1).getNode() == string("foo"));
+ Assert(cgraph.find(1).isTerminal() == false);
+ Assert(cgraph.find(2) == cgraph.end());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/data_structures/test_semistatic_map.py b/tests/data_structures/test_semistatic_map.py
new file mode 100644
index 0000000..c8936af
--- /dev/null
+++ b/tests/data_structures/test_semistatic_map.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ #define IN_FRUIT_CPP_FILE
+ #include <fruit/impl/data_structures/semistatic_map.templates.h>
+
+ using namespace std;
+ using namespace fruit::impl;
+ '''
+
+def test_empty():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{};
+
+ SemistaticMap<int, std::string> map(values.begin(), values.size(), memory_pool);
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(2) == nullptr);
+ Assert(map.find(5) == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_1_elem():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{{2, "foo"}};
+
+ SemistaticMap<int, std::string> map(values.begin(), values.size(), memory_pool);
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(2) != nullptr);
+ Assert(map.at(2) == "foo");
+ Assert(map.find(5) == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_1_inserted_elem():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{};
+
+ SemistaticMap<int, std::string> old_map(values.begin(), values.size(), memory_pool);
+ vector<pair<int, std::string>, ArenaAllocator<pair<int, std::string>>> new_values(
+ {{2, "bar"}},
+ ArenaAllocator<pair<int, std::string>>(memory_pool));
+ SemistaticMap<int, std::string> map(old_map, std::move(new_values));
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(2) != nullptr);
+ Assert(map.at(2) == "bar");
+ Assert(map.find(5) == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_3_elem():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{{1, "foo"}, {3, "bar"}, {4, "baz"}};
+
+ SemistaticMap<int, std::string> map(values.begin(), values.size(), memory_pool);
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(1) != nullptr);
+ Assert(map.at(1) == "foo");
+ Assert(map.find(2) == nullptr);
+ Assert(map.find(3) != nullptr);
+ Assert(map.at(3) == "bar");
+ Assert(map.find(4) != nullptr);
+ Assert(map.at(4) == "baz");
+ Assert(map.find(5) == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_1_elem_2_inserted():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{{1, "foo"}};
+
+ SemistaticMap<int, std::string> old_map(values.begin(), values.size(), memory_pool);
+ vector<pair<int, std::string>, ArenaAllocator<pair<int, std::string>>> new_values(
+ {{3, "bar"}, {4, "baz"}},
+ ArenaAllocator<pair<int, std::string>>(memory_pool));
+ SemistaticMap<int, std::string> map(old_map, std::move(new_values));
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(1) != nullptr);
+ Assert(map.at(1) == "foo");
+ Assert(map.find(2) == nullptr);
+ Assert(map.find(3) != nullptr);
+ Assert(map.at(3) == "bar");
+ Assert(map.find(4) != nullptr);
+ Assert(map.at(4) == "baz");
+ Assert(map.find(5) == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_3_elem_3_inserted():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{{1, "1"}, {3, "3"}, {5, "5"}};
+ SemistaticMap<int, std::string> old_map(values.begin(), values.size(), memory_pool);
+ vector<pair<int, std::string>, ArenaAllocator<pair<int, std::string>>> new_values(
+ {{2, "2"}, {4, "4"}, {16, "16"}},
+ ArenaAllocator<pair<int, std::string>>(memory_pool));
+ SemistaticMap<int, std::string> map(old_map, std::move(new_values));
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(1) != nullptr);
+ Assert(map.at(1) == "1");
+ Assert(map.find(2) != nullptr);
+ Assert(map.at(2) == "2");
+ Assert(map.find(3) != nullptr);
+ Assert(map.at(3) == "3");
+ Assert(map.find(4) != nullptr);
+ Assert(map.at(4) == "4");
+ Assert(map.find(5) != nullptr);
+ Assert(map.at(5) == "5");
+ Assert(map.find(6) == nullptr);
+ Assert(map.find(16) != nullptr);
+ Assert(map.at(16) == "16");
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_move_constructor():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{{1, "foo"}, {3, "bar"}, {4, "baz"}};
+ SemistaticMap<int, std::string> map1(values.begin(), values.size(), memory_pool);
+ SemistaticMap<int, std::string> map = std::move(map1);
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(1) != nullptr);
+ Assert(map.at(1) == "foo");
+ Assert(map.find(2) == nullptr);
+ Assert(map.find(3) != nullptr);
+ Assert(map.at(3) == "bar");
+ Assert(map.find(4) != nullptr);
+ Assert(map.at(4) == "baz");
+ Assert(map.find(5) == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_move_assignment():
+ source = '''
+ int main() {
+ MemoryPool memory_pool;
+ vector<pair<int, std::string>> values{{1, "foo"}, {3, "bar"}, {4, "baz"}};
+ SemistaticMap<int, std::string> map1(values.begin(), values.size(), memory_pool);
+ SemistaticMap<int, std::string> map;
+ map = std::move(map1);
+ Assert(map.find(0) == nullptr);
+ Assert(map.find(1) != nullptr);
+ Assert(map.at(1) == "foo");
+ Assert(map.find(2) == nullptr);
+ Assert(map.find(3) != nullptr);
+ Assert(map.at(3) == "bar");
+ Assert(map.find(4) != nullptr);
+ Assert(map.at(4) == "baz");
+ Assert(map.find(5) == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/fruit_test_common.py b/tests/fruit_test_common.py
new file mode 100644
index 0000000..ac9b3bd
--- /dev/null
+++ b/tests/fruit_test_common.py
@@ -0,0 +1,580 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import tempfile
+import unittest
+import textwrap
+import re
+import sys
+
+import itertools
+
+import subprocess
+
+import pytest
+
+from fruit_test_config import *
+
+run_under_valgrind = RUN_TESTS_UNDER_VALGRIND.lower() not in ('false', 'off', 'no', '0', '')
+
+def pretty_print_command(command):
+ return ' '.join('"' + x + '"' for x in command)
+
+class CommandFailedException(Exception):
+ def __init__(self, command, stdout, stderr, error_code):
+ self.command = command
+ self.stdout = stdout
+ self.stderr = stderr
+ self.error_code = error_code
+
+ def __str__(self):
+ return textwrap.dedent('''\
+ Ran command: {command}
+ Exit code {error_code}
+ Stdout:
+ {stdout}
+
+ Stderr:
+ {stderr}
+ ''').format(command=pretty_print_command(self.command), error_code=self.error_code, stdout=self.stdout, stderr=self.stderr)
+
+def run_command(executable, args=[], modify_env=lambda env: env):
+ command = [executable] + args
+ modified_env = modify_env(os.environ)
+ print('Executing command:', pretty_print_command(command))
+ try:
+ p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, env=modified_env)
+ (stdout, stderr) = p.communicate()
+ except Exception as e:
+ raise Exception("While executing: %s" % command)
+ if p.returncode != 0:
+ raise CommandFailedException(command, stdout, stderr, p.returncode)
+ print('Execution successful.')
+ print('stdout:')
+ print(stdout)
+ print('')
+ print('stderr:')
+ print(stderr)
+ print('')
+ return (stdout, stderr)
+
+def run_compiled_executable(executable):
+ if run_under_valgrind:
+ args = VALGRIND_FLAGS.split() + [executable]
+ run_command('valgrind', args = args, modify_env = modify_env_for_compiled_executables)
+ else:
+ run_command(executable, modify_env = modify_env_for_compiled_executables)
+
+class CompilationFailedException(Exception):
+ def __init__(self, command, error_message):
+ self.command = command
+ self.error_message = error_message
+
+ def __str__(self):
+ return textwrap.dedent('''\
+ Ran command: {command}
+ Error message:
+ {error_message}
+ ''').format(command=pretty_print_command(self.command), error_message=self.error_message)
+
+class PosixCompiler:
+ def __init__(self):
+ self.executable = CXX
+ self.name = CXX_COMPILER_NAME
+
+ def compile_discarding_output(self, source, include_dirs, args=[]):
+ try:
+ args = args + ['-c', source, '-o', os.path.devnull]
+ self._compile(include_dirs, args=args)
+ except CommandFailedException as e:
+ raise CompilationFailedException(e.command, e.stderr)
+
+ def compile_and_link(self, source, include_dirs, output_file_name, args=[]):
+ self._compile(
+ include_dirs,
+ args = (
+ [source]
+ + ADDITIONAL_LINKER_FLAGS.split()
+ + args
+ + ['-o', output_file_name]
+ ))
+
+ def _compile(self, include_dirs, args):
+ include_flags = ['-I%s' % include_dir for include_dir in include_dirs]
+ args = (
+ FRUIT_COMPILE_FLAGS.split()
+ + include_flags
+ + ['-g0', '-Werror']
+ + args
+ )
+ run_command(self.executable, args)
+
+ def get_disable_deprecation_warning_flags(self):
+ return ['-Wno-deprecated-declarations']
+
+ def get_disable_all_warnings_flags(self):
+ return ['-Wno-error']
+
+class MsvcCompiler:
+ def __init__(self):
+ self.executable = CXX
+ self.name = CXX_COMPILER_NAME
+
+ def compile_discarding_output(self, source, include_dirs, args=[]):
+ try:
+ args = args + ['/c', source]
+ self._compile(include_dirs, args = args)
+ except CommandFailedException as e:
+ # Note that we use stdout here, unlike above. MSVC reports compilation warnings and errors on stdout.
+ raise CompilationFailedException(e.command, e.stdout)
+
+ def compile_and_link(self, source, include_dirs, output_file_name, args=[]):
+ self._compile(
+ include_dirs,
+ args = (
+ [source]
+ + ADDITIONAL_LINKER_FLAGS.split()
+ + args
+ + ['/Fe' + output_file_name]
+ ))
+
+ def _compile(self, include_dirs, args):
+ include_flags = ['-I%s' % include_dir for include_dir in include_dirs]
+ args = (
+ FRUIT_COMPILE_FLAGS.split()
+ + include_flags
+ + ['/WX']
+ + args
+ )
+ run_command(self.executable, args)
+
+ def get_disable_deprecation_warning_flags(self):
+ return ['/wd4996']
+
+ def get_disable_all_warnings_flags(self):
+ return ['/WX:NO']
+
+if CXX_COMPILER_NAME == 'MSVC':
+ compiler = MsvcCompiler()
+ if PATH_TO_COMPILED_FRUIT_LIB.endswith('.dll'):
+ path_to_fruit_lib = PATH_TO_COMPILED_FRUIT_LIB[:-4] + '.lib'
+ else:
+ path_to_fruit_lib = PATH_TO_COMPILED_FRUIT_LIB
+ fruit_tests_linker_flags = [path_to_fruit_lib]
+ fruit_error_message_extraction_regex = 'error C2338: (.*)'
+else:
+ compiler = PosixCompiler()
+ fruit_tests_linker_flags = [
+ '-lfruit',
+ '-L' + PATH_TO_COMPILED_FRUIT,
+ '-Wl,-rpath,' + PATH_TO_COMPILED_FRUIT,
+ ]
+ fruit_error_message_extraction_regex = 'static.assert(.*)'
+
+fruit_tests_include_dirs = ADDITIONAL_INCLUDE_DIRS.splitlines() + [
+ PATH_TO_FRUIT_TEST_HEADERS,
+ PATH_TO_FRUIT_STATIC_HEADERS,
+ PATH_TO_FRUIT_GENERATED_HEADERS,
+]
+
+_assert_helper = unittest.TestCase()
+
+def modify_env_for_compiled_executables(env):
+ env = env.copy()
+ path_to_fruit_lib_dir = os.path.dirname(PATH_TO_COMPILED_FRUIT_LIB)
+ print('PATH_TO_COMPILED_FRUIT_LIB:', PATH_TO_COMPILED_FRUIT_LIB)
+ print('Adding directory to PATH:', path_to_fruit_lib_dir)
+ env["PATH"] += os.pathsep + path_to_fruit_lib_dir
+ return env
+
+def _create_temporary_file(file_content, file_name_suffix=''):
+ file_descriptor, file_name = tempfile.mkstemp(text=True, suffix=file_name_suffix)
+ file = os.fdopen(file_descriptor, mode='w')
+ file.write(file_content)
+ file.close()
+ return file_name
+
+def _cap_to_lines(s, n):
+ lines = s.splitlines()
+ if len(lines) <= n:
+ return s
+ else:
+ return '\n'.join(lines[0:n] + ['...'])
+
+def _replace_using_test_params(s, test_params):
+ for var_name, value in test_params.items():
+ if isinstance(value, str):
+ s = re.sub(r'\b%s\b' % var_name, value, s)
+ return s
+
+def _construct_final_source_code(setup_source_code, source_code, test_params):
+ setup_source_code = textwrap.dedent(setup_source_code)
+ source_code = textwrap.dedent(source_code)
+ source_code = _replace_using_test_params(source_code, test_params)
+ return setup_source_code + source_code
+
+def try_remove_temporary_file(filename):
+ try:
+ os.remove(filename)
+ except:
+ # When running Fruit tests on Windows using Appveyor, the remove command fails for temporary files sometimes.
+ # This shouldn't cause the tests to fail, so we ignore the exception and go ahead.
+ pass
+
+def expect_compile_error_helper(
+ check_error_fun,
+ setup_source_code,
+ source_code,
+ test_params={},
+ ignore_deprecation_warnings=False,
+ ignore_warnings=False):
+ source_code = _construct_final_source_code(setup_source_code, source_code, test_params)
+
+ source_file_name = _create_temporary_file(source_code, file_name_suffix='.cpp')
+
+ try:
+ args = []
+ if ignore_deprecation_warnings:
+ args += compiler.get_disable_deprecation_warning_flags()
+ if ignore_warnings:
+ args += compiler.get_disable_all_warnings_flags()
+ if ENABLE_COVERAGE:
+ # When collecting coverage these arguments are enabled by default; however we must disable them in tests
+ # expected to fail at compile-time because GCC would otherwise fail with an error like:
+ # /tmp/tmp4m22cey7.cpp:1:0: error: cannot open /dev/null.gcno
+ args += ['-fno-profile-arcs', '-fno-test-coverage']
+ compiler.compile_discarding_output(
+ source=source_file_name,
+ include_dirs=fruit_tests_include_dirs,
+ args=args)
+ raise Exception('The test should have failed to compile, but it compiled successfully')
+ except CompilationFailedException as e1:
+ e = e1
+
+ error_message = e.error_message
+ error_message_lines = error_message.splitlines()
+ # Different compilers output a different number of spaces when pretty-printing types.
+ # When using libc++, sometimes std::foo identifiers are reported as std::__1::foo.
+ normalized_error_message = error_message.replace(' ', '').replace('std::__1::', 'std::')
+ normalized_error_message_lines = normalized_error_message.splitlines()
+ error_message_head = _cap_to_lines(error_message, 40)
+
+ check_error_fun(e, error_message_lines, error_message_head, normalized_error_message_lines)
+
+ try_remove_temporary_file(source_file_name)
+
+def expect_generic_compile_error(expected_error_regex, setup_source_code, source_code, test_params={}):
+ """
+ Tests that the given source produces the expected error during compilation.
+
+ :param expected_fruit_error_regex: A regex used to match the Fruit error type,
+ e.g. 'NoBindingFoundForAbstractClassError<ScalerImpl>'.
+ Any identifiers contained in the regex will be replaced using test_params (where a replacement is defined).
+ :param expected_fruit_error_desc_regex: A regex used to match the Fruit error description,
+ e.g. 'No explicit binding was found for C, and C is an abstract class'.
+ :param setup_source_code: The first part of the source code. This is dedented separately from source_code and it's
+ *not* subject to test_params, unlike source_code.
+ :param source_code: The second part of the source code. Any identifiers will be replaced using test_params
+ (where a replacement is defined). This will be dedented.
+ :param test_params: A dict containing the definition of some identifiers. Each identifier in
+ expected_fruit_error_regex and source_code will be replaced (textually) with its definition (if a definition
+ was provided).
+ """
+
+ expected_error_regex = _replace_using_test_params(expected_error_regex, test_params)
+ expected_error_regex = expected_error_regex.replace(' ', '')
+
+ def check_error(e, error_message_lines, error_message_head, normalized_error_message_lines):
+ for line in normalized_error_message_lines:
+ if re.search(expected_error_regex, line):
+ return
+ raise Exception(textwrap.dedent('''\
+ Expected error {expected_error} but the compiler output did not contain that.
+ Compiler command line: {compiler_command}
+ Error message was:
+ {error_message}
+ ''').format(expected_error = expected_error_regex, compiler_command=e.command, error_message = error_message_head))
+
+ expect_compile_error_helper(check_error, setup_source_code, source_code, test_params)
+
+
+def expect_compile_error(
+ expected_fruit_error_regex,
+ expected_fruit_error_desc_regex,
+ setup_source_code,
+ source_code,
+ test_params={},
+ ignore_deprecation_warnings=False,
+ ignore_warnings=False,
+ disable_error_line_number_check=False):
+ """
+ Tests that the given source produces the expected error during compilation.
+
+ :param expected_fruit_error_regex: A regex used to match the Fruit error type,
+ e.g. 'NoBindingFoundForAbstractClassError<ScalerImpl>'.
+ Any identifiers contained in the regex will be replaced using test_params (where a replacement is defined).
+ :param expected_fruit_error_desc_regex: A regex used to match the Fruit error description,
+ e.g. 'No explicit binding was found for C, and C is an abstract class'.
+ :param setup_source_code: The first part of the source code. This is dedented separately from source_code and it's
+ *not* subject to test_params, unlike source_code.
+ :param source_code: The second part of the source code. Any identifiers will be replaced using test_params
+ (where a replacement is defined). This will be dedented.
+ :param test_params: A dict containing the definition of some identifiers. Each identifier in
+ expected_fruit_error_regex and source_code will be replaced (textually) with its definition (if a definition
+ was provided).
+ :param ignore_deprecation_warnings: A boolean. If True, deprecation warnings will be ignored.
+ :param ignore_warnings: A boolean. If True, all warnings will be ignored.
+ :param disable_error_line_number_check: A boolean. If True, the test will not fail if there are other diagnostic
+ lines before the expected error.
+ """
+ if '\n' in expected_fruit_error_regex:
+ raise Exception('expected_fruit_error_regex should not contain newlines')
+ if '\n' in expected_fruit_error_desc_regex:
+ raise Exception('expected_fruit_error_desc_regex should not contain newlines')
+
+ expected_fruit_error_regex = _replace_using_test_params(expected_fruit_error_regex, test_params)
+ expected_fruit_error_regex = expected_fruit_error_regex.replace(' ', '')
+
+ def check_error(e, error_message_lines, error_message_head, normalized_error_message_lines):
+ for line_number, line in enumerate(normalized_error_message_lines):
+ match = re.search('fruit::impl::(.*Error<.*>)', line)
+ if match:
+ actual_fruit_error_line_number = line_number
+ actual_fruit_error = match.groups()[0]
+ if CXX_COMPILER_NAME == 'MSVC':
+ # MSVC errors are of the form:
+ #
+ # C:\Path\To\header\foo.h(59): note: see reference to class template instantiation 'fruit::impl::NoBindingFoundError<fruit::Annotated<Annotation,U>>' being compiled
+ # with
+ # [
+ # Annotation=Annotation1,
+ # U=std::function<std::unique_ptr<ScalerImpl,std::default_delete<ScalerImpl>> (double)>
+ # ]
+ #
+ # So we need to parse the following few lines and use them to replace the placeholder types in the Fruit error type.
+ try:
+ replacement_lines = []
+ if normalized_error_message_lines[line_number + 1].strip() == 'with':
+ for line in itertools.islice(normalized_error_message_lines, line_number + 3, None):
+ line = line.strip()
+ if line == ']':
+ break
+ if line.endswith(','):
+ line = line[:-1]
+ replacement_lines.append(line)
+ for replacement_line in replacement_lines:
+ match = re.search('([A-Za-z0-9_-]*)=(.*)', replacement_line)
+ if not match:
+ raise Exception('Failed to parse replacement line: %s' % replacement_line) from e
+ (type_variable, type_expression) = match.groups()
+ actual_fruit_error = re.sub(r'\b' + type_variable + r'\b', type_expression, actual_fruit_error)
+ except Exception:
+ raise Exception('Failed to parse MSVC template type arguments')
+ break
+ else:
+ raise Exception(textwrap.dedent('''\
+ Expected error {expected_error} but the compiler output did not contain user-facing Fruit errors.
+ Compiler command line: {compiler_command}
+ Error message was:
+ {error_message}
+ ''').format(expected_error = expected_fruit_error_regex, compiler_command = e.command, error_message = error_message_head))
+
+ for line_number, line in enumerate(error_message_lines):
+ match = re.search(fruit_error_message_extraction_regex, line)
+ if match:
+ actual_static_assert_error_line_number = line_number
+ actual_static_assert_error = match.groups()[0]
+ break
+ else:
+ raise Exception(textwrap.dedent('''\
+ Expected error {expected_error} but the compiler output did not contain static_assert errors.
+ Compiler command line: {compiler_command}
+ Error message was:
+ {error_message}
+ ''').format(expected_error = expected_fruit_error_regex, compiler_command=e.command, error_message = error_message_head))
+
+ try:
+ regex_search_result = re.search(expected_fruit_error_regex, actual_fruit_error)
+ except Exception as e:
+ raise Exception('re.search() failed for regex \'%s\'' % expected_fruit_error_regex) from e
+ if not regex_search_result:
+ raise Exception(textwrap.dedent('''\
+ The compilation failed as expected, but with a different error type.
+ Expected Fruit error type: {expected_fruit_error_regex}
+ Error type was: {actual_fruit_error}
+ Expected static assert error: {expected_fruit_error_desc_regex}
+ Static assert was: {actual_static_assert_error}
+ Error message was:
+ {error_message}
+ '''.format(
+ expected_fruit_error_regex = expected_fruit_error_regex,
+ actual_fruit_error = actual_fruit_error,
+ expected_fruit_error_desc_regex = expected_fruit_error_desc_regex,
+ actual_static_assert_error = actual_static_assert_error,
+ error_message = error_message_head)))
+ try:
+ regex_search_result = re.search(expected_fruit_error_desc_regex, actual_static_assert_error)
+ except Exception as e:
+ raise Exception('re.search() failed for regex \'%s\'' % expected_fruit_error_desc_regex) from e
+ if not regex_search_result:
+ raise Exception(textwrap.dedent('''\
+ The compilation failed as expected, but with a different error message.
+ Expected Fruit error type: {expected_fruit_error_regex}
+ Error type was: {actual_fruit_error}
+ Expected static assert error: {expected_fruit_error_desc_regex}
+ Static assert was: {actual_static_assert_error}
+ Error message:
+ {error_message}
+ '''.format(
+ expected_fruit_error_regex = expected_fruit_error_regex,
+ actual_fruit_error = actual_fruit_error,
+ expected_fruit_error_desc_regex = expected_fruit_error_desc_regex,
+ actual_static_assert_error = actual_static_assert_error,
+ error_message = error_message_head)))
+
+ # 6 is just a constant that works for both g++ (<=6.0.0 at least) and clang++ (<=4.0.0 at least).
+ # It might need to be changed.
+ if not disable_error_line_number_check and (actual_fruit_error_line_number > 6 or actual_static_assert_error_line_number > 6):
+ raise Exception(textwrap.dedent('''\
+ The compilation failed with the expected message, but the error message contained too many lines before the relevant ones.
+ The error type was reported on line {actual_fruit_error_line_number} of the message (should be <=6).
+ The static assert was reported on line {actual_static_assert_error_line_number} of the message (should be <=6).
+ Error message:
+ {error_message}
+ '''.format(
+ actual_fruit_error_line_number = actual_fruit_error_line_number,
+ actual_static_assert_error_line_number = actual_static_assert_error_line_number,
+ error_message = error_message_head)))
+
+ for line in error_message_lines[:max(actual_fruit_error_line_number, actual_static_assert_error_line_number)]:
+ if re.search('fruit::impl::meta', line):
+ raise Exception(
+ 'The compilation failed with the expected message, but the error message contained some metaprogramming types in the output (besides Error). Error message:\n%s' + error_message_head)
+
+ expect_compile_error_helper(check_error, setup_source_code, source_code, test_params, ignore_deprecation_warnings, ignore_warnings)
+
+
+def expect_runtime_error(
+ expected_error_regex,
+ setup_source_code,
+ source_code,
+ test_params={},
+ ignore_deprecation_warnings=False):
+ """
+ Tests that the given source (compiles successfully and) produces the expected error at runtime.
+
+ :param expected_error_regex: A regex used to match the content of stderr.
+ Any identifiers contained in the regex will be replaced using test_params (where a replacement is defined).
+ :param setup_source_code: The first part of the source code. This is dedented separately from source_code and it's
+ *not* subject to test_params, unlike source_code.
+ :param source_code: The second part of the source code. Any identifiers will be replaced using test_params
+ (where a replacement is defined). This will be dedented.
+ :param test_params: A dict containing the definition of some identifiers. Each identifier in
+ expected_error_regex and source_code will be replaced (textually) with its definition (if a definition
+ was provided).
+ """
+ expected_error_regex = _replace_using_test_params(expected_error_regex, test_params)
+ source_code = _construct_final_source_code(setup_source_code, source_code, test_params)
+
+ source_file_name = _create_temporary_file(source_code, file_name_suffix='.cpp')
+ executable_suffix = {'posix': '', 'nt': '.exe'}[os.name]
+ output_file_name = _create_temporary_file('', executable_suffix)
+
+ args = fruit_tests_linker_flags.copy()
+ if ignore_deprecation_warnings:
+ args += compiler.get_disable_deprecation_warning_flags()
+ compiler.compile_and_link(
+ source=source_file_name,
+ include_dirs=fruit_tests_include_dirs,
+ output_file_name=output_file_name,
+ args=args)
+
+ try:
+ run_compiled_executable(output_file_name)
+ raise Exception('The test should have failed at runtime, but it ran successfully')
+ except CommandFailedException as e1:
+ e = e1
+
+ stderr = e.stderr
+ stderr_head = _cap_to_lines(stderr, 40)
+
+ if '\n' in expected_error_regex:
+ regex_flags = re.MULTILINE
+ else:
+ regex_flags = 0
+
+ try:
+ regex_search_result = re.search(expected_error_regex, stderr, flags=regex_flags)
+ except Exception as e:
+ raise Exception('re.search() failed for regex \'%s\'' % expected_error_regex) from e
+ if not regex_search_result:
+ raise Exception(textwrap.dedent('''\
+ The test failed as expected, but with a different message.
+ Expected: {expected_error_regex}
+ Was:
+ {stderr}
+ '''.format(expected_error_regex = expected_error_regex, stderr = stderr_head)))
+
+ # Note that we don't delete the temporary files if the test failed. This is intentional, keeping them around helps debugging the failure.
+ if not ENABLE_COVERAGE:
+ try_remove_temporary_file(source_file_name)
+ try_remove_temporary_file(output_file_name)
+
+
+def expect_success(setup_source_code, source_code, test_params={}, ignore_deprecation_warnings=False):
+ """
+ Tests that the given source compiles and runs successfully.
+
+ :param setup_source_code: The first part of the source code. This is dedented separately from source_code and it's
+ *not* subject to test_params, unlike source_code.
+ :param source_code: The second part of the source code. Any identifiers will be replaced using test_params
+ (where a replacement is defined). This will be dedented.
+ :param test_params: A dict containing the definition of some identifiers. Each identifier in
+ source_code will be replaced (textually) with its definition (if a definition was provided).
+ """
+ source_code = _construct_final_source_code(setup_source_code, source_code, test_params)
+
+ if 'main(' not in source_code:
+ source_code += textwrap.dedent('''
+ int main() {
+ }
+ ''')
+
+ source_file_name = _create_temporary_file(source_code, file_name_suffix='.cpp')
+ executable_suffix = {'posix': '', 'nt': '.exe'}[os.name]
+ output_file_name = _create_temporary_file('', executable_suffix)
+
+ args = fruit_tests_linker_flags.copy()
+ if ignore_deprecation_warnings:
+ args += compiler.get_disable_deprecation_warning_flags()
+ compiler.compile_and_link(
+ source=source_file_name,
+ include_dirs=fruit_tests_include_dirs,
+ output_file_name=output_file_name,
+ args=args)
+
+ run_compiled_executable(output_file_name)
+
+ # Note that we don't delete the temporary files if the test failed. This is intentional, keeping them around helps debugging the failure.
+ if not ENABLE_COVERAGE:
+ try_remove_temporary_file(source_file_name)
+ try_remove_temporary_file(output_file_name)
+
+
+# Note: this is not the main function of this file, it's meant to be used as main function from test_*.py files.
+def main(file):
+ code = pytest.main(args = sys.argv + [os.path.realpath(file)])
+ exit(code)
diff --git a/tests/meta/BUILD b/tests/meta/BUILD
new file mode 100644
index 0000000..1623eed
--- /dev/null
+++ b/tests/meta/BUILD
@@ -0,0 +1,15 @@
+
+licenses(["notice"])
+
+load("//third_party/fruit/tests:build_defs.bzl", "fruit_py_tests")
+
+filegroup(
+ name = "additional_test_headers_filegroup",
+ srcs = ["common.h"],
+ visibility = ["//third_party/fruit/tests:__subpackages__"],
+)
+
+fruit_py_tests(
+ srcs = glob(["test_*.py"]),
+ data = [":additional_test_headers_filegroup"],
+)
diff --git a/tests/meta/common.h b/tests/meta/common.h
new file mode 100644
index 0000000..f2e3078
--- /dev/null
+++ b/tests/meta/common.h
@@ -0,0 +1,102 @@
+
+#ifndef FRUIT_TESTS_META_COMMON_H
+#define FRUIT_TESTS_META_COMMON_H
+
+#define FRUIT_IN_META_TEST
+
+#include <fruit/impl/injection_debug_errors.h>
+#include <fruit/impl/injection_errors.h>
+#include <fruit/impl/meta/basics.h>
+#include <fruit/impl/meta/errors.h>
+#include <fruit/impl/meta/immutable_map.h>
+#include <fruit/impl/meta/set.h>
+#include <fruit/impl/meta/vector.h>
+
+using namespace std;
+using namespace fruit;
+using namespace fruit::impl;
+using namespace fruit::impl::meta;
+
+template <typename T, typename U>
+struct DifferentError {
+ static_assert(AlwaysFalse<T>::value, "T and U are different, but should have been equal/equivalent.");
+};
+
+template <typename T, typename U>
+struct SameError {
+ static_assert(AlwaysFalse<T>::value, "T and U are equal/equivalent but should have been different.");
+};
+
+struct DifferentErrorTag {
+ template <typename T, typename U>
+ using apply = DifferentError<T, U>;
+};
+
+struct SameErrorTag {
+ template <typename T, typename U>
+ using apply = SameError<T, U>;
+};
+
+template <typename... Types>
+using ToSet = Vector<Types...>;
+
+struct ConstructErrorWithoutUnwrapping {
+ template <typename ErrorTag, typename... Args>
+ struct apply {
+ using type = ConstructError(ErrorTag, Type<Args>...);
+ };
+};
+
+using True = Bool<true>;
+using False = Bool<false>;
+
+#undef Assert
+
+#define Assert(...) static_assert(Eval<__VA_ARGS__>::value, "")
+#define AssertNot(...) Assert(Not(__VA_ARGS__))
+#define AssertSame(...) \
+ static_assert( \
+ true || \
+ sizeof( \
+ typename CheckIfError<Eval<If(IsSame(__VA_ARGS__), True, \
+ ConstructErrorWithoutUnwrapping(DifferentErrorTag, __VA_ARGS__))>>::type), \
+ "")
+#define AssertSameType(...) \
+ static_assert( \
+ true || sizeof(typename CheckIfError< \
+ Eval<If(IsSame(__VA_ARGS__), True, ConstructError(DifferentErrorTag, __VA_ARGS__))>>::type), \
+ "")
+#define AssertSameSet(...) \
+ static_assert( \
+ true || sizeof(typename CheckIfError< \
+ Eval<If(IsSameSet(__VA_ARGS__), True, ConstructError(DifferentErrorTag, __VA_ARGS__))>>::type), \
+ "")
+#define AssertSameProof(...) \
+ static_assert(true || sizeof(typename CheckIfError<Eval<If(IsProofTreeEqualTo(__VA_ARGS__), True, \
+ ConstructError(DifferentErrorTag, __VA_ARGS__))>>::type), \
+ "")
+#define AssertSameForest(...) \
+ static_assert(true || sizeof(typename CheckIfError<Eval<CheckForestEqualTo(__VA_ARGS__)>>::type), "")
+#define AssertNotSame(...) \
+ static_assert( \
+ true || \
+ sizeof(typename CheckIfError<Eval<If(Not(IsSame(__VA_ARGS__)), True, \
+ ConstructErrorWithoutUnwrapping(SameErrorTag, __VA_ARGS__))>>::type), \
+ "")
+#define AssertNotSameType(...) \
+ static_assert( \
+ true || sizeof(typename CheckIfError< \
+ Eval<If(Not(IsSame(__VA_ARGS__)), True, ConstructError(SameErrorTag, __VA_ARGS__))>>::type), \
+ "")
+#define AssertNotSameProof(...) \
+ static_assert(true || sizeof(typename CheckIfError<Eval<If(Not(IsProofTreeEqualTo(__VA_ARGS__)), True, \
+ ConstructError(SameErrorTag, __VA_ARGS__))>>::type), \
+ "")
+#define AssertNotSameForest(...) \
+ static_assert( \
+ true || \
+ sizeof(typename CheckIfError< \
+ Eval<If(Not(IsForestEqualTo(__VA_ARGS__)), True, ConstructError(SameErrorTag, __VA_ARGS__))>>::type), \
+ "")
+
+#endif // FRUIT_TESTS_META_COMMON_H
diff --git a/tests/meta/test_algos.py b/tests/meta/test_algos.py
new file mode 100644
index 0000000..5ac1ced
--- /dev/null
+++ b/tests/meta/test_algos.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/algos.h>
+ '''
+
+def test_HasDuplicates():
+ source = '''
+ int main() {
+ AssertNot(HasDuplicates(Vector<>));
+ AssertNot(HasDuplicates(Vector<Int<0>>));
+ AssertNot(HasDuplicates(Vector<Int<0>, Int<1>>));
+ Assert(HasDuplicates(Vector<Int<0>, Int<0>>));
+ Assert(HasDuplicates(Vector<Int<2>, Int<0>, Int<1>, Int<0>, Int<3>>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_basics.py b/tests/meta/test_basics.py
new file mode 100644
index 0000000..1508eaf
--- /dev/null
+++ b/tests/meta/test_basics.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/vector.h>
+
+ #include <vector>
+
+ struct A {};
+ struct B {};
+ struct C {};
+
+ struct Select1st {
+ template <typename T, typename U>
+ struct apply {
+ using type = T;
+ };
+ };
+
+ struct Select2nd {
+ template <typename T, typename U>
+ struct apply {
+ using type = U;
+ };
+ };
+ '''
+
+def test_ImplicitCall():
+ source = '''
+ int main() {
+ AssertSameType(Type<int>, Id<Select1st(Type<int>, Type<float>)>);
+ AssertSameType(Type<float>, Id<Select2nd(Type<int>, Type<float>)>);
+ AssertSameType(Type<int>, Id<Select1st(Type<int>, Type<float>)>);
+ AssertSameType(Type<float>, Id<Select2nd(Type<int>, Type<float>)>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_Call():
+ source = '''
+ int main() {
+ AssertSameType(Type<int>, Id<Call(Select1st, Type<int>, Type<float>)>);
+ AssertSameType(Type<float>, Id<Call(Select2nd, Type<int>, Type<float>)>);
+ AssertSameType(Type<int>, Id<Call(Select1st, Type<int>, Type<float>)>);
+ AssertSameType(Type<float>, Id<Call(Select2nd, Type<int>, Type<float>)>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_DeferArgs():
+ source = '''
+ int main() {
+ AssertSameType(Type<int>, Id<Call(Id<Call(Id<DeferArgs(Select1st)>, Type<int>)>, Type<float>)>);
+ AssertSameType(Type<float>, Id<Call(Id<Call(Id<DeferArgs(Select2nd)>, Type<int>)>, Type<float>)>);
+ AssertSameType(Type<int>, Id<Call(Id<Call(Id<DeferArgs(Select1st)>, Type<int>)>, Type<float>)>);
+ AssertSameType(Type<float>, Id<Call(Id<Call(Id<DeferArgs(Select2nd)>, Type<int>)>, Type<float>)>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_graph.py b/tests/meta/test_graph.py
new file mode 100644
index 0000000..91eb36f
--- /dev/null
+++ b/tests/meta/test_graph.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/graph.h>
+
+ struct A1 {};
+ struct B1 {};
+ struct C1 {};
+ struct D1 {};
+ struct E1 {};
+
+ using A = Type<A1>;
+ using B = Type<B1>;
+ using C = Type<C1>;
+ using D = Type<D1>;
+ using E = Type<E1>;
+ '''
+
+def test_GraphFindLoop():
+ source = '''
+ int main() {
+ // A -> B, D
+ // C -> D
+ AssertSameType(Id<GraphFindLoop(Vector<Pair<A, Vector<B, D>>, Pair<C, Vector<D>>, Pair<B, Vector<>>>)>, None);
+
+ // A -> B
+ // B -> B
+ // C -> B
+ AssertSameType(Id<GraphFindLoop(Vector<Pair<A, Vector<B>>, Pair<B, Vector<B>>, Pair<C, Vector<B>>>)>, Vector<B>);
+
+ // A -> D, B
+ // B -> C
+ // C -> A
+ // The order in the result here *does* matter, but rotations of the correct (A,B,C) sequence are also ok.
+ // Fix this test as appropriate.
+ AssertSameType(Id<GraphFindLoop(Vector<Pair<A, Vector<D, B>>, Pair<B, Vector<C>>, Pair<C, Vector<A>>>)>, Vector<B, C, A>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_list.py b/tests/meta/test_list.py
new file mode 100644
index 0000000..b930838
--- /dev/null
+++ b/tests/meta/test_list.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/list.h>
+ #include <fruit/impl/meta/metaprogramming.h>
+ '''
+
+def test_FoldList():
+ source = '''
+ struct Helper {
+ template <typename CurrentResult, typename N>
+ struct apply {
+ using type = Int<(CurrentResult::value + 1) * N::value>;
+ };
+ };
+
+ int main() {
+ AssertSameType(Id<FoldList(EmptyList, Helper, Int<4>)>, Int<4>);
+ AssertSameType(Id<FoldList(Cons<Int<2>, EmptyList>, Helper, Int<4>)>, Int<10>);
+ AssertSameType(Id<FoldList(Cons<Int<3>, Cons<Int<2>, EmptyList>>, Helper, Int<4>)>, Int<32>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_map.py b/tests/meta/test_map.py
new file mode 100644
index 0000000..e943b9e
--- /dev/null
+++ b/tests/meta/test_map.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/map.h>
+ #include <fruit/impl/meta/metaprogramming.h>
+ '''
+
+def test_FindInMap():
+ source = '''
+ int main() {
+ AssertSameType(Id<FindInMap(ToSet<>, Int<2>)>, None);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<1>, Int<2>>>, Int<7>)>, None);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<1>, Int<2>>>, Int<2>)>, None);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<2>, Int<1>>>, Int<2>)>, Int<1>);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<7>)>, None);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<2>)>, None);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<20>)>, None);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<1>)>, Int<2>);
+ AssertSameType(Id<FindInMap(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<10>)>, Int<20>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_MapContainsKey():
+ source = '''
+ int main() {
+ AssertNot(MapContainsKey(ToSet<>, Int<2>));
+ AssertNot(MapContainsKey(ToSet<Pair<Int<1>, Int<2>>>, Int<7>));
+ AssertNot(MapContainsKey(ToSet<Pair<Int<1>, Int<2>>>, Int<2>));
+ Assert(MapContainsKey(ToSet<Pair<Int<2>, Int<1>>>, Int<2>));
+ AssertNot(MapContainsKey(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<7>));
+ AssertNot(MapContainsKey(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<2>));
+ AssertNot(MapContainsKey(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<20>));
+ Assert(MapContainsKey(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<1>));
+ Assert(MapContainsKey(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>, Int<10>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_GetMapKeys():
+ source = '''
+ int main() {
+ AssertSameSet(Id<GetMapKeys(ToSet<>)>, ToSet<>);
+ AssertSameSet(Id<GetMapKeys(ToSet<Pair<Int<1>, Int<2>>>)>, ToSet<Int<1>>);
+ AssertSameSet(Id<GetMapKeys(ToSet<Pair<Int<1>, Int<2>>, Pair<Int<10>, Int<20>>>)>, ToSet<Int<1>, Int<10>>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_meta_component.py b/tests/meta/test_meta_component.py
new file mode 100644
index 0000000..b8910dd
--- /dev/null
+++ b/tests/meta/test_meta_component.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/component.h>
+
+ struct A1 {};
+ struct B1 {};
+
+ using A = Type<A1>;
+ using B = Type<B1>;
+
+ using AssistedA = Type<Assisted<A1>>;
+ using AssistedB = Type<Assisted<B1>>;
+ '''
+
+def test_NumAssisted():
+ source = '''
+ int main() {
+ AssertSame(Int<0>, NumAssisted(Vector<>));
+ AssertSame(Int<0>, NumAssisted(Vector<A>));
+ AssertSame(Int<1>, NumAssisted(Vector<AssistedA>));
+ AssertSame(Int<0>, NumAssisted(Vector<A, B>));
+ AssertSame(Int<1>, NumAssisted(Vector<AssistedA, B>));
+ AssertSame(Int<1>, NumAssisted(Vector<A, AssistedB>));
+ AssertSame(Int<2>, NumAssisted(Vector<AssistedA, AssistedB>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_NumAssistedBefore():
+ source = '''
+ int main() {
+ AssertSame(Int<0>, NumAssistedBefore(Int<0>, Vector<>));
+
+ AssertSame(Int<0>, NumAssistedBefore(Int<0>, Vector<A>));
+ AssertSame(Int<0>, NumAssistedBefore(Int<1>, Vector<A>));
+
+ AssertSame(Int<0>, NumAssistedBefore(Int<0>, Vector<AssistedA>));
+ AssertSame(Int<1>, NumAssistedBefore(Int<1>, Vector<AssistedA>));
+
+ AssertSame(Int<0>, NumAssistedBefore(Int<0>, Vector<A, B>));
+ AssertSame(Int<0>, NumAssistedBefore(Int<1>, Vector<A, B>));
+ AssertSame(Int<0>, NumAssistedBefore(Int<2>, Vector<A, B>));
+
+ AssertSame(Int<0>, NumAssistedBefore(Int<0>, Vector<AssistedA, B>));
+ AssertSame(Int<1>, NumAssistedBefore(Int<1>, Vector<AssistedA, B>));
+ AssertSame(Int<1>, NumAssistedBefore(Int<2>, Vector<AssistedA, B>));
+
+ AssertSame(Int<0>, NumAssistedBefore(Int<0>, Vector<A, AssistedB>));
+ AssertSame(Int<0>, NumAssistedBefore(Int<1>, Vector<A, AssistedB>));
+ AssertSame(Int<1>, NumAssistedBefore(Int<2>, Vector<A, AssistedB>));
+
+ AssertSame(Int<0>, NumAssistedBefore(Int<0>, Vector<AssistedA, AssistedB>));
+ AssertSame(Int<1>, NumAssistedBefore(Int<1>, Vector<AssistedA, AssistedB>));
+ AssertSame(Int<2>, NumAssistedBefore(Int<2>, Vector<AssistedA, AssistedB>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_metaprogramming.py b/tests/meta/test_metaprogramming.py
new file mode 100644
index 0000000..6e0bfab
--- /dev/null
+++ b/tests/meta/test_metaprogramming.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/component.h>
+
+ struct A1 {};
+ struct B1 {};
+
+ using A = Type<A1>;
+ using B = Type<B1>;
+ '''
+
+def test_GetNthType():
+ source = '''
+ int main() {
+ AssertSameType(A, GetNthType(Int<0>, Vector<A>));
+
+ AssertSameType(A, GetNthType(Int<0>, Vector<A, B>));
+ AssertSameType(B, GetNthType(Int<1>, Vector<A, B>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_proof_trees.py b/tests/meta/test_proof_trees.py
new file mode 100644
index 0000000..380b64a
--- /dev/null
+++ b/tests/meta/test_proof_trees.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/metaprogramming.h>
+ #include <fruit/impl/meta/proof_trees.h>
+ #include <fruit/impl/meta/proof_tree_comparison.h>
+
+ #include <vector>
+
+ struct A1 {};
+ struct B1 {};
+ struct C1 {};
+ struct D1 {};
+ struct X1 {};
+ struct Y1 {};
+
+ using A = Type<A1>;
+ using B = Type<B1>;
+ using C = Type<C1>;
+ using D = Type<D1>;
+ using X = Type<X1>;
+ using Y = Type<Y1>;
+
+ using Proof1 = Pair<X, ToSet<A, B>>;
+ using Proof1b = Pair<X, ToSet<B, A>>;
+ using Proof2 = Pair<Y, ToSet<B, C>>;
+ '''
+
+def test_IsProofTreeEqualTo():
+ source = '''
+ int main() {
+ AssertNotSameProof(Pair<X, ToSet<A>>, Pair<X, ToSet<B>>);
+ AssertNotSameProof(Proof1, Proof2);
+ AssertSameProof(Proof1, Proof1b);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_IsForestEqualTo():
+ source = '''
+ int main() {
+ AssertSameForest(Vector<>, Vector<>);
+ AssertNotSameForest(Vector<Proof1>, Vector<Proof2>);
+ AssertSameForest(Vector<Proof1, Proof2>, Vector<Proof2, Proof1b>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_set.py b/tests/meta/test_set.py
new file mode 100644
index 0000000..bc73672
--- /dev/null
+++ b/tests/meta/test_set.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/set.h>
+ #include <fruit/impl/meta/metaprogramming.h>
+
+ struct A1 {};
+ struct B1 {};
+ struct C1 {};
+
+ using A = Type<A1>;
+ using B = Type<B1>;
+ using C = Type<C1>;
+
+ struct Square {
+ template <typename N>
+ struct apply {
+ using type = Int<N::value * N::value>;
+ };
+ };
+ '''
+
+def test_EmptySet():
+ source = '''
+ int main() {
+ AssertNot(IsInSet(A, EmptySet));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_ToSet1():
+ source = '''
+ int main() {
+ Assert(IsInSet(A, ToSet1<A>));
+ AssertNot(IsInSet(A, ToSet1<B>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_ToSet2():
+ source = '''
+ int main() {
+ Assert(IsInSet(A, ToSet2<A, B>));
+ Assert(IsInSet(B, ToSet2<A, B>));
+ AssertNot(IsInSet(C, ToSet2<A, B>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_IsSameSet():
+ source = '''
+ int main() {
+ AssertSameSet(EmptySet, EmptySet);
+ AssertSameSet(ToSet<A, B>, ToSet<B, A>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_FoldSet():
+ source = '''
+ int main() {
+ AssertSameType(Id<FoldSet(ToSet<>, Sum, Int<3>)>, Int<3>);
+ AssertSameType(Id<FoldSet(ToSet<Int<2>>, Sum, Int<3>)>, Int<5>);
+ AssertSameType(Id<FoldSet(ToSet<Int<3>, Int<2>, Int<5>, Int<9>, Int<13>>, Sum, Int<7>)>, Int<39>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_FoldSetWithCombine():
+ source = '''
+ int main() {
+ AssertSameType(Id<FoldSetWithCombine(ToSet<>, Square, Sum, Int<0>)>, Int<0>);
+ AssertSameType(Id<FoldSetWithCombine(ToSet<Int<2>>, Square, Sum, Int<0>)>, Int<4>);
+ AssertSameType(Id<FoldSetWithCombine(ToSet<Int<3>, Int<2>, Int<5>, Int<9>, Int<13>>, Square, Sum, Int<0>)>, Int<288>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_AddToSet():
+ source = '''
+ int main() {
+ AssertSameSet(Id<AddToSet(EmptySet, A)>, ToSet<A>);
+ AssertSameSet(Id<AddToSet(ToSet<A, B>, A)>, ToSet<A, B>);
+ AssertSameSet(Id<AddToSet(ToSet<C, B>, A)>, ToSet<A, C, B>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_TransformSet():
+ source = '''
+ int main() {
+ AssertSameSet(Id<TransformSet(ToSet<>, Square)>, ToSet<>);
+ AssertSameSet(Id<TransformSet(ToSet<Int<2>>, Square)>, ToSet<Int<4>>);
+ AssertSameSet(Id<TransformSet(ToSet<Int<3>, Int<2>, Int<5>, Int<9>, Int<13>>, Square)>, ToSet<Int<9>, Int<4>, Int<25>, Int<81>, Int<169>>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_SetSize():
+ source = '''
+ int main() {
+ AssertSameType(Id<SetSize(ToSet<>)>, Int<0>);
+ AssertSameType(Id<SetSize(ToSet<Int<2>>)>, Int<1>);
+ AssertSameType(Id<SetSize(ToSet<Int<3>, Int<2>, Int<5>, Int<9>, Int<13>>)>, Int<5>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_IsEmptySet():
+ source = '''
+ int main() {
+ Assert(IsEmptySet(ToSet<>));
+ AssertNot(IsEmptySet(ToSet<Int<2>>));
+ AssertNot(IsEmptySet(ToSet<Int<3>, Int<2>, Int<5>, Int<9>, Int<13>>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_SetDifference():
+ source = '''
+ int main() {
+ AssertSameSet(Id<SetDifference(ToSet<>, ToSet<>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<>, ToSet<A>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<>, ToSet<A, B>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<>, ToSet<A, B, C>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<A>, ToSet<>)>, ToSet<A>);
+ AssertSameSet(Id<SetDifference(ToSet<A>, ToSet<A>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<A>, ToSet<A, B>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<A>, ToSet<A, B, C>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<B>, ToSet<>)>, ToSet<B>);
+ AssertSameSet(Id<SetDifference(ToSet<B>, ToSet<A>)>, ToSet<B>);
+ AssertSameSet(Id<SetDifference(ToSet<B>, ToSet<A, B>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<B>, ToSet<A, B, C>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<B, C>, ToSet<>)>, ToSet<B, C>);
+ AssertSameSet(Id<SetDifference(ToSet<B, C>, ToSet<A>)>, ToSet<B, C>);
+ AssertSameSet(Id<SetDifference(ToSet<B, C>, ToSet<A, B>)>, ToSet<C>);
+ AssertSameSet(Id<SetDifference(ToSet<B, C>, ToSet<A, B, C>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<A, B>, ToSet<A, B>)>, ToSet<>);
+ AssertSameSet(Id<SetDifference(ToSet<A>, ToSet<A, B>)>, EmptySet);
+ AssertSameSet(Id<SetDifference(ToSet<A, B, C>, ToSet<A>)>, ToSet<B, C>);
+ AssertSameSet(Id<SetDifference(ToSet<A, B, C>, ToSet<B>)>, ToSet<A, C>);
+ AssertSameSet(Id<SetDifference(ToSet<A, B, C>, ToSet<C>)>, ToSet<A, B>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_SetIntersection():
+ source = '''
+ int main() {
+ AssertSameSet(Id<SetIntersection(ToSet<A, B>, ToSet<A, B>)>, ToSet<A, B>);
+ AssertSameSet(Id<SetIntersection(ToSet<A>, ToSet<A, B>)>, ToSet<A>);
+ AssertSameSet(Id<SetIntersection(ToSet<A, B>, ToSet<A>)>, ToSet<A>);
+ AssertSameSet(Id<SetIntersection(ToSet<A>, ToSet<B>)>, ToSet<>);
+ AssertSameSet(Id<SetIntersection(ToSet<>, ToSet<A, B>)>, ToSet<>);
+ AssertSameSet(Id<SetIntersection(ToSet<C>, ToSet<A, B>)>, ToSet<>);
+ AssertSameSet(Id<SetIntersection(ToSet<A, B>, ToSet<>)>, ToSet<>);
+ AssertSameSet(Id<SetIntersection(ToSet<A, B>, ToSet<C>)>, ToSet<>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_IsContained():
+ source = '''
+ int main() {
+ Assert(IsContained(ToSet<>, ToSet<>));
+ Assert(IsContained(ToSet<>, ToSet<A>));
+ Assert(IsContained(ToSet<A, B>, ToSet<A, B>));
+ Assert(IsContained(ToSet<A, B>, ToSet<B, A>));
+ Assert(IsContained(ToSet<A>, ToSet<A, B, C>));
+ Assert(IsContained(ToSet<B>, ToSet<A, B, C>));
+ Assert(IsContained(ToSet<C>, ToSet<A, B, C>));
+ AssertNot(IsContained(ToSet<A, B, C>, ToSet<A, B>));
+ AssertNot(IsContained(ToSet<A, B, C>, ToSet<A, C>));
+ AssertNot(IsContained(ToSet<A, B, C>, ToSet<B, C>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_IsDisjoint():
+ source = '''
+ int main() {
+ AssertNot(IsDisjoint(ToSet<A, B>, ToSet<A, B>));
+ AssertNot(IsDisjoint(ToSet<A>, ToSet<A, B>));
+ AssertNot(IsDisjoint(ToSet<A, B>, ToSet<A>));
+ Assert(IsDisjoint(ToSet<A>, ToSet<B>));
+ Assert(IsDisjoint(ToSet<>, ToSet<A, B>));
+ Assert(IsDisjoint(ToSet<C>, ToSet<A, B>));
+ Assert(IsDisjoint(ToSet<A, B>, ToSet<>));
+ Assert(IsDisjoint(ToSet<A, B>, ToSet<C>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_SetUnion():
+ source = '''
+ int main() {
+ AssertSameSet(Id<SetUnion(ToSet<A, B>, ToSet<A, B>)>, ToSet<A, B>);
+ AssertSameSet(Id<SetUnion(ToSet<A>, ToSet<A, B>)>, ToSet<A, B>);
+ AssertSameSet(Id<SetUnion(ToSet<A, B>, ToSet<A>)>, ToSet<A, B>);
+ AssertSameSet(Id<SetUnion(ToSet<A>, ToSet<B>)>, ToSet<A, B>);
+ AssertSameSet(Id<SetUnion(ToSet<>, ToSet<A, B>)>, ToSet<A, B>);
+ AssertSameSet(Id<SetUnion(ToSet<C>, ToSet<A, B>)>, ToSet<A, B, C>);
+ AssertSameSet(Id<SetUnion(ToSet<A, B>, ToSet<>)>, ToSet<A, B>);
+ AssertSameSet(Id<SetUnion(ToSet<A, B>, ToSet<C>)>, ToSet<A, B, C>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/meta/test_vector.py b/tests/meta/test_vector.py
new file mode 100644
index 0000000..12a39e9
--- /dev/null
+++ b/tests/meta/test_vector.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #define IN_FRUIT_CPP_FILE
+
+ #include "meta/common.h"
+ #include <fruit/impl/meta/vector.h>
+ #include <fruit/impl/meta/metaprogramming.h>
+
+ struct A1 {};
+ struct B1 {};
+ struct C1 {};
+
+ using A = A1;
+ using B = B1;
+ using C = C1;
+ '''
+
+def test_IsInVector():
+ source = '''
+ int main() {
+ AssertNot(IsInVector(A, Vector<>));
+ AssertNot(IsInVector(A, Vector<B>));
+ Assert(IsInVector(A, Vector<A>));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_IsSameVector():
+ source = '''
+ int main() {
+ AssertNotSameType(Vector<A, B>, Vector<B, A>);
+ AssertNotSameType(Vector<A>, Vector<>);
+ AssertNotSameType(Vector<>, Vector<A>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_VectorSize():
+ source = '''
+ int main() {
+ AssertSameType(Id<VectorSize(Vector<>)>, Int<0>);
+ AssertSameType(Id<VectorSize(Vector<A>)>, Int<1>);
+ AssertSameType(Id<VectorSize(Vector<A, B>)>, Int<2>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_ConcatVectors():
+ source = '''
+ int main() {
+ AssertSameType(Id<ConcatVectors(Vector<>, Vector<>)>, Vector<>);
+ AssertSameType(Id<ConcatVectors(Vector<>, Vector<A, B>)>, Vector<A, B>);
+ AssertSameType(Id<ConcatVectors(Vector<A, B>, Vector<>)>, Vector<A, B>);
+ AssertSameType(Id<ConcatVectors(Vector<A>, Vector<A, B>)>, Vector<A, A, B>);
+ AssertSameType(Id<ConcatVectors(Vector<A, B>, Vector<A, C>)>, Vector<A, B, A, C>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_VectorEndsWith():
+ source = '''
+ int main() {
+ Assert(VectorEndsWith(Vector<A, B>, B));
+ AssertNot(VectorEndsWith(Vector<A, B>, A));
+ AssertNot(VectorEndsWith(Vector<>, A));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_VectorRemoveFirstN():
+ source = '''
+ int main() {
+ AssertSameType(Id<VectorRemoveFirstN(Vector<>, Int<0>)>, Vector<>);
+ AssertSameType(Id<VectorRemoveFirstN(Vector<A>, Int<0>)>, Vector<A>);
+ AssertSameType(Id<VectorRemoveFirstN(Vector<A>, Int<1>)>, Vector<>);
+ AssertSameType(Id<VectorRemoveFirstN(Vector<A, B, C>, Int<0>)>, Vector<A, B, C>);
+ AssertSameType(Id<VectorRemoveFirstN(Vector<A, B, C>, Int<1>)>, Vector<B, C>);
+ AssertSameType(Id<VectorRemoveFirstN(Vector<A, B, C>, Int<2>)>, Vector<C>);
+ AssertSameType(Id<VectorRemoveFirstN(Vector<A, B, C>, Int<3>)>, Vector<>);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_bind_interface.py b/tests/test_bind_interface.py
new file mode 100755
index 0000000..14f1b35
--- /dev/null
+++ b/tests/test_bind_interface.py
@@ -0,0 +1,325 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation1 {};
+ struct Annotation2 {};
+ '''
+
+@pytest.mark.parametrize('XAnnot,MaybeConstXAnnot,XConstRefAnnot,YAnnot', [
+ ('X', 'X', 'const X&', 'Y'),
+ ('X', 'const X', 'const X&', 'Y'),
+ ('X', 'X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
+ ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_bind_interface(XAnnot, MaybeConstXAnnot, XConstRefAnnot, YAnnot):
+ source = '''
+ struct X {
+ virtual void f() const = 0;
+ };
+
+ struct Y : public X {
+ INJECT(Y()) = default;
+
+ void f() const override {
+ }
+ };
+
+ fruit::Component<MaybeConstXAnnot> getComponent() {
+ return fruit::createComponent()
+ .bind<XAnnot, YAnnot>();
+ }
+
+ int main() {
+ fruit::Injector<MaybeConstXAnnot> injector(getComponent);
+ const X& x = injector.get<XConstRefAnnot>();
+ x.f();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot,XConstRefAnnot,YAnnot', [
+ ('X', 'const X', 'const X&', 'Y'),
+ ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_bind_interface_to_constant(XAnnot, ConstXAnnot, XConstRefAnnot, YAnnot):
+ source = '''
+ struct X {
+ virtual void f() const = 0;
+ };
+
+ struct Y : public X {
+ void f() const override {
+ }
+ };
+
+ const Y y{};
+
+ fruit::Component<ConstXAnnot> getComponent() {
+ return fruit::createComponent()
+ .bindInstance<YAnnot, Y>(y)
+ .bind<XAnnot, YAnnot>();
+ }
+
+ int main() {
+ fruit::Injector<ConstXAnnot> injector(getComponent);
+ const X& x = injector.get<XConstRefAnnot>();
+ x.f();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [
+ ('X', 'X&', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_bind_interface_target_bound_in_other_component(XAnnot, XRefAnnot, YAnnot):
+ source = '''
+ struct X {
+ virtual void f() = 0;
+ };
+
+ struct Y : public X {
+ void f() override {
+ }
+ };
+
+ fruit::Component<fruit::Required<YAnnot>, XAnnot> getComponent() {
+ return fruit::createComponent()
+ .bind<XAnnot, YAnnot>();
+ }
+
+ fruit::Component<XAnnot> getRootComponent() {
+ return fruit::createComponent()
+ .registerConstructor<YAnnot()>()
+ .install(getComponent);
+ }
+
+ int main() {
+ fruit::Injector<XAnnot> injector(getRootComponent);
+ X& x = injector.get<XRefAnnot>();
+ x.f();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [
+ ('X', 'X&', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_bind_nonconst_interface_requires_nonconst_target(XAnnot, XRefAnnot, YAnnot):
+ source = '''
+ struct X {
+ virtual void f() = 0;
+ };
+
+ struct Y : public X {
+ void f() override {
+ }
+ };
+
+ fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() {
+ return fruit::createComponent()
+ .bind<XAnnot, YAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>',
+ 'The type T was declared as a const Required type in the returned Component, however a non-const binding',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,YAnnot', [
+ ('X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_bind_interface_to_constant_nonconst_required_const_bound_error(XAnnot, YAnnot):
+ source = '''
+ struct X {
+ virtual void f() const = 0;
+ };
+
+ struct Y : public X {
+ void f() const override {
+ }
+ };
+
+ const Y y{};
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent()
+ .bindInstance<YAnnot, Y>(y)
+ .bind<XAnnot, YAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnot>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [
+ ('X', 'X&', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_bind_nonconst_interface_requires_nonconst_target_abstract(XAnnot, XRefAnnot, YAnnot):
+ source = '''
+ struct X {
+ virtual void f() = 0;
+ };
+
+ struct Y : public X {};
+
+ fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() {
+ return fruit::createComponent()
+ .bind<XAnnot, YAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>',
+ 'The type T was declared as a const Required type in the returned Component, however a non-const binding',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,intAnnot', [
+ ('X', 'int'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, int>'),
+])
+def test_error_not_base(XAnnot, intAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<intAnnot> getComponent() {
+ return fruit::createComponent()
+ .bind<XAnnot, intAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'NotABaseClassOfError<X,int>',
+ 'I is not a base class of C.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+# TODO: maybe the error should include the annotation here.
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_error_bound_to_itself(XAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .bind<XAnnot, XAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'InterfaceBindingToSelfError<X>',
+ 'The type C was bound to itself.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_bound_to_itself_with_annotation_error():
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X()>()
+ .bind<fruit::Annotated<Annotation1, X>, X>();
+ }
+ '''
+ expect_compile_error(
+ 'InterfaceBindingToSelfError<X>',
+ 'The type C was bound to itself.',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_bound_chain_ok():
+ source = '''
+ struct X {
+ virtual void f() = 0;
+ };
+
+ struct Y : public X {};
+
+ struct Z : public Y {
+ INJECT(Z()) = default;
+ void f() override {
+ }
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .bind<X, Y>()
+ .bind<Y, Z>();
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ X& x = injector.get<X&>();
+ x.f();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_bind_non_normalized_types_error():
+ source = '''
+ struct X {};
+
+ struct Y : public std::shared_ptr<X> {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .bind<std::shared_ptr<X>, Y>();
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<std::shared_ptr<X>,X>',
+ 'A non-class type T was specified. Use C instead',
+ COMMON_DEFINITIONS,
+ source)
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_binding_clash.py b/tests/test_binding_clash.py
new file mode 100755
index 0000000..aad02b7
--- /dev/null
+++ b/tests/test_binding_clash.py
@@ -0,0 +1,522 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+ struct Y;
+ struct Z;
+
+ struct Annotation1 {};
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+ using YAnnot1 = fruit::Annotated<Annotation1, Y>;
+ using ZAnnot1 = fruit::Annotated<Annotation1, Z>;
+ using ConstXAnnot1 = fruit::Annotated<Annotation1, const X>;
+ using ConstYAnnot1 = fruit::Annotated<Annotation1, const Y>;
+ using ConstZAnnot1 = fruit::Annotated<Annotation1, const Z>;
+
+ struct Annotation2 {};
+ using XAnnot2 = fruit::Annotated<Annotation2, X>;
+ using YAnnot2 = fruit::Annotated<Annotation2, Y>;
+ using ZAnnot2 = fruit::Annotated<Annotation2, Z>;
+ using ConstXAnnot2 = fruit::Annotated<Annotation2, const X>;
+ using ConstYAnnot2 = fruit::Annotated<Annotation2, const Y>;
+ using ConstZAnnot2 = fruit::Annotated<Annotation2, const Z>;
+
+ struct Annotation3 {};
+ '''
+
+CONSTRUCTOR_BINDING=(
+ '',
+ '.registerConstructor<XAnnot()>()')
+INTERFACE_BINDING=(
+ '''
+ struct Y : public X {};
+ ''',
+ '''
+ .bind<XAnnot, YAnnot>()
+ .registerConstructor<YAnnot()>()
+ ''')
+INTERFACE_BINDING2=(
+ '''
+ struct Y2 : public X {};
+ ''',
+ '''
+ .bind<XAnnot, Y2Annot>()
+ .registerConstructor<Y2Annot()>()
+ ''')
+INSTALL=(
+ '''
+ fruit::Component<XAnnot> getParentComponent() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot()>();
+ }
+ ''',
+ '.install(getParentComponent)')
+INSTALL2=(
+ '''
+ fruit::Component<XAnnot> getParentComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot()>();
+ }
+ ''',
+ '.install(getParentComponent2)')
+CONST_BINDING_FROM_INSTALL=(
+ '''
+ fruit::Component<const XAnnot> getParentComponent() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot()>();
+ }
+ ''',
+ '.install(getParentComponent)')
+CONST_BINDING_FROM_INSTALL2=(
+ '''
+ fruit::Component<const XAnnot> getParentComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot()>();
+ }
+ ''',
+ '.install(getParentComponent2)')
+CONST_BINDING=(
+ '''
+ const X x{};
+ ''',
+ '.bindInstance<XAnnot, X>(x)')
+CONST_BINDING2=(
+ '''
+ const X x2{};
+ ''',
+ '.bindInstance<XAnnot, X>(x2)')
+
+@pytest.mark.parametrize(
+ 'binding1_preparation,binding1,binding2_preparation,binding2',
+ [
+ CONSTRUCTOR_BINDING + INSTALL,
+ INTERFACE_BINDING + INSTALL,
+ INSTALL + INSTALL2,
+ CONSTRUCTOR_BINDING + CONST_BINDING_FROM_INSTALL,
+ INTERFACE_BINDING + CONST_BINDING_FROM_INSTALL,
+ INSTALL2 + CONST_BINDING_FROM_INSTALL,
+ CONST_BINDING_FROM_INSTALL + INSTALL2,
+ CONST_BINDING + INSTALL2,
+ CONST_BINDING_FROM_INSTALL + CONST_BINDING_FROM_INSTALL2,
+ CONST_BINDING + CONST_BINDING_FROM_INSTALL,
+ ],
+ ids = [
+ 'CONSTRUCTOR_BINDING + INSTALL',
+ 'INTERFACE_BINDING + INSTALL',
+ 'INSTALL + INSTALL2',
+ 'CONSTRUCTOR_BINDING + CONST_BINDING_FROM_INSTALL',
+ 'INTERFACE_BINDING + CONST_BINDING_FROM_INSTALL',
+ 'INSTALL2 + CONST_BINDING_FROM_INSTALL',
+ 'CONST_BINDING_FROM_INSTALL + INSTALL2',
+ 'CONST_BINDING + INSTALL2',
+ 'CONST_BINDING_FROM_INSTALL + CONST_BINDING_FROM_INSTALL2',
+ 'CONST_BINDING + CONST_BINDING_FROM_INSTALL',
+ ])
+@pytest.mark.parametrize('XAnnot,YAnnot,Y2Annot', [
+ ('X', 'Y', 'Y2'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation3, Y2>'),
+])
+def test_clash_with_install(
+ binding1_preparation, binding1, binding2_preparation, binding2, XAnnot, YAnnot, Y2Annot):
+ source = '''
+ struct X{};
+
+ %s
+ %s
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent()
+ %s
+ %s;
+ }
+ ''' % (binding1_preparation, binding2_preparation, binding1, binding2)
+ expect_compile_error(
+ 'DuplicateTypesInComponentError<XAnnot>',
+ 'The installed component provides some types that are already provided by the current component.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize(
+ 'binding1_preparation,binding1,binding2_preparation,binding2',
+ [
+ CONSTRUCTOR_BINDING + CONSTRUCTOR_BINDING,
+ CONSTRUCTOR_BINDING + INTERFACE_BINDING,
+ INTERFACE_BINDING + CONSTRUCTOR_BINDING,
+ INTERFACE_BINDING + INTERFACE_BINDING2,
+ INSTALL + CONSTRUCTOR_BINDING,
+ INSTALL + INTERFACE_BINDING,
+ CONST_BINDING_FROM_INSTALL + CONSTRUCTOR_BINDING,
+ CONST_BINDING_FROM_INSTALL + INTERFACE_BINDING,
+ CONST_BINDING + CONSTRUCTOR_BINDING,
+ CONST_BINDING + INTERFACE_BINDING,
+ CONSTRUCTOR_BINDING + CONST_BINDING,
+ INTERFACE_BINDING + CONST_BINDING,
+ INSTALL2 + CONST_BINDING,
+ CONST_BINDING_FROM_INSTALL + CONST_BINDING,
+ CONST_BINDING + CONST_BINDING2,
+ ],
+ ids= [
+ 'CONSTRUCTOR_BINDING + CONSTRUCTOR_BINDING',
+ 'CONSTRUCTOR_BINDING + INTERFACE_BINDING',
+ 'INTERFACE_BINDING + CONSTRUCTOR_BINDING',
+ 'INTERFACE_BINDING + INTERFACE_BINDING2',
+ 'INSTALL + CONSTRUCTOR_BINDING',
+ 'INSTALL + INTERFACE_BINDING',
+ 'CONST_BINDING_FROM_INSTALL + CONSTRUCTOR_BINDING',
+ 'CONST_BINDING_FROM_INSTALL + INTERFACE_BINDING',
+ 'CONST_BINDING + CONSTRUCTOR_BINDING',
+ 'CONST_BINDING + INTERFACE_BINDING',
+ 'CONSTRUCTOR_BINDING + CONST_BINDING',
+ 'INTERFACE_BINDING + CONST_BINDING',
+ 'INSTALL2 + CONST_BINDING',
+ 'CONST_BINDING_FROM_INSTALL + CONST_BINDING',
+ 'CONST_BINDING + CONST_BINDING2',
+ ])
+@pytest.mark.parametrize('XAnnot,YAnnot,Y2Annot', [
+ ('X', 'Y', 'Y2'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation3, Y2>'),
+])
+def test_clash_with_binding(binding1_preparation, binding1, binding2_preparation, binding2, XAnnot, YAnnot, Y2Annot):
+ source = '''
+ struct X{};
+
+ %s
+ %s
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent()
+ %s
+ %s;
+ }
+
+ ''' % (binding1_preparation, binding2_preparation, binding1, binding2)
+ expect_compile_error(
+ 'TypeAlreadyBoundError<XAnnot>',
+ 'Trying to bind C but it is already bound.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+CONSTRUCTOR_BINDING_ANNOT1=(
+ '',
+ '.registerConstructor<XAnnot1()>()')
+CONSTRUCTOR_BINDING_ANNOT2=(
+ '',
+ '.registerConstructor<XAnnot2()>()')
+INTERFACE_BINDING_ANNOT1=(
+ '''
+ struct Y : public X {};
+ ''',
+ '''
+ .bind<XAnnot1, YAnnot1>()
+ .registerConstructor<YAnnot1()>()
+ ''')
+INTERFACE_BINDING_ANNOT2=(
+ '''
+ struct Z : public X {};
+ ''',
+ '''
+ .bind<XAnnot2, ZAnnot2>()
+ .registerConstructor<ZAnnot2()>()
+ ''')
+INSTALL_ANNOT1=(
+ '''
+ fruit::Component<XAnnot1> getParentComponent1() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot1()>();
+ }
+ ''',
+ '.install(getParentComponent1)')
+INSTALL_ANNOT2=(
+ '''
+ fruit::Component<XAnnot2> getParentComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot2()>();
+ }
+ ''',
+ '.install(getParentComponent2)')
+CONST_BINDING_FROM_INSTALL_ANNOT1=(
+ '''
+ fruit::Component<ConstXAnnot1> getParentComponent1() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot1()>();
+ }
+ ''',
+ '.install(getParentComponent1)')
+CONST_BINDING_FROM_INSTALL_ANNOT2=(
+ '''
+ fruit::Component<ConstXAnnot2> getParentComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot2()>();
+ }
+ ''',
+ '.install(getParentComponent2)')
+CONST_BINDING_ANNOT1=(
+ '''
+ const X x1{};
+ ''',
+ '.bindInstance<XAnnot1, X>(x1)')
+CONST_BINDING_ANNOT2=(
+ '''
+ const X x2{};
+ ''',
+ '.bindInstance<XAnnot2, X>(x2)')
+
+@pytest.mark.parametrize(
+ 'binding1_preparation,binding1,binding2_preparation,binding2',
+ [
+ CONSTRUCTOR_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
+ CONSTRUCTOR_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2,
+ INTERFACE_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
+ INTERFACE_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2,
+ INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
+ INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2,
+ CONST_BINDING_FROM_INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
+ CONST_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
+ CONST_BINDING_FROM_INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2,
+ CONST_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2,
+ CONSTRUCTOR_BINDING_ANNOT1 + INSTALL_ANNOT2,
+ INTERFACE_BINDING_ANNOT1 + INSTALL_ANNOT2,
+ CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
+ CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_ANNOT2,
+ INTERFACE_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
+ INTERFACE_BINDING_ANNOT1 + CONST_BINDING_ANNOT2,
+ INSTALL_ANNOT1 + INSTALL_ANNOT2,
+ CONST_BINDING_FROM_INSTALL_ANNOT1 + INSTALL_ANNOT2,
+ CONST_BINDING_ANNOT1 + INSTALL_ANNOT2,
+ INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
+ INSTALL_ANNOT1 + CONST_BINDING_ANNOT2,
+ CONST_BINDING_FROM_INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
+ CONST_BINDING_ANNOT1 + CONST_BINDING_ANNOT2,
+ CONST_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
+ CONST_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
+ ],
+ ids=[
+ 'CONSTRUCTOR_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',
+ 'CONSTRUCTOR_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2',
+ 'INTERFACE_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',
+ 'INTERFACE_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2',
+ 'INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',
+ 'INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2',
+ 'CONST_BINDING_FROM_INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',
+ 'CONST_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',
+ 'CONST_BINDING_FROM_INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2',
+ 'CONST_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2',
+ 'CONSTRUCTOR_BINDING_ANNOT1 + INSTALL_ANNOT2',
+ 'INTERFACE_BINDING_ANNOT1 + INSTALL_ANNOT2',
+ 'CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',
+ 'CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_ANNOT2',
+ 'INTERFACE_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',
+ 'INTERFACE_BINDING_ANNOT1 + CONST_BINDING_ANNOT2',
+ 'INSTALL_ANNOT1 + INSTALL_ANNOT2',
+ 'CONST_BINDING_FROM_INSTALL_ANNOT1 + INSTALL_ANNOT2',
+ 'CONST_BINDING_ANNOT1 + INSTALL_ANNOT2',
+ 'INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',
+ 'INSTALL_ANNOT1 + CONST_BINDING_ANNOT2',
+ 'CONST_BINDING_FROM_INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',
+ 'CONST_BINDING_ANNOT1 + CONST_BINDING_ANNOT2',
+ 'CONST_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',
+ 'CONST_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',
+ ])
+def test_no_clash_with_different_annotations(binding1_preparation, binding1, binding2_preparation, binding2):
+ source = '''
+ struct X {};
+
+ %s
+ %s
+
+ fruit::Component<const XAnnot1, const XAnnot2> getComponent() {
+ return fruit::createComponent()
+ %s
+ %s;
+ }
+
+ int main() {
+ fruit::Injector<const XAnnot1, const XAnnot2> injector(getComponent);
+ injector.get<XAnnot1>();
+ injector.get<XAnnot2>();
+ }
+ ''' % (binding1_preparation, binding2_preparation, binding1, binding2)
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('NormalizedComponentXAnnot,ComponentXAnnot,XAnnot', [
+ ('X', 'X', 'X'),
+ ('const X', 'X', 'X'),
+ ('X', 'const X', 'X'),
+ ('const X', 'const X', 'X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
+])
+def test_during_component_merge(NormalizedComponentXAnnot, ComponentXAnnot, XAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<NormalizedComponentXAnnot> getComponent1() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot()>();
+ }
+
+ fruit::Component<ComponentXAnnot> getComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot()>();
+ }
+
+ void f() {
+ fruit::NormalizedComponent<NormalizedComponentXAnnot> nc(getComponent1);
+ fruit::Injector<> injector(nc, getComponent2);
+ (void) injector;
+ }
+ '''
+ expect_compile_error(
+ 'DuplicateTypesInComponentError<XAnnot>',
+ 'The installed component provides some types that are already provided',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_during_component_merge_with_different_annotation_ok():
+ source = '''
+ struct X {};
+
+ fruit::Component<XAnnot1> getComponent1() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot1()>();
+ }
+
+ fruit::Component<XAnnot2> getComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot2()>();
+ }
+
+ int main() {
+ fruit::NormalizedComponent<XAnnot1> nc(getComponent1);
+ fruit::Injector<XAnnot1, XAnnot2> injector(nc, getComponent2);
+ injector.get<XAnnot1>();
+ injector.get<XAnnot2>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XAnnot,XAnnotRegex', [
+ ('X', '(struct )?X'),
+ ('fruit::Annotated<Annotation1, X>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
+])
+def test_bind_instance_and_bind_instance_runtime(XAnnot, XAnnotRegex):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponentForInstanceHelper() {
+ // Note: don't do this in real code, leaks memory.
+ return fruit::createComponent()
+ .bindInstance<XAnnot, X>(*(new X()));
+ }
+
+ fruit::Component<XAnnot> getComponentForInstance() {
+ // Note: don't do this in real code, leaks memory.
+ return fruit::createComponent()
+ .install(getComponentForInstanceHelper)
+ .bindInstance<XAnnot, X>(*(new X()));
+ }
+
+ int main() {
+ fruit::Injector<XAnnot> injector(getComponentForInstance);
+ injector.get<XAnnot>();
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: the type XAnnotRegex was provided more than once, with different bindings.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XAnnotRegex', [
+ ('X', '(struct )?X'),
+ ('fruit::Annotated<Annotation1, X>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
+])
+def test_bind_instance_and_binding_runtime(XAnnot, XAnnotRegex):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponentForInstanceHelper(X* x) {
+ return fruit::createComponent()
+ .bindInstance<XAnnot, X>(*x);
+ }
+
+ fruit::Component<XAnnot> getComponentForInstance(X* x) {
+ return fruit::createComponent()
+ .install(getComponentForInstanceHelper, x)
+ .registerConstructor<XAnnot()>();
+ }
+
+ int main() {
+ X x;
+ fruit::Injector<XAnnot> injector(getComponentForInstance, &x);
+ injector.get<XAnnot>();
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: the type XAnnotRegex was provided more than once, with different bindings.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_during_component_merge_consistent_ok(XAnnot):
+ source = '''
+ struct X : public ConstructionTracker<X> {
+ using Inject = X();
+ };
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .install(getComponent);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
+ fruit::Injector<XAnnot> injector(normalizedComponent, getComponent);
+
+ Assert(X::num_objects_constructed == 0);
+ injector.get<XAnnot>();
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_binding_compression.py b/tests/test_binding_compression.py
new file mode 100755
index 0000000..680449d
--- /dev/null
+++ b/tests/test_binding_compression.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation1 {};
+ struct Annotation2 {};
+
+ template <typename T>
+ using WithNoAnnot = T;
+
+ template <typename T>
+ using WithAnnot1 = fruit::Annotated<Annotation1, T>;
+
+ template <typename T>
+ using WithAnnot2 = fruit::Annotated<Annotation2, T>;
+ '''
+
+@pytest.mark.parametrize('IAnnot,XAnnot,WithAnnot', [
+ ('I', 'X', 'WithNoAnnot'),
+ ('fruit::Annotated<Annotation1, I>', 'fruit::Annotated<Annotation2, X>', 'WithAnnot1'),
+])
+def test_provider_returning_value_success_with_annotation(IAnnot, XAnnot, WithAnnot):
+ source = '''
+ struct I {
+ int value = 5;
+ };
+
+ struct X : public I, ConstructionTracker<X> {
+ };
+
+ fruit::Component<IAnnot> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<XAnnot()>([](){return X();})
+ .bind<IAnnot, XAnnot>();
+ }
+
+ int main() {
+ fruit::Injector<IAnnot> injector(getComponent);
+ Assert((injector.get<WithAnnot<I >>() .value == 5));
+ Assert((injector.get<WithAnnot<I* >>()->value == 5));
+ Assert((injector.get<WithAnnot<I& >>() .value == 5));
+ Assert((injector.get<WithAnnot<const I >>() .value == 5));
+ Assert((injector.get<WithAnnot<const I* >>()->value == 5));
+ Assert((injector.get<WithAnnot<const I& >>() .value == 5));
+ Assert((injector.get<WithAnnot<std::shared_ptr<I>>>()->value == 5));
+ Assert(fruit::impl::InjectorAccessorForTests::unsafeGet<WithAnnot<X>>(injector) == nullptr);
+
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('IAnnot,XAnnot,XPtrAnnot,WithAnnot', [
+ ('I', 'X', 'X*', 'WithNoAnnot'),
+ ('fruit::Annotated<Annotation1, I>', 'fruit::Annotated<Annotation2, X>', 'fruit::Annotated<Annotation2, X*>', 'WithAnnot1'),
+])
+def test_provider_returning_pointer_success_with_annotation(IAnnot, XAnnot, XPtrAnnot, WithAnnot):
+ source = '''
+ struct I {
+ int value = 5;
+ };
+
+ struct X : public I, ConstructionTracker<X> {
+ };
+
+ fruit::Component<IAnnot> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<XPtrAnnot()>([](){return new X();})
+ .bind<IAnnot, XAnnot>();
+ }
+
+ int main() {
+ fruit::Injector<IAnnot> injector(getComponent);
+ Assert((injector.get<WithAnnot<I >>() .value == 5));
+ Assert((injector.get<WithAnnot<I* >>()->value == 5));
+ Assert((injector.get<WithAnnot<I& >>() .value == 5));
+ Assert((injector.get<WithAnnot<const I >>() .value == 5));
+ Assert((injector.get<WithAnnot<const I* >>()->value == 5));
+ Assert((injector.get<WithAnnot<const I& >>() .value == 5));
+ Assert((injector.get<WithAnnot<std::shared_ptr<I>>>()->value == 5));
+ Assert(fruit::impl::InjectorAccessorForTests::unsafeGet<WithAnnot<X>>(injector) == nullptr);
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_compression_undone():
+ source = '''
+ struct I1 {};
+ struct C1 : public I1, ConstructionTracker<C1> {
+ INJECT(C1()) = default;
+ };
+
+ struct I2 {};
+ struct C2 : public I2 {
+ INJECT(C2(I1*)) {}
+ };
+
+ fruit::Component<I1> getI1Component() {
+ return fruit::createComponent()
+ .bind<I1, C1>();
+ }
+
+ fruit::Component<I2> getI2Component() {
+ return fruit::createComponent()
+ .install(getI1Component)
+ .bind<I2, C2>();
+ }
+
+ struct X {
+ // Intentionally C1 and not I1. This prevents binding compression for the I1->C1 edge.
+ INJECT(X(C1*)) {}
+ };
+
+ fruit::Component<X> getXComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ // Here the binding C2->I1->C1 is compressed into C2->C1.
+ fruit::NormalizedComponent<I2> normalizedComponent(getI2Component);
+
+ // However the binding X->C1 prevents binding compression on I1->C1, the binding compression must be undone.
+ fruit::Injector<I2, X> injector(normalizedComponent, getXComponent);
+
+ Assert(C1::num_objects_constructed == 0);
+ injector.get<I2*>();
+ injector.get<X*>();
+ Assert(fruit::impl::InjectorAccessorForTests::unsafeGet<C1>(injector) != nullptr);
+ Assert(C1::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_class_destruction.py b/tests/test_class_destruction.py
new file mode 100644
index 0000000..f36c83d
--- /dev/null
+++ b/tests/test_class_destruction.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ // The shared_ptr objects below ensure (since these tests are run under Valgrind) that deletion occurs, and only once.
+
+ struct I1 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ virtual ~I1() {}
+ };
+
+ struct I2 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct I3 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct I4 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X1 : I1 {
+ INJECT(X1()) = default;
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X2 : I2 {
+ // Taking an X1 here prevents binding compression.
+ INJECT(X2(X1)) {}
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X3 : public I3 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X4 : public I4 {
+ // Taking an X3 here prevents binding compression.
+ X4(X3) {};
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X5 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X6 : public I1 {
+ INJECT(X6()) = default;
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X7 : public I1 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ };
+
+ struct X8 : public I1 {
+ std::shared_ptr<int> x = std::make_shared<int>(3);
+ virtual ~X8() {}
+ };
+
+ struct Annotation {};
+
+ using I1Annot = fruit::Annotated<Annotation, I1>;
+ using I2Annot = fruit::Annotated<Annotation, I2>;
+ using I3Annot = fruit::Annotated<Annotation, I3>;
+ using I4Annot = fruit::Annotated<Annotation, I4>;
+
+ using X1Annot = fruit::Annotated<Annotation, X1>;
+ using X2Annot = fruit::Annotated<Annotation, X2>;
+ using X3Annot = fruit::Annotated<Annotation, X3>;
+ using X4Annot = fruit::Annotated<Annotation, X4>;
+ using X5Annot = fruit::Annotated<Annotation, X5>;
+ using X6Annot = fruit::Annotated<Annotation, X6>;
+ using X7Annot = fruit::Annotated<Annotation, X7>;
+ using X8Annot = fruit::Annotated<Annotation, X8>;
+
+ using X1PtrAnnot = fruit::Annotated<Annotation, X1*>;
+ '''
+
+@pytest.mark.parametrize(
+ 'I1Annot,I2Annot,I3Annot,I4Annot,X1Annot,X2Annot,X3Annot,X4Annot,X5Annot,X6Annot,X7Annot,X8Annot,X1PtrAnnot,bindX5Instance,addX7InstanceMultibinding', [
+ ('I1', 'I2', 'I3', 'I4', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X1*', 'bindInstance(x5)', 'addInstanceMultibinding(*x7)'),
+ ('I1Annot', 'I2Annot', 'I3Annot', 'I4Annot', 'X1Annot', 'X2Annot', 'X3Annot', 'X4Annot', 'X5Annot', 'X6Annot', 'X7Annot', 'X8Annot', 'X1PtrAnnot', 'bindInstance<X5Annot>(x5)', 'addInstanceMultibinding<X7Annot>(*x7)'),
+ ])
+def test_injector_creation_no_injection(
+ I1Annot, I2Annot, I3Annot, I4Annot, X1Annot, X2Annot, X3Annot, X4Annot, X5Annot, X6Annot, X7Annot, X8Annot, X1PtrAnnot, bindX5Instance, addX7InstanceMultibinding):
+ source = '''
+ fruit::Component<I1Annot, I2Annot, I3Annot, I4Annot, X5Annot> getComponent() {
+ static X5 x5;
+ static std::unique_ptr<X7> x7(new X7());
+ return fruit::createComponent()
+ .bind<I1Annot, X1Annot>()
+ .bind<I2Annot, X2Annot>()
+ .bind<I3Annot, X3Annot>()
+ .bind<I4Annot, X4Annot>()
+ .registerProvider<X3Annot()>([]() { return X3(); })
+ .registerProvider<X4Annot(X3Annot)>([](X3 x3) { return X4(x3); })
+ .bindX5Instance
+ .addMultibinding<I1Annot, X6Annot>()
+ .addX7InstanceMultibinding
+ .addMultibindingProvider<X1PtrAnnot()>([]() { return (X1*) new X8(); });
+ }
+
+ int main() {
+ fruit::Injector<I1Annot, I2Annot, I3Annot, I4Annot, X5Annot> injector(getComponent);
+ (void)injector;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('I1Annot,I2Annot,I3Annot,I4Annot,X1Annot,X2Annot,X3Annot,X4Annot,X5Annot,X6Annot,X7Annot,X8Annot,X1PtrAnnot,bindX5Instance,addX7InstanceMultibinding', [
+ ('I1', 'I2', 'I3', 'I4', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X1*', 'bindInstance(x5)', 'addInstanceMultibinding(*x7)'),
+ ('I1Annot', 'I2Annot', 'I3Annot', 'I4Annot', 'X1Annot', 'X2Annot', 'X3Annot', 'X4Annot', 'X5Annot', 'X6Annot', 'X7Annot', 'X8Annot', 'X1PtrAnnot', 'bindInstance<X5Annot>(x5)', 'addInstanceMultibinding<X7Annot>(*x7)'),
+])
+def test_injector_creation_and_injection(
+ I1Annot, I2Annot, I3Annot, I4Annot, X1Annot, X2Annot, X3Annot, X4Annot, X5Annot, X6Annot, X7Annot, X8Annot, X1PtrAnnot, bindX5Instance, addX7InstanceMultibinding):
+ source = '''
+ fruit::Component<I1Annot, I2Annot, I3Annot, I4Annot, X5Annot> getComponent() {
+ static X5 x5;
+ static std::unique_ptr<X7> x7(new X7());
+ return fruit::createComponent()
+ .bind<I1Annot, X1Annot>()
+ .bind<I2Annot, X2Annot>()
+ .bind<I3Annot, X3Annot>()
+ .bind<I4Annot, X4Annot>()
+ .registerProvider<X3Annot()>([]() { return X3(); })
+ .registerProvider<X4Annot(X3Annot)>([](X3 x3) { return X4(x3); })
+ .bindX5Instance
+ .addMultibinding<I1Annot, X6Annot>()
+ .addX7InstanceMultibinding
+ .addMultibindingProvider<X1PtrAnnot()>([]() { return (X1*) new X8(); });
+ }
+
+ int main() {
+ fruit::Injector<I1Annot, I2Annot, I3Annot, I4Annot, X5Annot> injector(getComponent);
+
+ injector.get<I1Annot>();
+ injector.get<I2Annot>();
+ injector.get<I3Annot>();
+ injector.get<I4Annot>();
+ injector.get<X5Annot>();
+
+ injector.getMultibindings<I1Annot>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_common.h b/tests/test_common.h
new file mode 100644
index 0000000..d48f110
--- /dev/null
+++ b/tests/test_common.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_TEST_COMMON_H
+#define FRUIT_TEST_COMMON_H
+
+// This file includes headers used in various tests.
+// This allows to improve compilation speed (and therefore test time) by pre-compiling this header.
+
+#include "class_construction_tracker.h"
+#include "test_macros.h"
+#include <fruit/fruit.h>
+#include <fruit/impl/injector/injector_accessor_for_tests.h>
+#include <map>
+#include <vector>
+
+#endif // FRUIT_TEST_COMMON_H
diff --git a/tests/test_component.py b/tests/test_component.py
new file mode 100755
index 0000000..5bb6017
--- /dev/null
+++ b/tests/test_component.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+
+ struct Annotation2 {};
+ using XAnnot2 = fruit::Annotated<Annotation2, X>;
+ '''
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_move(XAnnot):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<XAnnot> getComponent() {
+ fruit::Component<XAnnot> c = fruit::createComponent();
+ fruit::Component<XAnnot> c2 = std::move(c);
+ return fruit::Component<XAnnot>(std::move(c2));
+ }
+
+ int main() {
+ fruit::Injector<XAnnot> injector(getComponent);
+ injector.get<XAnnot>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_move_partial_component(XAnnot):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<XAnnot> getComponent() {
+ auto c = fruit::createComponent();
+ auto c1 = std::move(c);
+ return std::move(c1);
+ }
+
+ int main() {
+ fruit::Injector<XAnnot> injector(getComponent);
+ injector.get<XAnnot>();
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .fruit::PartialComponent<Bindings>::PartialComponent\(fruit::PartialComponent<Bindings>&&\).'
+ + '|error: call to deleted constructor of .fruit::PartialComponent<>.'
+ # MSVC 2017
+ + '|error C2280: .fruit::PartialComponent<>::PartialComponent\(fruit::PartialComponent<> &&\).: attempting to reference a deleted function'
+ # MSVC 2015
+ + '|error C2248: .fruit::PartialComponent<>::PartialComponent.: cannot access private member declared in class .fruit::PartialComponent<>.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'X'),
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_error_no_binding_found(XAnnot, ConstXAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<ConstXAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundError<XAnnot>',
+ 'No explicit binding nor C::Inject definition was found for T.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'X'),
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_error_no_binding_found_abstract_class(XAnnot, ConstXAnnot):
+ source = '''
+ struct X {
+ virtual void f() = 0;
+ };
+
+ fruit::Component<ConstXAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundForAbstractClassError<XAnnot,X>',
+ 'No explicit binding was found for T, and note that C is an abstract class',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('MaybeConst', [
+ '',
+ 'const',
+])
+def test_error_no_factory_binding_found(MaybeConst):
+ source = '''
+ struct X {};
+
+ fruit::Component<MaybeConst std::function<std::unique_ptr<X>()>> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundError<std::function<std::unique_ptr<X(,std::default_delete<X>)?>\((void)?\)>',
+ 'No explicit binding nor C::Inject definition was found for T.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('MaybeConst', [
+ '',
+ 'const',
+])
+def test_error_no_factory_binding_found_with_annotation(MaybeConst):
+ source = '''
+ struct X {};
+
+ fruit::Component<fruit::Annotated<Annotation1, MaybeConst std::function<std::unique_ptr<X>()>>> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundError<fruit::Annotated<Annotation1,std::function<std::unique_ptr<X(,std::default_delete<X>)?>\((void)?\)>>',
+ 'No explicit binding nor C::Inject definition was found for T.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_component_and_injector_params.py b/tests/test_component_and_injector_params.py
new file mode 100755
index 0000000..00ccdb5
--- /dev/null
+++ b/tests/test_component_and_injector_params.py
@@ -0,0 +1,430 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X {};
+ struct Y {};
+
+ struct Annotation1 {};
+ using IntAnnot1 = fruit::Annotated<Annotation1, int>;
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+
+ struct Annotation2 {};
+ using IntAnnot2 = fruit::Annotated<Annotation2, int>;
+ using XAnnot2 = fruit::Annotated<Annotation2, X>;
+ '''
+
+@pytest.mark.parametrize('XAnnot,MaybeConstXAnnot', [
+ ('X', 'X'),
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+ 'Injector',
+])
+def test_duplicate_type(XAnnot, MaybeConstXAnnot, Class):
+ source = '''
+ InstantiateType(fruit::Class<MaybeConstXAnnot, MaybeConstXAnnot>)
+ '''
+ expect_compile_error(
+ 'RepeatedTypesError<XAnnot,XAnnot>',
+ 'A type was specified more than once.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+ 'Injector',
+])
+def test_duplicate_type_different_constness(XAnnot, ConstXAnnot, Class):
+ source = '''
+ InstantiateType(fruit::Class<XAnnot, ConstXAnnot>)
+ '''
+ expect_compile_error(
+ 'RepeatedTypesError<XAnnot,XAnnot>',
+ 'A type was specified more than once.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_duplicate_type_with_different_annotation_ok():
+ source = '''
+ fruit::Component<XAnnot1, XAnnot2> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot1()>()
+ .registerConstructor<XAnnot2()>();
+ }
+
+ int main() {
+ fruit::Injector<XAnnot1, XAnnot2> injector1(getComponent);
+ injector1.get<XAnnot1>();
+ injector1.get<XAnnot2>();
+
+ fruit::NormalizedComponent<XAnnot1, XAnnot2> normalizedComponent(getComponent);
+ fruit::Injector<XAnnot1, XAnnot2> injector2(getComponent);
+ injector2.get<XAnnot1>();
+ injector2.get<XAnnot2>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XAnnot,MaybeConstXAnnot', [
+ ('X', 'X'),
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+])
+def test_duplicate_type_in_required(XAnnot, MaybeConstXAnnot, Class):
+ source = '''
+ InstantiateType(fruit::Class<fruit::Required<MaybeConstXAnnot, MaybeConstXAnnot>>)
+ '''
+ expect_compile_error(
+ 'RepeatedTypesError<XAnnot,XAnnot>',
+ 'A type was specified more than once.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+])
+def test_component_duplicate_type_in_required_different_constness(Class, XAnnot, ConstXAnnot):
+ source = '''
+ InstantiateType(fruit::Class<fruit::Required<XAnnot, ConstXAnnot>>)
+ '''
+ expect_compile_error(
+ 'RepeatedTypesError<XAnnot,XAnnot>',
+ 'A type was specified more than once.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,MaybeConstXAnnot', [
+ ('X', 'X'),
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+])
+def test_same_type_in_required_and_provided(XAnnot, MaybeConstXAnnot, Class):
+ source = '''
+ InstantiateType(fruit::Class<fruit::Required<MaybeConstXAnnot>, MaybeConstXAnnot>)
+ '''
+ expect_compile_error(
+ 'RepeatedTypesError<XAnnot,XAnnot>',
+ 'A type was specified more than once.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XAnnotInRequirements,XAnnotInProvides', [
+ ('X', 'X', 'const X'),
+ ('X', 'const X', 'X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+])
+def test_same_type_in_required_and_provided_different_constness(XAnnot, XAnnotInRequirements, XAnnotInProvides, Class):
+ source = '''
+ InstantiateType(fruit::Class<fruit::Required<XAnnotInRequirements>, XAnnotInProvides>)
+ '''
+ expect_compile_error(
+ 'RepeatedTypesError<XAnnot,XAnnot>',
+ 'A type was specified more than once.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_same_type_in_required_and_provided_different_annotation_ok():
+ source = '''
+ fruit::Component<fruit::Required<XAnnot1>, XAnnot2> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<XAnnot2()>();
+ }
+
+ fruit::Component<XAnnot1, XAnnot2> getRootComponent() {
+ return fruit::createComponent()
+ .install(getComponent)
+ .registerConstructor<XAnnot1()>();
+ }
+
+ fruit::Component<> getEmptyComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XAnnot1, XAnnot2> injector1(getRootComponent);
+ injector1.get<XAnnot1>();
+ injector1.get<XAnnot2>();
+
+ fruit::NormalizedComponent<XAnnot1, XAnnot2> normalizedComponent(getRootComponent);
+ fruit::Injector<XAnnot1, XAnnot2> injector2(normalizedComponent, getEmptyComponent);
+ injector2.get<XAnnot1>();
+ injector2.get<XAnnot2>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [
+ ('X*', 'X\*'),
+ ('const X*', 'const X\*'),
+ ('X&', 'X&'),
+ ('const X&', 'const X&'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+ ('fruit::Annotated<Annotation1, X*>', 'X\*'),
+ ('fruit::Annotated<Annotation1, const X*>', 'const X\*'),
+ ('fruit::Annotated<Annotation1, X&>', 'X&'),
+ ('fruit::Annotated<Annotation1, const X&>', 'const X&'),
+ ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+ 'Injector',
+])
+def test_error_non_class_type(XVariantAnnot, XVariantRegexp, Class):
+ source = '''
+ InstantiateType(fruit::Class<XVariantAnnot>)
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegexp,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [
+ ('const X', 'const X'),
+ ('fruit::Annotated<Annotation1, const X>', 'const X'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+ 'Injector',
+])
+def test_const_provided_type_ok(XVariantAnnot, XVariantRegexp, Class):
+ source = '''
+ InstantiateType(fruit::Class<XVariantAnnot>)
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [
+ ('X*', 'X\*'),
+ ('const X*', 'const X\*'),
+ ('X&', 'X&'),
+ ('const X&', 'const X&'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+ ('fruit::Annotated<Annotation1, X*>', 'X\*'),
+ ('fruit::Annotated<Annotation1, const X*>', 'const X\*'),
+ ('fruit::Annotated<Annotation1, X&>', 'X&'),
+ ('fruit::Annotated<Annotation1, const X&>', 'const X&'),
+ ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'),
+])
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+])
+def test_error_non_class_type_in_requirements(XVariantAnnot, XVariantRegexp, Class):
+ source = '''
+ InstantiateType(fruit::Class<fruit::Required<XVariantAnnot>>)
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegexp,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstZAnnot,ZAnnot', [
+ ('const Z', 'Z'),
+ ('fruit::Annotated<Annotation1, const Z>', 'fruit::Annotated<Annotation1, Z>'),
+])
+def test_const_class_type_ok(ConstZAnnot, ZAnnot):
+ source = '''
+ struct Z {};
+
+ const Z z{};
+
+ fruit::Component<ConstZAnnot> getComponent() {
+ return fruit::createComponent()
+ .bindInstance<ZAnnot, Z>(z);
+ }
+
+ fruit::Component<> getEmptyComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::NormalizedComponent<ConstZAnnot> normalizedComponent(getComponent);
+ fruit::Injector<ConstZAnnot> injector(normalizedComponent, getEmptyComponent);
+ injector.get<ZAnnot>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstZAnnot,ZAnnot', [
+ ('const Z', 'Z'),
+ ('fruit::Annotated<Annotation1, const Z>', 'fruit::Annotated<Annotation1, Z>'),
+])
+def test_const_class_type_in_requirements_ok(ConstZAnnot, ZAnnot):
+ source = '''
+ struct Z {};
+
+ fruit::Component<fruit::Required<ConstZAnnot>> getComponent() {
+ return fruit::createComponent();
+ }
+
+ const Z z{};
+
+ fruit::Component<ConstZAnnot> getEmptyComponent() {
+ return fruit::createComponent()
+ .bindInstance<ZAnnot, Z>(z);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<fruit::Required<ConstZAnnot>> normalizedComponent(getComponent);
+ fruit::Injector<ConstZAnnot> injector(normalizedComponent, getEmptyComponent);
+ injector.get<ZAnnot>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+])
+def test_two_required_lists_error(Class):
+ source = '''
+ InstantiateType(fruit::Class<fruit::Required<X>, fruit::Required<Y>>)
+ '''
+ expect_compile_error(
+ 'RequiredTypesInComponentArgumentsError<fruit::Required<Y>>',
+ 'A Required<...> type was passed as a non-first template parameter to fruit::Component or fruit::NormalizedComponent',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('Class', [
+ 'Component',
+ 'NormalizedComponent',
+])
+def test_required_list_not_first_argument_error(Class):
+ source = '''
+ InstantiateType(fruit::Class<X, fruit::Required<Y>>)
+ '''
+ expect_compile_error(
+ 'RequiredTypesInComponentArgumentsError<fruit::Required<Y>>',
+ 'A Required<...> type was passed as a non-first template parameter to fruit::Component or fruit::NormalizedComponent',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_multiple_required_types_ok():
+ source = '''
+ fruit::Component<fruit::Required<X, Y>> getEmptyComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<X, Y> getComponent() {
+ return fruit::createComponent()
+ .install(getEmptyComponent)
+ .registerConstructor<X()>()
+ .registerConstructor<Y()>();
+ }
+
+ int main() {
+ fruit::NormalizedComponent<fruit::Required<X, Y>> normalizedComponent(getEmptyComponent);
+ fruit::Injector<X> injector(normalizedComponent, getComponent);
+ injector.get<X>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XAnnot,YAnnot', [
+ ('X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_error_requirements_in_injector(XAnnot, YAnnot):
+ source = '''
+ InstantiateType(fruit::Injector<fruit::Required<YAnnot>, XAnnot>)
+ '''
+ expect_compile_error(
+ 'InjectorWithRequirementsError<YAnnot>',
+ 'Injectors can.t have requirements.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,YAnnot', [
+ ('X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_error_requirements_in_injector_second_argument(XAnnot, YAnnot):
+ source = '''
+ InstantiateType(fruit::Injector<XAnnot, fruit::Required<YAnnot>>)
+ '''
+ expect_compile_error(
+ 'InjectorWithRequirementsError<YAnnot>',
+ 'Injectors can.t have requirements.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_component_replacement.py b/tests/test_component_replacement.py
new file mode 100755
index 0000000..f2f3e57
--- /dev/null
+++ b/tests/test_component_replacement.py
@@ -0,0 +1,920 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ '''
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_success(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<int> injector(getRootComponent);
+ int n = injector.get<int>();
+ Assert(n == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_success_across_normalized_component(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<> getRootComponent1() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation);
+ }
+
+ fruit::Component<int> getRootComponent2() {
+ return fruit::createComponent()
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent1);
+ fruit::Injector<int> injector(normalizedComponent, getRootComponent2);
+ int n = injector.get<int>();
+ Assert(n == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_replace_component_success_with_conversion():
+ source = '''
+ fruit::Component<int> getReplacedComponent(std::string) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(double, std::string, int) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getRootComponent() {
+ return fruit::createComponent()
+ .replace(getReplacedComponent, "Hi").with(getReplacementComponent, 2.0, "Hello", 12)
+ .install(getReplacedComponent, "Hi");
+ }
+
+ int main() {
+ fruit::Injector<int> injector(getRootComponent);
+ int n = injector.get<int>();
+ Assert(n == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ComponentParamTypes,ReplacedComponentInstallation,ReplacementComponentInstallation,ReplacementReplacementComponentInstallation', [
+ ('', 'getReplacedComponent', 'getReplacementComponent', 'getReplacementReplacementComponent'),
+ ('double', 'getReplacedComponent, 1.0', 'getReplacementComponent, 1.0', 'getReplacementReplacementComponent, 1.0'),
+])
+def test_replace_component_chain_success(
+ ComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentInstallation, ReplacementReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementReplacementComponent(ComponentParamTypes) {
+ static int n = 30;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .replace(ReplacementComponentInstallation).with(ReplacementReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<int> injector(getRootComponent);
+ int n = injector.get<int>();
+ Assert(n == 30);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ComponentParamTypes,ReplacedComponentInstallation,ReplacementComponentInstallation,ReplacementReplacementComponentInstallation', [
+ ('', 'getReplacedComponent', 'getReplacementComponent', 'getReplacementReplacementComponent'),
+ ('double', 'getReplacedComponent, 1.0', 'getReplacementComponent, 1.0', 'getReplacementReplacementComponent, 1.0'),
+])
+def test_replace_component_chain_other_order_success(
+ ComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentInstallation, ReplacementReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementReplacementComponent(ComponentParamTypes) {
+ static int n = 30;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacementComponentInstallation).with(ReplacementReplacementComponentInstallation)
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<int> injector(getRootComponent);
+ int n = injector.get<int>();
+ Assert(n == 30);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_replace_component_different_type_error():
+ source = '''
+ fruit::Component<int> getReplacedComponent();
+ fruit::Component<double> getReplacementComponent();
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(getReplacedComponent).with(getReplacementComponent);
+ }
+ '''
+ expect_generic_compile_error(
+ # Clang
+ 'candidate template ignored: could not match .Component<int>. against .Component<double>.'
+ # GCC
+ '|mismatched types .int. and .double.'
+ # MSVC
+ '|could not deduce template argument for .fruit::Component<int> \(__cdecl \*\)\(FormalArgs...\). from .fruit::Component<double> \(void\).',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_already_replaced_consistent_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<int> injector(getRootComponent);
+ int n = injector.get<int>();
+ Assert(n == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_already_replaced_across_normalized_component_consistent_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<> getRootComponent1() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation);
+ }
+
+ fruit::Component<int> getRootComponent2() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent1);
+ fruit::Injector<int> injector(normalizedComponent, getRootComponent2);
+ int n = injector.get<int>();
+ Assert(n == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation,OtherReplacementComponentInstallation', [
+ ('', 'getReplacementComponent', 'getOtherReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")', 'getOtherReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_already_replaced_inconsistent_error(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation, OtherReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getOtherReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 30;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .replace(ReplacedComponentInstallation).with(OtherReplacementComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+ (void) injector;
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: the component function at (0x)?[0-9a-fA-F]* with signature '
+ + '(class )?fruit::Component<int> \((__cdecl)?\*\)\((void)?ReplacedComponentParamTypes\) was replaced '
+ + '\(using .replace\(...\).with\(...\)\) with both the component function at (0x)?[0-9a-fA-F]* with signature '
+ + '(class )?fruit::Component<int> \((__cdecl)?\*\)\(.*\) and the component function at '
+ + '(0x)?[0-9a-fA-F]* with signature (class )?fruit::Component<int> \((__cdecl)?\*\)\(.*\) .',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation,OtherReplacementComponentInstallation', [
+ ('', 'getReplacementComponent', 'getOtherReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")', 'getOtherReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_already_replaced_across_normalized_component_inconsistent_error(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation, OtherReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getOtherReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 30;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<> getRootComponent1() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation);
+ }
+
+ fruit::Component<> getRootComponent2() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(OtherReplacementComponentInstallation);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent1);
+ fruit::Injector<> injector(normalizedComponent, getRootComponent2);
+ (void) injector;
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: the component function at (0x)?[0-9a-fA-F]* with signature '
+ + '(class )?fruit::Component<int> \((__cdecl)?\*\)\((void)?ReplacedComponentParamTypes\) was replaced '
+ + '\(using .replace\(...\).with\(...\)\) with both the component function at (0x)?[0-9a-fA-F]* with signature '
+ + '(class )?fruit::Component<int> \((__cdecl)?\*\)\(.*\) and the component function at '
+ + '(0x)?[0-9a-fA-F]* with signature (class )?fruit::Component<int> \((__cdecl)?\*\)\(.*\) .',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_after_install_error(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getRootComponent() {
+ return fruit::createComponent()
+ .install(ReplacedComponentInstallation)
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<int> injector(getRootComponent);
+ (void) injector;
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: unable to replace \(using .replace\(...\).with\(...\)\) the component function at '
+ + '(0x)?[0-9a-fA-F]* with signature (class )?fruit::Component<int> \((__cdecl)?\*\)\((void)?ReplacedComponentParamTypes\) with the '
+ + 'component function at (0x)?[0-9a-fA-F]* with signature '
+ + '(class )?fruit::Component<int> \((__cdecl)?\*\)\(.*\) because the former component function '
+ + 'was installed before the .replace\(...\).with\(...\).',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_after_install_across_normalized_component_error(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<int> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .bindInstance(n);
+ }
+
+ fruit::Component<int> getRootComponent1() {
+ return fruit::createComponent()
+ .install(ReplacedComponentInstallation);
+ }
+
+ fruit::Component<> getRootComponent2() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<int> normalizedComponent(getRootComponent1);
+ fruit::Injector<int> injector(normalizedComponent, getRootComponent2);
+ (void) injector;
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: unable to replace \(using .replace\(...\).with\(...\)\) the component function at '
+ + '(0x)?[0-9a-fA-F]* with signature (class )?fruit::Component<int> \((__cdecl)?\*\)\((void)?ReplacedComponentParamTypes\) with the '
+ + 'component function at (0x)?[0-9a-fA-F]* with signature '
+ + '(class )?fruit::Component<int> \((__cdecl)?\*\)\(.*\) because the former component function '
+ + 'was installed before the .replace\(...\).with\(...\).',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation,ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacedComponent', '', 'getReplacementComponent'),
+ ('double', 'getReplacedComponent, 1.0', 'std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_unused_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 0);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_used_multiple_times_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 1);
+ Assert(*(multibindings[0]) == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_also_installed_directly_before_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .install(ReplacementComponentInstallation)
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 1);
+ Assert(*(multibindings[0]) == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_also_installed_directly_after_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation)
+ .install(ReplacementComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 1);
+ Assert(*(multibindings[0]) == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_also_installed_directly_before_across_normalized_component_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent1() {
+ return fruit::createComponent()
+ .install(ReplacementComponentInstallation);
+ }
+
+ fruit::Component<> getRootComponent2() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent1);
+ fruit::Injector<> injector(normalizedComponent, getRootComponent2);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 1);
+ Assert(*(multibindings[0]) == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation', [
+ ('', 'getReplacedComponent'),
+ ('double', 'getReplacedComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_component_also_installed_directly_after_across_normalized_component_ok(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent1() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation);
+ }
+
+ fruit::Component<> getRootComponent2() {
+ return fruit::createComponent()
+ .install(ReplacementComponentInstallation);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent1);
+ fruit::Injector<> injector(normalizedComponent, getRootComponent2);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 1);
+ Assert(*(multibindings[0]) == 20);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ReplacedComponentParamTypes,ReplacedComponentInstallation,OtherReplacedComponentInstallation', [
+ ('', 'getReplacedComponent', 'getOtherReplacementComponent'),
+ ('double', 'getReplacedComponent, 1.0', 'getOtherReplacementComponent, 1.0'),
+])
+@pytest.mark.parametrize('ReplacementComponentParamTypes,ReplacementComponentInstallation', [
+ ('', 'getReplacementComponent'),
+ ('std::string', 'getReplacementComponent, std::string("Hello, world")'),
+])
+def test_replace_multiple_components_with_same(
+ ReplacedComponentParamTypes, ReplacedComponentInstallation, OtherReplacedComponentInstallation, ReplacementComponentParamTypes, ReplacementComponentInstallation):
+ source = '''
+ fruit::Component<> getReplacedComponent(ReplacedComponentParamTypes) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getOtherReplacementComponent(ReplacedComponentParamTypes) {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent(ReplacementComponentParamTypes) {
+ static int n = 30;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(ReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .replace(OtherReplacedComponentInstallation).with(ReplacementComponentInstallation)
+ .install(ReplacedComponentInstallation)
+ .install(OtherReplacedComponentInstallation);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 1);
+ Assert(*(multibindings[0]) == 30);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_replace_component_one_set_of_args_only():
+ source = '''
+ fruit::Component<> getReplacedComponent(double) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent() {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(getReplacedComponent, 1.0).with(getReplacementComponent)
+ .install(getReplacedComponent, 1.0)
+ .install(getReplacedComponent, 5.0);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 2);
+ Assert(*(multibindings[0]) == 20);
+ Assert(*(multibindings[1]) == 10);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_replace_component_already_replaced_with_different_args():
+ source = '''
+ fruit::Component<> getReplacedComponent(double) {
+ static int n = 10;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getReplacementComponent() {
+ static int n = 20;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getOtherReplacementComponent() {
+ static int n = 30;
+ return fruit::createComponent()
+ .addInstanceMultibinding(n);
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .replace(getReplacedComponent, 1.0).with(getReplacementComponent)
+ .replace(getReplacedComponent, 5.0).with(getOtherReplacementComponent)
+ .install(getReplacedComponent, 1.0)
+ .install(getReplacedComponent, 5.0);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+
+ std::vector<int*> multibindings = injector.getMultibindings<int>();
+ Assert(multibindings.size() == 2);
+ Assert(*(multibindings[0]) == 20);
+ Assert(*(multibindings[1]) == 30);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_defn_h_includes.py b/tests/test_defn_h_includes.py
new file mode 100755
index 0000000..5b1d54f
--- /dev/null
+++ b/tests/test_defn_h_includes.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+import re
+import sys
+from fruit_test_common import *
+
+@pytest.mark.skipif(
+ os.sep != '/',
+ reason = 'This only works in platforms where paths are /-separated.')
+def test_defn_file_inclusion():
+ include_pattern = re.compile(' *#include *<(.*)> *')
+
+ fruit_headers_root_absolute_path = os.path.abspath(PATH_TO_FRUIT_STATIC_HEADERS)
+
+ includes = {}
+ for root, _, files in os.walk(fruit_headers_root_absolute_path):
+ for file in files:
+ if file.endswith('.h'):
+ path = os.path.join(root, file)
+ with open(path, 'r') as f:
+ current_includes = set()
+ for line in f.readlines():
+ # Remove the newline
+ line = line[:-1]
+ matches = re.match(include_pattern, line)
+ if matches:
+ current_includes.add(matches.groups()[0])
+ root_relative_path = root.replace(fruit_headers_root_absolute_path, '')
+ relative_path = os.path.join(root_relative_path, file)
+ if relative_path.startswith(os.sep):
+ relative_path = relative_path[1:]
+ includes[relative_path] = current_includes
+
+ for defn_file, defn_file_includes in includes.items():
+ if defn_file.endswith('.defn.h'):
+ main_header_file = defn_file.replace('.defn.h', '.h')
+ # This is a special case where we don't follow the convention, so we need to specify the corresponding main
+ # header file explicitly.
+ if defn_file == 'fruit/impl/component_functors.defn.h':
+ main_header_file = 'fruit/component.h'
+
+ # The .defn.h files for headers in fruit/ are in fruit/impl/
+ alternative_main_header_file = main_header_file.replace('fruit/impl/', 'fruit/')
+
+ if main_header_file not in includes and alternative_main_header_file not in includes:
+ raise Exception('Can\'t find the .h header corresponding to: %s. Considered: %s' % (defn_file, (main_header_file, alternative_main_header_file)))
+ if main_header_file not in defn_file_includes and alternative_main_header_file not in defn_file_includes:
+ raise Exception('%s should have included %s, but it includes only: %s' % (defn_file, main_header_file, defn_file_includes))
+ if main_header_file in includes and defn_file not in includes[main_header_file]:
+ raise Exception('%s should have included %s, but it includes only: %s' % (main_header_file, defn_file, includes[main_header_file]))
+ if alternative_main_header_file in includes and defn_file not in includes[alternative_main_header_file]:
+ raise Exception('%s should have included %s, but it includes only: %s' % (main_header_file, defn_file, includes[alternative_main_header_file]))
+ for other_header, other_header_includes in includes.items():
+ if other_header not in (main_header_file, alternative_main_header_file) and defn_file in other_header_includes:
+ raise Exception('Unexpected direct include: %s includes %s' % (other_header, defn_file))
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_dependency_loop.py b/tests/test_dependency_loop.py
new file mode 100755
index 0000000..b9698bb
--- /dev/null
+++ b/tests/test_dependency_loop.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+
+ struct Annotation2 {};
+ using XAnnot2 = fruit::Annotated<Annotation2, X>;
+
+ struct Annotation3 {};
+ using XAnnot3 = fruit::Annotated<Annotation3, X>;
+ '''
+
+@pytest.mark.parametrize('XAnnot,XConstRefAnnot,YAnnot,YConstRefAnnot', [
+ ('X', 'const X&', 'Y', 'const Y&'),
+ ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, const X&)',
+ 'fruit::Annotated<Annotation2, Y>', 'ANNOTATED(Annotation2, const Y&)')
+])
+def test_loop_in_autoinject(XAnnot, XConstRefAnnot, YAnnot, YConstRefAnnot):
+ source = '''
+ struct Y;
+
+ struct X {
+ INJECT(X(YConstRefAnnot)) {};
+ };
+
+ struct Y {
+ INJECT(Y(XConstRefAnnot)) {};
+ };
+
+ fruit::Component<XAnnot> mutuallyConstructibleComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'SelfLoopError<XAnnot,YAnnot>',
+ 'Found a loop in the dependencies',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot,XConstRefAnnot,YAnnot,YConstRefAnnot', [
+ ('X', 'const X', 'const X&', 'Y', 'const Y&'),
+ ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, const X)', 'ANNOTATED(Annotation1, const X&)',
+ 'fruit::Annotated<Annotation2, Y>', 'ANNOTATED(Annotation2, const Y&)')
+])
+def test_loop_in_autoinject_const(XAnnot, ConstXAnnot, XConstRefAnnot, YAnnot, YConstRefAnnot):
+ source = '''
+ struct Y;
+
+ struct X {
+ INJECT(X(YConstRefAnnot)) {};
+ };
+
+ struct Y {
+ INJECT(Y(XConstRefAnnot)) {};
+ };
+
+ fruit::Component<ConstXAnnot> mutuallyConstructibleComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'SelfLoopError<XAnnot,YAnnot>',
+ 'Found a loop in the dependencies',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_loop_in_register_provider():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<X> mutuallyConstructibleComponent() {
+ return fruit::createComponent()
+ .registerProvider<X(Y)>([](Y) {return X();})
+ .registerProvider<Y(X)>([](X) {return Y();});
+ }
+ '''
+ expect_compile_error(
+ 'SelfLoopError<X,Y>',
+ 'Found a loop in the dependencies',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_loop_in_register_provider_with_annotations():
+ source = '''
+ struct X {};
+
+ fruit::Component<fruit::Annotated<Annotation1, X>> mutuallyConstructibleComponent() {
+ return fruit::createComponent()
+ .registerProvider<fruit::Annotated<Annotation1, X>(fruit::Annotated<Annotation2, X>)>([](X x) {return x;})
+ .registerProvider<fruit::Annotated<Annotation2, X>(fruit::Annotated<Annotation1, X>)>([](X x) {return x;});
+ }
+ '''
+ expect_compile_error(
+ 'SelfLoopError<fruit::Annotated<Annotation1, X>, fruit::Annotated<Annotation2, X>>',
+ 'Found a loop in the dependencies',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_with_different_annotations_ok():
+ source = '''
+ struct X {};
+
+ fruit::Component<XAnnot3> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<XAnnot1()>([](){return X();})
+ .registerProvider<XAnnot2(XAnnot1)>([](X x){return x;})
+ .registerProvider<XAnnot3(XAnnot2)>([](X x){return x;});
+ }
+
+ int main() {
+ fruit::Injector<XAnnot3> injector(getComponent);
+ injector.get<XAnnot3>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_eager_injection.py b/tests/test_eager_injection.py
new file mode 100644
index 0000000..60a914b
--- /dev/null
+++ b/tests/test_eager_injection.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X {
+ INJECT(X()) {
+ Assert(!constructed);
+ constructed = true;
+ }
+
+ static bool constructed;
+ };
+
+ bool X::constructed = false;
+
+ struct Y {
+ Y() {
+ Assert(!constructed);
+ constructed = true;
+ }
+
+ static bool constructed;
+ };
+
+ bool Y::constructed = false;
+
+ struct Z {
+ Z() {
+ Assert(!constructed);
+ constructed = true;
+ }
+
+ static bool constructed;
+ };
+
+ bool Z::constructed = false;
+ '''
+
+def test_eager_injection_deprecated():
+ source = '''
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider([](){return new Y();})
+ .registerConstructor<Z()>();
+ }
+
+ int main() {
+
+ fruit::Injector<X> injector(getComponent);
+
+ Assert(!X::constructed);
+ Assert(!Y::constructed);
+ Assert(!Z::constructed);
+
+ injector.eagerlyInjectAll();
+
+ Assert(X::constructed);
+ Assert(Y::constructed);
+ // Z still not constructed, it's not reachable from Injector<X>.
+ Assert(!Z::constructed);
+
+ return 0;
+ }
+ '''
+ expect_generic_compile_error(
+ 'deprecation|deprecated',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_eager_injection():
+ source = '''
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider([](){return new Y();})
+ .registerConstructor<Z()>();
+ }
+
+ int main() {
+
+ fruit::Injector<X> injector(getComponent);
+
+ Assert(!X::constructed);
+ Assert(!Y::constructed);
+ Assert(!Z::constructed);
+
+ injector.eagerlyInjectAll();
+
+ Assert(X::constructed);
+ Assert(Y::constructed);
+ // Z still not constructed, it's not reachable from Injector<X>.
+ Assert(!Z::constructed);
+
+ return 0;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals(),
+ ignore_deprecation_warnings=True)
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_include_test.py b/tests/test_include_test.py
new file mode 100644
index 0000000..b9fc3fb
--- /dev/null
+++ b/tests/test_include_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ '''
+
+FRUIT_PUBLIC_HEADERS = [
+ "component.h",
+ "fruit.h",
+ "fruit_forward_decls.h",
+ "injector.h",
+ "macro.h",
+ "normalized_component.h",
+ "provider.h",
+]
+
+@pytest.mark.parametrize('HeaderFile', FRUIT_PUBLIC_HEADERS)
+def test_header_self_contained(HeaderFile):
+ source = '''
+ #include <fruit/HeaderFile>
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_injected_provider.py b/tests/test_injected_provider.py
new file mode 100755
index 0000000..8754610
--- /dev/null
+++ b/tests/test_injected_provider.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot = fruit::Annotated<Annotation1, X>;
+
+ struct Annotation2 {};
+ '''
+
+@pytest.mark.parametrize('XVariant,XVariantRegexp', [
+ ('X*', 'X\*'),
+ ('const X*', 'const X\*'),
+ ('X&', 'X&'),
+ ('const X&', 'const X&'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+])
+def test_error_non_class_type_parameter(XVariant, XVariantRegexp):
+ source = '''
+ struct X {};
+
+ fruit::Provider<XVariant> provider;
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegexp,X>',
+ 'A non-class type T was specified. Use C instead',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_error_annotated_type_parameter():
+ source = '''
+ struct X {};
+
+ fruit::Provider<XAnnot> provider;
+ '''
+ expect_compile_error(
+ 'AnnotatedTypeError<fruit::Annotated<Annotation1,X>,X>',
+ 'An annotated type was specified where a non-annotated type was expected.',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [
+ ('X', 'fruit::Provider<X>', 'X', 'X'),
+ ('X', 'fruit::Provider<X>', 'X', 'const X&'),
+ ('X', 'fruit::Provider<X>', 'X', 'const X*'),
+ ('X', 'fruit::Provider<X>', 'X', 'X&'),
+ ('X', 'fruit::Provider<X>', 'X', 'X*'),
+ ('X', 'fruit::Provider<X>', 'X', 'std::shared_ptr<X>'),
+ ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<X>'),
+ ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<const X>'),
+ ('X', 'fruit::Provider<const X>', 'const X', 'const X&'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<X>>', 'X', 'const X&'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'),
+])
+def test_provider_get_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<XBindingInInjector> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XBindingInInjector> injector(getComponent);
+ fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>();
+
+ XProviderGetParam x = provider.get<XProviderGetParam>();
+ (void)x;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [
+ ('const X', 'fruit::Provider<const X>', 'const X', 'X'),
+ ('const X', 'fruit::Provider<const X>', 'const X', 'const X&'),
+ ('const X', 'fruit::Provider<const X>', 'const X', 'const X*'),
+ ('const X', 'fruit::Provider<const X>', 'const X', 'fruit::Provider<const X>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'),
+])
+def test_provider_get_const_binding_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam):
+ XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '')
+ source = '''
+ struct X {};
+
+ const X x{};
+
+ fruit::Component<XBindingInInjector> getComponent() {
+ return fruit::createComponent()
+ .bindInstance<XBindingInInjectorWithoutConst, X>(x);
+ }
+
+ int main() {
+ fruit::Injector<XBindingInInjector> injector(getComponent);
+ fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>();
+
+ XProviderGetParam x = provider.get<XProviderGetParam>();
+ (void)x;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_provider_get_during_injection_ok():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ void foo() {
+ }
+ };
+
+ struct Y {
+ X x;
+ INJECT(Y(fruit::Provider<X> xProvider))
+ : x(xProvider.get<X>()) {
+ }
+
+ void foo() {
+ x.foo();
+ }
+ };
+
+ struct Z {
+ Y y;
+ INJECT(Z(fruit::Provider<Y> yProvider))
+ : y(yProvider.get<Y>()) {
+ }
+
+ void foo() {
+ y.foo();
+ }
+ };
+
+ fruit::Component<Z> getZComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<Z> injector(getZComponent);
+ fruit::Provider<Z> provider(injector);
+ // During provider.get<Z>(), yProvider.get() is called, and during that xProvider.get()
+ // is called.
+ Z z = provider.get<Z>();
+ z.foo();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_provider_get_error_type_not_provided():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ void f(fruit::Provider<X> provider) {
+ provider.get<Y>();
+ }
+ '''
+ expect_compile_error(
+ 'TypeNotProvidedError<Y>',
+ 'Trying to get an instance of T, but it is not provided by this Provider/Injector.',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X**', r'X\*\*'),
+ ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
+ ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'),
+ ('X* const', r'X\* const'),
+ ('const X* const', r'const X\* const'),
+ ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
+ ('X*&', r'X\*&'),
+ ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
+ ('void', r'void'),
+ ('fruit::Annotated<Annotation1, fruit::Annotated<Annotation1, X>>', r'fruit::Annotated<Annotation1, X>'),
+])
+def test_provider_get_error_type_not_injectable(XVariant,XVariantRegex):
+ source = '''
+ struct X {};
+
+ void f(fruit::Provider<X> provider) {
+ provider.get<XVariant>();
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<XVariantRegex>',
+ 'The type T is not injectable',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XProviderGetParam,XProviderGetParamRegex', [
+ ('X&', 'X&'),
+ ('X*', 'X\*'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+ ('fruit::Provider<X>', 'fruit::Provider<X>'),
+])
+def test_const_provider_get_does_not_allow_injecting_nonconst_variants(XProviderGetParam, XProviderGetParamRegex):
+ source = '''
+ void f(fruit::Provider<const X> provider) {
+ provider.get<XProviderGetParam>();
+ }
+ '''
+ expect_compile_error(
+ 'TypeProvidedAsConstOnlyError<XProviderGetParamRegex>',
+ 'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('Y_PROVIDER_ANNOT', [
+ ('fruit::Provider<Y>'),
+ ('ANNOTATED(Annotation1, fruit::Provider<Y>)'),
+])
+def test_lazy_injection_with_annotations(Y_PROVIDER_ANNOT):
+ source = '''
+ struct Y : public ConstructionTracker<Y> {
+ using Inject = Y();
+ };
+
+ struct X : public ConstructionTracker<X> {
+ INJECT(X(Y_PROVIDER_ANNOT provider)) : provider(provider) {
+ }
+
+ void run() {
+ Y* y(provider);
+ (void) y;
+ }
+
+ fruit::Provider<Y> provider;
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<> getEmptyComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
+ fruit::Injector<X> injector(normalizedComponent, getComponent);
+
+ Assert(X::num_objects_constructed == 0);
+ Assert(Y::num_objects_constructed == 0);
+
+ X* x(injector);
+
+ Assert(X::num_objects_constructed == 1);
+ Assert(Y::num_objects_constructed == 0);
+
+ x->run();
+
+ Assert(X::num_objects_constructed == 1);
+ Assert(Y::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_injector.py b/tests/test_injector.py
new file mode 100755
index 0000000..ed79b99
--- /dev/null
+++ b/tests/test_injector.py
@@ -0,0 +1,303 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+
+ struct Annotation2 {};
+ using XAnnot2 = fruit::Annotated<Annotation2, X>;
+ '''
+
+def test_empty_injector():
+ source = '''
+ fruit::Component<> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_error_component_with_requirements(XAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<fruit::Required<XAnnot>> getComponent();
+
+ void f(fruit::NormalizedComponent<XAnnot> normalizedComponent) {
+ fruit::Injector<XAnnot> injector(normalizedComponent, getComponent);
+ }
+ '''
+ expect_compile_error(
+ 'ComponentWithRequirementsInInjectorError<XAnnot>',
+ 'When using the two-argument constructor of Injector, the component used as second parameter must not have requirements',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_error_declared_types_not_provided(XAnnot):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<> getEmptyComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
+ fruit::Injector<XAnnot> injector(normalizedComponent, getEmptyComponent);
+ }
+ '''
+ expect_compile_error(
+ 'TypesInInjectorNotProvidedError<XAnnot>',
+ 'The types in TypesNotProvided are declared as provided by the injector, but none of the two components passed to the Injector constructor provides them.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_error_declared_nonconst_types_provided_as_const(XAnnot, ConstXAnnot):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<ConstXAnnot> getComponent();
+
+ int main() {
+ fruit::Injector<XAnnot> injector(getComponent);
+ }
+ '''
+ expect_generic_compile_error(
+ 'no matching constructor for initialization of .fruit::Injector<XAnnot>.'
+ '|no matching function for call to .fruit::Injector<XAnnot>::Injector\(fruit::Component<ConstXAnnot> \(&\)\(\)\).'
+ # MSVC
+ '|.fruit::Injector<XAnnot>::Injector.: none of the 2 overloads could convert all the argument types',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_error_declared_nonconst_types_provided_as_const_with_normalized_component(XAnnot, ConstXAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getEmptyComponent();
+
+ void f(fruit::NormalizedComponent<ConstXAnnot> normalizedComponent) {
+ fruit::Injector<XAnnot> injector(normalizedComponent, getEmptyComponent);
+ }
+ '''
+ expect_compile_error(
+ 'TypesInInjectorProvidedAsConstOnlyError<XAnnot>',
+ 'The types in TypesProvidedAsConstOnly are declared as non-const provided types by the injector',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,YAnnot', [
+ ('X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_injector_get_error_type_not_provided(XAnnot, YAnnot):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ struct Y {};
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XAnnot> injector(getComponent);
+ injector.get<YAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'TypeNotProvidedError<YAnnot>',
+ 'Trying to get an instance of T, but it is not provided by this Provider/Injector.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstXAnnot,XInjectorGetParam,XInjectorGetParamRegex', [
+ ('const X', 'X&', 'X&'),
+ ('const X', 'X*', 'X\*'),
+ ('const X', 'std::shared_ptr<X>', 'std::shared_ptr<X>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation1, X&>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X*>', 'fruit::Annotated<Annotation1, X\*>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>'),
+])
+def test_injector_const_provided_type_does_not_allow_injecting_nonconst_variants(ConstXAnnot, XInjectorGetParam, XInjectorGetParamRegex):
+ source = '''
+ void f(fruit::Injector<ConstXAnnot> injector) {
+ injector.get<XInjectorGetParam>();
+ }
+ '''
+ expect_compile_error(
+ 'TypeProvidedAsConstOnlyError<XInjectorGetParamRegex>',
+ 'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XBindingInInjector,XInjectorGetParam', [
+ ('X', 'X'),
+ ('X', 'const X&'),
+ ('X', 'const X*'),
+ ('X', 'X&'),
+ ('X', 'X*'),
+ ('X', 'std::shared_ptr<X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X*>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>'),
+])
+def test_injector_get_ok(XBindingInInjector, XInjectorGetParam):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<XBindingInInjector> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XBindingInInjector> injector(getComponent);
+
+ auto x = injector.get<XInjectorGetParam>();
+ (void)x;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XBindingInInjector,XInjectorGetParam', [
+ ('const X', 'X'),
+ ('const X', 'const X&'),
+ ('const X', 'const X*'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X*>'),
+])
+def test_injector_get_const_binding_ok(XBindingInInjector, XInjectorGetParam):
+ XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '')
+ source = '''
+ struct X {};
+
+ const X x{};
+
+ fruit::Component<XBindingInInjector> getComponent() {
+ return fruit::createComponent()
+ .bindInstance<XBindingInInjectorWithoutConst, X>(x);
+ }
+
+ int main() {
+ fruit::Injector<XBindingInInjector> injector(getComponent);
+
+ auto x = injector.get<XInjectorGetParam>();
+ (void)x;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X**', r'X\*\*'),
+ ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
+ ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'),
+ ('X* const', r'X\* const'),
+ ('const X* const', r'const X\* const'),
+ ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
+ ('X*&', r'X\*&'),
+ ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
+ ('void', r'void'),
+ ('fruit::Annotated<Annotation1, X**>', r'X\*\*'),
+])
+def test_injector_get_error_type_not_injectable(XVariant,XVariantRegex):
+ source = '''
+ struct X {};
+
+ void f(fruit::Injector<X> injector) {
+ injector.get<XVariant>();
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<XVariantRegex>',
+ 'The type T is not injectable.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X[]', r'X\[\]'),
+])
+def test_injector_get_error_array_type(XVariant,XVariantRegex):
+ source = '''
+ struct X {};
+
+ void f(fruit::Injector<X> injector) {
+ injector.get<XVariant>();
+ }
+ '''
+ expect_generic_compile_error(
+ 'function cannot return array type'
+ '|function returning an array'
+ # MSVC
+ '|.fruit::Injector<X>::get.: no matching overloaded function found',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_injector_unsafe_get.py b/tests/test_injector_unsafe_get.py
new file mode 100755
index 0000000..8768c63
--- /dev/null
+++ b/tests/test_injector_unsafe_get.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation1 {};
+ struct Annotation2 {};
+ struct Annotation3 {};
+ '''
+
+@pytest.mark.parametrize('XAnnot,YAnnot,ZAnnot', [
+ ('X', 'Y', 'Z'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation3, Z>'),
+])
+def test_success(XAnnot, YAnnot, ZAnnot):
+ source = '''
+ struct Y {
+ using Inject = Y();
+ Y() = default;
+ };
+
+ struct X {
+ using Inject = X(YAnnot);
+ X(Y) {
+ }
+ };
+
+ struct Z {};
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .install(getComponent);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+ const X* x = fruit::impl::InjectorAccessorForTests::unsafeGet<XAnnot>(injector);
+ const Y* y = fruit::impl::InjectorAccessorForTests::unsafeGet<YAnnot>(injector);
+ const Z* z = fruit::impl::InjectorAccessorForTests::unsafeGet<ZAnnot>(injector);
+
+ (void) x;
+ (void) y;
+ (void) z;
+ Assert(x != nullptr);
+ Assert(y != nullptr);
+ Assert(z == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_install.py b/tests/test_install.py
new file mode 100755
index 0000000..2901da1
--- /dev/null
+++ b/tests/test_install.py
@@ -0,0 +1,1067 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+ '''
+
+@pytest.mark.parametrize('XParamInChildComponent,XParamInRootComponent', [
+ ('X', 'X'),
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_success(XParamInChildComponent, XParamInRootComponent):
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ fruit::Component<XParamInChildComponent> getChildComponent() {
+ return fruit::createComponent()
+ .registerProvider<XParamInChildComponent()>([]() { return X(5); });
+ }
+
+ fruit::Component<XParamInRootComponent> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent);
+ }
+
+ int main() {
+ fruit::Injector<XParamInRootComponent> injector(getRootComponent);
+ X x = injector.get<XParamInRootComponent>();
+ Assert(x.n == 5);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XParamInChildComponent,XParamInRootComponent', [
+ ('const X', 'X'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
+])
+def test_install_error_child_component_provides_const(XParamInChildComponent, XParamInRootComponent):
+ source = '''
+ struct X {};
+
+ fruit::Component<XParamInChildComponent> getChildComponent();
+
+ fruit::Component<XParamInRootComponent> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<XParamInRootComponent>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ProvidedXParam,RequiredXParam', [
+ ('X', 'X'),
+ ('X', 'const X'),
+ ('const X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_with_requirements_success(ProvidedXParam, RequiredXParam):
+ ProvidedXParamWithoutConst = ProvidedXParam.replace('const ', '')
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Y {
+ X x;
+ Y(X x): x(x) {}
+ };
+
+ fruit::Component<fruit::Required<RequiredXParam>, Y> getChildComponent1() {
+ return fruit::createComponent()
+ .registerProvider<Y(RequiredXParam)>([](X x) { return Y(x); });
+ }
+
+ fruit::Component<ProvidedXParam> getChildComponent2() {
+ return fruit::createComponent()
+ .registerProvider<ProvidedXParamWithoutConst()>([]() { return X(5); });
+ }
+
+ fruit::Component<Y> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent1)
+ .install(getChildComponent2);
+ }
+
+ int main() {
+ fruit::Injector<Y> injector(getRootComponent);
+ Y y = injector.get<Y>();
+ Assert(y.x.n == 5);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ProvidedXParam,RequiredXParam', [
+ ('const X', 'X'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
+])
+def test_with_requirements_error_only_nonconst_provided(ProvidedXParam, RequiredXParam):
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<fruit::Required<RequiredXParam>, Y> getChildComponent1();
+
+ fruit::Component<ProvidedXParam> getChildComponent2();
+
+ fruit::Component<Y> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent1)
+ .install(getChildComponent2);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<RequiredXParam>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ProvidedXParam,RequiredXParam', [
+ ('const X', 'X'),
+ ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
+])
+def test_with_requirements_error_only_nonconst_provided_reversed_install_order(ProvidedXParam, RequiredXParam):
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<fruit::Required<RequiredXParam>, Y> getChildComponent1();
+
+ fruit::Component<ProvidedXParam> getChildComponent2();
+
+ fruit::Component<Y> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent2)
+ .install(getChildComponent1);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<RequiredXParam>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_with_requirements_not_specified_in_child_component_error():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Y {
+ X x;
+ Y(X x): x(x) {}
+ };
+
+ fruit::Component<fruit::Required<X>, Y> getParentYComponent() {
+ return fruit::createComponent()
+ .registerProvider([](X x) { return Y(x); });
+ }
+
+ // We intentionally don't have fruit::Required<X> here, we want to test that this results in an error.
+ fruit::Component<Y> getYComponent() {
+ return fruit::createComponent()
+ .install(getParentYComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundError<X>',
+ 'No explicit binding nor C::Inject definition was found for T',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_install_requiring_nonconst_then_install_requiring_const_ok(XAnnot, ConstXAnnot):
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<XAnnot>, Y> getChildComponent1() {
+ return fruit::createComponent()
+ .registerConstructor<Y()>();
+ }
+
+ fruit::Component<fruit::Required<ConstXAnnot>, Z> getChildComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<Z()>();
+ }
+
+ fruit::Component<Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent1)
+ .install(getChildComponent2)
+ .registerConstructor<XAnnot()>();
+ }
+
+ int main() {
+ fruit::Injector<Y, Z> injector(getRootComponent);
+ injector.get<Y>();
+ injector.get<Z>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_install_requiring_nonconst_then_install_requiring_const_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<X>, Y> getChildComponent1();
+ fruit::Component<fruit::Required<const X>, Z> getChildComponent2();
+
+ fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent1)
+ .install(getChildComponent2);
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_install_requiring_const_then_install_requiring_nonconst_ok():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<const X>, Y> getChildComponent1() {
+ return fruit::createComponent()
+ .registerConstructor<Y()>();
+ }
+
+ fruit::Component<fruit::Required<X>, Z> getChildComponent2() {
+ return fruit::createComponent()
+ .registerConstructor<Z()>();
+ }
+
+ fruit::Component<Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent1)
+ .install(getChildComponent2)
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<Y, Z> injector(getRootComponent);
+ injector.get<Y>();
+ injector.get<Z>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_install_requiring_const_then_install_requiring_nonconst_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<const X>, Y> getChildComponent1();
+ fruit::Component<fruit::Required<X>, Z> getChildComponent2();
+
+ fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent1)
+ .install(getChildComponent2);
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_install_with_args_success():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Arg {
+ Arg(int) {}
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&) {
+ return true;
+ }
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&) {
+ return 0;
+ }
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg, Arg) {
+ return fruit::createComponent()
+ .registerProvider([]() { return X(5); });
+ }
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), Arg{}, 15);
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ X x = injector.get<X>();
+ Assert(x.n == 5);
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_install_with_args_error_not_move_constructible():
+ source = '''
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = delete;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), Arg{});
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg::Arg\(Arg&&\).'
+ + '|error: call to deleted constructor of .Arg.'
+ + '|.Arg::Arg\(Arg &&\).: cannot convert argument 1 from .std::_Tuple_val<_This>. to .const Arg &.',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_move_constructible_with_conversion():
+ source = '''
+ struct Arg {
+ Arg(int) {}
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = delete;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), 15);
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg::Arg\(Arg&&\).'
+ + '|error: call to deleted constructor of .Arg.'
+ + '|.Arg::Arg\(Arg &&\).: cannot convert argument 1 from .std::_Tuple_val<_This>. to .int.',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_copy_constructible():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = delete;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), Arg{});
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg::Arg\(const Arg&\).'
+ + '|error: call to deleted constructor of .Arg.'
+ + '|error C2280: .Arg::Arg\(const Arg &\).: attempting to reference a deleted function',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_copy_constructible_with_conversion():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Arg {
+ Arg(int) {}
+ Arg() = default;
+ Arg(const Arg&) = delete;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), 15);
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg::Arg\(const Arg&\).'
+ + '|error: call to deleted constructor of .Arg.'
+ + '|error C2280: .Arg::Arg\(const Arg &\).: attempting to reference a deleted function',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_move_assignable():
+ source = '''
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = delete;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), Arg{});
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg& Arg::operator=\(Arg&&\).'
+ + '|error: overload resolution selected deleted operator .=.'
+ + '|error C2280: .Arg &Arg::operator =\(Arg &&\).: attempting to reference a deleted function',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_move_assignable_with_conversion():
+ source = '''
+ struct Arg {
+ Arg(int) {}
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = delete;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), 15);
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg& Arg::operator=\(Arg&&\).'
+ + '|error: overload resolution selected deleted operator .=.'
+ + '|error C2280: .Arg &Arg::operator =\(Arg &&\).: attempting to reference a deleted function',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_copy_assignable():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = delete;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), Arg{});
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg& Arg::operator=\(const Arg&\).'
+ + '|error: overload resolution selected deleted operator .=.'
+ + '|error C2280: .Arg &Arg::operator =\(const Arg &\).: attempting to reference a deleted function',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_copy_assignable_with_conversion():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Arg {
+ Arg(int) {}
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = delete;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), 15);
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .Arg& Arg::operator=\(const Arg&\).'
+ + '|error: overload resolution selected deleted operator .=.'
+ + '|error C2280: .Arg &Arg::operator =\(const Arg &\).: attempting to reference a deleted function',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_equality_comparable():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), Arg{});
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: no match for .operator==. \(operand types are .const Arg. and .const Arg.\)'
+ + '|error: invalid operands to binary expression \(.const Arg. and .const Arg.\)'
+ + '|error C2676: binary .==.: .const Arg. does not define this operator',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_equality_comparable_with_conversion():
+ source = '''
+ struct X {
+ int n;
+ X(int n) : n(n) {}
+ };
+
+ struct Arg {
+ Arg(int) {}
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ namespace std {
+ template <>
+ struct hash<Arg> {
+ size_t operator()(const Arg&);
+ };
+ }
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), 15);
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: no match for .operator==. \(operand types are .const Arg. and .const Arg.\)'
+ + '|error: invalid operands to binary expression \(.const Arg. and .const Arg.\)'
+ + '|error C2676: binary .==.: .const Arg. does not define this operator',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_hashable():
+ source = '''
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), Arg{});
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .std::hash<Arg>::hash\(\).'
+ + '|error: call to implicitly-deleted default constructor of .std::hash<Arg>.'
+ + '|error: invalid use of incomplete type .struct std::hash<Arg>.'
+ + '|error: implicit instantiation of undefined template .std::(__1::)?hash<Arg>.'
+ + '|error C2338: The C\+\+ Standard doesn.t provide a hash for this type.'
+ + '|error C2064: term does not evaluate to a function taking 1 arguments',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_install_with_args_error_not_hashable_with_conversion():
+ source = '''
+ struct Arg {
+ Arg(int) {}
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = default;
+ Arg& operator=(const Arg&) = default;
+ Arg& operator=(Arg&&) = default;
+ };
+
+ bool operator==(const Arg&, const Arg&);
+
+ fruit::Component<X> getParentComponent(int, std::string, Arg);
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getParentComponent, 5, std::string("Hello"), 15);
+ }
+ '''
+ expect_generic_compile_error(
+ 'error: use of deleted function .std::hash<Arg>::hash\(\).'
+ + '|error: call to implicitly-deleted default constructor of .std::hash<Arg>.'
+ + '|error: invalid use of incomplete type .struct std::hash<Arg>.'
+ + '|error: implicit instantiation of undefined template .std::(__1::)?hash<Arg>.'
+ + '|error C2338: The C\+\+ Standard doesn.t provide a hash for this type.'
+ + '|error C2064: term does not evaluate to a function taking 1 arguments',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_install_component_functions_deduped(XAnnot):
+ source = '''
+ struct X {};
+
+ X x;
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XAnnot, X>(x);
+ }
+
+ fruit::Component<> getComponent2() {
+ return fruit::createComponent()
+ .install(getComponent);
+ }
+
+ fruit::Component<> getComponent3() {
+ return fruit::createComponent()
+ .install(getComponent);
+ }
+
+ fruit::Component<> getComponent4() {
+ return fruit::createComponent()
+ .install(getComponent2)
+ .install(getComponent3);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent4);
+
+ // We test multibindings because the effect on other bindings is not user-visible (that only affects
+ // performance).
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 1);
+ Assert(multibindings[0] == &x);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_install_component_functions_deduped_across_normalized_component(XAnnot):
+ source = '''
+ struct X {};
+
+ X x;
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XAnnot, X>(x);
+ }
+
+ fruit::Component<> getComponent2() {
+ return fruit::createComponent()
+ .install(getComponent);
+ }
+
+ fruit::Component<> getComponent3() {
+ return fruit::createComponent()
+ .install(getComponent);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getComponent2);
+ fruit::Injector<> injector(normalizedComponent, getComponent3);
+
+ // We test multibindings because the effect on other bindings is not user-visible (that only affects
+ // performance).
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 1);
+ Assert(multibindings[0] == &x);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_install_component_functions_with_args_deduped(XAnnot):
+ source = '''
+ struct X {};
+
+ X x;
+
+ fruit::Component<> getComponent(int) {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XAnnot, X>(x);
+ }
+
+ fruit::Component<> getComponent2() {
+ return fruit::createComponent()
+ .install(getComponent, 1);
+ }
+
+ fruit::Component<> getComponent3() {
+ return fruit::createComponent()
+ .install(getComponent, 1);
+ }
+
+ fruit::Component<> getComponent4() {
+ return fruit::createComponent()
+ .install(getComponent2)
+ .install(getComponent3);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent4);
+
+ // We test multibindings because the effect on other bindings is not user-visible (that only affects
+ // performance).
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 1);
+ Assert(multibindings[0] == &x);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_install_component_functions_different_args_not_deduped(XAnnot):
+ source = '''
+ struct X {};
+
+ X x;
+
+ fruit::Component<> getComponent(int) {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XAnnot, X>(x);
+ }
+
+ fruit::Component<> getComponent2() {
+ return fruit::createComponent()
+ .install(getComponent, 1);
+ }
+
+ fruit::Component<> getComponent3() {
+ return fruit::createComponent()
+ .install(getComponent, 2);
+ }
+
+ fruit::Component<> getComponent4() {
+ return fruit::createComponent()
+ .install(getComponent2)
+ .install(getComponent3);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent4);
+
+ // We test multibindings because the effect on other bindings is not user-visible (it only affects
+ // performance).
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 2);
+ Assert(multibindings[0] == &x);
+ Assert(multibindings[1] == &x);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_install_component_functions_loop():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ // X -> Y -> Z -> Y
+
+ fruit::Component<X> getXComponent();
+ fruit::Component<Y> getYComponent();
+ fruit::Component<Z> getZComponent();
+
+ fruit::Component<X> getXComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X()>()
+ .install(getYComponent);
+ }
+
+ fruit::Component<Y> getYComponent() {
+ return fruit::createComponent()
+ .registerConstructor<Y()>()
+ .install(getZComponent);
+ }
+
+ fruit::Component<Z> getZComponent() {
+ return fruit::createComponent()
+ .registerConstructor<Z()>()
+ .install(getYComponent);
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getXComponent);
+ (void)injector;
+ }
+ '''
+ expect_runtime_error(
+ 'Component installation trace \(from top-level to the most deeply-nested\):\n'
+ + '(class )?fruit::Component<(struct )?X> ?\((__cdecl)?\*\)\((void)?\)\n'
+ + '<-- The loop starts here\n'
+ + '(class )?fruit::Component<(struct )?Y> ?\((__cdecl)?\*\)\((void)?\)\n'
+ + '(class )?fruit::Component<(struct )?Z> ?\((__cdecl)?\*\)\((void)?\)\n'
+ + '(class )?fruit::Component<(struct )?Y> ?\((__cdecl)?\*\)\((void)?\)\n',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_install_component_functions_different_arguments_loop_not_reported():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ // X -> Y(1) -> Z -> Y(2)
+
+ fruit::Component<X> getXComponent();
+ fruit::Component<Y> getYComponent(int);
+ fruit::Component<Z> getZComponent();
+
+ fruit::Component<X> getXComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X()>()
+ .install(getYComponent, 1);
+ }
+
+ fruit::Component<Y> getYComponent(int n) {
+ if (n == 1) {
+ return fruit::createComponent()
+ .registerConstructor<Y()>()
+ .install(getZComponent);
+ } else {
+ return fruit::createComponent()
+ .registerConstructor<Y()>();
+ }
+ }
+
+ fruit::Component<Z> getZComponent() {
+ return fruit::createComponent()
+ .registerConstructor<Z()>()
+ .install(getYComponent, 2);
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getXComponent);
+ injector.get<X>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_macros.h b/tests/test_macros.h
new file mode 100644
index 0000000..8d850e1
--- /dev/null
+++ b/tests/test_macros.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRUIT_TEST_MACROS
+#define FRUIT_TEST_MACROS
+
+#include <iostream>
+
+#define Assert(...) \
+ do { \
+ if (!(__VA_ARGS__)) { \
+ std::cerr << __FILE__ << ":" << __LINE__ << ": " << __func__ << ": Assertion \"" << #__VA_ARGS__ << "\" failed." \
+ << std::endl; \
+ abort(); \
+ } \
+ } while (false)
+
+#define InstantiateType(...) \
+ void f() { \
+ (void)sizeof(__VA_ARGS__); \
+ }
+
+#endif // FRUIT_TEST_MACROS
diff --git a/tests/test_misc.py b/tests/test_misc.py
new file mode 100644
index 0000000..0cd6bec
--- /dev/null
+++ b/tests/test_misc.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ template <typename T>
+ class V {};
+
+ template <typename T>
+ class X {
+ private:
+ X() {}
+
+ public:
+ INJECT(X(ASSISTED(int))) {
+ }
+ };
+
+ using XFactory = std::function<X<V<float>>(int)>;
+ '''
+
+def test_misc():
+ source = '''
+ fruit::Component<X<V<float>>> getXProvider2() {
+ return fruit::createComponent()
+ .registerProvider([](){return X<V<float>>(1);});
+ }
+
+ struct AssistedMultiparamExample {
+ INJECT(AssistedMultiparamExample(ASSISTED(std::map<int, float>))) {}
+ };
+
+ struct Implementation1 {
+ bool constructed = true;
+
+ Implementation1(V<int>&&, XFactory) {
+ std::cout << "Called Implementation1() for object " << this << std::endl;
+ }
+
+ Implementation1() = delete;
+ Implementation1(const Implementation1&) = delete;
+
+ Implementation1& operator=(const Implementation1&) = delete;
+ Implementation1& operator=(Implementation1&&) = delete;
+
+ Implementation1(Implementation1&&) {
+ std::cout << "Moving an Implementation1 into object" << this << std::endl;
+ }
+
+ ~Implementation1() {
+ std::cout << "Called ~Implementation1() for object " << this << std::endl;
+ constructed = 0;
+ }
+
+ int x;
+ };
+
+ struct Interface2 {
+ virtual void f() = 0;
+ };
+
+ struct Implementation2 : public Interface2 {
+ INJECT(Implementation2(std::function<Implementation1(int)>)) {
+ std::cout << "Called Implementation2()" << std::endl;
+ }
+
+ virtual ~Implementation2() {}
+
+ virtual void f() {};
+ };
+
+ fruit::Component<Interface2, XFactory, std::function<Implementation1(int)>> getParentComponent() {
+ return fruit::createComponent()
+ .registerFactory<Implementation1(fruit::Assisted<int>, XFactory)>(
+ [](int, XFactory xFactory) {
+ return Implementation1(V<int>(), xFactory);
+ })
+ .bind<Interface2, Implementation2>();
+ }
+
+ //*************************************
+
+ struct Interface3 {
+ virtual void f() = 0;
+ };
+
+ struct Implementation3 : public Interface3 {
+ INJECT(Implementation3(Implementation2*, fruit::Provider<Implementation2> provider)) {
+ (void) provider.get();
+ std::cout << "Called Implementation2()" << std::endl;
+ }
+
+ virtual ~Implementation3() {}
+
+ virtual void f() {};
+ };
+
+ fruit::Component<Interface3, std::function<Implementation1(int)>> getMyComponent() {
+ return fruit::createComponent()
+ // Must fail at runtime.
+ // .install(getXProvider2)
+ .bind<Interface3, Implementation3>()
+ .install(getParentComponent);
+ }
+
+ fruit::Component<std::function<AssistedMultiparamExample(std::map<int, float>)>> getAssistedMultiparamExampleComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<
+ Interface3,
+ // XFactory,
+ std::function<Implementation1(int)>
+ > oldInjector(getMyComponent);
+
+ // The move is completely unnecessary, it's just to check that it works.
+ fruit::Injector<
+ Interface3,
+ // XFactory,
+ std::function<Implementation1(int)>
+ > injector(std::move(oldInjector));
+
+ std::cout << "Constructing an Interface3" << std::endl;
+ Interface3* interface3(injector);
+ std::cout << std::endl;
+ (void) interface3;
+
+ std::cout << "Constructing another Interface3" << std::endl;
+ Interface3* interface3_obj2 = injector.get<Interface3*>();
+ std::cout << std::endl;
+ (void) interface3_obj2;
+
+ std::function<Implementation1(int)> implementation1Factory(injector);
+ {
+ std::cout << "Constructing another Implementation1" << std::endl;
+ Implementation1 implementation1 = implementation1Factory(12);
+ (void) implementation1;
+ }
+ std::cout << "Destroying injector" << std::endl;
+
+ fruit::Injector<std::function<AssistedMultiparamExample(std::map<int, float>)>> assistedMultiparamExampleInjector(
+ getAssistedMultiparamExampleComponent);
+
+ return 0;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_multibindings_bind_instance.py b/tests/test_multibindings_bind_instance.py
new file mode 100755
index 0000000..634a627
--- /dev/null
+++ b/tests/test_multibindings_bind_instance.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation1 {};
+ '''
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_multibindings_bind_instance_ok(XAnnot):
+ source = '''
+ struct X {};
+
+ X x;
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XAnnot, X>(x);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 1);
+ Assert(multibindings[0] == &x);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_multibindings_bind_const_instance_error(XAnnot):
+ source = '''
+ struct X {};
+
+ const X x{};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XAnnot, X>(x);
+ }
+ '''
+ expect_generic_compile_error(
+ 'candidate function not viable: 1st argument \(.const X.\) would lose const qualifier'
+ '|no matching function for call to .fruit::PartialComponent<.*>::addInstanceMultibinding(<XAnnot,X>)?\(const X&\).'
+ '|error: no matching member function for call to .addInstanceMultibinding.'
+ '|cannot convert argument 1 from .const X. to .X &.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_multibindings_bind_instance_vector(XAnnot):
+ source = '''
+ struct X {};
+
+ std::vector<X> values = {X(), X()};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibindings<XAnnot, X>(values);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 2);
+ Assert(multibindings[0] == &(values[0]));
+ Assert(multibindings[1] == &(values[1]));
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_multibindings_bind_const_instance_vector_error(XAnnot):
+ source = '''
+ struct X {};
+
+ const std::vector<X> values{};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibindings<XAnnot, X>(values);
+ }
+ '''
+ expect_generic_compile_error(
+ 'candidate function not viable: 1st argument \(.const std::vector<X>.\) would lose const qualifier'
+ '|cannot convert .values. \(type .const std::(__debug::)?vector<X>.\) to type .std::(__debug::)?vector<X>&.'
+ '|no matching member function for call to .addInstanceMultibindings.'
+ '|cannot convert argument 1 from .const std::vector<X,std::allocator<.*>>. to .std::vector<X,std::allocator<.*>> &.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_multibindings_bind_instance_vector_of_consts_error(XAnnot):
+ source = '''
+ struct X {};
+
+ std::vector<const X> values;
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibindings<XAnnot, X>(values);
+ }
+ '''
+ expect_generic_compile_error(
+ '.*',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X**', r'X\*\*'),
+ ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
+ ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'),
+ ('X* const', r'X\* const'),
+ ('const X* const', r'const X\* const'),
+ ('X*&', r'X\*&'),
+ ('fruit::Annotated<Annotation1, X**>', r'X\*\*'),
+])
+def test_multibindings_bind_instance_non_class_type_error(XVariant, XVariantRegex):
+ source = '''
+ struct X {};
+
+ using XVariantT = XVariant;
+ fruit::Component<> getComponent(XVariantT x) {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XVariant, XVariant>(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegex,X>',
+ 'A non-class type T was specified.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
+ ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
+])
+def test_multibindings_bind_instance_non_injectable_type_error(XVariant, XVariantRegex):
+ source = '''
+ struct X {};
+
+ using XVariantT = XVariant;
+ fruit::Component<> getComponent(XVariantT x) {
+ return fruit::createComponent()
+ .addInstanceMultibinding<XVariant, XVariant>(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<XVariantRegex>',
+ 'The type T is not injectable.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_multibindings_bind_interface.py b/tests/test_multibindings_bind_interface.py
new file mode 100755
index 0000000..90c4678
--- /dev/null
+++ b/tests/test_multibindings_bind_interface.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+from fruit_test_config import CXX_COMPILER_NAME
+import re
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation {};
+ struct Annotation1 {};
+ struct Annotation2 {};
+ '''
+
+@pytest.mark.parametrize('XAnnot,XImplAnnot', [
+ ('X', 'XImpl'),
+ ('X', 'fruit::Annotated<Annotation2, XImpl>'),
+ ('fruit::Annotated<Annotation1, X>', 'XImpl'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, XImpl>'),
+])
+def test_add_interface_multibinding_success(XAnnot, XImplAnnot):
+ source = '''
+ struct X {
+ virtual int foo() = 0;
+ };
+
+ struct XImpl : public X {
+ INJECT(XImpl()) = default;
+
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibinding<XAnnot, XImplAnnot>();
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 1);
+ Assert(multibindings[0]->foo() == 5);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XImplAnnot,ConstXImplAnnot', [
+ ('X', 'XImpl', 'const XImpl'),
+ ('X', 'fruit::Annotated<Annotation2, XImpl>', 'fruit::Annotated<Annotation2, const XImpl>'),
+])
+def test_add_interface_multibinding_const_target_error_install_first(XAnnot, XImplAnnot, ConstXImplAnnot):
+ source = '''
+ struct X {
+ virtual int foo() = 0;
+ };
+
+ struct XImpl : public X {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<ConstXImplAnnot> getXImplComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .install(getXImplComponent)
+ .addMultibinding<XAnnot, XImplAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<XImplAnnot>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XImplAnnot,ConstXImplAnnot', [
+ ('X', 'XImpl', 'const XImpl'),
+ ('X', 'fruit::Annotated<Annotation2, XImpl>', 'fruit::Annotated<Annotation2, const XImpl>'),
+])
+def test_add_interface_multibinding_const_target_error_binding_first(XAnnot, XImplAnnot, ConstXImplAnnot):
+ source = '''
+ struct X {
+ virtual int foo() = 0;
+ };
+
+ struct XImpl : public X {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<ConstXImplAnnot> getXImplComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibinding<XAnnot, XImplAnnot>()
+ .install(getXImplComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<XImplAnnot>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,intAnnot', [
+ ('X', 'int'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, int>'),
+])
+def test_error_not_base(XAnnot, intAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibinding<XAnnot, intAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'NotABaseClassOfError<X,int>',
+ 'I is not a base class of C.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot', [
+ ('Scaler', 'ScalerImpl'),
+ ('fruit::Annotated<Annotation1, Scaler>', 'fruit::Annotated<Annotation2, ScalerImpl>'),
+])
+def test_error_abstract_class(ScalerAnnot, ScalerImplAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ // Note: here we "forgot" to implement scale() (on purpose, for this test) so ScalerImpl is an abstract class.
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibinding<ScalerAnnot, ScalerImplAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundForAbstractClassError<ScalerImplAnnot,ScalerImpl>',
+ 'No explicit binding was found for T, and note that C is an abstract class',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot', [
+ ('Scaler', 'ScalerImpl'),
+ ('fruit::Annotated<Annotation1, Scaler>', 'fruit::Annotated<Annotation2, ScalerImpl>'),
+])
+@pytest.mark.skipif(
+ re.search('Clang', CXX_COMPILER_NAME) is None,
+ reason = 'This is Clang-only because GCC >=4.9 refuses to even mention the type C() when C is an abstract class, '
+ 'while Clang allows to mention the type (but of course there can be no functions with this type)')
+def test_error_abstract_class_clang(ScalerAnnot, ScalerImplAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ INJECT(ScalerImpl()) = default;
+
+ // Note: here we "forgot" to implement scale() (on purpose, for this test) so ScalerImpl is an abstract class.
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibinding<ScalerAnnot, ScalerImplAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'CannotConstructAbstractClassError<ScalerImpl>',
+ 'The specified class can.t be constructed because it.s an abstract class.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_multibindings_bind_provider.py b/tests/test_multibindings_bind_provider.py
new file mode 100755
index 0000000..9008c2f
--- /dev/null
+++ b/tests/test_multibindings_bind_provider.py
@@ -0,0 +1,569 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation1 {};
+ struct Annotation2 {};
+
+ template <typename T>
+ using WithNoAnnot = T;
+
+ template <typename T>
+ using WithAnnot1 = fruit::Annotated<Annotation1, T>;
+ '''
+
+@pytest.mark.parametrize('ConstructX', [
+ 'X()',
+ 'new X()',
+])
+def test_bind_multibinding_provider_success(ConstructX):
+ source = '''
+ struct X : public ConstructionTracker<X> {
+ INJECT(X()) = default;
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider([](){return ConstructX;});
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ Assert(X::num_objects_constructed == 0);
+ Assert(injector.getMultibindings<X>().size() == 1);
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+def test_bind_multibinding_provider_abstract_class_success(WithAnnot):
+ source = '''
+ struct I {
+ virtual int foo() = 0;
+ virtual ~I() = default;
+ };
+
+ struct X : public I {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ Assert(injector.getMultibindings<WithAnnot<I>>().size() == 1);
+ Assert(injector.getMultibindings<WithAnnot<I>>()[0]->foo() == 5);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+def test_bind_multibinding_provider_abstract_class_with_no_virtual_destructor_error(WithAnnot):
+ source = '''
+ struct I {
+ virtual int foo() = 0;
+ };
+
+ struct X : public I {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
+ }
+ '''
+ expect_compile_error(
+ 'MultibindingProviderReturningPointerToAbstractClassWithNoVirtualDestructorError<I>',
+ 'registerMultibindingProvider\(\) was called with a lambda that returns a pointer to T, but T is an abstract class with no virtual destructor',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'const Y',
+ 'Y*',
+ 'const Y*',
+ 'Y&',
+ 'const Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+ 'fruit::Provider<const Y>',
+])
+def test_bind_multibinding_provider_with_param_success(ConstructX, XPtr, WithAnnot, YVariant):
+ source = '''
+ struct Y {};
+
+ struct X : public ConstructionTracker<X> {};
+
+ fruit::Component<WithAnnot<Y>> getYComponent() {
+ return fruit::createComponent()
+ .registerConstructor<WithAnnot<Y>()>();
+ }
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ Assert(X::num_objects_constructed == 0);
+ Assert(injector.getMultibindings<X>().size() == 1);
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'const Y',
+ 'const Y*',
+ 'const Y&',
+ 'fruit::Provider<const Y>',
+])
+def test_bind_multibinding_provider_with_param_const_binding_success(ConstructX, XPtr, WithAnnot, YVariant):
+ source = '''
+ struct Y {};
+
+ struct X : public ConstructionTracker<X> {};
+
+ const Y y{};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent() {
+ return fruit::createComponent()
+ .bindInstance<WithAnnot<Y>, Y>(y);
+ }
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ Assert(X::num_objects_constructed == 0);
+ Assert(injector.getMultibindings<X>().size() == 1);
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
+ ('WithNoAnnot', 'Y'),
+ ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_bind_multibinding_provider_with_param_error_nonconst_param_required(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
+ ('WithNoAnnot', 'Y'),
+ ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_bind_multibinding_provider_with_param_error_nonconst_param_required_install_after(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; })
+ .install(getYComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_bind_multibinding_provider_requiring_nonconst_then_requiring_const_ok():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider([](X&) { return Y(); })
+ .addMultibindingProvider([](const X&) { return Y(); })
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+ injector.getMultibindings<Y>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_bind_multibinding_provider_requiring_nonconst_then_requiring_const_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<fruit::Required<const X>> getRootComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider([](X&) { return Y(); })
+ .addMultibindingProvider([](const X&) { return Y(); });
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_bind_multibinding_provider_requiring_const_then_requiring_nonconst_ok():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider([](const X&) { return Y(); })
+ .addMultibindingProvider([](X&) { return Y(); })
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+ injector.getMultibindings<Y>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_bind_multibinding_provider_requiring_const_then_requiring_nonconst_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<fruit::Required<const X>> getRootComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider([](const X&) { return Y(); })
+ .addMultibindingProvider([](X&) { return Y(); });
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('YAnnot,ConstYAnnot,YVariant,YVariantRegex', [
+ ('Y', 'Y', 'Y**', 'Y\*\*'),
+ ('Y', 'Y', 'std::shared_ptr<Y>*', 'std::shared_ptr<Y>\*'),
+ ('Y', 'const Y', 'Y**', 'Y\*\*'),
+ ('Y', 'const Y', 'std::shared_ptr<Y>*', 'std::shared_ptr<Y>\*'),
+ ('fruit::Annotated<Annotation1, Y>', 'fruit::Annotated<Annotation1, Y>', 'Y**', 'Y\*\*'),
+ ('fruit::Annotated<Annotation1, Y>', 'fruit::Annotated<Annotation1, const Y>', 'Y**', 'Y\*\*'),
+])
+def test_bind_multibinding_provider_with_param_error_type_not_injectable(ConstructX, XPtr, YAnnot, ConstYAnnot, YVariant, YVariantRegex):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<XPtr(YVariant)>([](YVariant){ return ConstructX; });
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<YVariantRegex>',
+ 'The type T is not injectable.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XAnnot,XPtrAnnot', [
+ ('X()', 'X', 'X'),
+ ('X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('new X()', 'X', 'X*'),
+ ('new X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'),
+])
+def test_bind_multibinding_provider_explicit_signature_success(ConstructX, XAnnot, XPtrAnnot):
+ source = '''
+ struct X : public ConstructionTracker<X> {
+ INJECT(X()) = default;
+
+ static bool constructed;
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<XPtrAnnot()>([](){return ConstructX;});
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ Assert(X::num_objects_constructed == 0);
+ Assert(injector.getMultibindings<XAnnot>().size() == 1);
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XAnnot,XPtrAnnot', [
+ ('X()', 'X', 'X'),
+ ('X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
+ ('new X()', 'X', 'X*'),
+ ('new X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'),
+])
+def test_bind_multibinding_provider_explicit_signature_with_normalized_component_success(ConstructX, XAnnot, XPtrAnnot):
+ source = '''
+ struct X : public ConstructionTracker<X> {
+ INJECT(X()) = default;
+
+ static bool constructed;
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<XPtrAnnot()>([](){return ConstructX;});
+ }
+
+ fruit::Component<> getEmptyComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getComponent);
+ fruit::Injector<> injector(normalizedComponent, getEmptyComponent);
+
+ Assert(X::num_objects_constructed == 0);
+ Assert(injector.getMultibindings<XAnnot>().size() == 1);
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XPtrAnnot,intAnnot', [
+ ('X', 'X*', 'int'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', 'fruit::Annotated<Annotation2, int>'),
+])
+def test_multiple_providers(XAnnot, XPtrAnnot, intAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<intAnnot()>([](){return 42;})
+ .addMultibindingProvider<XAnnot(intAnnot)>([](int){return X();})
+ .addMultibindingProvider<XPtrAnnot(intAnnot)>([](int){return new X();});
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
+ Assert(multibindings.size() == 2);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX', [
+ 'X()',
+ 'new X()',
+])
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_bind_multibinding_provider_malformed_signature(ConstructX, XAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<XAnnot>([](){return ConstructX;});
+ }
+ '''
+ expect_compile_error(
+ 'NotASignatureError<XAnnot>',
+ 'CandidateSignature was specified as parameter, but it.s not a signature.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX', [
+ 'X(n)',
+ 'new X(n)',
+])
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_bind_multibinding_provider_lambda_with_captures_error(ConstructX, XAnnot):
+ source = '''
+ struct X {
+ X(int) {}
+ };
+
+ fruit::Component<> getComponent() {
+ int n = 3;
+ return fruit::createComponent()
+ .addMultibindingProvider<XAnnot()>([=]{return ConstructX;});
+ }
+ '''
+ expect_compile_error(
+ 'FunctorUsedAsProviderError<.*>',
+ 'A stateful lambda or a non-lambda functor was used as provider',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+# TODO: should XPtrAnnot be just XAnnot in the signature?
+# Make sure the behavior here is consistent with registerProvider() and registerFactory().
+@pytest.mark.parametrize('XAnnot,XPtrAnnot,XAnnotRegex', [
+ ('X', 'X*', '(struct )?X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
+])
+def test_provider_returns_nullptr_error(XAnnot, XPtrAnnot, XAnnotRegex):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .addMultibindingProvider<XPtrAnnot()>([](){return (X*)nullptr;});
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+ injector.getMultibindings<XAnnot>();
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: attempting to get an instance for the type XAnnotRegex but the provider returned nullptr',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_multibindings_misc.py b/tests/test_multibindings_misc.py
new file mode 100755
index 0000000..5b85700
--- /dev/null
+++ b/tests/test_multibindings_misc.py
@@ -0,0 +1,577 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Listener;
+
+ struct X {};
+
+ struct Annotation {};
+ struct Annotation1 {};
+ using ListenerAnnot = fruit::Annotated<Annotation, Listener>;
+ '''
+
+def test_get_none():
+ source = '''
+ fruit::Component<> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<> injector(getComponent);
+
+ std::vector<X*> multibindings = injector.getMultibindings<X>();
+ (void) multibindings;
+ Assert(multibindings.empty());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_multiple_various_kinds():
+ source = '''
+ static int numNotificationsToListener1 = 0;
+ static int numNotificationsToListener2 = 0;
+ static int numNotificationsToListener3 = 0;
+
+ struct Listener {
+ public:
+ virtual ~Listener() = default;
+
+ virtual void notify() = 0;
+ };
+
+ struct Listener1 : public Listener {
+ public:
+ INJECT(Listener1()) = default;
+
+ virtual ~Listener1() = default;
+
+ void notify() override {
+ ++numNotificationsToListener1;
+ }
+ };
+
+ struct Writer {
+ public:
+ virtual void write(std::string s) = 0;
+ };
+
+ struct StdoutWriter : public Writer {
+ public:
+ INJECT(StdoutWriter()) = default;
+
+ void write(std::string s) override {
+ std::cout << s << std::endl;
+ }
+ };
+
+ struct Listener2 : public Listener {
+ private:
+ Writer* writer;
+
+ public:
+ INJECT(Listener2(Writer* writer))
+ : writer(writer) {
+ }
+
+ virtual ~Listener2() = default;
+
+ void notify() override {
+ (void) writer;
+ ++numNotificationsToListener2;
+ }
+ };
+
+ struct Listener3 : public Listener {
+ private:
+ Writer* writer;
+
+ public:
+ INJECT(Listener3(Writer* writer))
+ : writer(writer) {
+ }
+
+ virtual ~Listener3() = default;
+
+ void notify() override {
+ (void) writer;
+ ++numNotificationsToListener3;
+ }
+ };
+
+ fruit::Component<> getListenersComponent() {
+ return fruit::createComponent()
+ .bind<Writer, StdoutWriter>()
+ // Note: this is just to exercise the other method, but in real code you should split this in
+ // an addMultibinding<Listener, Listener1> and a registerProvider with the lambda.
+ .addMultibindingProvider([]() {
+ Listener1* listener1 = new Listener1();
+ return static_cast<Listener*>(listener1);
+ })
+ .addMultibinding<Listener, Listener2>()
+ .addMultibinding<ListenerAnnot, Listener3>();
+ }
+
+ int main() {
+ fruit::Injector<> injector(getListenersComponent);
+ std::vector<Listener*> listeners = injector.getMultibindings<Listener>();
+ for (Listener* listener : listeners) {
+ listener->notify();
+ }
+
+ std::vector<Listener*> listeners2 = injector.getMultibindings<Listener>();
+ Assert(listeners == listeners2);
+
+ if (numNotificationsToListener1 != 1 || numNotificationsToListener2 != 1
+ || numNotificationsToListener3 != 0) {
+ abort();
+ }
+
+ std::vector<Listener*> listenersWithAnnotation = injector.getMultibindings<ListenerAnnot>();
+ for (Listener* listener : listenersWithAnnotation) {
+ listener->notify();
+ }
+
+ if (numNotificationsToListener1 != 1 || numNotificationsToListener2 != 1
+ || numNotificationsToListener3 != 1) {
+ abort();
+ }
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_order():
+ source = '''
+ std::vector<int> numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
+ // *
+ // |-- 0
+ // |-- A
+ // | |-- 1
+ // | |-- B
+ // | | |-- 2
+ // | | `-- 3
+ // | |-- 4
+ // | |-- C
+ // | | |-- 5
+ // | | |-- 6
+ // | | |-- D
+ // | | | |-- 7
+ // | | | |-- E
+ // | | | | |-- 8
+ // | | | | `-- 9
+ // | | | `-- 10
+ // | | |-- 11
+ // | | |-- F
+ // | | | |-- 12
+ // | | | `-- 13
+ // | | `-- 14
+ // | |-- 15
+ // | |-- C (won't be expanded)
+ // | `-- 16
+ // |-- 17
+ // |-- C (won't be expanded)
+ // `-- 18
+
+ fruit::Component<> getRootComponent();
+ fruit::Component<> getComponentA();
+ fruit::Component<> getComponentB();
+ fruit::Component<> getComponentC();
+ fruit::Component<> getComponentD();
+ fruit::Component<> getComponentE();
+ fruit::Component<> getComponentF();
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[0])
+ .install(getComponentA)
+ .addInstanceMultibinding(numbers[17])
+ .install(getComponentC)
+ .addInstanceMultibinding(numbers[18]);
+ }
+
+ fruit::Component<> getComponentA() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[1])
+ .install(getComponentB)
+ .addInstanceMultibinding(numbers[4])
+ .install(getComponentC)
+ .addInstanceMultibinding(numbers[15])
+ .install(getComponentC)
+ .addInstanceMultibinding(numbers[16]);
+ }
+
+ fruit::Component<> getComponentB() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[2])
+ .addInstanceMultibinding(numbers[3]);
+ }
+
+ fruit::Component<> getComponentC() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[5])
+ .addInstanceMultibinding(numbers[6])
+ .install(getComponentD)
+ .addInstanceMultibinding(numbers[11])
+ .install(getComponentF)
+ .addInstanceMultibinding(numbers[14]);
+ }
+
+ fruit::Component<> getComponentD() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[7])
+ .install(getComponentE)
+ .addInstanceMultibinding(numbers[10]);
+ }
+
+ fruit::Component<> getComponentE() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[8])
+ .addInstanceMultibinding(numbers[9]);
+ }
+
+ fruit::Component<> getComponentF() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[12])
+ .addInstanceMultibinding(numbers[13]);
+ }
+
+ int main() {
+ fruit::Injector<> injector(getRootComponent);
+ std::vector<int*> result_ptrs = injector.getMultibindings<int>();
+ std::vector<int> results;
+ std::cout << "Results: ";
+ for (int* result : result_ptrs) {
+ std::cout << *result << ", ";
+ results.push_back(*result);
+ }
+ std::cout << std::endl;
+ Assert(results == numbers);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+
+def test_order_with_normalized_component():
+ source = '''
+ std::vector<int> numbers = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37};
+ // root1
+ // |-- 0
+ // |-- A
+ // | |-- 1
+ // | |-- B
+ // | | |-- 2
+ // | | `-- 3
+ // | |-- 4
+ // | |-- C
+ // | | |-- 5
+ // | | |-- 6
+ // | | |-- D
+ // | | | |-- 7
+ // | | | |-- E
+ // | | | | |-- 8
+ // | | | | `-- 9
+ // | | | `-- 10
+ // | | |-- 11
+ // | | |-- F
+ // | | | |-- 12
+ // | | | `-- 13
+ // | | `-- 14
+ // | |-- 15
+ // | |-- C (won't be expanded)
+ // | `-- 16
+ // |-- 17
+ // |-- C (won't be expanded)
+ // `-- 18
+
+ // root2
+ // |-- 19
+ // |-- A2
+ // | |-- 20
+ // | |-- B2
+ // | | |-- 21
+ // | | `-- 22
+ // | |-- 23
+ // | |-- C2
+ // | | |-- 24
+ // | | |-- 25
+ // | | |-- D2
+ // | | | |-- 26
+ // | | | |-- E2
+ // | | | | |-- 27
+ // | | | | `-- 28
+ // | | | `-- 29
+ // | | |-- 30
+ // | | |-- F2
+ // | | | |-- 31
+ // | | | `-- 32
+ // | | `-- 33
+ // | |-- 34
+ // | |-- C2 (won't be expanded)
+ // | `-- 35
+ // |-- 36
+ // |-- C2 (won't be expanded)
+ // `-- 37
+
+ fruit::Component<> getRootComponent();
+ fruit::Component<> getComponentA();
+ fruit::Component<> getComponentB();
+ fruit::Component<> getComponentC();
+ fruit::Component<> getComponentD();
+ fruit::Component<> getComponentE();
+ fruit::Component<> getComponentF();
+
+ fruit::Component<> getRootComponent2();
+ fruit::Component<> getComponentA2();
+ fruit::Component<> getComponentB2();
+ fruit::Component<> getComponentC2();
+ fruit::Component<> getComponentD2();
+ fruit::Component<> getComponentE2();
+ fruit::Component<> getComponentF2();
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[0])
+ .install(getComponentA)
+ .addInstanceMultibinding(numbers[17])
+ .install(getComponentC)
+ .addInstanceMultibinding(numbers[18]);
+ }
+
+ fruit::Component<> getComponentA() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[1])
+ .install(getComponentB)
+ .addInstanceMultibinding(numbers[4])
+ .install(getComponentC)
+ .addInstanceMultibinding(numbers[15])
+ .install(getComponentC)
+ .addInstanceMultibinding(numbers[16]);
+ }
+
+ fruit::Component<> getComponentB() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[2])
+ .addInstanceMultibinding(numbers[3]);
+ }
+
+ fruit::Component<> getComponentC() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[5])
+ .addInstanceMultibinding(numbers[6])
+ .install(getComponentD)
+ .addInstanceMultibinding(numbers[11])
+ .install(getComponentF)
+ .addInstanceMultibinding(numbers[14]);
+ }
+
+ fruit::Component<> getComponentD() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[7])
+ .install(getComponentE)
+ .addInstanceMultibinding(numbers[10]);
+ }
+
+ fruit::Component<> getComponentE() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[8])
+ .addInstanceMultibinding(numbers[9]);
+ }
+
+ fruit::Component<> getComponentF() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[12])
+ .addInstanceMultibinding(numbers[13]);
+ }
+
+ fruit::Component<> getRootComponent2() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[19])
+ .install(getComponentA2)
+ .addInstanceMultibinding(numbers[36])
+ .install(getComponentC2)
+ .addInstanceMultibinding(numbers[37]);
+ }
+
+ fruit::Component<> getComponentA2() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[20])
+ .install(getComponentB2)
+ .addInstanceMultibinding(numbers[23])
+ .install(getComponentC2)
+ .addInstanceMultibinding(numbers[34])
+ .install(getComponentC2)
+ .addInstanceMultibinding(numbers[35]);
+ }
+
+ fruit::Component<> getComponentB2() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[21])
+ .addInstanceMultibinding(numbers[22]);
+ }
+
+ fruit::Component<> getComponentC2() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[24])
+ .addInstanceMultibinding(numbers[25])
+ .install(getComponentD2)
+ .addInstanceMultibinding(numbers[30])
+ .install(getComponentF2)
+ .addInstanceMultibinding(numbers[33]);
+ }
+
+ fruit::Component<> getComponentD2() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[26])
+ .install(getComponentE2)
+ .addInstanceMultibinding(numbers[29]);
+ }
+
+ fruit::Component<> getComponentE2() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[27])
+ .addInstanceMultibinding(numbers[28]);
+ }
+
+ fruit::Component<> getComponentF2() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[31])
+ .addInstanceMultibinding(numbers[32]);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
+ fruit::Injector<> injector(normalizedComponent, getRootComponent2);
+ std::vector<int*> result_ptrs = injector.getMultibindings<int>();
+ std::vector<int> results;
+ std::cout << "Results: ";
+ for (int* result : result_ptrs) {
+ std::cout << *result << ", ";
+ results.push_back(*result);
+ }
+ std::cout << std::endl;
+ Assert(results == numbers);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_with_normalized_component_lazy_components_not_deduped_across():
+ source = '''
+ std::vector<int> numbers = {0, 1, 2, 3, 4};
+
+ // *
+ // |-- 0
+ // |-- A (lazy)
+ // | |-- 1
+ // | `-- 2
+ // |-- 3
+ // |-- A (lazy, won't be expanded)
+ // `-- 4
+
+ fruit::Component<> getRootComponent();
+ fruit::Component<> getComponentA();
+
+ fruit::Component<> getRootComponent() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[0])
+ .install(getComponentA)
+ .addInstanceMultibinding(numbers[3])
+ .install(getComponentA)
+ .addInstanceMultibinding(numbers[4]);
+ }
+
+ fruit::Component<> getComponentA() {
+ return fruit::createComponent()
+ .addInstanceMultibinding(numbers[1])
+ .addInstanceMultibinding(numbers[2]);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
+ fruit::Injector<> injector(normalizedComponent, getRootComponent);
+ std::vector<int*> result_ptrs = injector.getMultibindings<int>();
+ std::vector<int> results;
+ std::cout << "Results: ";
+ for (int* result : result_ptrs) {
+ std::cout << *result << ", ";
+ results.push_back(*result);
+ }
+ std::cout << std::endl;
+ std::vector<int> expected_numbers = {0, 1, 2, 3, 4};
+ Assert(results == expected_numbers);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [
+ ('const X', 'const X'),
+ ('X*', 'X\*'),
+ ('const X*', 'const X\*'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+ ('fruit::Annotated<Annotation1, const X>', 'const X'),
+ ('fruit::Annotated<Annotation1, X*>', 'X\*'),
+ ('fruit::Annotated<Annotation1, const X*>', 'const X\*'),
+ ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'),
+])
+def test_multibindings_get_error_non_class_type(XVariantAnnot, XVariantRegexp):
+ source = '''
+ void f(fruit::Injector<> injector) {
+ injector.getMultibindings<XVariantAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegexp,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [
+ ('X&', 'X&'),
+ ('const X&', 'const X&'),
+ ('fruit::Annotated<Annotation1, X&>', 'X&'),
+ ('fruit::Annotated<Annotation1, const X&>', 'const X&'),
+])
+def test_multibindings_get_error_reference_type(XVariantAnnot, XVariantRegexp):
+ source = '''
+ void f(fruit::Injector<> injector) {
+ injector.getMultibindings<XVariantAnnot>();
+ }
+ '''
+ expect_generic_compile_error(
+ 'declared as a pointer to a reference of type'
+ '|forming pointer to reference type'
+ '|fruit::Injector<.*>::getMultibindings.: no matching overloaded function found',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_normalized_component.py b/tests/test_normalized_component.py
new file mode 100755
index 0000000..4d0025d
--- /dev/null
+++ b/tests/test_normalized_component.py
@@ -0,0 +1,206 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+
+ struct Annotation2 {};
+ using XAnnot2 = fruit::Annotated<Annotation2, X>;
+ '''
+
+@pytest.mark.parametrize('XAnnot,X_ANNOT,YAnnot', [
+ ('X', 'X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, X)', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_success_normalized_component_provides_unused(XAnnot, X_ANNOT, YAnnot):
+ source = '''
+ struct X {};
+
+ struct Y {
+ INJECT(Y(X_ANNOT)) {};
+ };
+
+ fruit::Component<fruit::Required<XAnnot>, YAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<XAnnot> getXComponent(X* x) {
+ return fruit::createComponent()
+ .bindInstance<XAnnot, X>(*x);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent(getComponent);
+
+ X x{};
+
+ fruit::Injector<XAnnot> injector(normalizedComponent, getXComponent, &x);
+ injector.get<XAnnot>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,X_ANNOT,YAnnot', [
+ ('X', 'X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, X)', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_success(XAnnot, X_ANNOT, YAnnot):
+ source = '''
+ struct X {};
+
+ struct Y {
+ INJECT(Y(X_ANNOT)) {};
+ };
+
+ fruit::Component<fruit::Required<XAnnot>, YAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<XAnnot> getXComponent(X* x) {
+ return fruit::createComponent()
+ .bindInstance<XAnnot, X>(*x);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent(getComponent);
+
+ X x{};
+
+ fruit::Injector<YAnnot> injector(normalizedComponent, getXComponent, &x);
+ injector.get<YAnnot>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,X_ANNOT,YAnnot', [
+ ('X', 'X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, X)', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_success_inline_component(XAnnot, X_ANNOT, YAnnot):
+ source = '''
+ struct X {};
+
+ struct Y {
+ INJECT(Y(X_ANNOT)) {};
+ };
+
+ fruit::Component<fruit::Required<XAnnot>, YAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<XAnnot> getAdditionalComponent(X* x) {
+ return fruit::createComponent()
+ .bindInstance<XAnnot, X>(*x);
+ }
+
+ int main() {
+ fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent(getComponent);
+
+ X x{};
+
+ fruit::Injector<YAnnot> injector(normalizedComponent, getAdditionalComponent, &x);
+ injector.get<YAnnot>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_injector_from_normalized_component_unsatisfied_requirements(XAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<fruit::Required<XAnnot>> getComponent();
+ fruit::Component<> getEmptyComponent();
+
+ int main() {
+ fruit::NormalizedComponent<fruit::Required<XAnnot>> normalizedComponent(getComponent);
+ fruit::Injector<> injector(normalizedComponent, getEmptyComponent);
+ }
+ '''
+ expect_compile_error(
+ 'UnsatisfiedRequirementsInNormalizedComponentError<XAnnot>',
+ 'The requirements in UnsatisfiedRequirements are required by the NormalizedComponent but are not provided by the Component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,ConstXAnnot', [
+ ('X', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
+])
+def test_normalized_component_providing_nonconst_from_component_providing_const_error(XAnnot, ConstXAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<XAnnot> getComponent();
+
+ int main() {
+ fruit::NormalizedComponent<ConstXAnnot> normalizedComponent(getComponent);
+ (void) normalizedComponent;
+ }
+ '''
+ expect_generic_compile_error(
+ 'no matching function for call to .fruit::NormalizedComponent<ConstXAnnot>::NormalizedComponent\(fruit::Component<XAnnot> \(&\)\(\)\).'
+ '|no matching constructor for initialization of .fruit::NormalizedComponent<ConstXAnnot>.'
+ '|.fruit::NormalizedComponent<ConstXAnnot>::NormalizedComponent.: none of the 2 overloads could convert all the argument types',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+# TODO: we should probably return a more specific error here.
+@pytest.mark.parametrize('XAnnot,YAnnot', [
+ ('X', 'Y'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
+])
+def test_injector_from_normalized_component_nonconst_requirements_provided_as_const_error(XAnnot, YAnnot):
+ source = '''
+ struct X {};
+ struct Y {};
+
+ fruit::Component<const XAnnot> getXComponent();
+
+ void f(fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent) {
+ fruit::Injector<YAnnot> injector(normalizedComponent, getXComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<XAnnot>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_register_constructor.py b/tests/test_register_constructor.py
new file mode 100755
index 0000000..3333075
--- /dev/null
+++ b/tests/test_register_constructor.py
@@ -0,0 +1,590 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot = fruit::Annotated<Annotation1, X>;
+
+ struct Annotation2 {};
+
+ struct Annotation3 {};
+
+ template <typename T>
+ using WithNoAnnotation = T;
+
+ template <typename T>
+ using WithAnnotation1 = fruit::Annotated<Annotation1, T>;
+ '''
+
+def test_register_constructor_success_copyable_and_movable():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ X(X&&) = default;
+ X(const X&) = default;
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X*>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_constructor_success_movable_only():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ X(X&&) = default;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X*>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_constructor_success_not_movable():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ X(X&&) = delete;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X*>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+# TODO: consider moving to test_normalized_component.py
+@pytest.mark.parametrize('XAnnot,YAnnot,MaybeConstYAnnot,ZAnnot', [
+ ('X', 'Y', 'Y', 'Z'),
+ ('X', 'Y', 'const Y', 'Z'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation2, const Y>', 'fruit::Annotated<Annotation3, Z>'),
+])
+def test_autoinject_with_annotation_success(XAnnot, YAnnot, MaybeConstYAnnot, ZAnnot):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ struct Y : public ConstructionTracker<Y> {
+ using Inject = Y();
+ };
+
+ struct Z {
+ using Inject = Z();
+ };
+
+ fruit::Component<ZAnnot, MaybeConstYAnnot, XAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<> getEmptyComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
+ fruit::Injector<MaybeConstYAnnot> injector(normalizedComponent, getComponent);
+
+ Assert(Y::num_objects_constructed == 0);
+ injector.get<YAnnot>();
+ Assert(Y::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_autoinject_annotation_in_signature_return_type():
+ source = '''
+ struct X {
+ using Inject = XAnnot();
+ };
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'InjectTypedefWithAnnotationError<X>',
+ 'C::Inject is a signature that returns an annotated type',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_wrong_class_in_typedef():
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ struct Y : public X {
+ };
+
+ fruit::Component<Y> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'InjectTypedefForWrongClassError<Y,X>',
+ 'C::Inject is a signature, but does not return a C. Maybe the class C has no Inject typedef and',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_constructor_error_abstract_class():
+ source = '''
+ struct X {
+ X(int*) {}
+
+ virtual void foo() = 0;
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<fruit::Annotated<Annotation1, X>(int*)>();
+ }
+ '''
+ if re.search('GNU|MSVC', CXX_COMPILER_NAME) is not None:
+ expect_generic_compile_error(
+ 'invalid abstract return type'
+ '|.X.: cannot instantiate abstract class',
+ COMMON_DEFINITIONS,
+ source)
+ else:
+ expect_compile_error(
+ 'CannotConstructAbstractClassError<X>',
+ 'The specified class can.t be constructed because it.s an abstract class',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_constructor_error_malformed_signature():
+ source = '''
+ struct X {
+ X(int) {}
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X[]>();
+ }
+ '''
+ expect_compile_error(
+ 'NotASignatureError<X\[\]>',
+ 'CandidateSignature was specified as parameter, but it.s not a signature. Signatures are of the form',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_constructor_error_malformed_signature_autoinject():
+ source = '''
+ struct X {
+ using Inject = X[];
+ X(int) {}
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'InjectTypedefNotASignatureError<X,X\[\]>',
+ 'C::Inject should be a typedef to a signature',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('charPtrAnnot', [
+ 'char*',
+ 'fruit::Annotated<Annotation1, char*>',
+])
+def test_register_constructor_does_not_exist_error(charPtrAnnot):
+ source = '''
+ struct X {
+ X(int*) {}
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X(charPtrAnnot)>();
+ }
+ '''
+ expect_compile_error(
+ 'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>',
+ 'contains an Inject typedef but it.s not constructible with the specified types',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('charPtrAnnot', [
+ 'char*',
+ 'fruit::Annotated<Annotation1, char*>',
+])
+def test_autoinject_constructor_does_not_exist_error(charPtrAnnot):
+ source = '''
+ struct X {
+ using Inject = X(charPtrAnnot);
+ X(int*) {}
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>',
+ 'contains an Inject typedef but it.s not constructible with the specified types',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_autoinject_abstract_class_error():
+ source = '''
+ struct X {
+ using Inject = fruit::Annotated<Annotation1, X>();
+
+ virtual void scale() = 0;
+ // Note: here we "forgot" to implement scale() (on purpose, for this test) so X is an abstract class.
+ };
+
+ fruit::Component<fruit::Annotated<Annotation1, X>> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'CannotConstructAbstractClassError<X>',
+ 'The specified class can.t be constructed because it.s an abstract class.',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('WithAnnotation', [
+ 'WithNoAnnotation',
+ 'WithAnnotation1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'const Y',
+ 'Y*',
+ 'const Y*',
+ 'Y&',
+ 'const Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+ 'fruit::Provider<const Y>',
+])
+def test_register_constructor_with_param_success(WithAnnotation, YVariant):
+ source = '''
+ struct Y {};
+ struct X {
+ X(YVariant) {
+ }
+ };
+
+ fruit::Component<WithAnnotation<Y>> getYComponent() {
+ return fruit::createComponent()
+ .registerConstructor<WithAnnotation<Y>()>();
+ }
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerConstructor<X(WithAnnotation<YVariant>)>();
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnotation', [
+ 'WithNoAnnotation',
+ 'WithAnnotation1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'const Y',
+ 'const Y*',
+ 'const Y&',
+ 'fruit::Provider<const Y>',
+])
+def test_register_constructor_with_param_const_binding_success(WithAnnotation, YVariant):
+ source = '''
+ struct Y {};
+ struct X {
+ X(YVariant) {
+ }
+ };
+
+ const Y y{};
+
+ fruit::Component<WithAnnotation<const Y>> getYComponent() {
+ return fruit::createComponent()
+ .bindInstance<WithAnnotation<Y>, Y>(y);
+ }
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerConstructor<X(WithAnnotation<YVariant>)>();
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnotation,YAnnotRegex', [
+ ('WithNoAnnotation', 'Y'),
+ ('WithAnnotation1', 'fruit::Annotated<Annotation1,Y>'),
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_register_constructor_with_param_error_nonconst_param_required(WithAnnotation, YAnnotRegex, YVariant):
+ source = '''
+ struct Y {};
+ struct X {
+ X(YVariant);
+ };
+
+ fruit::Component<WithAnnotation<const Y>> getYComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerConstructor<X(WithAnnotation<YVariant>)>();
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnotation,YAnnotRegex', [
+ ('WithNoAnnotation', 'Y'),
+ ('WithAnnotation1', 'fruit::Annotated<Annotation1, Y>'),
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_register_constructor_with_param_error_nonconst_param_required_install_after(WithAnnotation, YAnnotRegex, YVariant):
+ source = '''
+ struct Y {};
+ struct X {
+ X(YVariant);
+ };
+
+ fruit::Component<WithAnnotation<const Y>> getYComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X(WithAnnotation<YVariant>)>()
+ .install(getYComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_constructor_requiring_nonconst_then_requiring_const_ok():
+ source = '''
+ struct X {};
+
+ struct Y {
+ Y(X&) {}
+ };
+
+ struct Z {
+ Z(const X&) {}
+ };
+
+ fruit::Component<Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerConstructor<Y(X&)>()
+ .registerConstructor<Z(const X&)>()
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<Y, Z> injector(getRootComponent);
+ injector.get<Y>();
+ injector.get<Z>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_constructor_requiring_nonconst_then_requiring_const_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+
+ struct Y {
+ Y(X&) {}
+ };
+
+ struct Z {
+ Z(const X&) {}
+ };
+
+ fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerConstructor<Y(X&)>()
+ .registerConstructor<Z(const X&)>();
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_constructor_requiring_const_then_requiring_nonconst_ok():
+ source = '''
+ struct X {};
+
+ struct Y {
+ Y(const X&) {}
+ };
+
+ struct Z {
+ Z(X&) {}
+ };
+
+ fruit::Component<Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerConstructor<Y(const X&)>()
+ .registerConstructor<Z(X&)>()
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<Y, Z> injector(getRootComponent);
+ injector.get<Y>();
+ injector.get<Z>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_constructor_requiring_const_then_requiring_nonconst_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+
+ struct Y {
+ Y(const X&) {}
+ };
+
+ struct Z {
+ Z(X&) {}
+ };
+
+ fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerConstructor<Y(const X&)>()
+ .registerConstructor<Z(X&)>();
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('YVariant,YVariantRegex', [
+ ('Y**', r'Y\*\*'),
+ ('std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
+ ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
+ ('Y*&', r'Y\*&'),
+ ('Y(*)()', r'Y(\((__cdecl)?\*\))?\((void)?\)'),
+ ('fruit::Annotated<Annotation1, Y**>', r'Y\*\*'),
+])
+def test_register_constructor_with_param_error_type_not_injectable(YVariant, YVariantRegex):
+ source = '''
+ struct Y {};
+ struct X {
+ X(YVariant);
+ };
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X(YVariant)>();
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<YVariantRegex>',
+ 'The type T is not injectable.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_register_factory.py b/tests/test_register_factory.py
new file mode 100755
index 0000000..02d1169
--- /dev/null
+++ b/tests/test_register_factory.py
@@ -0,0 +1,2320 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+from fruit_test_config import CXX_COMPILER_NAME
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Scaler;
+ struct ScalerImpl;
+
+ struct Annotation1 {};
+ using ScalerAnnot1 = fruit::Annotated<Annotation1, Scaler>;
+ using ScalerImplAnnot1 = fruit::Annotated<Annotation1, ScalerImpl>;
+
+ struct Annotation2 {};
+ using ScalerAnnot2 = fruit::Annotated<Annotation2, Scaler>;
+ using ScalerImplAnnot2 = fruit::Annotated<Annotation2, ScalerImpl>;
+
+ template <typename T>
+ using WithNoAnnotation = T;
+
+ template <typename T>
+ using WithAnnotation1 = fruit::Annotated<Annotation1, T>;
+ '''
+
+@pytest.mark.parametrize('XFactoryAnnot', [
+ 'std::function<X()>',
+ 'fruit::Annotated<Annotation1, std::function<X()>>',
+])
+def test_register_factory_success_no_params_autoinject(XFactoryAnnot):
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ fruit::Component<XFactoryAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XFactoryAnnot> injector(getComponent);
+ injector.get<XFactoryAnnot>()();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtrAnnot,XPtrFactoryAnnot', [
+ ('X()', 'X', 'std::function<X()>'),
+ ('X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, std::function<X()>>'),
+ ('std::unique_ptr<X>()', 'std::unique_ptr<X>', 'std::function<std::unique_ptr<X>()>'),
+ ('std::unique_ptr<X>()', 'fruit::Annotated<Annotation1, std::unique_ptr<X>>', 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<X>()>>'),
+])
+def test_register_factory_success_no_params(ConstructX, XPtrAnnot, XPtrFactoryAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<XPtrFactoryAnnot> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<XPtrAnnot()>([](){return ConstructX;});
+ }
+
+ int main() {
+ fruit::Injector<XPtrFactoryAnnot> injector(getComponent);
+ injector.get<XPtrFactoryAnnot>()();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('MaybeConst', [
+ '',
+ 'const',
+])
+def test_register_factory_autoinject_success(MaybeConst):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ INJECT(ScalerImpl(ASSISTED(double) factor))
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<MaybeConst ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImpl>();
+ }
+
+ int main() {
+ fruit::Injector<MaybeConst ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_autoinject_abstract_class_with_no_virtual_destructor_error():
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ public:
+ INJECT(ScalerImpl(ASSISTED(double))) {
+ }
+
+ double scale(double x) override {
+ return x;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImpl>();
+ }
+ '''
+ expect_compile_error(
+ 'FactoryBindingForUniquePtrOfClassWithNoVirtualDestructorError<std::function<std::unique_ptr<Scaler(,std::default_delete<Scaler>)?>\(double\)>,std::function<std::unique_ptr<ScalerImpl(,std::default_delete<ScalerImpl>)?>\(double\)>>',
+ 'Fruit was trying to bind BaseFactory to DerivedFactory but the return type of BaseFactory is a std::unique_ptr of a class with no virtual destructor',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_autoinject_non_abstract_class_with_no_virtual_destructor_error():
+ source = '''
+ struct Scaler {
+ };
+
+ struct ScalerImpl : public Scaler {
+ public:
+ INJECT(ScalerImpl(ASSISTED(double))) {
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImpl>();
+ }
+ '''
+ expect_compile_error(
+ 'FactoryBindingForUniquePtrOfClassWithNoVirtualDestructorError<std::function<std::unique_ptr<Scaler(,std::default_delete<Scaler>)?>\(double\)>,std::function<std::unique_ptr<ScalerImpl(,std::default_delete<ScalerImpl>)?>\(double\)>>',
+ 'Fruit was trying to bind BaseFactory to DerivedFactory but the return type of BaseFactory is a std::unique_ptr of a class with no virtual destructor',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerFactoryAnnot,MaybeConstScalerFactoryAnnot', [
+ ('Scaler',
+ 'std::function<std::unique_ptr<Scaler>(double)>',
+ 'std::function<std::unique_ptr<Scaler>(double)>'),
+ ('Scaler',
+ 'std::function<std::unique_ptr<Scaler>(double)>',
+ 'const std::function<std::unique_ptr<Scaler>(double)>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation1, const std::function<std::unique_ptr<Scaler>(double)>>'),
+])
+def test_autoinject(ScalerAnnot, ScalerFactoryAnnot, MaybeConstScalerFactoryAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ INJECT(ScalerImpl(ASSISTED(double) factor))
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<MaybeConstScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot, ScalerImpl>();
+ }
+
+ int main() {
+ fruit::Injector<MaybeConstScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('MaybeConst', [
+ '',
+ 'const',
+])
+def test_autoinject_returning_value(MaybeConst):
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ INJECT(Scaler(ASSISTED(double) factor, X))
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<MaybeConst ScalerFactory> getScalerComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<MaybeConst ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot,ScalerFactoryAnnot,ScalerImplFactoryAnnotRegex', [
+ ('Scaler',
+ 'ScalerImpl',
+ 'std::function<std::unique_ptr<Scaler>(double)>',
+ 'std::function<std::unique_ptr<ScalerImpl(,std::default_delete<ScalerImpl>)?>\(double\)>',
+ ),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation2,std::function<std::unique_ptr<ScalerImpl(,std::default_delete<ScalerImpl>)?>\(double\)>>',
+ ),
+])
+def test_autoinject_error_abstract_class(ScalerAnnot, ScalerImplAnnot, ScalerFactoryAnnot, ScalerImplFactoryAnnotRegex):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ // Note: here we "forgot" to implement scale() (on purpose, for this test) so ScalerImpl is an abstract class.
+ };
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot, ScalerImplAnnot>();
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundForAbstractClassError<ScalerImplFactoryAnnotRegex,ScalerImpl>',
+ 'No explicit binding was found for T, and note that C is an abstract class',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_autoinject_nonmovable_ok():
+ source = '''
+ struct I {
+ virtual ~I() = default;
+ };
+
+ struct C : public I {
+ INJECT(C()) = default;
+
+ C(const C&) = delete;
+ C(C&&) = delete;
+ C& operator=(const C&) = delete;
+ C& operator=(C&&) = delete;
+ };
+
+ using IFactory = std::function<std::unique_ptr<I>()>;
+
+ fruit::Component<IFactory> getIFactory() {
+ return fruit::createComponent()
+ .bind<I, C>();
+ }
+
+ int main() {
+ fruit::Injector<IFactory> injector(getIFactory);
+ IFactory iFactory(injector);
+ std::unique_ptr<I> i = iFactory();
+ (void)i;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_2_assisted_params():
+ source = '''
+ struct Foo {
+ Foo(int x, float y) {
+ (void)x;
+ (void)y;
+ }
+ };
+
+ using FooFactory = std::function<Foo(int, float)>;
+
+ fruit::Component<FooFactory> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<Foo(fruit::Assisted<int>, fruit::Assisted<float>)>(
+ [](int x, float y) {
+ return Foo(x, y);
+ });
+ }
+
+ int main() {
+ fruit::Injector<FooFactory> injector(getComponent);
+ FooFactory fooFactory(injector);
+ Foo foo = fooFactory(1, 2.3f);
+ (void)foo;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_2_assisted_params_returning_value():
+ source = '''
+ struct Foo {
+ Foo(int x, float y) {
+ (void)x;
+ (void)y;
+ }
+ };
+
+ using FooFactory = std::function<Foo(int, float)>;
+
+ fruit::Component<FooFactory> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<Foo(fruit::Assisted<int>, fruit::Assisted<float>)>(
+ [](int x, float y) {
+ return Foo(x, y);
+ });
+ }
+
+ int main() {
+ fruit::Injector<FooFactory> injector(getComponent);
+ FooFactory fooFactory(injector);
+ Foo foo = fooFactory(1, 2.3f);
+ (void)foo;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_instances_bound_to_assisted_params():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ struct Foo {
+ Foo(X x, Y y) {
+ (void)x;
+ (void)y;
+ }
+ };
+
+ using FooFactory = std::function<Foo()>;
+
+ fruit::Component<FooFactory> getComponent() {
+ static X x = X();
+ static Y y = Y();
+ return fruit::createComponent()
+ .bindInstance(x)
+ .bindInstance(y)
+ .registerFactory<Foo(X, Y)>(
+ [](X x, Y y) {
+ return Foo(x, y);
+ });
+ }
+
+
+ int main() {
+ fruit::Injector<FooFactory> injector(getComponent);
+ FooFactory fooFactory(injector);
+ Foo foo = fooFactory();
+ (void)foo;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_2_assisted_params_plus_nonassisted_params():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ struct Foo {
+ Foo(X, Y, int, float, Z) {
+ }
+ };
+
+ using FooPtrFactory = std::function<std::unique_ptr<Foo>(int, float)>;
+
+ fruit::Component<FooPtrFactory> getComponent() {
+ static X x = X();
+ static Y y = Y();
+ static Z z = Z();
+ return fruit::createComponent()
+ .bindInstance(x)
+ .bindInstance(y)
+ .bindInstance(z)
+ .registerFactory<std::unique_ptr<Foo>(X, Y, fruit::Assisted<int>, fruit::Assisted<float>, Z)>(
+ [](X x, Y y, int n, float a, Z z) {
+ return std::unique_ptr<Foo>(new Foo(x, y, n, a, z));
+ });
+ }
+
+ int main() {
+ fruit::Injector<FooPtrFactory> injector(getComponent);
+ FooPtrFactory fooPtrFactory(injector);
+ std::unique_ptr<Foo> foo = fooPtrFactory(1, 3.4f);
+ (void)foo;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_2_assisted_params_plus_nonassisted_params_returning_value():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ struct Foo {
+ Foo(X, Y, int, float, Z) {
+ }
+ };
+
+ using FooFactory = std::function<Foo(int, float)>;
+
+ fruit::Component<FooFactory> getComponent() {
+ static X x = X();
+ static Y y = Y();
+ static Z z = Z();
+ return fruit::createComponent()
+ .bindInstance(x)
+ .bindInstance(y)
+ .bindInstance(z)
+ .registerFactory<Foo(X, Y, fruit::Assisted<int>, fruit::Assisted<float>, Z)>(
+ [](X x, Y y, int n, float a, Z z) {
+ return Foo(x, y, n, a, z);
+ });
+ }
+
+ int main() {
+ fruit::Injector<FooFactory> injector(getComponent);
+ FooFactory fooFactory(injector);
+ Foo foo = fooFactory(1, 3.4f);
+ (void)foo;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_mixed_assisted_and_injected_params():
+ source = '''
+ struct X {};
+ struct Y {};
+
+ struct Foo {
+ Foo(int, float, X, Y, double) {
+ }
+ };
+
+ using FooFactory = std::function<Foo(int, float, double)>;
+
+ fruit::Component<FooFactory> getComponent() {
+ static X x = X();
+ static Y y = Y();
+ return fruit::createComponent()
+ .bindInstance(x)
+ .bindInstance(y)
+ .registerFactory<Foo(fruit::Assisted<int>, fruit::Assisted<float>, X, Y, fruit::Assisted<double>)>(
+ [](int n, float a, X x, Y y, double d) {
+ return Foo(n, a, x, y, d);
+ });
+ }
+
+ int main() {
+ fruit::Injector<FooFactory> injector(getComponent);
+ FooFactory fooFactory(injector);
+ Foo foo = fooFactory(1, 3.4f, 3.456);
+ (void)foo;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_annotation_in_signature_return_type():
+ source = '''
+ struct X {
+ using Inject = fruit::Annotated<Annotation1, X>();
+ };
+
+ fruit::Component<fruit::Annotated<Annotation1, std::function<std::unique_ptr<X>()>>> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'InjectTypedefWithAnnotationError<X>',
+ 'C::Inject is a signature that returns an annotated type',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_annotation_in_signature_return_type_returning_value():
+ source = '''
+ struct X {
+ using Inject = fruit::Annotated<Annotation1, X>();
+ };
+
+ fruit::Component<fruit::Annotated<Annotation1, std::function<X()>>> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'InjectTypedefWithAnnotationError<X>',
+ 'C::Inject is a signature that returns an annotated type',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_from_provider_simple():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor, X)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .registerProvider([](X x) {
+ return std::function<std::unique_ptr<ScalerImpl>(double)>([x](double n){
+ return std::unique_ptr<ScalerImpl>(new ScalerImpl(n, x));
+ });
+ })
+ .bind<Scaler, ScalerImpl>();
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerFactoryAnnot,MaybeConstScalerFactoryAnnot,ScalerImplAnnot,ScalerImplFactoryAnnot', [
+ ('Scaler',
+ 'std::function<std::unique_ptr<Scaler>(double)>',
+ 'std::function<std::unique_ptr<Scaler>(double)>',
+ 'ScalerImpl',
+ 'std::function<std::unique_ptr<ScalerImpl>(double)>'),
+ ('Scaler',
+ 'std::function<std::unique_ptr<Scaler>(double)>',
+ 'const std::function<std::unique_ptr<Scaler>(double)>',
+ 'ScalerImpl',
+ 'std::function<std::unique_ptr<ScalerImpl>(double)>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation2, std::function<std::unique_ptr<ScalerImpl>(double)>>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation1, const std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation2, std::function<std::unique_ptr<ScalerImpl>(double)>>'),
+])
+def test_autoinject_from_provider(ScalerAnnot, ScalerFactoryAnnot, MaybeConstScalerFactoryAnnot, ScalerImplAnnot, ScalerImplFactoryAnnot):
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor, X)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+ using ScalerImplFactory = std::function<std::unique_ptr<ScalerImpl>(double)>;
+
+ fruit::Component<MaybeConstScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .registerProvider<ScalerImplFactoryAnnot(X)>([](X x) {
+ return std::function<std::unique_ptr<ScalerImpl>(double)>([x](double n){
+ return std::unique_ptr<ScalerImpl>(new ScalerImpl(n, x));
+ });
+ })
+ .bind<ScalerAnnot, ScalerImplAnnot>();
+ }
+
+ int main() {
+ fruit::Injector<MaybeConstScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerFactoryAnnot', [
+ 'ScalerFactory',
+ 'fruit::Annotated<Annotation1, ScalerFactory>',
+])
+def test_autoinject_from_provider_returning_value(ScalerFactoryAnnot):
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ Scaler(double factor, X)
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .registerProvider<ScalerFactoryAnnot(X)>([](X x) {
+ return std::function<Scaler(double)>([x](double n){
+ return Scaler(n, x);
+ });
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('MaybeConst', [
+ '',
+ 'const',
+])
+@pytest.mark.parametrize('X_ANNOT', [
+ 'X',
+ 'ANNOTATED(Annotation1, X)',
+])
+def test_autoinject_with_binding(MaybeConst, X_ANNOT):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ INJECT(ScalerImpl(ASSISTED(double) factor, X_ANNOT x))
+ : factor(factor) {
+ (void)x;
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<MaybeConst ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImpl>();
+ }
+
+ int main() {
+ fruit::Injector<MaybeConst ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('MaybeConst', [
+ '',
+ 'const',
+])
+@pytest.mark.parametrize('X_ANNOT', [
+ 'X',
+ 'ANNOTATED(Annotation1, X)',
+])
+def test_autoinject_with_binding_returning_value(MaybeConst, X_ANNOT):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ INJECT(Scaler(ASSISTED(double) factor, X_ANNOT x))
+ : factor(factor) {
+ (void)x;
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<MaybeConst ScalerFactory> getScalerComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<MaybeConst ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_autoinject_with_binding2():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ INJECT(ScalerImpl(X, ASSISTED(double) factor))
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImpl>();
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_autoinject_with_binding2_returning_value():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ INJECT(Scaler(X, ASSISTED(double) factor))
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot,ScalerFactoryAnnot', [
+ ('Scaler',
+ 'ScalerImpl',
+ 'std::function<std::unique_ptr<Scaler>(double)>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>'),
+])
+def test_register_factory_success(ScalerAnnot, ScalerImplAnnot, ScalerFactoryAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot, ScalerImplAnnot>()
+ .registerFactory<ScalerImplAnnot(fruit::Assisted<double>)>([](double factor) { return ScalerImpl(factor); });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_with_annotation_returning_value():
+ source = '''
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ Scaler(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+ using ScalerFactoryAnnot1 = fruit::Annotated<Annotation1, ScalerFactory>;
+
+ fruit::Component<ScalerFactoryAnnot1> getScalerComponent() {
+ return fruit::createComponent()
+ .registerFactory<ScalerAnnot1(fruit::Assisted<double>)>(
+ [](double factor) {
+ return Scaler(factor);
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot1> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot1>();
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_factory_with_different_annotation():
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+ using ScalerFactoryAnnot1 = fruit::Annotated<Annotation1, ScalerFactory>;
+
+ fruit::Component<ScalerFactoryAnnot1> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot1, ScalerImplAnnot2>()
+ .registerFactory<ScalerImplAnnot2(fruit::Assisted<double>)>(
+ [](double factor) {
+ return ScalerImpl(factor);
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot1> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot1>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot,ScalerFactoryAnnot', [
+ ('Scaler',
+ 'ScalerImpl',
+ 'std::function<std::unique_ptr<Scaler>(double, double)>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double, double)>>'),
+])
+def test_register_factory_2arg_success(ScalerAnnot, ScalerImplAnnot, ScalerFactoryAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double, double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot, ScalerImplAnnot>()
+ .registerFactory<ScalerImplAnnot(fruit::Assisted<double>, fruit::Assisted<double>)>(
+ [](double factor, double) {
+ return ScalerImpl(factor);
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1, 34.2);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_with_different_annotation_error():
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+ using ScalerFactoryAnnot1 = fruit::Annotated<Annotation1, ScalerFactory>;
+
+ fruit::Component<ScalerFactoryAnnot1> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot1, ScalerImplAnnot1>()
+ .registerFactory<ScalerImplAnnot2(fruit::Assisted<double>)>([](double factor) { return ScalerImpl(factor); });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot1> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot1>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_compile_error(
+ 'NoBindingFoundError<fruit::Annotated<Annotation1,std::function<std::unique_ptr<ScalerImpl(,std::default_delete<ScalerImpl>)?>\(double\)>>>',
+ '',
+ COMMON_DEFINITIONS,
+ source)
+
+
+def test_register_factory_dep_on_provider():
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImpl>()
+ .registerProvider([](){return 23;})
+ .registerFactory<ScalerImpl(fruit::Assisted<double>, fruit::Provider<int>)>(
+ [](double factor, fruit::Provider<int> provider) {
+ return ScalerImpl(factor * provider.get<int>());
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_factory_dep_on_provider_returning_value():
+ source = '''
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ Scaler(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .registerProvider([](){return 23;})
+ .registerFactory<Scaler(fruit::Assisted<double>, fruit::Provider<int>)>(
+ [](double factor, fruit::Provider<int> provider) {
+ return Scaler(factor * provider.get<int>());
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_factory_error_abstract_class():
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ // Note: here we "forgot" to implement scale() (on purpose, for this test) so ScalerImpl is an abstract class.
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImpl>()
+ .registerFactory<fruit::Annotated<Annotation1, ScalerImpl>(fruit::Assisted<double>)>([](double) { return (ScalerImpl*)nullptr; });
+ }
+ '''
+ expect_compile_error(
+ 'CannotConstructAbstractClassError<ScalerImpl>',
+ 'The specified class can.t be constructed because it.s an abstract class.',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_factory_error_not_function():
+ source = '''
+ struct X {
+ X(int) {}
+ };
+
+ fruit::Component<std::function<X()>> getComponent() {
+ int n = 3;
+ return fruit::createComponent()
+ .registerFactory<X()>([=]{return X(n);});
+ }
+ '''
+ expect_compile_error(
+ 'LambdaWithCapturesError<.*>',
+ 'Only lambdas with no captures are supported',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot,ScalerImplPtrAnnot,ScalerFactoryAnnot,ScalerImplFactorySignatureAnnotRegex', [
+ ('Scaler',
+ 'ScalerImpl',
+ 'ScalerImpl*',
+ 'std::function<std::unique_ptr<Scaler>(double)>',
+ 'ScalerImpl\*\(fruit::Assisted<double>\)'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation2, ScalerImpl*>',
+ 'fruit::Annotated<Annotation2, std::function<std::unique_ptr<Scaler>(double)>>',
+ 'fruit::Annotated<Annotation2,ScalerImpl\*>\(fruit::Assisted<double>\)')
+])
+def test_register_factory_for_pointer(ScalerAnnot, ScalerImplAnnot, ScalerImplPtrAnnot, ScalerFactoryAnnot, ScalerImplFactorySignatureAnnotRegex):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot, ScalerImplAnnot>()
+ .registerFactory<ScalerImplPtrAnnot(fruit::Assisted<double>)>([](double factor) { return new ScalerImpl(factor); });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_compile_error(
+ 'FactoryReturningPointerError<ScalerImplFactorySignatureAnnotRegex>',
+ 'The specified factory returns a pointer. This is not supported',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerPtrAnnot,ScalerFactoryAnnot,ScalerFactorySignatureAnnotRegex', [
+ ('Scaler*',
+ 'std::function<Scaler(double)>',
+ 'Scaler\*\(fruit::Assisted<double>\)'),
+ ('fruit::Annotated<Annotation1, Scaler*>',
+ 'fruit::Annotated<Annotation1, std::function<Scaler(double)>>',
+ 'fruit::Annotated<Annotation1,Scaler\*>\(fruit::Assisted<double>\)'),
+])
+def test_register_factory_for_pointer_returning_value(ScalerPtrAnnot, ScalerFactoryAnnot, ScalerFactorySignatureAnnotRegex):
+ source = '''
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ Scaler(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .registerFactory<ScalerPtrAnnot(fruit::Assisted<double>)>([](double factor) { return new Scaler(factor); });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_compile_error(
+ 'FactoryReturningPointerError<ScalerFactorySignatureAnnotRegex>',
+ 'The specified factory returns a pointer. This is not supported',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot,ScalerImplPtrAnnot,ScalerFactoryAnnot', [
+ ('Scaler',
+ 'ScalerImpl',
+ 'std::unique_ptr<ScalerImpl>',
+ 'std::function<std::unique_ptr<Scaler>(double)>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation2, std::unique_ptr<ScalerImpl>>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>'),
+])
+def test_register_factory_for_unique_pointer(ScalerAnnot, ScalerImplAnnot, ScalerImplPtrAnnot, ScalerFactoryAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot, ScalerImplAnnot>()
+ .registerFactory<ScalerImplPtrAnnot(fruit::Assisted<double>)>(
+ [](double factor) {
+ return std::unique_ptr<ScalerImpl>(new ScalerImpl(factor));
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot,ScalerImplPtrAnnot,ScalerFactoryAnnot', [
+ ('Scaler',
+ 'ScalerImpl',
+ 'std::unique_ptr<ScalerImpl>',
+ 'std::function<std::unique_ptr<Scaler>(double)>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation2, ScalerImpl>',
+ 'fruit::Annotated<Annotation2, std::unique_ptr<ScalerImpl>>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<Scaler>(double)>>'),
+])
+def test_register_factory_for_unique_pointer_returning_invalid_unique_ptr_ok(ScalerAnnot, ScalerImplAnnot, ScalerImplPtrAnnot, ScalerFactoryAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ virtual ~Scaler() = default;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<ScalerAnnot, ScalerImplAnnot>()
+ .registerFactory<ScalerImplPtrAnnot(fruit::Assisted<double>)>(
+ [](double) {
+ return std::unique_ptr<ScalerImpl>(nullptr);
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ Assert(scaler.get() == nullptr);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerAnnot,ScalerFactoryAnnot', [
+ ('Scaler',
+ 'std::function<Scaler(double)>'),
+ ('fruit::Annotated<Annotation1, Scaler>',
+ 'fruit::Annotated<Annotation1, std::function<Scaler(double)>>'),
+])
+def test_register_factory_for_unique_pointer_returning_value(ScalerAnnot, ScalerFactoryAnnot):
+ source = '''
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ Scaler(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<ScalerFactoryAnnot> getScalerComponent() {
+ return fruit::createComponent()
+ .registerFactory<ScalerAnnot(fruit::Assisted<double>)>(
+ [](double factor) {
+ return Scaler(factor);
+ });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactoryAnnot> injector(getScalerComponent);
+ ScalerFactory scalerFactory = injector.get<ScalerFactoryAnnot>();
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ScalerImplAnnot', [
+ 'ScalerImpl',
+ 'fruit::Annotated<Annotation1, ScalerImpl>',
+])
+def test_register_factory_inconsistent_signature(ScalerImplAnnot):
+ source = '''
+ struct Scaler {
+ virtual double scale(double x) = 0;
+ };
+
+ struct ScalerImpl : public Scaler {
+ private:
+ double factor;
+
+ public:
+ ScalerImpl(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) override {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .bind<Scaler, ScalerImplAnnot>()
+ .registerFactory<ScalerImplAnnot(fruit::Assisted<double>)>([](float factor) { return ScalerImpl(factor); });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
+ std::cout << scaler->scale(3) << std::endl;
+ }
+ '''
+ expect_compile_error(
+ 'FunctorSignatureDoesNotMatchError<ScalerImpl\(double\),ScalerImpl\(float\)>',
+ 'Unexpected functor signature',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_inconsistent_signature_returning_value():
+ source = '''
+ struct Scaler {
+ private:
+ double factor;
+
+ public:
+ Scaler(double factor)
+ : factor(factor) {
+ }
+
+ double scale(double x) {
+ return x * factor;
+ }
+ };
+
+ using ScalerFactory = std::function<Scaler(double)>;
+
+ fruit::Component<ScalerFactory> getScalerComponent() {
+ return fruit::createComponent()
+ .registerFactory<Scaler(fruit::Assisted<double>)>([](float factor) { return Scaler(factor); });
+ }
+
+ int main() {
+ fruit::Injector<ScalerFactory> injector(getScalerComponent);
+ ScalerFactory scalerFactory(injector);
+ Scaler scaler = scalerFactory(12.1);
+ std::cout << scaler.scale(3) << std::endl;
+ }
+ '''
+ expect_compile_error(
+ 'FunctorSignatureDoesNotMatchError<Scaler\(double\),Scaler\(float\)>',
+ 'Unexpected functor signature',
+ COMMON_DEFINITIONS,
+ source)
+
+def test_register_factory_nonmovable_ok():
+ source = '''
+ struct C {
+ INJECT(C()) = default;
+
+ C(const C&) = delete;
+ C(C&&) = delete;
+ C& operator=(const C&) = delete;
+ C& operator=(C&&) = delete;
+ };
+
+ using CFactory = std::function<std::unique_ptr<C>()>;
+
+ fruit::Component<CFactory> getCFactory() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<CFactory> injector(getCFactory);
+ CFactory cFactory(injector);
+ std::unique_ptr<C> c = cFactory();
+ (void)c;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+# TODO: this might not be the best error message, maybe we should ignore the constructor entirely in the message,
+# or mention that there are other ways to satisfy that dependency.
+@pytest.mark.parametrize('XAnnot,XFactoryAnnot', [
+ ('X',
+ 'std::function<X(int)>'),
+ ('fruit::Annotated<Annotation1, X>',
+ 'fruit::Annotated<Annotation1, std::function<X(int)>>'),
+])
+def test_register_factory_not_existing_constructor1(XAnnot, XFactoryAnnot):
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ fruit::Component<XFactoryAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'FunctorSignatureDoesNotMatchError<XAnnot\(int\),XAnnot\((void)?\)>',
+ 'Unexpected functor signature',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+# TODO: this might not be the best error message, maybe we should ignore the constructor entirely in the message,
+# or mention that there are other ways to satisfy that dependency.
+@pytest.mark.parametrize('XIntFactoryAnnot,XIntFactoryAnnotRegex,XVoidFactoryAnnotRegex', [
+ ('std::function<std::unique_ptr<X>(int)>',
+ 'std::unique_ptr<X(,std::default_delete<X>)?>\(int\)',
+ 'std::unique_ptr<X(,std::default_delete<X>)?>\((void)?\)'),
+ ('fruit::Annotated<Annotation1, std::function<std::unique_ptr<X>(int)>>',
+ 'fruit::Annotated<Annotation1,std::unique_ptr<X(,std::default_delete<X>)?>>\(int\)',
+ 'fruit::Annotated<Annotation1,std::unique_ptr<X(,std::default_delete<X>)?>>\((void)?\)')
+])
+def test_register_factory_not_existing_constructor2(XIntFactoryAnnot, XIntFactoryAnnotRegex, XVoidFactoryAnnotRegex):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<XIntFactoryAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'FunctorSignatureDoesNotMatchError<XIntFactoryAnnotRegex,XVoidFactoryAnnotRegex>',
+ 'Unexpected functor signature',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+# TODO: this might not be the best error message, maybe we should ignore the constructor entirely in the message,
+# or mention that there are other ways to satisfy that dependency.
+@pytest.mark.parametrize('XAnnot,XFactoryAnnot', [
+ ('X',
+ 'std::function<X(int)>'),
+ ('fruit::Annotated<Annotation1, X>',
+ 'fruit::Annotated<Annotation1, std::function<X(int)>>'),
+])
+def test_register_factory_not_existing_constructor2_returning_value(XAnnot, XFactoryAnnot):
+ source = '''
+ struct X {
+ using Inject = X();
+ };
+
+ fruit::Component<XFactoryAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+ '''
+ expect_compile_error(
+ 'FunctorSignatureDoesNotMatchError<XAnnot\(int\), XAnnot\((void)?\)>',
+ 'Unexpected functor signature',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+
+@pytest.mark.parametrize('XFactoryAnnot', [
+ 'std::function<X()>',
+ 'fruit::Annotated<Annotation1, std::function<X()>>',
+])
+def test_register_factory_success_factory_movable_only_implicit(XFactoryAnnot):
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ X(X&&) = default;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<XFactoryAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XFactoryAnnot> injector(getComponent);
+ injector.get<XFactoryAnnot>()();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XPtrAnnot,ConstructX,XPtrFactoryAnnot', [
+ ('X', 'X()', 'std::function<X()>'),
+ ('fruit::Annotated<Annotation1, X>', 'X()', 'fruit::Annotated<Annotation1, std::function<X()>>'),
+ ('std::unique_ptr<X>', 'std::unique_ptr<X>(new X())', 'std::function<std::unique_ptr<X>()>'),
+ ('fruit::Annotated<Annotation1, std::unique_ptr<X>>', 'std::unique_ptr<X>(new X())', 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<X>()>>'),
+])
+def test_register_factory_success_factory_movable_only_explicit(XPtrAnnot, ConstructX, XPtrFactoryAnnot):
+ source = '''
+ struct X {
+ X() = default;
+ X(X&&) = default;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<XPtrFactoryAnnot> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<XPtrAnnot()>([](){return ConstructX;});
+ }
+
+ int main() {
+ fruit::Injector<XPtrFactoryAnnot> injector(getComponent);
+ injector.get<XPtrFactoryAnnot>()();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XPtrFactoryAnnot', [
+ 'std::function<std::unique_ptr<X>()>',
+ 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<X>()>>',
+])
+def test_register_factory_success_factory_not_movable_implicit(XPtrFactoryAnnot):
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ X(X&&) = delete;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<XPtrFactoryAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XPtrFactoryAnnot> injector(getComponent);
+ injector.get<XPtrFactoryAnnot>()();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XPtrAnnot,XPtrFactoryAnnot', [
+ ('std::unique_ptr<X>', 'std::function<std::unique_ptr<X>()>'),
+ ('fruit::Annotated<Annotation1, std::unique_ptr<X>>', 'fruit::Annotated<Annotation1, std::function<std::unique_ptr<X>()>>'),
+])
+def test_register_factory_success_factory_not_movable_explicit_returning_pointer(XPtrAnnot, XPtrFactoryAnnot):
+ source = '''
+ struct X {
+ X() = default;
+ X(X&&) = delete;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<XPtrFactoryAnnot> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<XPtrAnnot()>([](){return std::unique_ptr<X>(new X());});
+ }
+
+ int main() {
+ fruit::Injector<XPtrFactoryAnnot> injector(getComponent);
+ injector.get<XPtrFactoryAnnot>()();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('std::unique_ptr<X>(new X())', 'std::unique_ptr<X>'),
+])
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnotation',
+ 'WithAnnotation1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'Y*',
+ 'const Y*',
+ 'Y&',
+ 'const Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+ 'fruit::Provider<const Y>',
+])
+def test_register_factory_with_param_success(ConstructX, XPtr, WithAnnot, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<Y>> getYComponent() {
+ return fruit::createComponent()
+ .registerConstructor<WithAnnot<Y>()>();
+ }
+
+ fruit::Component<std::function<XPtr()>> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerFactory<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+
+ int main() {
+ fruit::Injector<std::function<XPtr()>> injector(getComponent);
+ XPtr x = injector.get<std::function<XPtr()>>()();
+ (void) x;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('std::unique_ptr<X>(new X())', 'std::unique_ptr<X>'),
+])
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnotation',
+ 'WithAnnotation1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'const Y*',
+ 'const Y&',
+ 'fruit::Provider<const Y>',
+])
+def test_register_factory_with_param_const_binding_success(ConstructX, XPtr, WithAnnot, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ const Y y{};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent() {
+ return fruit::createComponent()
+ .bindInstance<WithAnnot<Y>, Y>(y);
+ }
+
+ fruit::Component<std::function<XPtr()>> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerFactory<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+
+ int main() {
+ fruit::Injector<std::function<XPtr()>> injector(getComponent);
+ XPtr x = injector.get<std::function<XPtr()>>()();
+ (void) x;
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('std::unique_ptr<X>(new X())', 'std::unique_ptr<X>'),
+])
+@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
+ ('WithNoAnnotation', 'Y'),
+ ('WithAnnotation1', 'fruit::Annotated<Annotation1, Y>'),
+])
+@pytest.mark.parametrize('XFactoryResult', [
+ 'X',
+ 'std::unique_ptr<X>',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_register_factory_with_param_error_nonconst_param_required(ConstructX, XPtr, WithAnnot, YAnnotRegex, XFactoryResult, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent();
+
+ fruit::Component<std::function<XFactoryResult()>> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerFactory<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('std::unique_ptr<X>(new X())', 'std::unique_ptr<X>'),
+])
+@pytest.mark.parametrize('XFactoryResult', [
+ 'X',
+ 'std::unique_ptr<X>',
+])
+@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
+ ('WithNoAnnotation', 'Y'),
+ ('WithAnnotation1', 'fruit::Annotated<Annotation1, Y>'),
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_register_factory_with_param_error_nonconst_param_required_install_after(ConstructX, XPtr, XFactoryResult, WithAnnot, YAnnotRegex, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent();
+
+ fruit::Component<std::function<XFactoryResult()>> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; })
+ .install(getYComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_requiring_nonconst_then_requiring_const_ok():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<std::function<Y()>, std::function<Z()>> getRootComponent() {
+ return fruit::createComponent()
+ .registerFactory<Y(X&)>([](X&) { return Y();})
+ .registerFactory<Z(const X&)>([](const X&) { return Z();})
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<std::function<Y()>, std::function<Z()>> injector(getRootComponent);
+ std::function<Y()> yFactory = injector.get<std::function<Y()>>();
+ yFactory();
+ std::function<Z()> zFactory = injector.get<std::function<Z()>>();
+ zFactory();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_requiring_nonconst_then_requiring_const_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<const X>, std::function<Y()>, std::function<Z()>> getRootComponent() {
+ return fruit::createComponent()
+ .registerFactory<Y(X&)>([](X&) { return Y();})
+ .registerFactory<Z(const X&)>([](const X&) { return Z();});
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_requiring_const_then_requiring_nonconst_ok():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<std::function<Y()>, std::function<Z()>> getRootComponent() {
+ return fruit::createComponent()
+ .registerFactory<Y(const X&)>([](const X&) { return Y();})
+ .registerFactory<Z(X&)>([](X&) { return Z();})
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<std::function<Y()>, std::function<Z()>> injector(getRootComponent);
+ std::function<Y()> yFactory = injector.get<std::function<Y()>>();
+ yFactory();
+ std::function<Z()> zFactory = injector.get<std::function<Z()>>();
+ zFactory();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_requiring_const_then_requiring_nonconst_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<const X>, std::function<Y()>, std::function<Z()>> getRootComponent() {
+ return fruit::createComponent()
+ .registerFactory<Y(const X&)>([](const X&) { return Y();})
+ .registerFactory<Z(X&)>([](X&) { return Z();});
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_provider_get_error_type_unique_pointer_pointer_not_provided():
+ source = '''
+ struct X {};
+
+ void f(fruit::Provider<X> provider) {
+ provider.get<std::unique_ptr<X>*>();
+ }
+ '''
+ expect_compile_error(
+ 'TypeNotProvidedError<std::unique_ptr<X(,std::default_delete<X>)?>\*>',
+ 'Trying to get an instance of T, but it is not provided by this Provider/Injector.',
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('std::unique_ptr<X>(new X())', 'std::unique_ptr<X>'),
+])
+@pytest.mark.parametrize('XFactoryResult', [
+ 'X',
+ 'std::unique_ptr<X>',
+])
+@pytest.mark.parametrize('YVariant,YVariantRegex', [
+ ('Y**', r'Y\*\*'),
+ ('std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
+ ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
+ ('Y*&', r'Y\*&'),
+ ('Y(*)()', r'Y(\((__cdecl)?\*\))?\((void)?\)'),
+ ('fruit::Annotated<Annotation1, Y**>', r'Y\*\*'),
+])
+def test_register_factory_with_param_error_type_not_injectable(
+ ConstructX, XPtr, XFactoryResult, YVariant, YVariantRegex):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<XPtr(YVariant)>([](YVariant){ return ConstructX; });
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<YVariantRegex>',
+ 'The type T is not injectable.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_bind_nonconst_unique_ptr_factory_to_const_value_factory():
+ source = '''
+ struct X {
+ INJECT(X()) = default;
+ };
+
+ fruit::Component<const std::function<X()>> getChildComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<std::function<std::unique_ptr<X>()>> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent);
+ }
+
+ int main() {
+ fruit::Injector<std::function<std::unique_ptr<X>()>> injector(getRootComponent);
+ std::function<std::unique_ptr<X>()> xFactory(injector);
+ xFactory();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_bind_const_interface_factory_to_nonconst_implementation_factory():
+ source = '''
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+
+ struct Y : public X {
+ INJECT(Y()) = default;
+
+ void foo() override {
+ }
+ };
+
+ fruit::Component<std::function<std::unique_ptr<Y>()>> getChildComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<const std::function<std::unique_ptr<X>()>> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent)
+ .bind<X, Y>();
+ }
+
+ int main() {
+ fruit::Injector<const std::function<std::unique_ptr<X>()>> injector(getRootComponent);
+ std::function<std::unique_ptr<X>()> xFactory(injector);
+ xFactory();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_factory_bind_nonconst_interface_factory_to_const_implementation_factory():
+ source = '''
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+
+ struct Y : public X {
+ INJECT(Y()) = default;
+
+ void foo() override {
+ }
+ };
+
+ fruit::Component<const std::function<std::unique_ptr<Y>()>> getChildComponent() {
+ return fruit::createComponent();
+ }
+
+ fruit::Component<std::function<std::unique_ptr<X>()>> getRootComponent() {
+ return fruit::createComponent()
+ .install(getChildComponent)
+ .bind<X, Y>();
+ }
+
+ int main() {
+ fruit::Injector<std::function<std::unique_ptr<X>()>> injector(getRootComponent);
+ std::function<std::unique_ptr<X>()> xFactory(injector);
+ xFactory();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnotation',
+ 'WithAnnotation1',
+])
+def test_register_factory_abstract_class_ok(WithAnnot):
+ source = '''
+ struct I {
+ virtual int foo() = 0;
+ virtual ~I() = default;
+ };
+
+ struct X : public I {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<WithAnnot<std::function<std::unique_ptr<I>()>>> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<WithAnnot<std::unique_ptr<I>>()>([](){return std::unique_ptr<I>(static_cast<I*>(new X()));});
+ }
+
+ int main() {
+ fruit::Injector<WithAnnot<std::function<std::unique_ptr<I>()>>> injector(getComponent);
+
+ Assert(injector.get<WithAnnot<std::function<std::unique_ptr<I>()>>>()()->foo() == 5);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+# TODO: investigate why this doesn't fail to compile with MSVC and then re-enable it for MSVC too.
+@pytest.mark.skipif(
+ re.search('MSVC', CXX_COMPILER_NAME),
+ reason = 'This is disabled in MSVC because it compiles cleanly with the latest MSVC.')
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnotation',
+ 'WithAnnotation1',
+])
+def test_register_factory_abstract_class_with_no_virtual_destructor_error(WithAnnot):
+ source = '''
+ struct I {
+ virtual int foo() = 0;
+ };
+
+ struct X : public I {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<WithAnnot<std::function<std::unique_ptr<I>()>>> getComponent() {
+ return fruit::createComponent()
+ .registerFactory<WithAnnot<std::unique_ptr<I>>()>([](){return std::unique_ptr<I>(static_cast<I*>(new X()));});
+ }
+ '''
+ expect_compile_error(
+ 'RegisterFactoryForUniquePtrOfAbstractClassWithNoVirtualDestructorError<I>',
+ 'registerFactory\(\) was called with a lambda that returns a std::unique_ptr<T>, but T is an abstract class',
+ COMMON_DEFINITIONS,
+ source,
+ locals(),
+ ignore_warnings=True,
+ disable_error_line_number_check=True)
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_register_instance.py b/tests/test_register_instance.py
new file mode 100755
index 0000000..f449809
--- /dev/null
+++ b/tests/test_register_instance.py
@@ -0,0 +1,496 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation1 {};
+ '''
+
+def escape_regex(regex):
+ # We un-escape the space because we strip the spaces in fruit_test_common, and this would otherwise leave a
+ # stray backslash.
+ return re.escape(regex).replace('\\ ', ' ')
+
+def test_bind_instance_success():
+ source = '''
+ struct X {
+ int n;
+
+ X(int n)
+ : n(n) {
+ }
+ };
+
+ fruit::Component<X> getComponent(X* x) {
+ return fruit::createComponent()
+ .bindInstance(*x);
+ }
+
+ int main() {
+ X x(34);
+ fruit::Injector<X> injector(getComponent, &x);
+ X& x1 = injector.get<X&>();
+ Assert(&x == &x1);
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_bind_instance_annotated_success():
+ source = '''
+ struct X {
+ int n;
+
+ X(int n)
+ : n(n) {
+ }
+ };
+
+ fruit::Component<fruit::Annotated<Annotation1, X>> getComponent(X* x) {
+ return fruit::createComponent()
+ .bindInstance<fruit::Annotated<Annotation1, X>>(*x);
+ }
+
+ int main() {
+ X x(34);
+ fruit::Injector<fruit::Annotated<Annotation1, X>> injector(getComponent, &x);
+ X& x1 = injector.get<fruit::Annotated<Annotation1, X&>>();
+ Assert(&x == &x1);
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_bind_const_instance_success():
+ source = '''
+ struct X {
+ int n;
+
+ X(int n)
+ : n(n) {
+ }
+ };
+
+ fruit::Component<const X> getComponent(const X* x) {
+ return fruit::createComponent()
+ .bindInstance(*x);
+ }
+
+ const X x(34);
+
+ int main() {
+ fruit::Injector<const X> injector(getComponent, &x);
+ const X& x1 = injector.get<const X&>();
+ Assert(&x == &x1);
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_bind_const_instance_annotated_success():
+ source = '''
+ struct X {
+ int n;
+
+ X(int n)
+ : n(n) {
+ }
+ };
+
+ fruit::Component<fruit::Annotated<Annotation1, const X>> getComponent(const X* x) {
+ return fruit::createComponent()
+ .bindInstance<fruit::Annotated<Annotation1, X>>(*x);
+ }
+
+ const X x(34);
+
+ int main() {
+ fruit::Injector<fruit::Annotated<Annotation1, const X>> injector(getComponent, &x);
+ const X& x1 = injector.get<fruit::Annotated<Annotation1, const X&>>();
+ Assert(&x == &x1);
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+@pytest.mark.parametrize('XAnnot,MaybeConstXAnnot,XPtr,XPtrAnnot', [
+ ('X', 'X', 'X*', 'X*'),
+ ('X', 'const X', 'const X*', 'const X*'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'X*', 'fruit::Annotated<Annotation1, X*>'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'const X*', 'fruit::Annotated<Annotation1, const X*>'),
+])
+def test_bind_instance_two_explicit_type_arguments_success(XAnnot, MaybeConstXAnnot, XPtr, XPtrAnnot):
+ source = '''
+ struct X {
+ int n;
+
+ X(int n)
+ : n(n) {
+ }
+ };
+
+ fruit::Component<MaybeConstXAnnot> getComponent(XPtr x) {
+ return fruit::createComponent()
+ .bindInstance<XAnnot, X>(*x);
+ }
+
+ int main() {
+ X x(34);
+ fruit::Injector<MaybeConstXAnnot> injector(getComponent, &x);
+ XPtr x1 = injector.get<XPtrAnnot>();
+ Assert(&x == x1);
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source, locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_bind_instance_abstract_class_ok(XAnnot):
+ source = '''
+ struct X {
+ virtual void foo() = 0;
+ };
+
+ fruit::Component<> getComponentForInstanceHelper(X* x) {
+ return fruit::createComponent()
+ .bindInstance<XAnnot, X>(*x);
+ }
+
+ fruit::Component<XAnnot> getComponentForInstance(X* x) {
+ return fruit::createComponent()
+ .install(getComponentForInstanceHelper, x)
+ .bindInstance<XAnnot, X>(*x);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('intAnnot,intPtrAnnot', [
+ ('int', 'int*'),
+ ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
+])
+def test_bind_instance_multiple_equivalent_bindings_success(intAnnot, intPtrAnnot):
+ source = '''
+ fruit::Component<> getComponentForInstanceHelper(int* n) {
+ return fruit::createComponent()
+ .bindInstance<intAnnot, int>(*n);
+ }
+
+ fruit::Component<intAnnot> getComponentForInstance(int* n) {
+ return fruit::createComponent()
+ .install(getComponentForInstanceHelper, n)
+ .bindInstance<intAnnot, int>(*n);
+ }
+
+ int main() {
+ int n = 5;
+ fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
+ if (injector.get<intPtrAnnot>() != &n)
+ abort();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('intAnnot,intPtrAnnot', [
+ ('int', 'int*'),
+ ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
+])
+def test_bind_instance_multiple_equivalent_bindings_different_constness_success(intAnnot, intPtrAnnot):
+ source = '''
+ fruit::Component<> getComponentForInstanceHelper(const int* n) {
+ return fruit::createComponent()
+ .bindInstance<intAnnot, int>(*n);
+ }
+
+ fruit::Component<intAnnot> getComponentForInstance(int* n) {
+ return fruit::createComponent()
+ .install(getComponentForInstanceHelper, n)
+ .bindInstance<intAnnot, int>(*n);
+ }
+
+ int main() {
+ int n = 5;
+ fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
+ if (injector.get<intPtrAnnot>() != &n)
+ abort();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('intAnnot,intPtrAnnot', [
+ ('int', 'int*'),
+ ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
+])
+def test_bind_instance_multiple_equivalent_bindings_different_constness_other_order_success(intAnnot, intPtrAnnot):
+ source = '''
+ fruit::Component<> getComponentForInstanceHelper(const int* n) {
+ return fruit::createComponent()
+ .bindInstance<intAnnot, int>(*n);
+ }
+
+ fruit::Component<intAnnot> getComponentForInstance(int* n) {
+ return fruit::createComponent()
+ .bindInstance<intAnnot, int>(*n)
+ .install(getComponentForInstanceHelper, n);
+ }
+
+ int main() {
+ int n = 5;
+ fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
+ if (injector.get<intPtrAnnot>() != &n)
+ abort();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant', [
+ 'X*',
+ 'const X*',
+ 'std::shared_ptr<X>',
+])
+def test_bind_instance_non_normalized_type_error(XVariant):
+ if XVariant.endswith('&'):
+ XVariantRegexp = escape_regex(XVariant[:-1])
+ else:
+ XVariantRegexp = escape_regex(XVariant)
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent(XVariant x) {
+ return fruit::createComponent()
+ .bindInstance(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegexp,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegexp', [
+ ('const X', 'const X'),
+ ('X*', 'X\*'),
+ ('const X*', 'const X\*'),
+ ('X&', 'X&'),
+ ('const X&', 'const X&'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+])
+def test_bind_instance_non_normalized_type_error_with_annotation(XVariant, XVariantRegexp):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent(XVariant x) {
+ return fruit::createComponent()
+ .bindInstance<fruit::Annotated<Annotation1, XVariant>>(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegexp,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnotVariant,XVariant', [
+ ('const X', 'const X'),
+ ('X*', 'X*'),
+ ('const X*', 'const X*'),
+ ('X&', 'X&'),
+ ('const X&', 'const X&'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+
+ ('fruit::Annotated<Annotation1, const X>', 'const X'),
+ ('fruit::Annotated<Annotation1, X*>', 'X*'),
+ ('fruit::Annotated<Annotation1, const X*>', 'const X*'),
+ ('fruit::Annotated<Annotation1, X&>', 'X&'),
+ ('fruit::Annotated<Annotation1, const X&>', 'const X&'),
+ ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'),
+
+ ('fruit::Annotated<Annotation1, X>', 'const X'),
+ ('fruit::Annotated<Annotation1, X>', 'X*'),
+ ('fruit::Annotated<Annotation1, X>', 'const X*'),
+ ('fruit::Annotated<Annotation1, X>', 'X&'),
+ ('fruit::Annotated<Annotation1, X>', 'const X&'),
+ ('fruit::Annotated<Annotation1, X>', 'std::shared_ptr<X>'),
+])
+def test_bind_instance_non_normalized_type_error_two_explicit_type_arguments(XAnnotVariant, XVariant):
+ XVariantRegexp = escape_regex(XVariant)
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent(XVariant x) {
+ return fruit::createComponent()
+ .bindInstance<XAnnotVariant, XVariant>(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegexp,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X*', 'X\*'),
+ ('const X*', 'const X\*'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+])
+def test_register_instance_error_must_be_reference(XVariant, XVariantRegex):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponentForInstance(XVariant x) {
+ return fruit::createComponent()
+ .bindInstance(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegex,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X*', 'X\*'),
+ ('const X*', 'const X\*'),
+ ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
+])
+def test_register_instance_error_must_be_reference_with_annotation(XVariant, XVariantRegex):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponentForInstance(XVariant x) {
+ return fruit::createComponent()
+ .bindInstance<fruit::Annotated<Annotation1, X>>(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegex,X>',
+ 'A non-class type T was specified. Use C instead.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_bind_instance_mismatched_type_arguments(XAnnot):
+ source = '''
+ struct X {};
+
+ fruit::Component<> getComponent(int* n) {
+ return fruit::createComponent()
+ .bindInstance<XAnnot, int>(*n);
+ }
+ '''
+ expect_compile_error(
+ 'TypeMismatchInBindInstanceError<X,int>',
+ 'A type parameter was specified in bindInstance.. but it doesn.t match the value type',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('BaseAnnot,BasePtrAnnot', [
+ ('Base', 'Base*'),
+ ('fruit::Annotated<Annotation1, Base>', 'fruit::Annotated<Annotation1, Base*>'),
+])
+def test_bind_instance_to_subclass(BaseAnnot, BasePtrAnnot):
+ source = '''
+ struct Base {
+ virtual void f() = 0;
+ virtual ~Base() {
+ }
+ };
+
+ struct Derived : public Base {
+ void f() override {
+ }
+ };
+
+ fruit::Component<BaseAnnot> getComponent(Derived* derived) {
+ return fruit::createComponent()
+ .bindInstance<BaseAnnot, Base>(*derived);
+ }
+
+ int main() {
+ Derived derived;
+ fruit::Injector<BaseAnnot> injector(getComponent, &derived);
+ Base* base = injector.get<BasePtrAnnot>();
+ base->f();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source, locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X**', r'X\*\*'),
+ ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
+ ('X*&', r'X\*&'),
+ ('fruit::Annotated<Annotation1, X**>', r'X\*\*'),
+])
+def test_bind_instance_type_not_normalized(XVariant, XVariantRegex):
+ source = '''
+ struct X {};
+
+ using XVariantT = XVariant;
+ fruit::Component<> getComponent(XVariantT x) {
+ return fruit::createComponent()
+ .bindInstance<XVariant, XVariant>(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonClassTypeError<XVariantRegex,X>',
+ 'A non-class type T was specified.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XVariant,XVariantRegex', [
+ ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
+])
+def test_bind_instance_type_not_injectable_error(XVariant, XVariantRegex):
+ source = '''
+ struct X {};
+
+ using XVariantT = XVariant;
+ fruit::Component<> getComponent(XVariantT x) {
+ return fruit::createComponent()
+ .bindInstance<XVariant, XVariant>(x);
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<XVariantRegex>',
+ 'The type T is not injectable.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_register_provider.py b/tests/test_register_provider.py
new file mode 100755
index 0000000..d34803d
--- /dev/null
+++ b/tests/test_register_provider.py
@@ -0,0 +1,517 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation1 {};
+
+ template <typename T>
+ using WithNoAnnot = T;
+
+ template <typename T>
+ using WithAnnot1 = fruit::Annotated<Annotation1, T>;
+ '''
+
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+def test_register_provider_success(WithAnnot,ConstructX, XPtr):
+ source = '''
+ struct X : public ConstructionTracker<X> {
+ int value = 5;
+ };
+
+ fruit::Component<WithAnnot<X>> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<WithAnnot<XPtr>()>([](){return ConstructX;});
+ }
+
+ int main() {
+ fruit::Injector<WithAnnot<X>> injector(getComponent);
+
+ Assert((injector.get<WithAnnot<X >>(). value == 5));
+ Assert((injector.get<WithAnnot<X* >>()->value == 5));
+ Assert((injector.get<WithAnnot<X& >>(). value == 5));
+ Assert((injector.get<WithAnnot<const X >>(). value == 5));
+ Assert((injector.get<WithAnnot<const X* >>()->value == 5));
+ Assert((injector.get<WithAnnot<const X& >>(). value == 5));
+ Assert((injector.get<WithAnnot<std::shared_ptr<X>>>()->value == 5));
+
+ Assert(X::num_objects_constructed == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+def test_register_provider_abstract_class_ok(WithAnnot):
+ source = '''
+ struct I {
+ virtual int foo() = 0;
+ virtual ~I() = default;
+ };
+
+ struct X : public I {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<WithAnnot<I>> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
+ }
+
+ int main() {
+ fruit::Injector<WithAnnot<I>> injector(getComponent);
+
+ Assert(injector.get<WithAnnot<I*>>()->foo() == 5);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+def test_register_provider_abstract_class_with_no_virtual_destructor_error(WithAnnot):
+ source = '''
+ struct I {
+ virtual int foo() = 0;
+ };
+
+ struct X : public I {
+ int foo() override {
+ return 5;
+ }
+ };
+
+ fruit::Component<WithAnnot<I>> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
+ }
+ '''
+ expect_compile_error(
+ 'ProviderReturningPointerToAbstractClassWithNoVirtualDestructorError<I>',
+ 'registerProvider\(\) was called with a lambda that returns a pointer to T, but T is an abstract class',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX', [
+ 'X()',
+ 'new X()',
+])
+def test_register_provider_not_copyable_success(ConstructX):
+ source = '''
+ struct X {
+ X() = default;
+ X(X&&) = default;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .registerProvider([](){return ConstructX;});
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X*>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_provider_not_movable_returning_pointer_success():
+ source = '''
+ struct X {
+ X() = default;
+ X(X&&) = delete;
+ X(const X&) = delete;
+ };
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .registerProvider([](){return new X();});
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X*>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source)
+
+@pytest.mark.parametrize('XAnnot', [
+ 'X',
+ 'fruit::Annotated<Annotation1, X>',
+])
+def test_register_provider_error_not_function(XAnnot):
+ source = '''
+ struct X {
+ X(int) {}
+ };
+
+ fruit::Component<XAnnot> getComponent() {
+ int n = 3;
+ return fruit::createComponent()
+ .registerProvider<XAnnot()>([=]{return X(n);});
+ }
+ '''
+ expect_compile_error(
+ 'FunctorUsedAsProviderError<.*>',
+ 'A stateful lambda or a non-lambda functor was used as provider',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('intAnnot', [
+ 'int',
+ 'fruit::Annotated<Annotation1, int>',
+])
+def test_register_provider_error_malformed_signature(intAnnot):
+ source = '''
+ fruit::Component<intAnnot> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<intAnnot>([](){return 42;});
+ }
+ '''
+ expect_compile_error(
+ 'NotASignatureError<intAnnot>',
+ 'CandidateSignature was specified as parameter, but it.s not a signature. Signatures are of the form',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('XAnnot,XPtrAnnot,XAnnotRegex', [
+ ('X', 'X*', '(struct )?X'),
+ ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
+])
+def test_register_provider_error_returned_nullptr(XAnnot, XPtrAnnot, XAnnotRegex):
+ source = '''
+ struct X {};
+
+ fruit::Component<XAnnot> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<XPtrAnnot()>([](){return (X*)nullptr;});
+ }
+
+ int main() {
+ fruit::Injector<XAnnot> injector(getComponent);
+ injector.get<XAnnot>();
+ }
+ '''
+ expect_runtime_error(
+ 'Fatal injection error: attempting to get an instance for the type XAnnotRegex but the provider returned nullptr',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'const Y',
+ 'Y*',
+ 'const Y*',
+ 'Y&',
+ 'const Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+ 'fruit::Provider<const Y>',
+])
+def test_register_provider_with_param_success(ConstructX, XPtr, WithAnnot, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<Y>> getYComponent() {
+ return fruit::createComponent()
+ .registerConstructor<WithAnnot<Y>()>();
+ }
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot', [
+ 'WithNoAnnot',
+ 'WithAnnot1',
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y',
+ 'const Y',
+ 'const Y*',
+ 'const Y&',
+ 'fruit::Provider<const Y>',
+])
+def test_register_provider_with_param_const_binding_success(ConstructX, XPtr, WithAnnot, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ const Y y{};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent() {
+ return fruit::createComponent()
+ .bindInstance<WithAnnot<Y>, Y>(y);
+ }
+
+ fruit::Component<X> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+
+ int main() {
+ fruit::Injector<X> injector(getComponent);
+ injector.get<X>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
+ ('WithNoAnnot', 'Y'),
+ ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_register_provider_with_param_error_nonconst_param_required(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
+ ('WithNoAnnot', 'Y'),
+ ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
+])
+@pytest.mark.parametrize('YVariant', [
+ 'Y*',
+ 'Y&',
+ 'std::shared_ptr<Y>',
+ 'fruit::Provider<Y>',
+])
+def test_register_provider_with_param_error_nonconst_param_required_install_after(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<WithAnnot<const Y>> getYComponent();
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; })
+ .install(getYComponent);
+ }
+ '''
+ expect_compile_error(
+ 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
+ 'The type T was provided as constant, however one of the constructors/providers/factories in this component',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_provider_requiring_nonconst_then_requiring_const_ok():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerProvider([](X&) { return Y();})
+ .registerProvider([](const X&) { return Z();})
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<Y, Z> injector(getRootComponent);
+ injector.get<Y>();
+ injector.get<Z>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_provider_requiring_nonconst_then_requiring_const_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerProvider([](X&) { return Y();})
+ .registerProvider([](const X&) { return Z();});
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_provider_requiring_const_then_requiring_nonconst_ok():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerProvider([](const X&) { return Y();})
+ .registerProvider([](X&) { return Z();})
+ .registerConstructor<X()>();
+ }
+
+ int main() {
+ fruit::Injector<Y, Z> injector(getRootComponent);
+ injector.get<Y>();
+ injector.get<Z>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_register_provider_requiring_const_then_requiring_nonconst_declaring_const_requirement_error():
+ source = '''
+ struct X {};
+ struct Y {};
+ struct Z {};
+
+ fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
+ return fruit::createComponent()
+ .registerProvider([](const X&) { return Y();})
+ .registerProvider([](X&) { return Z();});
+ }
+ '''
+ expect_compile_error(
+ 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
+ 'The type T was declared as a const Required type in the returned Component, however',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+@pytest.mark.parametrize('ConstructX,XPtr', [
+ ('X()', 'X'),
+ ('new X()', 'X*'),
+])
+@pytest.mark.parametrize('YVariant,YVariantRegex', [
+ ('Y**', r'Y\*\*'),
+ ('std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
+ ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
+ ('Y*&', r'Y\*&'),
+ ('Y(*)()', r'Y(\((__cdecl)?\*\))?\((void)?\)'),
+])
+def test_register_provider_with_param_error_type_not_injectable(ConstructX, XPtr, YVariant, YVariantRegex):
+ source = '''
+ struct Y {};
+ struct X {};
+
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerProvider<XPtr(YVariant)>([](YVariant){ return ConstructX; });
+ }
+ '''
+ expect_compile_error(
+ 'NonInjectableTypeError<YVariantRegex>',
+ 'The type T is not injectable.',
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_required_types.py b/tests/test_required_types.py
new file mode 100755
index 0000000..ab375f6
--- /dev/null
+++ b/tests/test_required_types.py
@@ -0,0 +1,303 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X;
+
+ struct Annotation1 {};
+ using XAnnot1 = fruit::Annotated<Annotation1, X>;
+ '''
+
+def test_required_success():
+ source = '''
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+ using XFactory = std::function<std::unique_ptr<X>()>;
+ struct Y {
+ XFactory xFactory;
+
+ INJECT(Y(XFactory xFactory))
+ : xFactory(xFactory) {
+ }
+
+ void doStuff() {
+ xFactory()->foo();
+ }
+ };
+ fruit::Component<fruit::Required<XFactory>, Y> getYComponent() {
+ return fruit::createComponent();
+ }
+ struct XImpl : public X {
+ INJECT(XImpl()) = default;
+ void foo() override {}
+ };
+ fruit::Component<XFactory> getXFactoryComponent() {
+ return fruit::createComponent()
+ .bind<X, XImpl>();
+ }
+ fruit::Component<Y> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .install(getXFactoryComponent);
+ }
+ int main() {
+ fruit::Injector<Y> injector(getComponent);
+ Y* y(injector);
+ y->doStuff();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_required_annotated_success():
+ source = '''
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+ using XFactory = std::function<std::unique_ptr<X>()>;
+ using XFactoryAnnot = fruit::Annotated<Annotation1, XFactory>;
+ struct Y {
+ XFactory xFactory;
+
+ INJECT(Y(ANNOTATED(Annotation1, XFactory) xFactory))
+ : xFactory(xFactory) {
+ }
+
+ void doStuff() {
+ xFactory()->foo();
+ }
+ };
+ fruit::Component<fruit::Required<XFactoryAnnot>, Y> getYComponent() {
+ return fruit::createComponent();
+ }
+ struct XImpl : public X {
+ INJECT(XImpl()) = default;
+ void foo() override {}
+ };
+ fruit::Component<XFactoryAnnot> getXFactoryComponent() {
+ return fruit::createComponent()
+ .bind<fruit::Annotated<Annotation1, X>, fruit::Annotated<Annotation1, XImpl>>();
+ }
+ fruit::Component<Y> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .install(getXFactoryComponent);
+ }
+ int main() {
+ fruit::Injector<Y> injector(getComponent);
+ Y* y(injector);
+ y->doStuff();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_required_forward_declared_success():
+ source = '''
+ struct X;
+ using XFactory = std::function<std::unique_ptr<X>()>;
+ struct Y {
+ XFactory xFactory;
+
+ INJECT(Y(XFactory xFactory))
+ : xFactory(xFactory) {
+ }
+
+ void doStuff();
+ };
+ fruit::Component<fruit::Required<XFactory>, Y> getYComponent() {
+ return fruit::createComponent();
+ }
+ fruit::Component<XFactory> getXFactoryComponent();
+ fruit::Component<Y> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .install(getXFactoryComponent);
+ }
+ int main() {
+ fruit::Injector<Y> injector(getComponent);
+ Y* y(injector);
+ y->doStuff();
+ }
+
+ // We define X as late as possible, to make sure that all the above compiles even if X is only forward-declared.
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+ void Y::doStuff() {
+ xFactory()->foo();
+ }
+ struct XImpl : public X {
+ INJECT(XImpl()) = default;
+ void foo() override {}
+ };
+ fruit::Component<XFactory> getXFactoryComponent() {
+ return fruit::createComponent()
+ .bind<X, XImpl>();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_required_annotated_forward_declared_success():
+ source = '''
+ struct X;
+ using XFactory = std::function<std::unique_ptr<X>()>;
+ using XFactoryAnnot = fruit::Annotated<Annotation1, XFactory>;
+ struct Y {
+ XFactory xFactory;
+
+ INJECT(Y(ANNOTATED(Annotation1, XFactory) xFactory))
+ : xFactory(xFactory) {
+ }
+
+ void doStuff();
+ };
+ fruit::Component<fruit::Required<XFactoryAnnot>, Y> getYComponent() {
+ return fruit::createComponent();
+ }
+ fruit::Component<XFactoryAnnot> getXFactoryComponent();
+ fruit::Component<Y> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .install(getXFactoryComponent);
+ }
+ int main() {
+ fruit::Injector<Y> injector(getComponent);
+ Y* y(injector);
+ y->doStuff();
+ }
+
+ // We define X as late as possible, to make sure that all the above compiles even if X is only forward-declared.
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+ void Y::doStuff() {
+ xFactory()->foo();
+ }
+ struct XImpl : public X {
+ INJECT(XImpl()) = default;
+ void foo() override {}
+ };
+ fruit::Component<XFactoryAnnot> getXFactoryComponent() {
+ return fruit::createComponent()
+ .bind<fruit::Annotated<Annotation1, X>, fruit::Annotated<Annotation1, XImpl>>();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_required_const_forward_declared_success():
+ source = '''
+ struct X;
+ using XFactory = std::function<std::unique_ptr<X>()>;
+ struct Y {
+ XFactory xFactory;
+
+ INJECT(Y(XFactory xFactory))
+ : xFactory(xFactory) {
+ }
+
+ void doStuff();
+ };
+ fruit::Component<fruit::Required<const XFactory>, Y> getYComponent() {
+ return fruit::createComponent();
+ }
+ fruit::Component<const XFactory> getXFactoryComponent();
+ fruit::Component<Y> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .install(getXFactoryComponent);
+ }
+ int main() {
+ fruit::Injector<Y> injector(getComponent);
+ Y* y(injector);
+ y->doStuff();
+ }
+
+ // We define X as late as possible, to make sure that all the above compiles even if X is only forward-declared.
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+ void Y::doStuff() {
+ xFactory()->foo();
+ }
+ struct XImpl : public X {
+ INJECT(XImpl()) = default;
+ void foo() override {}
+ };
+ fruit::Component<const XFactory> getXFactoryComponent() {
+ return fruit::createComponent()
+ .bind<X, XImpl>();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+def test_required_const_annotated_forward_declared_success():
+ source = '''
+ struct X;
+ using XFactory = std::function<std::unique_ptr<X>()>;
+ using ConstXFactoryAnnot = fruit::Annotated<Annotation1, const XFactory>;
+ struct Y {
+ XFactory xFactory;
+
+ INJECT(Y(ANNOTATED(Annotation1, XFactory) xFactory))
+ : xFactory(xFactory) {
+ }
+
+ void doStuff();
+ };
+ fruit::Component<fruit::Required<ConstXFactoryAnnot>, Y> getYComponent() {
+ return fruit::createComponent();
+ }
+ fruit::Component<ConstXFactoryAnnot> getXFactoryComponent();
+ fruit::Component<Y> getComponent() {
+ return fruit::createComponent()
+ .install(getYComponent)
+ .install(getXFactoryComponent);
+ }
+ int main() {
+ fruit::Injector<Y> injector(getComponent);
+ Y* y(injector);
+ y->doStuff();
+ }
+
+ // We define X as late as possible, to make sure that all the above compiles even if X is only forward-declared.
+ struct X {
+ virtual void foo() = 0;
+ virtual ~X() = default;
+ };
+ void Y::doStuff() {
+ xFactory()->foo();
+ }
+ struct XImpl : public X {
+ INJECT(XImpl()) = default;
+ void foo() override {}
+ };
+ fruit::Component<ConstXFactoryAnnot> getXFactoryComponent() {
+ return fruit::createComponent()
+ .bind<fruit::Annotated<Annotation1, X>, fruit::Annotated<Annotation1, XImpl>>();
+ }
+ '''
+ expect_success(COMMON_DEFINITIONS, source)
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_semistatic_map_hash_selection.py b/tests/test_semistatic_map_hash_selection.py
new file mode 100644
index 0000000..6bf557a
--- /dev/null
+++ b/tests/test_semistatic_map_hash_selection.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct X1 {};
+ struct X2 {};
+ struct X3 {};
+ struct X4 {};
+ struct X5 {};
+ struct X6 {};
+ struct X7 {};
+ '''
+
+def test_semistatic_map_hash_selection():
+ source = '''
+ fruit::Component<> getComponent() {
+ return fruit::createComponent()
+ .registerConstructor<X1()>()
+ .registerConstructor<X2()>()
+ .registerConstructor<X3()>()
+ .registerConstructor<X4()>()
+ .registerConstructor<X5()>()
+ .registerConstructor<X6()>()
+ .registerConstructor<X7()>();
+ }
+
+ int main() {
+ // The component normalization generates a random hash. By looping 50 times it's very likely that we'll get at
+ // least one hash with too many collisions (and we'll generate another).
+ for (int i = 0; i < 50; i++) {
+ fruit::NormalizedComponent<> normalizedComponent(getComponent);
+ (void) normalizedComponent;
+ }
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_type_alignment.py b/tests/test_type_alignment.py
new file mode 100644
index 0000000..39d8193
--- /dev/null
+++ b/tests/test_type_alignment.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct alignas(1) X {
+ INJECT(X()) {
+ Assert(reinterpret_cast<std::uintptr_t>(this) % 1 == 0);
+ }
+ };
+
+ struct alignas(4) Y {
+ INJECT(Y()) {
+ Assert(reinterpret_cast<std::uintptr_t>(this) % 4 == 0);
+ }
+ };
+
+ struct alignas(128) Z {
+ INJECT(Z()) {
+ Assert(reinterpret_cast<std::uintptr_t>(this) % 128 == 0);
+ }
+ };
+ '''
+
+def test_everything():
+ source = '''
+ fruit::Component<X, Y, Z> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<X, Y, Z> injector(getComponent);
+
+ injector.get<X*>();
+ injector.get<Y*>();
+ injector.get<Z*>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/test_type_alignment_with_annotation.py b/tests/test_type_alignment_with_annotation.py
new file mode 100644
index 0000000..c830c4d
--- /dev/null
+++ b/tests/test_type_alignment_with_annotation.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ struct Annotation {};
+
+ struct alignas(1) X {
+ using Inject = X();
+ X() {
+ Assert(reinterpret_cast<std::uintptr_t>(this) % 1 == 0);
+ }
+ };
+
+ struct alignas(4) Y {
+ using Inject = Y();
+ Y() {
+ Assert(reinterpret_cast<std::uintptr_t>(this) % 4 == 0);
+ }
+ };
+
+ struct alignas(128) Z {
+ using Inject = Z();
+ Z() {
+ Assert(reinterpret_cast<std::uintptr_t>(this) % 128 == 0);
+ }
+ };
+
+ using XAnnot = fruit::Annotated<Annotation, X>;
+ using YAnnot = fruit::Annotated<Annotation, Y>;
+ using ZAnnot = fruit::Annotated<Annotation, Z>;
+ '''
+
+def test_type_alignment_with_annotation():
+ source = '''
+ fruit::Component<XAnnot, YAnnot, ZAnnot> getComponent() {
+ return fruit::createComponent();
+ }
+
+ int main() {
+ fruit::Injector<XAnnot, YAnnot, ZAnnot> injector(getComponent);
+
+ injector.get<fruit::Annotated<Annotation, X*>>();
+ injector.get<fruit::Annotated<Annotation, Y*>>();
+ injector.get<fruit::Annotated<Annotation, Z*>>();
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/tested_features.md b/tests/tested_features.md
new file mode 100644
index 0000000..8f2fc4c
--- /dev/null
+++ b/tests/tested_features.md
@@ -0,0 +1,182 @@
+
+# Features tested in end-to-end tests
+
+#### INJECT macro
+* **TODO** Typical use-case
+* **TODO** With assisted params
+* **TODO** Check what happens with non-normalized types (all kinds)
+
+#### Binding to an instance
+* Using `bind(x)` or `bind<fruit::Annotated<A, T>>(x)`.
+* Check that calling bindInstance with a non-normalized type (e.g. const pointer, nonconst ptr, etc.) causes an error
+* Abstract class (ok)
+* Mismatched type arguments
+* Bind to subclass
+
+#### Interface bindings
+* Check that bind<T, T> causes an easy-to-understand error
+* bind<T, Annotated<A, T>>
+* Check that bind<X, Y>, bind<Y, Z> is allowed if Z derives from Y and Y derives from X
+* bind<X, Y> with X not a base class of Y
+* Check that the types passed to bind<> are normalized
+* Check that bind<I, C> also means bind<std::function<std::unique_ptr<I>(Args...)>, std::function<std::unique_ptr<C>(Args...)>> (with and without Args)
+
+##### Binding to a constructor
+* Explicitly, with a non-signature (not ok)
+* Implicitly, with a non-signature (not ok)
+* Implicitly, with a signature "returning" another type (not ok)
+* Implicitly, with a signature "returning" an annotated type (not ok)
+* Explicitly, with a signature that doesn't match any of the type's constructors
+* Implicitly, with a signature that doesn't match any of the type's constructors
+* **TODO** Using the Inject typedef
+* **TODO** Using the INJECT macro
+* **TODO** Also with no params
+* **TODO** Also for a templated class
+* **TODO** Also for a templated constructor (only explicitly or using Inject)
+* **TODO** With all kinds of non-normalized params (esp. with INJECT)
+* **TODO** With a constructor mistakenly taking an Assisted<X> or Annotated<A,X> parameter (instead of just using Assisted/Annotated in the Inject typedef)
+* For an abstract type (not ok), both implicit and explicit
+* **TODO** Check that a default-constructible type without an Inject typedef can't be auto-injected
+
+##### Binding to a provider
+* Returning a value
+* **TODO: ownership check** Returning a pointer (also check that Fruit takes ownership)
+* Check that lambdas with captures are forbidden
+* **TODO** Check that non-lambda functors/functions are forbidden
+* **TODO** Check that objects without operator() are forbidden
+* Passing a non-signature type
+* **TODO** Passing a signature type incompatible with the lambda's signature
+* **TODO** With a lambda mistakenly taking an Assisted<X> or Annotated<A,X> parameter (instead of just using Assisted/Annotated in the Inject typedef)
+* **TODO** For an abstract type (ok)
+* With a provider that returns nullptr (runtime error)
+
+#### Factory bindings
+* Explicit, using `registerFactory()`
+* Implicitly, with a signature "returning" an annotated type (not ok)
+* **TODO** Explicit, using `registerFactory()`, but passing a non-signature
+* Explicit, using `registerFactory()`, but with a lambda that has a different signature compared to the one given explicitly
+* Implicitly, with a signature that doesn't match any of the type's constructors
+* Check that lambdas with captures are forbidden in `registerFactory()`
+* **TODO** Check that non-lambda functors/functions are forbidden in `registerFactory()`
+* **TODO** Check that objects without operator() are forbidden in `registerFactory()`
+* Using the INJECT macro
+* With some assisted params and some injected params
+* **TODO** With no assisted params but some injected params
+* With some assisted params but no injected params
+* **TODO** With no assisted params and no injected params
+* **TODO** Using the factory in another class' constructor instead of getting it from the injector directly
+* **TODO** With a lambda mistakenly taking a Assisted<X>/Annotated<A,X> parameter (instead of just using Assisted/Annotated in the Inject typedef)
+* Explicit, for an abstract type (ok)
+* Implicit, for an abstract class (not ok)
+* Explicit, with a lambda returning a pointer (not supported)
+* Explicit, with a lambda returning a unique ptr (ok)
+* **TODO** With assisted params of all kinds of non-normalized types (especially in ASSISTED)
+* Implicitly, registering a `std::function<T(...)>` instead of a `std::function<std::unique_ptr<T>(...)>`
+* Explicitly, registering a `std::function<T(...)>` instead of a `std::function<std::unique_ptr<T>(...)>`
+* Implicitly, generating a binding for std::function<T()> when there is a binding for T
+* Implicitly, generating a binding for std::function<std::unique_ptr<T>()> when there is a binding for T
+* **TODO** Check that assisted params are passed in the right order when there are multiple
+* **TODO** Try calling the factory multiple times
+* Injecting a std::function<std::unique_ptr<T>(...)> with T not movable
+
+#### Annotated bindings
+* **TODO** Using `fruit::Annotated<>`
+* **TODO** Using the ANNOTATED macro (only in constructors using INJECT)
+* **TODO** Check possibly-misleading behavior of binding Annotated<A1, I> and Annotated<A2, I> to C (only 1 C instance is created and shared)
+* **TODO** With assisted params of all kinds of non-normalized types (especially in ANNOTATED)
+
+#### Multibindings
+* Interface multibindings
+* **TODO** Check that addMultibinding<I, I> causes an easy-to-understand error
+* Instance multibindings
+* **TODO** Check that calling addInstanceMultibinding with a non-normalized type (e.g. const pointer, nonconst ptr, etc.) causes an error
+* **TODO** `addInstanceMultibindings(x)`, `addInstanceMultibindings<T>(x)` and `addInstanceMultibindings<Annotated<A, T>>(x)`
+* **TODO** `addInstanceMultibindings()` with an empty vector
+* **TODO** Check that calling `addInstanceMultibindings()` with a non-normalized type causes an error
+* `addMultibindingProvider`:
+ * Returning a value
+ * **TODO: ownership check** Returning a pointer (also check that Fruit takes ownership)
+ * Check that lambdas with captures are forbidden
+ * **TODO** Check that non-lambda functors/functions are forbidden
+ * **TODO** Check that objects without operator() are forbidden
+ * Passing a non-signature type
+ * **TODO** Passing a signature type incompatible with the lambda's signature
+ * **TODO** With a lambda mistakenly taking an Assisted<X> or Annotated<A,X> parameter (instead of just using Assisted/Annotated in the Inject typedef)
+ * For an abstract type (not ok)
+ * With a provider that returns nullptr (runtime error)
+
+#### PartialComponent and Component
+* copy a Component
+* move a Component
+* move a PartialComponent
+* construction of a Component from another Component
+* construction of a Component from a PartialComponent
+* install() (old and new style)
+* Type already bound (various combinations, incl. binding+install)
+* No binding found for abstract class
+* Dependency loops
+* Run-time error for multiple inconsistent bindings in different components
+* Class-level static_asserts in Component
+ * Check that there are no repeated types
+ * Check that no type is both in Required<> and outside
+ * Check that all types are normalized
+ * Check that Required only appears once
+ * Check that Required only appears as first parameter (if at all)
+
+#### Normalized components
+* Constructing an injector from NC + C
+* **TODO** Constructing an injector from NC + C with empty NC or empty C
+* With requirements
+* Class-level static_asserts
+ * Check that there are no repeated types
+ * Check that no type is both in Required<> and outside
+ * **TODO** Check that all types are normalized
+ * Check that Required only appears once
+ * Check that Required only appears as first parameter (if at all)
+
+#### Components with requirements
+* Usual case (where the required type is only forward-declared, with no definition available)
+* Usual case (where the required type is defined but it's an abstract class)
+* **TODO** Check that requirements aren't allowed in injectors
+* Check that multiple Required<...> params are not allowed
+* Check that the Required<...> param is only allowed if it's the 1st
+* **TODO** Check that an empty Required<...> param is allowed
+
+#### Injectors
+* **TODO** `std::move()`-ing an injector
+* Getting instances from an Injector:
+ * **TODO** Using `get<T>` (for all type variations)
+ * **TODO** Using `get()` or casting to try to get a value that the injector doesn't provide
+ * **TODO** Casting the injector to the desired type
+* Getting multibindings from an Injector
+ * for a type that has no multibindings
+ * for a type that has 1 multibinding
+ * for a type that has >1 multibindings
+* **TODO** Eager injection
+* **TODO** Check that the component (in the constructor from C) has no requirements
+* **TODO** Check that the resulting component (in the constructor from C+NC) has no requirements
+* **TODO: partial** Empty injector (construct, get multibindings, eager injection, etc.)
+* **TODO** Injector with a single instance type bound and nothing else
+* **TODO** Injector with a single bindProvider and nothing else
+* **TODO** Injector with a single multibinding and nothing else
+* **TODO** Injector with a single factory and nothing else
+* Injector<T> where the C doesn't provide T
+* Injector<T> where the C+NC don't provide T
+* Class-level static_asserts
+ * Check that there are no repeated types
+ * Check that all types are normalized
+ * Check that there are no Required types
+
+#### Injecting Provider<>s
+* **TODO** In constructors
+* Getting a Provider<> from an injector using get<> or casting the injector)
+* **TODO** Getting a Provider<> from an injector by casting the injector
+* In a constructor and calling get() before the constructor completes
+* **TODO** casting a Provider to the desired value instead of calling `get()`
+* **TODO** Calling either `get<T>()` or `get()` on the Provider
+* **TODO** Check that a Provider's type argument is normalized and not annotated
+* Copying a Provider and using the copy
+* Using `get()` to try to get a value that the provider doesn't provide
+* Class-level static_asserts
+ * Check that the type is normalized
+ * Check that the type is not annotated
diff --git a/tests/util/BUILD b/tests/util/BUILD
new file mode 100644
index 0000000..632aa14
--- /dev/null
+++ b/tests/util/BUILD
@@ -0,0 +1,9 @@
+
+licenses(["notice"])
+
+load("//third_party/fruit/tests:build_defs.bzl", "fruit_py_tests")
+
+fruit_py_tests(
+ srcs = glob(["test_*.py"]),
+)
+
diff --git a/tests/util/test_lambda_invoker.py b/tests/util/test_lambda_invoker.py
new file mode 100644
index 0000000..321962f
--- /dev/null
+++ b/tests/util/test_lambda_invoker.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ using namespace std;
+ using namespace fruit::impl;
+ '''
+
+def test_invoke_no_args():
+ source = '''
+ int main() {
+ // This is static because the lambda must have no captures.
+ static int num_invocations = 0;
+
+ auto l = []() {
+ ++num_invocations;
+ };
+ using L = decltype(l);
+ LambdaInvoker::invoke<L>();
+ Assert(num_invocations == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_invoke_some_args():
+ source = '''
+ int main() {
+ // This is static because the lambda must have no captures.
+ static int num_invocations = 0;
+
+ auto l = [](int n, double x) {
+ Assert(n == 5);
+ Assert(x == 3.14);
+ ++num_invocations;
+ };
+ using L = decltype(l);
+ LambdaInvoker::invoke<L>(5, 3.14);
+ Assert(num_invocations == 1);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/util/test_type_info.py b/tests/util/test_type_info.py
new file mode 100644
index 0000000..c1225e0
--- /dev/null
+++ b/tests/util/test_type_info.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from fruit_test_common import *
+
+COMMON_DEFINITIONS = '''
+ #include "test_common.h"
+
+ using namespace std;
+ using namespace fruit::impl;
+ '''
+
+def test_size():
+ source = '''
+ int main() {
+ Assert(getTypeId<char[27]>().type_info->size() == 27);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_alignment():
+ source = '''
+ struct alignas(128) TypeAligned128 {
+ };
+
+ int main() {
+ Assert(getTypeId<TypeAligned128>().type_info->alignment() == 128);
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_name():
+ source = '''
+ struct MyStruct {
+ };
+
+ int main() {
+ std::string result = getTypeId<MyStruct>().type_info->name();
+ if (result != "MyStruct" && result != "struct MyStruct") {
+ std::cerr << "Demangling failed." << std::endl;
+ std::cerr << "typeid(MyStruct).name() == " << typeid(MyStruct).name() << std::endl;
+ std::cerr << "getTypeId<MyStruct>().type_info->name() == " << result << std::endl;
+ abort();
+ }
+ Assert(std::string(getTypeId<MyStruct>()) == "MyStruct" || std::string(getTypeId<MyStruct>()) == "struct MyStruct");
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_isTriviallyDestructible_true():
+ source = '''
+ int main() {
+ Assert(getTypeId<int>().type_info->isTriviallyDestructible());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+def test_isTriviallyDestructible_false():
+ source = '''
+ int main() {
+ Assert(!getTypeId<std::vector<int>>().type_info->isTriviallyDestructible());
+ }
+ '''
+ expect_success(
+ COMMON_DEFINITIONS,
+ source,
+ locals())
+
+if __name__== '__main__':
+ main(__file__)
diff --git a/tests/valgrind_suppressions.supp b/tests/valgrind_suppressions.supp
new file mode 100644
index 0000000..d56279e
--- /dev/null
+++ b/tests/valgrind_suppressions.supp
@@ -0,0 +1,48 @@
+{
+ malloc/__emutls_get_address
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:malloc
+ fun:__emutls_get_address
+}
+{
+ malloc/__dtoa
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ ...
+ fun:__dtoa
+}
+{
+ malloc/tlv_get_addr
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ ...
+ fun:tlv_get_addr
+}
+{
+ malloc/__cxa_get_globals
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:__cxa_get_globals
+}
+{
+ malloc/ImageLoader
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ ...
+ fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE
+}
+{
+ malloc/fwrite
+ Memcheck:Leak
+ fun:malloc
+ fun:__smakebuf
+ fun:__swsetup
+ fun:__sfvwrite
+ fun:fwrite
+ ...
+}