Merge remote-tracking branch 'aosp/upstream-master' into HEAD

Change-Id: I6a5f7ff9c61f4c8767765c921c2447098e808511
diff --git a/.gitignore b/.gitignore
index 5da26d1..128e3cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
 .idea
 *.pyc
 /build*
+/test_package/build
 /cmake-build-debug
 /cmake-build-release
 /.vs
diff --git a/.travis.yml b/.travis.yml
index 6560579..e5cf995 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,38 +10,38 @@
   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
+    env: COMPILER=gcc-8 UBUNTU=18.10 TEST=ReleasePlain
+    install: export OS=linux; export COMPILER='gcc-8'; export UBUNTU='18.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
+    script: export OS=linux; export COMPILER='gcc-8'; export UBUNTU='18.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
+    env: COMPILER=gcc-8 UBUNTU=18.10 TEST=DebugPlain
+    install: export OS=linux; export COMPILER='gcc-8'; export UBUNTU='18.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
+    script: export OS=linux; export COMPILER='gcc-8'; export UBUNTU='18.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
+    env: COMPILER=clang-7.0 STL=libstdc++ UBUNTU=18.10 TEST=ReleasePlain
+    install: export OS=linux; export COMPILER='clang-7.0'; export STL='libstdc++';
+      export UBUNTU='18.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
+    script: export OS=linux; export COMPILER='clang-7.0'; export STL='libstdc++';
+      export UBUNTU='18.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
+    env: COMPILER=clang-7.0 STL=libstdc++ UBUNTU=18.10 TEST=DebugAsanUbsan
+    install: export OS=linux; export COMPILER='clang-7.0'; export STL='libstdc++';
+      export UBUNTU='18.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
+    script: export OS=linux; export COMPILER='clang-7.0'; export STL='libstdc++';
+      export UBUNTU='18.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
+    env: COMPILER=clang-7.0 STL=libstdc++ UBUNTU=18.10 TEST=DebugPlain
+    install: export OS=linux; export COMPILER='clang-7.0'; export STL='libstdc++';
+      export UBUNTU='18.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
+    script: export OS=linux; export COMPILER='clang-7.0'; export STL='libstdc++';
+      export UBUNTU='18.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
@@ -66,9 +66,23 @@
     install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
       extras/scripts/travis_ci_install_osx.sh
     os: osx
-    osx_image: xcode8.2
+    osx_image: xcode10
     script: export OS=osx; export COMPILER='clang-default'; export STL='libc++'; extras/scripts/postsubmit.sh
       DebugPlain
+  - compiler: clang
+    env: COMPILER=clang-4.0 STL=libstdc++ UBUNTU=18.10 TEST=ReleasePlain
+    install: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+      export UBUNTU='18.10'; extras/scripts/travis_ci_install_linux.sh
+    os: linux
+    script: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+      export UBUNTU='18.10'; extras/scripts/postsubmit.sh ReleasePlain
+  - compiler: clang
+    env: COMPILER=clang-4.0 STL=libstdc++ UBUNTU=18.10 TEST=DebugAsanUbsan
+    install: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+      export UBUNTU='18.10'; extras/scripts/travis_ci_install_linux.sh
+    os: linux
+    script: export OS=linux; export COMPILER='clang-4.0'; export STL='libstdc++';
+      export UBUNTU='18.10'; extras/scripts/postsubmit.sh DebugAsanUbsan
   - 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
@@ -156,18 +170,6 @@
     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
@@ -213,6 +215,38 @@
     osx_image: xcode8.2
     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: xcode9.4
+    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=DebugAsanUbsan
+    install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
+      extras/scripts/travis_ci_install_osx.sh
+    os: osx
+    osx_image: xcode9.4
+    script: export OS=osx; export COMPILER='clang-default'; 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: xcode10
+    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=DebugAsanUbsan
+    install: export OS=osx; export COMPILER='clang-default'; export STL='libc++';
+      extras/scripts/travis_ci_install_osx.sh
+    os: osx
+    osx_image: xcode10
+    script: export OS=osx; export COMPILER='clang-default'; export STL='libc++'; extras/scripts/postsubmit.sh
+      DebugAsanUbsan
 services:
 - docker
 sudo: required
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ac08da..20e5ba0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,10 +58,12 @@
         "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(FRUIT_IS_BEING_BUILT_BY_CONAN FALSE CACHE BOOL "This is set in Conan builds.")
+
+if("${WIN32}" AND "${FRUIT_USES_BOOST}" AND NOT "${FRUIT_IS_BEING_BUILT_BY_CONAN}")
   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.")
+    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, or specify -DFRUIT_USES_BOOST=False to not use boost.")
   endif()
   include_directories("${BOOST_DIR}")
 endif()
@@ -72,7 +74,7 @@
 endif()
 
 # Unsafe, only for debugging/benchmarking.
-#set(FRUIT_ADDITIONAL_COMPILE_FLAGS "${FRUIT_ADDITIONAL_COMPILE_FLAGS} -DFRUIT_NO_LOOP_CHECK")
+#set(FRUIT_ADDITIONAL_COMPILE_FLAGS "${FRUIT_ADDITIONAL_COMPILE_FLAGS} -DFRUIT_NO_LOOP_CHECK=1")
 
 add_definitions(${FRUIT_ADDITIONAL_COMPILE_FLAGS})
 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FRUIT_ADDITIONAL_LINKER_FLAGS}")
@@ -90,12 +92,12 @@
 
 # (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)
+#add_definitions(-DFRUIT_DEEP_TEMPLATE_INSTANTIATION_STACKTRACES_FOR_ERRORS=1)
 
 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")
+set(FRUIT_VERSION "3.4.0")
 
 add_subdirectory(configuration)
 add_subdirectory(src)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bf461be..95c127b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,7 +27,7 @@
 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"
+cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-Werror -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1"
 make -j 16
 cd tests
 py.test-3 -n auto
@@ -140,9 +140,19 @@
 
 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.
 
+If you don't want to use Boost, you can replace the `-DBOOST_DIR=...` flags above with `-DFRUIT_USES_BOOST=False`.
+
 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 that, you'll need python3 installed (you can download it [here](https://www.python.org/downloads/)).
+
+You'll also the `pytest` and `pytest-xdist` packages. You can install them with:
+
+    pip install pytest
+    pip install pytest-xdist
+
 To do so:
 
 * Open the Start menu
diff --git a/METADATA b/METADATA
index 084579a..5354135 100644
--- a/METADATA
+++ b/METADATA
@@ -1,13 +1,5 @@
 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."
-
+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
@@ -17,7 +9,11 @@
     type: GIT
     value: "https://github.com/google/fruit.git"
   }
-  version: "4f1aacf4aaeec2565b2945bba311d2e0efbbb579"
-  last_upgrade_date { year: 2018 month: 7 day: 26 }
+  version: "3c40e442d52aee6fa696ab5aaf449645c83ab6fa"
   license_type: NOTICE
+  last_upgrade_date {
+    year: 2018
+    month: 10
+    day: 29
+  }
 }
diff --git a/README.md b/README.md
index b87cae3..3875ed5 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 [![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.
+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 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.
 
diff --git a/appveyor.yml b/appveyor.yml
index 1c3a847..bc9a449 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -3,34 +3,34 @@
 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'
+  - 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 2017
+    MINGW_PATH: 'C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\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"'
+    ADDITIONAL_CMAKE_ARGS: '-DFRUIT_USES_BOOST=False -DCMAKE_CXX_FLAGS="-Werror -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1"'
     CONFIGURATION: Debug
 
-  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
-    MINGW_PATH: 'C:\MinGW\bin'
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+    MINGW_PATH: 'C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\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'
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+    MINGW_PATH: 'C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin'
     CMAKE_GENERATOR: 'MinGW Makefiles'
     VCVARSALL_DIR: ''
-    ADDITIONAL_CMAKE_ARGS: '-DBOOST_DIR=C:\Libraries\boost_1_63_0 -DCMAKE_CXX_FLAGS="-Werror"'
+    ADDITIONAL_CMAKE_ARGS: '-DBOOST_DIR=C:\Libraries\boost_1_64_0 -DCMAKE_CXX_FLAGS="-Werror"'
     CONFIGURATION: Release
   - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
     CMAKE_GENERATOR: 'Visual Studio 15 2017 Win64'
@@ -42,11 +42,11 @@
     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'
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+    MINGW_PATH: 'C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\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"'
+    ADDITIONAL_CMAKE_ARGS: '-DBOOST_DIR=C:\Libraries\boost_1_64_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'
diff --git a/conanfile.py b/conanfile.py
index 02e208e..93eeebd 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -1,37 +1,74 @@
 from conans import ConanFile, CMake, tools
+from conans.errors import ConanException
 
 
 class FruitConan(ConanFile):
     name = "fruit"
-    version = "3.2.0"
+    version = "3.4.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"
+    options = {"shared": [True, False], "use_boost": [True, False]}
+    default_options = "shared=False", "use_boost=True"
     generators = "cmake"
 
+    def configure(self):
+        min_version = {
+            "gcc": "5",
+            "clang": "3.5",
+            "apple-clang": "7.3",
+            "Visual Studio": "14", # MSVC 2015
+        }.get(str(self.settings.compiler))
+        if not min_version:
+            # Unknown minimum version, let's try going ahead with the build to see if it works.
+            return
+        if str(self.settings.compiler.version) < min_version:
+            raise ConanException("%s %s is not supported, must be at least %s" % (self.settings.compiler,
+                                                                                  self.settings.compiler.version,
+                                                                                  min_version))
+
+    def requirements(self):
+        if self.options.use_boost:
+            self.requires("boost/1.68.0@conan/stable")
+
     def source(self):
         self.run("git clone https://github.com/google/fruit")
-        self.run("cd fruit && git checkout v3.2.0")
+        self.run("cd fruit && git checkout v%s" % self.version)
+        # This small hack might be useful to guarantee proper /MT /MD linkage
+        # in MSVC if the packaged project doesn't have variables to set it
+        # properly
+        tools.replace_in_file("fruit/CMakeLists.txt", "project(Fruit)",
+                              '''PROJECT(Myfruit)
+include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+conan_basic_setup()''')
 
     def build(self):
         cmake = CMake(self)
-        if self.options.shared:
-            cmake.definitions["BUILD_SHARED_LIBS"] = "YES"
+        cmake.definitions["FRUIT_IS_BEING_BUILT_BY_CONAN"] = "YES"
+        cmake.definitions["BUILD_SHARED_LIBS"] = "YES" if self.options.shared else "NO"
+        if self.options.use_boost:
+            if self.settings.os == "Windows":
+                cmake.definitions["BOOST_DIR"] = "."
         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.definitions["FRUIT_USES_BOOST"] = "NO"
+        if self.settings.os == "Windows":
+            cmake.definitions["FRUIT_TESTS_USE_PRECOMPILED_HEADERS"] = "NO"
+        cmake.definitions["CMAKE_BUILD_TYPE"] = self.settings.build_type
         cmake.configure(source_folder="fruit")
         cmake.build()
         cmake.install()
 
     def package(self):
-        pass
+        self.copy("*.h", dst="include", src="include")
+        self.copy("*.h", dst="include", src="fruit/include")
+        self.copy("*fruit.lib", dst="lib", keep_path=False)
+        self.copy("*.dll", dst="bin", keep_path=False)
+        self.copy("*.so", dst="lib", keep_path=False)
+        self.copy("*.dylib", dst="lib", keep_path=False)
+        self.copy("*.a", dst="lib", keep_path=False)
+        tools.save("LICENSE", tools.load("fruit/COPYING"))
+        self.copy("COPYING", dst="licenses", ignore_case=True, keep_path=False)
 
     def package_info(self):
         self.cpp_info.libs = ["fruit"]
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 1e33fad..a00f93c 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -10,3 +10,4 @@
 add_subdirectory(multibindings)
 add_subdirectory(scaling_doubles)
 add_subdirectory(annotated_injection)
+add_subdirectory(testing)
diff --git a/examples/testing/BUILD b/examples/testing/BUILD
new file mode 100644
index 0000000..4744d1b
--- /dev/null
+++ b/examples/testing/BUILD
@@ -0,0 +1,59 @@
+
+licenses(["notice"])
+
+cc_library(
+    name = "greeter_lib",
+    srcs = [
+        "cached.h",
+        "cached_greeter.h",
+        "cached_greeter.cpp",
+        "greeter.h",
+        "greeter.cpp",
+        "key_value_storage.h",
+        "key_value_storage.cpp",
+    ],
+    deps = ["//third_party/fruit"],
+)
+
+cc_binary(
+    name = "cached_greeter",
+    srcs = ["main.cpp"],
+    deps = [
+        ":greeter_lib",
+        "//third_party/fruit",
+    ],
+)
+
+cc_library(
+    name = "greeter_testing_lib",
+    srcs = [
+        "fake_key_value_storage.h",
+        "fake_key_value_storage.cpp",
+    ],
+    deps = [
+        ":greeter_lib",
+        "//third_party/fruit",
+    ],
+)
+
+cc_test(
+    name = "cached_greeter_test",
+    srcs = ["cached_greeter_test.cpp"],
+    deps = [
+        ":greeter_lib",
+        ":greeter_testing_lib",
+        "//third_party/fruit",
+        "@com_google_googletest//:gtest",
+    ],
+)
+
+cc_test(
+    name = "cached_greeter_test_with_normalized_component",
+    srcs = ["cached_greeter_test_with_normalized_component.cpp"],
+    deps = [
+        ":greeter_lib",
+        ":greeter_testing_lib",
+        "//third_party/fruit",
+        "@com_google_googletest//:gtest",
+    ],
+)
diff --git a/examples/testing/CMakeLists.txt b/examples/testing/CMakeLists.txt
new file mode 100644
index 0000000..ab16476
--- /dev/null
+++ b/examples/testing/CMakeLists.txt
@@ -0,0 +1,19 @@
+
+
+set(GREETER_SOURCES
+cached_greeter.cpp
+cached_greeter.h
+cached.h
+fake_key_value_storage.cpp
+fake_key_value_storage.h
+greeter.cpp
+greeter.h
+key_value_storage.cpp
+key_value_storage.h
+main.cpp
+)
+
+add_executable(greeter ${GREETER_SOURCES})
+target_link_libraries(greeter fruit)
+
+# TODO: run the tests here under CMake (ATM they only run when using Bazel).
diff --git a/examples/testing/cached.h b/examples/testing/cached.h
new file mode 100644
index 0000000..24ed9f0
--- /dev/null
+++ b/examples/testing/cached.h
@@ -0,0 +1,23 @@
+/*
+ * 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_CACHED_H
+#define FRUIT_CACHED_H
+
+// Used to annotate (using fruit::Annotated<>) types that have a cached implementation.
+struct Cached {};
+
+#endif // FRUIT_CACHED_H
diff --git a/examples/testing/cached_greeter.cpp b/examples/testing/cached_greeter.cpp
new file mode 100644
index 0000000..12279fb
--- /dev/null
+++ b/examples/testing/cached_greeter.cpp
@@ -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.
+ */
+
+#include "cached_greeter.h"
+#include "key_value_storage.h"
+
+class CachedGreeterImpl : public Greeter {
+private:
+  Greeter* greeter;
+  KeyValueStorage* keyValueStorage;
+
+public:
+  INJECT(CachedGreeterImpl(Greeter* greeter, KeyValueStorage* keyValueStorage))
+    : greeter(greeter), keyValueStorage(keyValueStorage) {
+  }
+
+  std::string greet() override {
+    std::string greeting = keyValueStorage->get("greeting");
+    if (!greeting.empty()) {
+      return greeting;
+    }
+
+    // Not in the cache, we need to compute the greeting.
+    greeting = greeter->greet();
+
+    // We also add it in the cache so that later calls don't need to call greeter->greet().
+    keyValueStorage->put("greeting", greeting);
+
+    return greeting;
+  }
+};
+
+fruit::Component<fruit::Annotated<Cached, Greeter>> getCachedGreeterComponent() {
+  return fruit::createComponent()
+      .bind<fruit::Annotated<Cached, Greeter>, CachedGreeterImpl>()
+      .install(getKeyValueStorageComponent)
+      .install(getGreeterComponent);
+}
diff --git a/examples/testing/cached_greeter.h b/examples/testing/cached_greeter.h
new file mode 100644
index 0000000..f8c6a88
--- /dev/null
+++ b/examples/testing/cached_greeter.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 FRUIT_CACHED_GREETER_H
+#define FRUIT_CACHED_GREETER_H
+
+#include "cached.h"
+#include "greeter.h"
+
+fruit::Component<fruit::Annotated<Cached, Greeter>> getCachedGreeterComponent();
+
+#endif // FRUIT_CACHED_GREETER_H
diff --git a/examples/testing/cached_greeter_test.cpp b/examples/testing/cached_greeter_test.cpp
new file mode 100644
index 0000000..08fd101
--- /dev/null
+++ b/examples/testing/cached_greeter_test.cpp
@@ -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.
+ */
+
+#include "cached_greeter.h"
+#include "fake_key_value_storage.h"
+#include <gtest/gtest.h>
+
+fruit::Component<fruit::Annotated<Cached, Greeter>> getMainComponent() {
+  return fruit::createComponent()
+      // Note: order matters here. This replace().with() must be before the install. Otherwise Fruit will report the
+      // wrong order as a run-time error.
+      .replace(getKeyValueStorageComponent).with(getFakeKeyValueStorageComponent)
+      .install(getCachedGreeterComponent);
+}
+
+fruit::Injector<fruit::Annotated<Cached, Greeter>> createInjector() {
+  return fruit::Injector<fruit::Annotated<Cached, Greeter>>(getMainComponent);
+}
+
+TEST(CachedGreeter, NotYetCached) {
+  fruit::Injector<fruit::Annotated<Cached, Greeter>> injector = createInjector();
+  Greeter* greeter = injector.get<fruit::Annotated<Cached, Greeter*>>();
+  ASSERT_EQ(greeter->greet(), "Hello, world!");
+}
+
+TEST(CachedGreeter, Cached) {
+  fruit::Injector<fruit::Annotated<Cached, Greeter>> injector = createInjector();
+  Greeter* greeter = injector.get<fruit::Annotated<Cached, Greeter*>>();
+  greeter->greet();
+  ASSERT_EQ(greeter->greet(), "Hello, world!");
+}
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/examples/testing/cached_greeter_test_with_normalized_component.cpp b/examples/testing/cached_greeter_test_with_normalized_component.cpp
new file mode 100644
index 0000000..c859798
--- /dev/null
+++ b/examples/testing/cached_greeter_test_with_normalized_component.cpp
@@ -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.
+ */
+
+#include "cached_greeter.h"
+#include "fake_key_value_storage.h"
+
+#include <gtest/gtest.h>
+
+fruit::Component<fruit::Annotated<Cached, Greeter>> getMainComponent() {
+  return fruit::createComponent()
+      // Note: order matters here. This replace().with() must be before the install. Otherwise Fruit will report the
+      // wrong order as a run-time error.
+      .replace(getKeyValueStorageComponent).with(getFakeKeyValueStorageComponent)
+      .install(getCachedGreeterComponent);
+}
+
+fruit::Component<> getEmptyComponent() {
+  return fruit::createComponent();
+}
+
+fruit::Injector<fruit::Annotated<Cached, Greeter>> createInjector() {
+  static fruit::NormalizedComponent<fruit::Annotated<Cached, Greeter>> normalizedComponent(getMainComponent);
+  return fruit::Injector<fruit::Annotated<Cached, Greeter>>(normalizedComponent, getEmptyComponent);
+}
+
+TEST(CachedGreeter, NotYetCached) {
+  fruit::Injector<fruit::Annotated<Cached, Greeter>> injector = createInjector();
+  Greeter* greeter = injector.get<fruit::Annotated<Cached, Greeter*>>();
+  ASSERT_EQ(greeter->greet(), "Hello, world!");
+}
+
+TEST(CachedGreeter, Cached) {
+  fruit::Injector<fruit::Annotated<Cached, Greeter>> injector = createInjector();
+  Greeter* greeter = injector.get<fruit::Annotated<Cached, Greeter*>>();
+  greeter->greet();
+  ASSERT_EQ(greeter->greet(), "Hello, world!");
+}
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/examples/testing/fake_key_value_storage.cpp b/examples/testing/fake_key_value_storage.cpp
new file mode 100644
index 0000000..e0bafba
--- /dev/null
+++ b/examples/testing/fake_key_value_storage.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "fake_key_value_storage.h"
+
+#include <map>
+
+class FakeKeyValueStorage : public KeyValueStorage {
+private:
+  std::map<std::string, std::string> storage;
+
+public:
+  INJECT(FakeKeyValueStorage()) = default;
+
+  void put(std::string key, std::string value) override {
+    storage[key] = value;
+  }
+
+  std::string get(std::string key) override {
+    return storage[key];
+  }
+};
+
+fruit::Component<KeyValueStorage> getFakeKeyValueStorageComponent() {
+  return fruit::createComponent()
+      .bind<KeyValueStorage, FakeKeyValueStorage>();
+}
diff --git a/examples/testing/fake_key_value_storage.h b/examples/testing/fake_key_value_storage.h
new file mode 100644
index 0000000..e697419
--- /dev/null
+++ b/examples/testing/fake_key_value_storage.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_FAKE_KEY_VALUE_STORAGE_H
+#define FRUIT_FAKE_KEY_VALUE_STORAGE_H
+
+#include "key_value_storage.h"
+
+fruit::Component<KeyValueStorage> getFakeKeyValueStorageComponent();
+
+#endif // FRUIT_FAKE_KEY_VALUE_STORAGE_H
diff --git a/examples/testing/greeter.cpp b/examples/testing/greeter.cpp
new file mode 100644
index 0000000..9ed16ce
--- /dev/null
+++ b/examples/testing/greeter.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 "greeter.h"
+
+class GreeterImpl : public Greeter {
+public:
+  INJECT(GreeterImpl()) = default;
+
+  std::string greet() override {
+    return "Hello, world!";
+  }
+};
+
+fruit::Component<Greeter> getGreeterComponent() {
+  return fruit::createComponent()
+      .bind<Greeter, GreeterImpl>();
+}
diff --git a/examples/testing/greeter.h b/examples/testing/greeter.h
new file mode 100644
index 0000000..ce1ac6f
--- /dev/null
+++ b/examples/testing/greeter.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_GREETER_H
+#define FRUIT_GREETER_H
+
+#include <string>
+#include <fruit/fruit.h>
+
+class Greeter {
+public:
+  virtual std::string greet() = 0;
+};
+
+fruit::Component<Greeter> getGreeterComponent();
+
+#endif // FRUIT_GREETER_H
diff --git a/examples/testing/key_value_storage.cpp b/examples/testing/key_value_storage.cpp
new file mode 100644
index 0000000..74a583f
--- /dev/null
+++ b/examples/testing/key_value_storage.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "key_value_storage.h"
+
+class KeyValueStorageImpl : public KeyValueStorage {
+public:
+  INJECT(KeyValueStorageImpl()) = default;
+
+  void put(std::string key, std::string value) override {
+    // Imagine the real implementation here, with network communication.
+    (void) key;
+    (void) value;
+    throw "Not implemented";
+  }
+
+  std::string get(std::string key) override {
+    // Imagine the real implementation here, with network communication.
+    (void) key;
+    throw "Not implemented";
+  }
+};
+
+fruit::Component<KeyValueStorage> getKeyValueStorageComponent() {
+  return fruit::createComponent()
+      .bind<KeyValueStorage, KeyValueStorageImpl>();
+}
diff --git a/examples/testing/key_value_storage.h b/examples/testing/key_value_storage.h
new file mode 100644
index 0000000..7ea1242
--- /dev/null
+++ b/examples/testing/key_value_storage.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_KEY_VALUE_STORAGE_H
+#define FRUIT_KEY_VALUE_STORAGE_H
+
+#include <string>
+#include <fruit/fruit.h>
+
+class KeyValueStorage {
+public:
+  // Stores a value associated with the given key.
+  virtual void put(std::string key, std::string value) = 0;
+
+  // Returns the value previously associated with the given key, or an empty string otherwise.
+  virtual std::string get(std::string key) = 0;
+};
+
+fruit::Component<KeyValueStorage> getKeyValueStorageComponent();
+
+#endif // FRUIT_KEY_VALUE_STORAGE_H
diff --git a/examples/testing/main.cpp b/examples/testing/main.cpp
new file mode 100644
index 0000000..4cb3ead
--- /dev/null
+++ b/examples/testing/main.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 "cached_greeter.h"
+
+#include <iostream>
+
+int main() {
+  fruit::Injector<fruit::Annotated<Cached, Greeter>> injector(getCachedGreeterComponent);
+  Greeter* greeter = injector.get<fruit::Annotated<Cached, Greeter*>>();
+  std::cout << greeter->greet() << std::endl;
+
+  return 0;
+}
diff --git a/extras/bazel_root/WORKSPACE b/extras/bazel_root/WORKSPACE
index e69de29..68f44c8 100644
--- a/extras/bazel_root/WORKSPACE
+++ b/extras/bazel_root/WORKSPACE
@@ -0,0 +1,6 @@
+git_repository(
+    name = "com_google_googletest",
+    remote = "https://github.com/google/googletest",
+    # GTest HEAD as of August 2018.
+    commit = "9c96f500a39df6915f8f1ab53b60be9889f1572b",
+)
diff --git a/extras/benchmark/README.md b/extras/benchmark/README.md
index 775ba21..f54fbc2 100644
--- a/extras/benchmark/README.md
+++ b/extras/benchmark/README.md
@@ -33,9 +33,6 @@
   `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.
 
@@ -71,11 +68,8 @@
 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
 
diff --git a/extras/benchmark/boost_di_source_generator.py b/extras/benchmark/boost_di_source_generator.py
index 0f96015..6a3f91f 100644
--- a/extras/benchmark/boost_di_source_generator.py
+++ b/extras/benchmark/boost_di_source_generator.py
@@ -12,52 +12,91 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+def generate_files(injection_graph, generate_runtime_bench_code):
+    file_content_by_name = dict()
 
-class BoostDiSourceGenerator:
-    def generate_component_header(self, component_index):
-        template = """
+    for node_id in injection_graph.nodes_iter():
+        deps = injection_graph.successors(node_id)
+        file_content_by_name['component%s.h' % node_id] = _generate_component_header(node_id, deps)
+        file_content_by_name['component%s.cpp' % node_id] = _generate_component_source(node_id, deps)
+
+    [toplevel_node] = [node_id
+                       for node_id in injection_graph.nodes_iter()
+                       if not injection_graph.predecessors(node_id)]
+    file_content_by_name['main.cpp'] = _generate_main(injection_graph, toplevel_node, generate_runtime_bench_code)
+
+    return file_content_by_name
+
+def _generate_component_header(component_index, deps):
+    fields = ''.join(['std::shared_ptr<Interface%s> x%s;\n' % (dep, dep)
+                      for dep in deps])
+    component_deps = ''.join([', std::shared_ptr<Interface%s>' % dep for dep in deps])
+
+    include_directives = ''.join(['#include "component%s.h"\n' % index for index in deps])
+
+    template = """
 #ifndef COMPONENT{component_index}_H
 #define COMPONENT{component_index}_H
 
 #include <boost/di.hpp>
+#include <boost/di/extension/scopes/scoped_scope.hpp>
 #include <memory>
 
+// Example include that the code might use
+#include <vector>
+
 namespace di = boost::di;
 
+{include_directives}
+
 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}) {{}}
+    {fields}
+    
+    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});
+auto x{component_index}Component = [] {{
+    return di::make_injector(di::bind<Interface{component_index}>().to<X{component_index}>().in(di::extension::scoped));
+}};
+
+#endif // COMPONENT{component_index}_H
+"""
+    return template.format(**locals())
+
+def _generate_component_source(component_index, deps):
+    param_initializers = ', '.join('x%s(x%s)' % (dep, dep)
+                                   for dep in deps)
+    if param_initializers:
+        param_initializers = ': ' + param_initializers
+    component_deps = ', '.join('std::shared_ptr<Interface%s> x%s' % (dep, dep)
+                               for dep in deps)
+
+    template = """
+#include "component{component_index}.h"
+
+X{component_index}::X{component_index}({component_deps}) 
+    {param_initializers} {{
 }}
 """
-        return template.format(**locals())
+    return template.format(**locals())
 
-    def generate_main(self, toplevel_component):
+def _generate_main(injection_graph, toplevel_component, generate_runtime_bench_code):
+    include_directives = ''.join('#include "component%s.h"\n' % index
+                                 for index in injection_graph.nodes_iter())
+
+    injector_params = ', '.join('x%sComponent()' % index
+                                for index in injection_graph.nodes_iter())
+
+    if generate_runtime_bench_code:
         template = """
+{include_directives}
+
 #include "component{toplevel_component}.h"
 #include <ctime>
 #include <iostream>
@@ -67,6 +106,11 @@
 
 using namespace std;
 
+void f() {{
+  auto injector = di::make_injector({injector_params});
+  injector.create<std::shared_ptr<Interface{toplevel_component}>>();
+}}
+
 int main(int argc, char* argv[]) {{
   if (argc != 2) {{
     std::cout << "Need to specify num_loops as argument." << std::endl;
@@ -76,8 +120,7 @@
   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}>>();
+    f();
   }}
   perRequestTime += std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start_time).count();
   std::cout << std::fixed;
@@ -88,4 +131,19 @@
   return 0;
 }}
 """
-        return template.format(**locals())
+    else:
+        template = """
+{include_directives}
+
+#include "component{toplevel_component}.h"
+
+#include <iostream>
+
+int main() {{
+  auto injector = di::make_injector({injector_params});
+  injector.create<std::shared_ptr<Interface{toplevel_component}>>();
+  std::cout << "Hello, world" << std::endl;
+  return 0;
+}}
+"""
+    return template.format(**locals())
diff --git a/extras/benchmark/compile_time_benchmark.cpp b/extras/benchmark/compile_time_benchmark.cpp
index ddc6fd3..528a6b5 100644
--- a/extras/benchmark/compile_time_benchmark.cpp
+++ b/extras/benchmark/compile_time_benchmark.cpp
@@ -115,7 +115,7 @@
 
 #define PARAMETERS(N) B##N &b##N,
 
-#ifdef USE_FRUIT_2_X_SYNTAX
+#if 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
diff --git a/extras/benchmark/fruit_source_generator.py b/extras/benchmark/fruit_source_generator.py
index 8b2a9a6..894bd28 100644
--- a/extras/benchmark/fruit_source_generator.py
+++ b/extras/benchmark/fruit_source_generator.py
@@ -13,24 +13,37 @@
 # 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 generate_files(injection_graph, generate_runtime_bench_code, use_normalized_component=False):
+    if use_normalized_component:
+        assert not generate_runtime_bench_code
 
-    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())
+    file_content_by_name = dict()
 
-    def generate_component_header(self, component_index):
-        component_type = self._get_component_type(component_index)
-        template = """
+    for node_id in injection_graph.nodes_iter():
+        file_content_by_name['component%s.h' % node_id] = _generate_component_header(node_id)
+        file_content_by_name['component%s.cpp' % node_id] = _generate_component_source(node_id, injection_graph.successors(node_id))
+
+    [toplevel_node] = [node_id
+                       for node_id in injection_graph.nodes_iter()
+                       if not injection_graph.predecessors(node_id)]
+    file_content_by_name['main.cpp'] = _generate_main(toplevel_node, generate_runtime_bench_code)
+
+    return file_content_by_name
+
+def _get_component_type(component_index):
+    return 'fruit::Component<Interface{component_index}>'.format(**locals())
+
+def _generate_component_header(component_index):
+    component_type = _get_component_type(component_index)
+    template = """
 #ifndef COMPONENT{component_index}_H
 #define COMPONENT{component_index}_H
 
 #include <fruit/fruit.h>
 
+// Example include that the code might use
+#include <vector>
+
 struct Interface{component_index} {{
   virtual ~Interface{component_index}() = default;
 }};
@@ -39,116 +52,51 @@
 
 #endif // COMPONENT{component_index}_H
 """
-        return template.format(**locals())
+    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]])
+def _generate_component_source(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])
+    fields = ''.join(['Interface%s& x%s;\n' % (dep, 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_deps = ', '.join(['Interface%s& x%s' % (dep, dep)
+                                for dep in deps])
+    param_initializers = ', '.join('x%s(x%s)' % (dep, dep)
+                                   for dep in deps)
+    if param_initializers:
+        param_initializers = ': ' + param_initializers
 
-        component_type = self._get_component_type(component_index)
+    install_expressions = ''.join(['        .install(getComponent%s)\n' % dep for dep in deps])
 
-        template = """
+    component_type = _get_component_type(component_index)
+
+    template = """
 {include_directives}
 
+namespace {{
 struct X{component_index} : public Interface{component_index} {{
-INJECT(X{component_index}({component_deps})) {{}}
+  {fields}
 
-virtual ~X{component_index}() = default;
+  INJECT(X{component_index}({component_deps})) {param_initializers} {{}}
+
+  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 += """
+
+    template += """
 {component_type} getComponent{component_index}() {{
     return fruit::createComponent(){install_expressions}
         .bind<Interface{component_index}, X{component_index}>();
 }}
 """
 
-        return template.format(**locals())
+    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):
+def _generate_main(toplevel_component, generate_runtime_bench_code):
+    if generate_runtime_bench_code:
         template = """
 #include "component{toplevel_component}.h"
 
@@ -171,23 +119,9 @@
   }}
   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();
+  std::chrono::high_resolution_clock::time_point 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}>>();
@@ -196,11 +130,27 @@
 
   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())
+    else:
+        template = """
+#include "component{toplevel_component}.h"
+
+#include <iostream>
+
+fruit::Component<> getEmptyComponent() {{
+  return fruit::createComponent();
+}}
+
+int main(void) {{
+  fruit::NormalizedComponent<Interface{toplevel_component}> normalizedComponent(getComponent{toplevel_component});
+  fruit::Injector<Interface{toplevel_component}> injector(normalizedComponent, getEmptyComponent);
+  injector.get<std::shared_ptr<Interface{toplevel_component}>>();
+  std::cout << "Hello, world" << std::endl;
+  return 0;
+}}
+    """
+
+    return template.format(**locals())
diff --git a/extras/benchmark/generate_benchmark.py b/extras/benchmark/generate_benchmark.py
index 7a3d361..cee0017 100755
--- a/extras/benchmark/generate_benchmark.py
+++ b/extras/benchmark/generate_benchmark.py
@@ -16,32 +16,78 @@
 import random
 import os
 
-from fruit_source_generator import FruitSourceGenerator
-from boost_di_source_generator import BoostDiSourceGenerator
+import fruit_source_generator
+import boost_di_source_generator
+import no_di_library_source_generator
 from makefile_generator import generate_makefile
 import argparse
+import networkx as nx
 
 
-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_injection_graph(num_components_with_no_deps,
+                             num_components_with_deps,
+                             num_deps):
+    injection_graph = nx.DiGraph()
 
+    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
+        toplevel_components.add(id)
+
+    # Then the rest have num_deps deps, chosen (pseudo-)randomly from the previous components.
+    # The last few components depend more 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))
+
+        # Add other deps to get to the desired num_deps.
+        deps |= set(random.sample(range(0, num_components_with_no_deps + i), 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)
+        for dep in deps_list:
+            injection_graph.add_edge(component_id, dep)
+
+    assert len(toplevel_components) == 1, toplevel_components
+    toplevel_component = num_used_ids - 1
+    assert is_toplevel[toplevel_component]
+
+    return injection_graph
 
 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,
+        generate_runtime_bench_code,
+        use_exceptions=True,
+        use_rtti=True,
+        fruit_build_dir=None,
+        fruit_sources_dir=None,
         boost_di_sources_dir=None,
-        use_fruit_2_x_syntax=False,
-        generate_debuginfo=False):
+        generate_debuginfo=False,
+        use_new_delete=False,
+        use_interfaces=False,
+        use_normalized_component=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.
@@ -56,97 +102,60 @@
     # This is a constant so that we always generate the same file (=> benchmark more repeatable).
     random.seed(42)
 
+    injection_graph = generate_injection_graph(num_components_with_no_deps=num_components_with_no_deps,
+                                               num_components_with_deps=num_components_with_deps,
+                                               num_deps=num_deps)
+
     if di_library == 'fruit':
-        source_generator = FruitSourceGenerator(
-            use_fruit_2_x_syntax = use_fruit_2_x_syntax)
+        file_content_by_name = fruit_source_generator.generate_files(injection_graph, generate_runtime_bench_code)
         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']
+        file_content_by_name = boost_di_source_generator.generate_files(injection_graph, generate_runtime_bench_code)
+        include_dirs = [boost_di_sources_dir + '/include', boost_di_sources_dir + '/extension/include']
+        library_dirs = []
+        link_libraries = []
+    elif di_library == 'none':
+        file_content_by_name = no_di_library_source_generator.generate_files(injection_graph, use_new_delete, use_interfaces, generate_runtime_bench_code)
+        include_dirs = []
         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))
+    if not use_exceptions:
+        other_compile_flags.append('-fno-exceptions')
+    if not use_rtti:
+        other_compile_flags.append('-fno-rtti')
+    compile_command = '%s -std=%s -MMD -MP -O2 -W -Wall -Werror -DNDEBUG -ftemplate-depth=10000 %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']
+    cpp_files = [file_name
+                 for file_name in file_content_by_name.keys()
+                 if file_name.endswith('.cpp')]
 
-    with open("%s/Makefile" % output_dir, 'w') as makefile:
-        makefile.write(generate_makefile(sources, 'main', compile_command, link_command, link_command_suffix))
+    file_content_by_name['Makefile'] = generate_makefile(cpp_files, 'main', compile_command, link_command, link_command_suffix)
 
+    os.makedirs(output_dir, exist_ok=True)
+    for file_name, file_content in file_content_by_name.items():
+        with open('%s/%s' % (output_dir, file_name), 'w') as file:
+            file.write(file_content)
+
+    return file_content_by_name.keys()
 
 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('--di-library', default='fruit', help='DI library to use. One of {fruit, boost_di, none}. (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\')')
@@ -157,8 +166,13 @@
     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).')
+    parser.add_argument('--use-new-delete', default='false', help='Set this to \'true\' to use new/delete. Only relevant when --di_library=none.')
+    parser.add_argument('--use-interfaces', default='false', help='Set this to \'true\' to use interfaces. Only relevant when --di_library=none.')
+    parser.add_argument('--use-normalized-component', default='false', help='Set this to \'true\' to create a NormalizedComponent and create the injector from that. Only relevant when --di_library=fruit and --generate-runtime-bench-code=false.')
+    parser.add_argument('--generate-runtime-bench-code', default='true', help='Set this to \'false\' for compile benchmarks.')
+    parser.add_argument('--generate-debuginfo', default='false', help='Set this to \'true\' to generate debugging information (-g).')
+    parser.add_argument('--use-exceptions', default='true', help='Set this to \'false\' to disable exceptions.')
+    parser.add_argument('--use-rtti', default='true', help='Set this to \'false\' to disable RTTI.')
 
     args = parser.parse_args()
 
@@ -173,8 +187,10 @@
     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\'.')
+    elif args.di_library == 'none':
+        pass
     else:
-        raise Exception('Unrecognized --di-library: \'%s\'. Allowed values are %s' % (args.di_library, {'fruit', 'boost_di'}))
+        raise Exception('Unrecognized --di-library: \'%s\'. Allowed values are %s' % (args.di_library, {'fruit', 'boost_di', 'none'}))
 
     num_components_with_deps = int(args.num_components_with_deps)
     num_components_with_no_deps = int(args.num_components_with_no_deps)
@@ -194,8 +210,13 @@
         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'))
+        generate_debuginfo=(args.generate_debuginfo == 'true'),
+        use_new_delete=(args.use_new_delete == 'true'),
+        use_interfaces=(args.use_interfaces == 'true'),
+        use_normalized_component=(args.use_normalized_component == 'true'),
+        generate_runtime_bench_code=(args.generate_runtime_bench_code == 'true'),
+        use_exceptions=(args.use_exceptions == 'true'),
+        use_rtti=(args.use_rtti == 'true'))
 
 
 if __name__ == "__main__":
diff --git a/extras/benchmark/makefile_generator.py b/extras/benchmark/makefile_generator.py
index 2a5b2c4..083fd36 100644
--- a/extras/benchmark/makefile_generator.py
+++ b/extras/benchmark/makefile_generator.py
@@ -13,7 +13,9 @@
 # limitations under the License.
 
 
-def generate_makefile(sources, executable_name, compile_command, link_command, link_command_suffix):
+def generate_makefile(cpp_files, executable_name, compile_command, link_command, link_command_suffix):
+    assert executable_name + '.cpp' in cpp_files, '%s.cpp in %s' % (executable_name, cpp_files)
+
     link_rule_template = """
 {executable_name}: {object_files}
 \t{link_command} {object_files} -o {executable_name} {link_command_suffix}
@@ -25,17 +27,30 @@
 
     clean_rule_template = """
 clean:
-\trm -f {object_files} {executable_name}
+\trm -f {object_files} {dep_files} {executable_name}
+"""
+
+    dep_file_deps = """
+%.d: ;
+"""
+
+    dep_files_includes_template = """
+include {dep_files}
 """
 
     compile_rules = []
     object_files = []
-    for source in sources:
+    dep_files = []
+    for cpp_file in cpp_files:
+        assert cpp_file.endswith('.cpp')
+        source = cpp_file[:-len('.cpp')]
+
         compile_rule = compile_rule_template.format(
             name=source,
             compile_command=compile_command)
-        compile_rules += [compile_rule]
-        object_files += ['%s.o' % source]
+        compile_rules.append(compile_rule)
+        object_files.append('%s.o' % source)
+        dep_files.append('%s.d' % source)
 
     link_rule = link_rule_template.format(
         object_files=' '.join(object_files),
@@ -45,7 +60,10 @@
 
     clean_rule = clean_rule_template.format(
         object_files=' '.join(object_files),
-        executable_name=executable_name)
+        executable_name=executable_name,
+        dep_files=' '.join(dep_files))
+
+    dep_files_includes = dep_files_includes_template.format(dep_files=' '.join(dep_files))
 
     # We put the link rule first so that it's the default Make target.
-    return link_rule + ''.join(compile_rules) + clean_rule
+    return link_rule + ''.join(compile_rules) + clean_rule + dep_file_deps + dep_files_includes
diff --git a/extras/benchmark/no_di_library_source_generator.py b/extras/benchmark/no_di_library_source_generator.py
new file mode 100644
index 0000000..6b66aa9
--- /dev/null
+++ b/extras/benchmark/no_di_library_source_generator.py
@@ -0,0 +1,227 @@
+# 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 itertools
+import networkx as nx
+
+def generate_files(injection_graph, use_new_delete, use_interfaces, generate_runtime_bench_code):
+    file_content_by_name = dict()
+
+    for node_id in injection_graph.nodes_iter():
+        deps = injection_graph.successors(node_id)
+        if use_interfaces:
+            file_content_by_name['class%s_interface.h' % node_id] = _generate_class_interface_header(node_id)
+            file_content_by_name['class%s.h' % node_id] = _generate_class_header_with_interfaces(node_id, deps)
+            file_content_by_name['class%s.cpp' % node_id] = _generate_class_cpp_file_with_interfaces(node_id, deps)
+        else:
+            file_content_by_name['class%s.h' % node_id] = _generate_class_header_without_interfaces(node_id, deps)
+            file_content_by_name['class%s.cpp' % node_id] = _generate_class_cpp_file_without_interfaces(node_id, deps)
+
+    file_content_by_name['main.cpp'] = _generate_main(injection_graph, use_interfaces, use_new_delete, generate_runtime_bench_code)
+
+    return file_content_by_name
+
+def _generate_class_interface_header(class_index):
+    template = """
+#ifndef CLASS{class_index}_INTERFACE_H
+#define CLASS{class_index}_INTERFACE_H
+
+// Example include that the code might use
+#include <vector>
+
+struct Interface{class_index} {{
+  virtual void foo() = 0;
+  virtual ~Interface{class_index}();
+}};
+
+#endif // CLASS{class_index}_INTERFACE_H
+"""
+    return template.format(**locals())
+
+def _generate_class_header_with_interfaces(class_index, deps):
+    include_directives = ''.join('#include "class%s_interface.h"\n' % index
+                                 for index in itertools.chain(deps, (class_index,)))
+    fields = ''.join('Interface%s& x%s;\n' % (index, index)
+                     for index in deps)
+    constructor_params = ', '.join('Interface%s& x%s' % (index, index)
+                                   for index in deps)
+
+    template = """
+#ifndef CLASS{class_index}_H
+#define CLASS{class_index}_H
+
+{include_directives}
+
+struct Class{class_index} : public Interface{class_index} {{
+  {fields}
+  Class{class_index}({constructor_params});
+
+  virtual void foo() override;
+
+  virtual ~Class{class_index}();
+}};
+
+#endif // CLASS{class_index}_H
+"""
+    return template.format(**locals())
+
+def _generate_class_header_without_interfaces(class_index, deps):
+    include_directives = ''.join('#include "class%s.h"\n' % index
+                                 for index in deps)
+    fields = ''.join('Class%s& x%s;\n' % (index, index)
+                     for index in deps)
+    constructor_params = ', '.join('Class%s& x%s' % (index, index)
+                                   for index in deps)
+
+    template = """
+#ifndef CLASS{class_index}_H
+#define CLASS{class_index}_H
+
+// Example include that the code might use
+#include <vector>
+
+{include_directives}
+
+struct Class{class_index} {{
+  {fields}
+  Class{class_index}({constructor_params});
+}};
+
+#endif // CLASS{class_index}_H
+"""
+    return template.format(**locals())
+
+def _generate_class_cpp_file_with_interfaces(class_index, deps):
+    constructor_params = ', '.join('Interface%s& x%s' % (index, index)
+                                   for index in deps)
+    field_initializers = ', '.join('x%s(x%s)' % (index, index)
+                                   for index in deps)
+    if field_initializers:
+        field_initializers = ': ' + field_initializers
+
+    template = """
+#include "class{class_index}.h"
+
+Interface{class_index}::~Interface{class_index}() {{
+}}
+
+Class{class_index}::Class{class_index}({constructor_params})
+  {field_initializers} {{
+}}
+
+void Class{class_index}::foo() {{
+}}
+
+Class{class_index}::~Class{class_index}() {{
+}}
+"""
+    return template.format(**locals())
+
+def _generate_class_cpp_file_without_interfaces(class_index, deps):
+    constructor_params = ', '.join('Class%s& x%s' % (index, index)
+                                   for index in deps)
+    field_initializers = ', '.join('x%s(x%s)' % (index, index)
+                                   for index in deps)
+    if field_initializers:
+        field_initializers = ': ' + field_initializers
+
+    template = """
+#include "class{class_index}.h"
+
+Class{class_index}::Class{class_index}({constructor_params})
+  {field_initializers} {{
+}}
+"""
+    return template.format(**locals())
+
+
+def _generate_main(injection_graph, use_interfaces, use_new_delete, generate_runtime_bench_code):
+    [toplevel_class_index] = [node_id
+                              for node_id in injection_graph.nodes_iter()
+                                if not injection_graph.predecessors(node_id)]
+
+    if use_interfaces:
+        include_directives = ''.join('#include "class%s.h"\n' % index
+                                     for index in injection_graph.nodes_iter())
+    else:
+        include_directives = '#include "class%s.h"\n' % toplevel_class_index
+
+    if use_new_delete:
+        instance_creations = ''.join('std::unique_ptr<Class%s> x%s(new Class%s(%s));\n' % (class_index,
+                                                                                           class_index,
+                                                                                           class_index,
+                                                                                           ', '.join('*x%s' % dep_index
+                                                                                                     for dep_index in injection_graph.successors(class_index)))
+                                     for class_index in reversed(list(nx.topological_sort(injection_graph))))
+    else:
+        instance_creations = ''.join('Class%s x%s{%s};\n' % (class_index,
+                                                             class_index,
+                                                             ', '.join('x%s' % dep_index
+                                                                       for dep_index in injection_graph.successors(class_index)))
+                                     for class_index in reversed(list(nx.topological_sort(injection_graph))))
+
+    void_casts = ''.join('(void) x%s;\n' % index
+                         for index in injection_graph.nodes_iter())
+
+    if generate_runtime_bench_code:
+        template = """
+{include_directives}
+
+#include <ctime>
+#include <iostream>
+#include <cstdlib>
+#include <iomanip>
+#include <chrono>
+
+using namespace std;
+
+void do_injection() {{
+  {instance_creations}
+  {void_casts}
+}}
+
+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;
+
+  start_time = std::chrono::high_resolution_clock::now();
+  for (size_t i = 0; i < 1 + num_loops/100; i++) {{
+    do_injection();
+  }}
+  double fullInjectionTime = 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 per request          = " << fullInjectionTime * 100 / num_loops << std::endl;
+  return 0;
+}}
+"""
+    else:
+        template = """
+{include_directives}
+
+#include <memory>
+#include <iostream>
+
+int main() {{
+  {instance_creations}
+  {void_casts}
+  std::cout << "Hello, world" << std::endl;
+  return 0;
+}}
+"""
+    return template.format(**locals())
diff --git a/extras/benchmark/run_benchmarks.py b/extras/benchmark/run_benchmarks.py
index cbbea78..449d81d 100755
--- a/extras/benchmark/run_benchmarks.py
+++ b/extras/benchmark/run_benchmarks.py
@@ -140,7 +140,7 @@
     return benchmark_params
 
 
-class NewDeleteRunTimeBenchmark:
+class SimpleNewDeleteRunTimeBenchmark:
     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
@@ -171,10 +171,10 @@
 
 
 class FruitSingleFileCompileTimeBenchmark:
-    def __init__(self, benchmark_definition, fruit_sources_dir, fruit_build_tmpdir, fruit_benchmark_sources_dir):
+    def __init__(self, benchmark_definition, fruit_sources_dir, fruit_build_dir, 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_build_dir = fruit_build_dir
         self.fruit_benchmark_sources_dir = fruit_benchmark_sources_dir
         num_bindings = self.benchmark_definition['num_bindings']
         assert (num_bindings % 5) == 0, num_bindings
@@ -187,19 +187,13 @@
         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 + [
+                    args = compile_flags + [
                         '-std=%s' % cxx_std,
                         '-DMULTIPLIER=%s' % (num_bindings // 5),
                         '-I', self.fruit_sources_dir + '/include',
-                        '-I', self.fruit_build_tmpdir + '/include',
+                        '-I', self.fruit_build_dir + '/include',
                         '-ftemplate-depth=1000',
                         '-c',
                         self.fruit_benchmark_sources_dir + '/extras/benchmark/compile_time_benchmark.cpp',
@@ -222,12 +216,15 @@
 
 
 class GenericGeneratedSourcesBenchmark:
-    def __init__(self, di_library, benchmark_definition, fruit_sources_dir, fruit_build_tmpdir, path_to_code_under_test, **other_args):
+    def __init__(self,
+                 di_library,
+                 benchmark_definition,
+                 path_to_code_under_test=None,
+                 **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
+        self.arbitrary_file = None
 
     def prepare_compile_benchmark(self):
         num_classes = self.benchmark_definition['num_classes']
@@ -238,10 +235,8 @@
         self.tmpdir = tempfile.gettempdir() + '/fruit-benchmark-dir'
         ensure_empty_dir(self.tmpdir)
         num_classes_with_no_deps = int(num_classes * 0.1)
-        generate_benchmark(
+        return 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,
@@ -251,9 +246,26 @@
             **benchmark_generation_flags,
             **self.other_args)
 
+    def run_make_build(self):
+        run_command('make', args=make_args, cwd=self.tmpdir)
+
+    def prepare_incremental_compile_benchmark(self):
+        files = self.prepare_compile_benchmark()
+        self.run_make_build()
+        files = list(sorted(file for file in files if file.endswith('.h')))
+        # 5 files, equally spaced (but not at beginning/end) in the sorted sequence.
+        num_files_changed = 5
+        self.arbitrary_files = [files[i * (len(files) // (num_files_changed + 2))]
+                                for i in range(1, num_files_changed + 1)]
+
     def prepare_runtime_benchmark(self):
         self.prepare_compile_benchmark()
-        run_command('make', args=make_args, cwd=self.tmpdir)
+        self.run_make_build()
+
+    def prepare_startup_benchmark(self):
+        self.prepare_compile_benchmark()
+        self.run_make_build()
+        run_command('strip', args=[self.tmpdir + '/main'])
 
     def prepare_executable_size_benchmark(self):
         self.prepare_runtime_benchmark()
@@ -264,13 +276,19 @@
                     args=make_args + ['clean'],
                     cwd=self.tmpdir)
         start = timer()
-        run_command('make',
-                    args=make_args,
-                    cwd=self.tmpdir)
+        self.run_make_build()
         end = timer()
         result = {'compile_time': end - start}
         return result
 
+    def run_incremental_compile_benchmark(self):
+        run_command('touch', args=self.arbitrary_files, cwd=self.tmpdir)
+        start = timer()
+        self.run_make_build()
+        end = timer()
+        result = {'incremental_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']
@@ -281,6 +299,15 @@
                                      int(4 * 1000 * 1000 * 1000 * loop_factor / num_classes),
                                  ])
         return parse_results(results.splitlines())
+    
+    def run_startup_benchmark(self):
+        N=1000
+        start = timer()
+        for i in range(0, N):
+            run_command(self.tmpdir + '/main', args = [])
+        end = timer()
+        result = {'startup_time': (end - start) / N}
+        return result
 
     def run_executable_size_benchmark(self):
         wc_result, _ = run_command('wc', args=['-c', self.tmpdir + '/main'])
@@ -291,123 +318,245 @@
         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)
+class CompileTimeBenchmark(GenericGeneratedSourcesBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(generate_runtime_bench_code=False,
+                         **kwargs)
 
     def prepare(self):
-        self.generic_benchmark.prepare_compile_benchmark()
+        self.prepare_compile_benchmark()
 
     def run(self):
-        return self.generic_benchmark.run_compile_benchmark()
+        return self.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)
+class IncrementalCompileTimeBenchmark(GenericGeneratedSourcesBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(generate_runtime_bench_code=False,
+                         **kwargs)
 
     def prepare(self):
-        self.generic_benchmark.prepare_runtime_benchmark()
+        self.prepare_incremental_compile_benchmark()
 
     def run(self):
-        return self.generic_benchmark.run_runtime_benchmark()
+        return self.run_incremental_compile_benchmark()
 
-    def describe(self):
-        return self.generic_benchmark.describe()
+class StartupTimeBenchmark(GenericGeneratedSourcesBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(generate_runtime_bench_code=False,
+                         **kwargs)
 
+    def prepare(self):
+        self.prepare_startup_benchmark()
+
+    def run(self):
+        return self.run_startup_benchmark()
+
+class RunTimeBenchmark(GenericGeneratedSourcesBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(generate_runtime_bench_code=True,
+                         **kwargs)
+
+    def prepare(self):
+        self.prepare_runtime_benchmark()
+
+    def run(self):
+        return self.run_runtime_benchmark()
 
 # 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)
+class ExecutableSizeBenchmark(GenericGeneratedSourcesBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(generate_runtime_bench_code=False,
+                         **kwargs)
 
     def prepare(self):
-        self.generic_benchmark.prepare_executable_size_benchmark()
+        self.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()
-
+        return self.run_executable_size_benchmark()
 
 # 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)
+class ExecutableSizeBenchmarkWithoutExceptionsAndRtti(ExecutableSizeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_exceptions=False,
+                         use_rtti=False,
+                         **kwargs)
 
-    def prepare(self):
-        self.generic_benchmark.prepare_executable_size_benchmark()
+class FruitCompileTimeBenchmark(CompileTimeBenchmark):
+    def __init__(self, fruit_sources_dir, **kwargs):
+        super().__init__(di_library='fruit',
+                         path_to_code_under_test=fruit_sources_dir,
+                         fruit_sources_dir=fruit_sources_dir,
+                         **kwargs)
 
-    def run(self):
-        return self.generic_benchmark.run_executable_size_benchmark()
+class FruitIncrementalCompileTimeBenchmark(IncrementalCompileTimeBenchmark):
+    def __init__(self, fruit_sources_dir, **kwargs):
+        super().__init__(di_library='fruit',
+                         path_to_code_under_test=fruit_sources_dir,
+                         fruit_sources_dir=fruit_sources_dir,
+                         **kwargs)
 
-    def describe(self):
-        return self.generic_benchmark.describe()
+class FruitRunTimeBenchmark(RunTimeBenchmark):
+    def __init__(self, fruit_sources_dir, **kwargs):
+        super().__init__(di_library='fruit',
+                         path_to_code_under_test=fruit_sources_dir,
+                         fruit_sources_dir=fruit_sources_dir,
+                         **kwargs)
+
+class FruitStartupTimeBenchmark(StartupTimeBenchmark):
+    def __init__(self, fruit_sources_dir, **kwargs):
+        super().__init__(di_library='fruit',
+                         path_to_code_under_test=fruit_sources_dir,
+                         fruit_sources_dir=fruit_sources_dir,
+                         **kwargs)
+
+class FruitStartupTimeWithNormalizedComponentBenchmark(FruitStartupTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_normalized_component=True,
+                         **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class FruitExecutableSizeBenchmark(ExecutableSizeBenchmark):
+    def __init__(self, fruit_sources_dir, **kwargs):
+        super().__init__(di_library='fruit',
+                         path_to_code_under_test=fruit_sources_dir,
+                         fruit_sources_dir=fruit_sources_dir,
+                         **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class FruitExecutableSizeBenchmarkWithoutExceptionsAndRtti(ExecutableSizeBenchmarkWithoutExceptionsAndRtti):
+    def __init__(self, fruit_sources_dir, **kwargs):
+        super().__init__(di_library='fruit',
+                         path_to_code_under_test=fruit_sources_dir,
+                         fruit_sources_dir=fruit_sources_dir,
+                         **kwargs)
+
+class BoostDiCompileTimeBenchmark(CompileTimeBenchmark):
+    def __init__(self, boost_di_sources_dir, **kwargs):
+        super().__init__(di_library='boost_di',
+                         path_to_code_under_test=boost_di_sources_dir,
+                         boost_di_sources_dir=boost_di_sources_dir,
+                         **kwargs)
+
+class BoostDiIncrementalCompileTimeBenchmark(IncrementalCompileTimeBenchmark):
+    def __init__(self, boost_di_sources_dir, **kwargs):
+        super().__init__(di_library='boost_di',
+                         path_to_code_under_test=boost_di_sources_dir,
+                         boost_di_sources_dir=boost_di_sources_dir,
+                         **kwargs)
+
+class BoostDiRunTimeBenchmark(RunTimeBenchmark):
+    def __init__(self, boost_di_sources_dir, **kwargs):
+        super().__init__(di_library='boost_di',
+                         path_to_code_under_test=boost_di_sources_dir,
+                         boost_di_sources_dir=boost_di_sources_dir,
+                         **kwargs)
+
+class BoostDiStartupTimeBenchmark(StartupTimeBenchmark):
+    def __init__(self, boost_di_sources_dir, **kwargs):
+        super().__init__(di_library='boost_di',
+                         path_to_code_under_test=boost_di_sources_dir,
+                         boost_di_sources_dir=boost_di_sources_dir,
+                         **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class BoostDiExecutableSizeBenchmark(ExecutableSizeBenchmark):
+    def __init__(self, boost_di_sources_dir, **kwargs):
+        super().__init__(di_library='boost_di',
+                         path_to_code_under_test=boost_di_sources_dir,
+                         boost_di_sources_dir=boost_di_sources_dir,
+                         **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class BoostDiExecutableSizeBenchmarkWithoutExceptionsAndRtti(ExecutableSizeBenchmarkWithoutExceptionsAndRtti):
+    def __init__(self, boost_di_sources_dir, **kwargs):
+        super().__init__(di_library='boost_di',
+                         path_to_code_under_test=boost_di_sources_dir,
+                         boost_di_sources_dir=boost_di_sources_dir,
+                         **kwargs)
+
+class SimpleDiCompileTimeBenchmark(CompileTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(di_library='none',
+                         **kwargs)
+
+class SimpleDiIncrementalCompileTimeBenchmark(IncrementalCompileTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(di_library='none',
+                         **kwargs)
+
+class SimpleDiRunTimeBenchmark(RunTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(di_library='none',
+                         **kwargs)
+
+class SimpleDiStartupTimeBenchmark(StartupTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(di_library='none',
+                         **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class SimpleDiExecutableSizeBenchmark(ExecutableSizeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(di_library='none',
+                         **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class SimpleDiExecutableSizeBenchmarkWithoutExceptionsAndRtti(ExecutableSizeBenchmarkWithoutExceptionsAndRtti):
+    def __init__(self, **kwargs):
+        super().__init__(di_library='none',
+                         **kwargs)
+
+class SimpleDiWithInterfacesCompileTimeBenchmark(SimpleDiCompileTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_interfaces=True, **kwargs)
+
+class SimpleDiWithInterfacesIncrementalCompileTimeBenchmark(SimpleDiIncrementalCompileTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_interfaces=True, **kwargs)
+
+class SimpleDiWithInterfacesRunTimeBenchmark(SimpleDiRunTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_interfaces=True, **kwargs)
+
+class SimpleDiWithInterfacesStartupTimeBenchmark(SimpleDiStartupTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_interfaces=True, **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class SimpleDiWithInterfacesExecutableSizeBenchmark(SimpleDiExecutableSizeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_interfaces=True, **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class SimpleDiWithInterfacesExecutableSizeBenchmarkWithoutExceptionsAndRtti(SimpleDiExecutableSizeBenchmarkWithoutExceptionsAndRtti):
+    def __init__(self, **kwargs):
+        super().__init__(use_interfaces=True, **kwargs)
+
+class SimpleDiWithInterfacesAndNewDeleteCompileTimeBenchmark(SimpleDiWithInterfacesCompileTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_new_delete=True, **kwargs)
+
+class SimpleDiWithInterfacesAndNewDeleteIncrementalCompileTimeBenchmark(SimpleDiWithInterfacesIncrementalCompileTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_new_delete=True, **kwargs)
+
+class SimpleDiWithInterfacesAndNewDeleteRunTimeBenchmark(SimpleDiWithInterfacesRunTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_new_delete=True, **kwargs)
+
+class SimpleDiWithInterfacesAndNewDeleteStartupTimeBenchmark(SimpleDiWithInterfacesStartupTimeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_new_delete=True, **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class SimpleDiWithInterfacesAndNewDeleteExecutableSizeBenchmark(SimpleDiWithInterfacesExecutableSizeBenchmark):
+    def __init__(self, **kwargs):
+        super().__init__(use_new_delete=True, **kwargs)
+
+# This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure.
+class SimpleDiWithInterfacesAndNewDeleteExecutableSizeBenchmarkWithoutExceptionsAndRtti(SimpleDiWithInterfacesExecutableSizeBenchmarkWithoutExceptionsAndRtti):
+    def __init__(self, **kwargs):
+        super().__init__(use_new_delete=True, **kwargs)
 
 
 def round_to_significant_digits(n, num_significant_digits):
@@ -416,7 +565,7 @@
         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(benchmark, max_runs, timeout_hours, output_file, min_runs=3):
     def run_benchmark_once():
         print('Running benchmark... ', end='', flush=True)
         result = benchmark.run()
@@ -429,12 +578,18 @@
     benchmark.prepare()
     print('Done.')
 
+    start_time = timer()
+
     # 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:
+        if timer() - start_time > timeout_hours * 3600:
+            print("Warning: timed out, couldn't determine a result with the desired precision.")
+            break
+
         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.
@@ -533,7 +688,7 @@
         previous_run_completed_benchmarks = []
         run_command('rm', args=['-f', args.output_file])
 
-    fruit_build_tmpdir = tempfile.gettempdir() + '/fruit-benchmark-build-dir'
+    fruit_build_dir = tempfile.gettempdir() + '/fruit-benchmark-build-dir'
 
     with open(args.benchmark_definition, 'r') as f:
         yaml_file_content = yaml.load(f)
@@ -552,9 +707,9 @@
         # 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)
+        # Build Fruit in fruit_build_dir, so that fruit_build_dir points to a built Fruit (useful for e.g. the config header).
+        shutil.rmtree(fruit_build_dir, ignore_errors=True)
+        os.makedirs(fruit_build_dir)
         modified_env = os.environ.copy()
         modified_env['CXX'] = compiler_executable_name
         run_command('cmake',
@@ -563,9 +718,9 @@
                         '-DCMAKE_BUILD_TYPE=Release',
                         *additional_cmake_args,
                     ],
-                    cwd=fruit_build_tmpdir,
+                    cwd=fruit_build_dir,
                     env=modified_env)
-        run_command('make', args=make_args, cwd=fruit_build_tmpdir)
+        run_command('make', args=make_args, cwd=fruit_build_dir)
 
         for benchmark_definition in benchmark_definitions_with_current_config:
             benchmark_index += 1
@@ -577,7 +732,7 @@
                 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 = SimpleNewDeleteRunTimeBenchmark(
                     benchmark_definition,
                     fruit_benchmark_sources_dir=args.fruit_benchmark_sources_dir)
             elif benchmark_name == 'fruit_single_file_compile_time':
@@ -585,40 +740,56 @@
                     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_build_dir=fruit_build_dir)
+            elif benchmark_name.startswith('fruit_'):
+                benchmark_class = {
+                    'fruit_compile_time': FruitCompileTimeBenchmark,
+                    'fruit_incremental_compile_time': FruitIncrementalCompileTimeBenchmark,
+                    'fruit_run_time': FruitRunTimeBenchmark,
+                    'fruit_startup_time': FruitStartupTimeBenchmark,
+                    'fruit_startup_time_with_normalized_component': FruitStartupTimeWithNormalizedComponentBenchmark,
+                    'fruit_executable_size': FruitExecutableSizeBenchmark,
+                    'fruit_executable_size_without_exceptions_and_rtti': FruitExecutableSizeBenchmarkWithoutExceptionsAndRtti,
+                }[benchmark_name]
+                benchmark = benchmark_class(
+                    benchmark_definition=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,
+                    fruit_build_dir=fruit_build_dir)
+            elif benchmark_name.startswith('boost_di_'):
+                benchmark_class = {
+                    'boost_di_compile_time': BoostDiCompileTimeBenchmark,
+                    'boost_di_incremental_compile_time': BoostDiIncrementalCompileTimeBenchmark,
+                    'boost_di_run_time': BoostDiRunTimeBenchmark,
+                    'boost_di_startup_time': BoostDiStartupTimeBenchmark,
+                    'boost_di_executable_size': BoostDiExecutableSizeBenchmark,
+                    'boost_di_executable_size_without_exceptions_and_rtti': BoostDiExecutableSizeBenchmarkWithoutExceptionsAndRtti,
+                }[benchmark_name]
+                benchmark = benchmark_class(
+                    benchmark_definition=benchmark_definition,
                     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)
+            elif benchmark_name.startswith('simple_di_'):
+                benchmark_class = {
+                    'simple_di_compile_time': SimpleDiCompileTimeBenchmark,
+                    'simple_di_incremental_compile_time': SimpleDiIncrementalCompileTimeBenchmark,
+                    'simple_di_run_time': SimpleDiRunTimeBenchmark,
+                    'simple_di_startup_time': SimpleDiStartupTimeBenchmark,
+                    'simple_di_executable_size': SimpleDiExecutableSizeBenchmark,
+                    'simple_di_executable_size_without_exceptions_and_rtti': SimpleDiExecutableSizeBenchmarkWithoutExceptionsAndRtti,
+                    'simple_di_with_interfaces_compile_time': SimpleDiWithInterfacesCompileTimeBenchmark,
+                    'simple_di_with_interfaces_incremental_compile_time': SimpleDiWithInterfacesIncrementalCompileTimeBenchmark,
+                    'simple_di_with_interfaces_run_time': SimpleDiWithInterfacesRunTimeBenchmark,
+                    'simple_di_with_interfaces_startup_time': SimpleDiWithInterfacesStartupTimeBenchmark,
+                    'simple_di_with_interfaces_executable_size': SimpleDiWithInterfacesExecutableSizeBenchmark,
+                    'simple_di_with_interfaces_executable_size_without_exceptions_and_rtti': SimpleDiWithInterfacesExecutableSizeBenchmarkWithoutExceptionsAndRtti,
+                    'simple_di_with_interfaces_and_new_delete_compile_time': SimpleDiWithInterfacesAndNewDeleteCompileTimeBenchmark,
+                    'simple_di_with_interfaces_and_new_delete_incremental_compile_time': SimpleDiWithInterfacesAndNewDeleteIncrementalCompileTimeBenchmark,
+                    'simple_di_with_interfaces_and_new_delete_run_time': SimpleDiWithInterfacesAndNewDeleteRunTimeBenchmark,
+                    'simple_di_with_interfaces_and_new_delete_startup_time': SimpleDiWithInterfacesAndNewDeleteStartupTimeBenchmark,
+                    'simple_di_with_interfaces_and_new_delete_executable_size': SimpleDiWithInterfacesAndNewDeleteExecutableSizeBenchmark,
+                    'simple_di_with_interfaces_and_new_delete_executable_size_without_exceptions_and_rtti': SimpleDiWithInterfacesAndNewDeleteExecutableSizeBenchmarkWithoutExceptionsAndRtti,
+                }[benchmark_name]
+                benchmark = benchmark_class(
+                    benchmark_definition=benchmark_definition)
             else:
                 raise Exception("Unrecognized benchmark: %s" % benchmark_name)
 
@@ -626,7 +797,10 @@
                 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'])
+            run_benchmark(benchmark,
+                          output_file=args.output_file,
+                          max_runs=global_definitions['max_runs'],
+                          timeout_hours=global_definitions['max_hours_per_combination'])
 
 
 if __name__ == "__main__":
diff --git a/extras/benchmark/suites/boost_di.yml b/extras/benchmark/suites/boost_di.yml
index 7aa9750..815991c 100644
--- a/extras/benchmark/suites/boost_di.yml
+++ b/extras/benchmark/suites/boost_di.yml
@@ -14,22 +14,24 @@
 
 global:
   max_runs: 20
+  max_hours_per_combination: 2
 
 # These values are ignored, they are here just to be referenced below.
 constants:
   compilers: &compilers
-    - "g++-7"
-    - "clang++-4.0"
+    - "g++-8"
+    - "clang++-6.0"
 
 benchmarks:
   - name:
       - "boost_di_compile_time"
+      - "boost_di_incremental_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.
+      - 1000
     compiler: *compilers
     cxx_std: "c++14"
     additional_cmake_args:
diff --git a/extras/benchmark/suites/fruit_debug.yml b/extras/benchmark/suites/debug.yml
similarity index 67%
rename from extras/benchmark/suites/fruit_debug.yml
rename to extras/benchmark/suites/debug.yml
index 30ca90c..13d6c0b 100644
--- a/extras/benchmark/suites/fruit_debug.yml
+++ b/extras/benchmark/suites/debug.yml
@@ -17,6 +17,7 @@
 
 global:
   max_runs: 3
+  max_hours_per_combination: 2
 
 # These values are ignored, they are here just to be referenced below.
 constants:
@@ -38,10 +39,24 @@
       - []
     benchmark_generation_flags:
       - []
-      #- ["use_fruit_2_x_syntax"]
 
   - name:
-      - "new_delete_run_time"
+    - "new_delete_run_time"
+    - "simple_di_compile_time"
+    - "simple_di_incremental_compile_time"
+    - "simple_di_run_time"
+    - "simple_di_startup_time"
+    - "simple_di_executable_size"
+    - "simple_di_with_interfaces_compile_time"
+    - "simple_di_with_interfaces_incremental_compile_time"
+    - "simple_di_with_interfaces_run_time"
+    - "simple_di_with_interfaces_startup_time"
+    - "simple_di_with_interfaces_executable_size"
+    - "simple_di_with_interfaces_and_new_delete_compile_time"
+    - "simple_di_with_interfaces_and_new_delete_incremental_compile_time"
+    - "simple_di_with_interfaces_and_new_delete_run_time"
+    - "simple_di_with_interfaces_and_new_delete_startup_time"
+    - "simple_di_with_interfaces_and_new_delete_executable_size"
     loop_factor: 0.01
     num_classes:
       - 100
@@ -54,7 +69,10 @@
 
   - name:
       - "fruit_compile_time"
+      - "fruit_incremental_compile_time"
       - "fruit_run_time"
+      - "fruit_startup_time"
+      - "fruit_startup_time_with_normalized_component"
       - "fruit_executable_size"
     loop_factor: 0.01
     num_classes:
@@ -70,7 +88,10 @@
 
   - name:
       - "fruit_compile_time"
+      - "fruit_incremental_compile_time"
       - "fruit_run_time"
+      - "fruit_startup_time"
+      - "fruit_startup_time_with_normalized_component"
       - "fruit_executable_size"
     loop_factor: 0.01
     num_classes:
@@ -84,7 +105,9 @@
 
   - name:
       - "boost_di_compile_time"
+      - "boost_di_incremental_compile_time"
       - "boost_di_run_time"
+      - "boost_di_startup_time"
       - "boost_di_executable_size"
     loop_factor: 0.01
     num_classes:
diff --git a/extras/benchmark/suites/fruit_full.yml b/extras/benchmark/suites/fruit_full.yml
index aeff08b..32ac6d1 100644
--- a/extras/benchmark/suites/fruit_full.yml
+++ b/extras/benchmark/suites/fruit_full.yml
@@ -14,12 +14,13 @@
 
 global:
   max_runs: 20
+  max_hours_per_combination: 2
 
 # These values are ignored, they are here just to be referenced below.
 constants:
   compilers: &compilers
-    - "g++-7"
-    - "clang++-4.0"
+    - "g++-8"
+    - "clang++-6.0"
   num_classes: &num_classes
     - 100
     - 1000
@@ -40,6 +41,7 @@
   - name:
       - "new_delete_run_time"
       - "fruit_compile_time"
+      - "fruit_incremental_compile_time"
       - "fruit_run_time"
       - "fruit_executable_size"
     loop_factor: 1.0
@@ -53,6 +55,7 @@
 
   - name:
       - "fruit_compile_time"
+      - "fruit_incremental_compile_time"
       - "fruit_run_time"
       - "fruit_executable_size"
     loop_factor: 1.0
diff --git a/extras/benchmark/suites/fruit_full_old_style.yml b/extras/benchmark/suites/fruit_full_old_style.yml
deleted file mode 100644
index 413a05f..0000000
--- a/extras/benchmark/suites/fruit_full_old_style.yml
+++ /dev/null
@@ -1,66 +0,0 @@
-# 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
index 00ee0dc..e212ac8 100644
--- a/extras/benchmark/suites/fruit_mostly_full.yml
+++ b/extras/benchmark/suites/fruit_mostly_full.yml
@@ -14,12 +14,13 @@
 
 global:
   max_runs: 20
+  max_hours_per_combination: 2
 
 # These values are ignored, they are here just to be referenced below.
 constants:
   compilers: &compilers
-    - "g++-7"
-    - "clang++-4.0"
+    - "g++-8"
+    - "clang++-6.0"
   num_classes: &num_classes
     - 100
     - 1000
@@ -39,6 +40,7 @@
   - name:
       - "new_delete_run_time"
       - "fruit_compile_time"
+      - "fruit_incremental_compile_time"
       - "fruit_run_time"
       - "fruit_executable_size"
     loop_factor: 1.0
diff --git a/extras/benchmark/suites/fruit_quick.yml b/extras/benchmark/suites/fruit_quick.yml
index 778bf60..002e7e8 100644
--- a/extras/benchmark/suites/fruit_quick.yml
+++ b/extras/benchmark/suites/fruit_quick.yml
@@ -14,6 +14,7 @@
 
 global:
   max_runs: 10
+  max_hours_per_combination: 2
 
 # These values are ignored, they are here just to be referenced below.
 constants:
@@ -37,6 +38,7 @@
 
   - name:
       - "fruit_compile_time"
+      - "fruit_incremental_compile_time"
       - "fruit_run_time"
       - "fruit_executable_size"
     loop_factor: 1.0
diff --git a/extras/benchmark/suites/fruit_quick_old_style.yml b/extras/benchmark/suites/fruit_quick_old_style.yml
deleted file mode 100644
index cb6b70e..0000000
--- a/extras/benchmark/suites/fruit_quick_old_style.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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
index a6dae93..d269da9 100644
--- a/extras/benchmark/suites/fruit_single.yml
+++ b/extras/benchmark/suites/fruit_single.yml
@@ -16,6 +16,7 @@
 
 global:
   max_runs: 8
+  max_hours_per_combination: 2
 
 # These values are ignored, they are here just to be referenced below.
 constants:
diff --git a/extras/benchmark/suites/fruit_single_old_style.yml b/extras/benchmark/suites/fruit_single_old_style.yml
deleted file mode 100644
index 32efa0f..0000000
--- a/extras/benchmark/suites/fruit_single_old_style.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-# 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/suites/simple_di_full.yml b/extras/benchmark/suites/simple_di_full.yml
new file mode 100644
index 0000000..6ed8d0f
--- /dev/null
+++ b/extras/benchmark/suites/simple_di_full.yml
@@ -0,0 +1,54 @@
+# 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: 20
+  max_hours_per_combination: 2
+
+# 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:
+    - "new_delete_run_time"
+    - "simple_di_compile_time"
+    - "simple_di_incremental_compile_time"
+    - "simple_di_run_time"
+    - "simple_di_executable_size"
+    - "simple_di_with_interfaces_compile_time"
+    - "simple_di_with_interfaces_incremental_compile_time"
+    - "simple_di_with_interfaces_run_time"
+    - "simple_di_with_interfaces_executable_size"
+    - "simple_di_with_interfaces_and_new_delete_compile_time"
+    - "simple_di_with_interfaces_and_new_delete_incremental_compile_time"
+    - "simple_di_with_interfaces_and_new_delete_run_time"
+    - "simple_di_with_interfaces_and_new_delete_executable_size"
+    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/simple_di_mostly_full.yml b/extras/benchmark/suites/simple_di_mostly_full.yml
new file mode 100644
index 0000000..a833652
--- /dev/null
+++ b/extras/benchmark/suites/simple_di_mostly_full.yml
@@ -0,0 +1,53 @@
+# 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: 20
+  max_hours_per_combination: 2
+
+# 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
+
+benchmarks:
+  - name:
+    - "new_delete_run_time"
+    - "simple_di_compile_time"
+    - "simple_di_incremental_compile_time"
+    - "simple_di_run_time"
+    - "simple_di_executable_size"
+    - "simple_di_with_interfaces_compile_time"
+    - "simple_di_with_interfaces_incremental_compile_time"
+    - "simple_di_with_interfaces_run_time"
+    - "simple_di_with_interfaces_executable_size"
+    - "simple_di_with_interfaces_and_new_delete_compile_time"
+    - "simple_di_with_interfaces_and_new_delete_incremental_compile_time"
+    - "simple_di_with_interfaces_and_new_delete_run_time"
+    - "simple_di_with_interfaces_and_new_delete_executable_size"
+    loop_factor: 1.0
+    num_classes:
+      - 100
+    compiler: *compilers
+    cxx_std: "c++11"
+    additional_cmake_args:
+      - []
+    benchmark_generation_flags:
+      - []
diff --git a/extras/benchmark/tables/fruit_internal.yml b/extras/benchmark/tables/fruit_internal.yml
index 4dcef73..48ac40e 100644
--- a/extras/benchmark/tables/fruit_internal.yml
+++ b/extras/benchmark/tables/fruit_internal.yml
@@ -39,6 +39,17 @@
       dimension: "compile_time"
       unit: "seconds"
 
+  - name: "Fruit incremental compile time"
+    benchmark_filter:
+      name: "fruit_incremental_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"
@@ -105,11 +116,33 @@
         fixed_map:
           "fruit_compile_time": "Fruit"
           "boost_di_compile_time": "Boost.DI"
+          "simple_di_incremental_compile_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_compile_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_compile_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "compile_time"
       unit: "seconds"
   
+  - name: "Incremental compile time (100 classes)"
+    benchmark_filter:
+      num_classes: 100
+      benchmark_generation_flags: []
+      additional_cmake_args: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_incremental_compile_time": "Fruit"
+          "boost_di_incremental_compile_time": "Boost.DI"
+          "simple_di_incremental_compile_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_compile_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_compile_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "compile_time"
+      unit: "seconds"
+
   - name: "Fruit full injection time (100 classes)"
     benchmark_filter:
       num_classes: 100
@@ -121,6 +154,9 @@
         fixed_map:
           "fruit_run_time": "Fruit"
           "boost_di_run_time": "Boost.DI"
+          "simple_di_incremental_run_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "Full injection time"
@@ -137,6 +173,9 @@
         fixed_map:
           "fruit_run_time": "Fruit"
           "boost_di_run_time": "Boost.DI"
+          "simple_di_incremental_run_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "componentNormalizationTime"
@@ -154,6 +193,9 @@
         fixed_map:
           "fruit_run_time": "Fruit"
           "boost_di_run_time": "Boost.DI"
+          "simple_di_incremental_run_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "Total for setup"
@@ -170,6 +212,9 @@
         fixed_map:
           "fruit_run_time": "Fruit"
           "boost_di_run_time": "Boost.DI"
+          "simple_di_incremental_run_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "Total per request"
@@ -186,6 +231,9 @@
         fixed_map:
           "fruit_executable_size": "Fruit"
           "boost_di_executable_size": "Boost.DI"
+          "simple_di_incremental_run_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "num_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
deleted file mode 100644
index 3a74047..0000000
--- a/extras/benchmark/tables/fruit_internal_old_vs_new_style.yml
+++ /dev/null
@@ -1,108 +0,0 @@
-
-# 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
index 6a0c304..0af3b3e 100644
--- a/extras/benchmark/tables/fruit_wiki.yml
+++ b/extras/benchmark/tables/fruit_wiki.yml
@@ -85,7 +85,7 @@
       dimension: "Total"
       unit: "seconds"
 
-  # The following tables compare Fruit to Boost.DI
+  # The following tables compare Fruit to Boost.DI or to no DI library
     
   - name: "Compile time (100 classes)"
     benchmark_filter:
@@ -98,12 +98,72 @@
         fixed_map:
           "fruit_compile_time": "Fruit"
           "boost_di_compile_time": "Boost.DI"
+          "simple_di_compile_time": "Simple DI"
+          "simple_di_with_interfaces_compile_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_compile_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "compile_time"
       unit: "seconds"
 
-  - name: "Full injection time"
+  - name: "Compile time (1000 classes)"
+    benchmark_filter:
+      num_classes: 1000
+      additional_cmake_args: []
+      benchmark_generation_flags: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_compile_time": "Fruit"
+          "boost_di_compile_time": "Boost.DI"
+          "simple_di_compile_time": "Simple DI"
+          "simple_di_with_interfaces_compile_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_compile_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "compile_time"
+      unit: "seconds"
+
+  - name: "Incremental compile time (100 classes)"
+    benchmark_filter:
+      num_classes: 100
+      additional_cmake_args: []
+      benchmark_generation_flags: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_incremental_compile_time": "Fruit"
+          "boost_di_incremental_compile_time": "Boost.DI"
+          "simple_di_incremental_compile_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_compile_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_compile_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "compile_time"
+      unit: "seconds"
+
+  - name: "Incremental compile time (1000 classes)"
+    benchmark_filter:
+      num_classes: 1000
+      additional_cmake_args: []
+      benchmark_generation_flags: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_incremental_compile_time": "Fruit"
+          "boost_di_incremental_compile_time": "Boost.DI"
+          "simple_di_incremental_compile_time": "Simple DI"
+          "simple_di_with_interfaces_incremental_compile_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_incremental_compile_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "compile_time"
+      unit: "seconds"
+
+  - name: "Full injection time (100 classes)"
     benchmark_filter:
       num_classes: 100
       additional_cmake_args: []
@@ -114,6 +174,28 @@
         fixed_map:
           "fruit_run_time": "Fruit"
           "boost_di_run_time": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "Full injection time"
+      unit: "seconds"
+
+  - name: "Full injection time (1000 classes)"
+    benchmark_filter:
+      num_classes: 1000
+      additional_cmake_args: []
+      benchmark_generation_flags: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_run_time": "Fruit"
+          "boost_di_run_time": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "Full injection time"
@@ -130,11 +212,33 @@
         fixed_map:
           "fruit_run_time": "Fruit"
           "boost_di_run_time": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "Total for setup"
       unit: "seconds"
   
+  - name: "Setup time (1000 classes)"
+    benchmark_filter:
+      num_classes: 1000
+      additional_cmake_args: []
+      benchmark_generation_flags: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_run_time": "Fruit"
+          "boost_di_run_time": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "Total for setup"
+      unit: "seconds"
+
   - name: "Per-request time (100 classes)"
     benchmark_filter:
       num_classes: 100
@@ -146,11 +250,33 @@
         fixed_map:
           "fruit_run_time": "Fruit"
           "boost_di_run_time": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "Total per request"
       unit: "seconds"
-  
+
+  - name: "Per-request time (1000 classes)"
+    benchmark_filter:
+      num_classes: 1000
+      additional_cmake_args: []
+      benchmark_generation_flags: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_run_time": "Fruit"
+          "boost_di_run_time": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "Total per request"
+      unit: "seconds"
+
   - name: "Executable size (stripped, 100 classes)"
     benchmark_filter:
       num_classes: 100
@@ -162,6 +288,28 @@
         fixed_map:
           "fruit_executable_size": "Fruit"
           "boost_di_executable_size": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
+    rows: *compiler_name_row
+    results:
+      dimension: "num_bytes"
+      unit: "bytes"
+
+  - name: "Executable size (stripped, 1000 classes)"
+    benchmark_filter:
+      num_classes: 1000
+      additional_cmake_args: []
+      benchmark_generation_flags: []
+    columns:
+      dimension: "name"
+      pretty_printer:
+        fixed_map:
+          "fruit_executable_size": "Fruit"
+          "boost_di_executable_size": "Boost.DI"
+          "simple_di_run_time": "Simple DI"
+          "simple_di_with_interfaces_run_time": "Simple DI w/ interfaces"
+          "simple_di_with_interfaces_and_new_delete_run_time": "Simple DI w/ interfaces, new/delete"
     rows: *compiler_name_row
     results:
       dimension: "num_bytes"
diff --git a/extras/benchmark/tables/fruit_wiki_old_style.yml b/extras/benchmark/tables/fruit_wiki_old_style.yml
deleted file mode 100644
index 135445a..0000000
--- a/extras/benchmark/tables/fruit_wiki_old_style.yml
+++ /dev/null
@@ -1,270 +0,0 @@
-
-# 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
index 5907cf8..21e1cc1 100644
--- a/extras/doc/CMakeLists.txt
+++ b/extras/doc/CMakeLists.txt
@@ -17,6 +17,8 @@
 bar_handler.tex
 bind.tex
 bind_instance.tex
+cached_greeter.tex
+cached_greeter_test.tex
 car_component.tex
 checked_adder.tex
 checked_incrementer.tex
diff --git a/extras/doc/cached_greeter.tex b/extras/doc/cached_greeter.tex
new file mode 100644
index 0000000..15b7fbc
--- /dev/null
+++ b/extras/doc/cached_greeter.tex
@@ -0,0 +1,26 @@
+
+\input{header}
+\begin{tikzpicture}
+
+\begin{component}{20cm}{1}{0}{CachedGreeterComponent}
+
+\begin{component}{4cm}{7.5}{0}{GreeterComponent}
+\end{component}
+
+\begin{component}{4cm}{7.5}{2.5}{KeyValueStorageComponent}
+\end{component}
+
+\newcomponent{0}{1.3}{CachedGreeterImpl}
+
+\umlassemblyconnector[interface=Greeter]{GreeterComponent}{CachedGreeterImpl}
+\umlassemblyconnector[interface=KeyValueStorage]{KeyValueStorageComponent}{CachedGreeterImpl}
+
+\umlprovidedinterface[interface={Annotated<Cached,Greeter>}, distance=4.3, with port, padding=2.1cm]{CachedGreeterImpl}
+
+\end{component}
+\umlprovidedinterface[interface={Annotated<Cached,Greeter>}, distance=11.2, with port, padding=0.2cm]{CachedGreeterComponent}
+
+\umldep{CachedGreeterImpl-west-interface}{CachedGreeterComponent-west-port}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/cached_greeter_test.tex b/extras/doc/cached_greeter_test.tex
new file mode 100644
index 0000000..d67e35c
--- /dev/null
+++ b/extras/doc/cached_greeter_test.tex
@@ -0,0 +1,26 @@
+
+\input{header}
+\begin{tikzpicture}
+
+\begin{component}{20cm}{1}{0}{CachedGreeterComponent with replacement}
+
+\begin{component}{4cm}{7.5}{0}{GreeterComponent}
+\end{component}
+
+\begin{component}{4cm}{7.5}{2.5}{FakeKeyValueStorageComponent}
+\end{component}
+
+\newcomponent{0}{1.3}{CachedGreeterImpl}
+
+\umlassemblyconnector[interface=Greeter]{GreeterComponent}{CachedGreeterImpl}
+\umlassemblyconnector[interface=KeyValueStorage]{FakeKeyValueStorageComponent}{CachedGreeterImpl}
+
+\umlprovidedinterface[interface={Annotated<Cached,Greeter>}, distance=4.3, with port, padding=2.1cm]{CachedGreeterImpl}
+
+\end{component}
+\umlprovidedinterface[interface={Annotated<Cached,Greeter>}, distance=11.2, with port, padding=0.2cm]{CachedGreeterComponent with replacement}
+
+\umldep{CachedGreeterImpl-west-interface}{CachedGreeterComponent with replacement-west-port}
+
+\end{tikzpicture}
+\input{footer}
diff --git a/extras/doc/common-header.tex b/extras/doc/common-header.tex
index 2c01e63..80b5250 100644
--- a/extras/doc/common-header.tex
+++ b/extras/doc/common-header.tex
@@ -6,7 +6,7 @@
 \usepackage[english]{babel}
 \usepackage{tikz-uml}
 \usepackage{graphicx}
-\usepackage[margin=0in, paperwidth=18.5cm]{geometry}
+\usepackage[margin=0in, paperwidth=23cm]{geometry}
 \usepackage{color}
 \usepackage{framed}
 
diff --git a/extras/dockerfiles/Dockerfile.ubuntu-17.04 b/extras/dockerfiles/Dockerfile.ubuntu-17.04
index 6718523..ee50dcc 100644
--- a/extras/dockerfiles/Dockerfile.ubuntu-17.04
+++ b/extras/dockerfiles/Dockerfile.ubuntu-17.04
@@ -4,6 +4,7 @@
 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 && \
+RUN sed -i -re 's/([a-z]{2}\.)?archive.ubuntu.com|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list; \
+    bash -x /common_install.sh && \
     bash -x /ubuntu-17.04_install.sh && \
     bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/Dockerfile.ubuntu-18.04 b/extras/dockerfiles/Dockerfile.ubuntu-18.04
new file mode 100644
index 0000000..ec5d5cb
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu-18.04
@@ -0,0 +1,9 @@
+FROM ubuntu:18.04
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu-18.04_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu-18.04_install.sh /
+
+RUN bash -x /common_install.sh && \
+    bash -x /ubuntu-18.04_install.sh && \
+    bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/Dockerfile.ubuntu-18.10 b/extras/dockerfiles/Dockerfile.ubuntu-18.10
new file mode 100644
index 0000000..09bd008
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu-18.10
@@ -0,0 +1,9 @@
+FROM ubuntu:18.10
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu-18.10_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu-18.10_install.sh /
+
+RUN bash -x /common_install.sh && \
+    bash -x /ubuntu-18.10_install.sh && \
+    bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/Dockerfile.ubuntu_arm-16.04 b/extras/dockerfiles/Dockerfile.ubuntu_arm-16.04
new file mode 100644
index 0000000..35342b9
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu_arm-16.04
@@ -0,0 +1,9 @@
+FROM multiarch/ubuntu-core:arm64-xenial
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu_arm-16.04_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu_arm-16.04_install.sh /
+
+RUN bash -x /common_install.sh && \
+    bash -x /ubuntu_arm-16.04_install.sh && \
+    bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/Dockerfile.ubuntu_arm-17.10 b/extras/dockerfiles/Dockerfile.ubuntu_arm-17.10
new file mode 100644
index 0000000..f2d8b30
--- /dev/null
+++ b/extras/dockerfiles/Dockerfile.ubuntu_arm-17.10
@@ -0,0 +1,9 @@
+FROM multiarch/ubuntu-core:arm64-artful
+MAINTAINER Marco Poletti <poletti.marco@gmail.com>
+
+COPY ubuntu_arm-17.10_custom.list /etc/apt/sources.list.d/
+COPY common_install.sh common_cleanup.sh ubuntu_arm-17.10_install.sh /
+
+RUN bash -x /common_install.sh && \
+    bash -x /ubuntu_arm-17.10_install.sh && \
+    bash -x /common_cleanup.sh
diff --git a/extras/dockerfiles/common_cleanup.sh b/extras/dockerfiles/common_cleanup.sh
index a02258a..5ef0745 100644
--- a/extras/dockerfiles/common_cleanup.sh
+++ b/extras/dockerfiles/common_cleanup.sh
@@ -3,13 +3,8 @@
 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
+find /usr/lib/ /usr/bin -type f | fgrep -v bazel | fgrep -v python | \
+    xargs -P 32 -L 1 bash -c 'file "$0" | fgrep executable | fgrep -q stripped && strip --strip-unneeded "$0" || true'
 
 # This was only needed above, we don't need it in the final image.
 apt-get remove -y wget file python3-pip
diff --git a/extras/dockerfiles/common_install.sh b/extras/dockerfiles/common_install.sh
index 8f32478..1215ebd 100644
--- a/extras/dockerfiles/common_install.sh
+++ b/extras/dockerfiles/common_install.sh
@@ -3,7 +3,7 @@
 set -e
 
 apt-get update -qq
-apt-get install -y --no-install-recommends wget
+apt-get install -y --no-install-recommends wget gnupg
 
 wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
 
@@ -21,16 +21,16 @@
     cmake \
     libboost-dev \
     g++-5 \
-    clang-3.8 \
     clang-3.9 \
     clang-4.0 \
     libc++-dev \
     python3-pip \
     python3-setuptools \
+    python3-networkx \
     dirmngr
     
-pip3 install --upgrade pip
 pip3 install wheel
 pip3 install pytest
 pip3 install pytest-xdist
 pip3 install sh
+pip3 install bidict
diff --git a/extras/dockerfiles/ubuntu-14.04_install.sh b/extras/dockerfiles/ubuntu-14.04_install.sh
index edb7825..974c3d1 100644
--- a/extras/dockerfiles/ubuntu-14.04_install.sh
+++ b/extras/dockerfiles/ubuntu-14.04_install.sh
@@ -6,4 +6,5 @@
     clang-3.5 \
     clang-3.6 \
     clang-3.7 \
+    clang-3.8 \
     g++-4.9
diff --git a/extras/dockerfiles/ubuntu-16.04_install.sh b/extras/dockerfiles/ubuntu-16.04_install.sh
index 1452630..391231b 100644
--- a/extras/dockerfiles/ubuntu-16.04_install.sh
+++ b/extras/dockerfiles/ubuntu-16.04_install.sh
@@ -12,6 +12,7 @@
     clang-3.5 \
     clang-3.6 \
     clang-3.7 \
+    clang-3.8 \
     g++-4.9 \
     g++-6 \
     python \
diff --git a/extras/dockerfiles/ubuntu-17.04_install.sh b/extras/dockerfiles/ubuntu-17.04_install.sh
index afa7b5e..89a9f5c 100644
--- a/extras/dockerfiles/ubuntu-17.04_install.sh
+++ b/extras/dockerfiles/ubuntu-17.04_install.sh
@@ -6,6 +6,7 @@
 
 apt-get install -y --allow-unauthenticated --no-install-recommends \
     clang-3.7 \
+    clang-3.8 \
     g++-4.9 \
     g++-6 \
     python \
diff --git a/extras/dockerfiles/ubuntu-17.10_install.sh b/extras/dockerfiles/ubuntu-17.10_install.sh
index 36fd97e..799ddf9 100644
--- a/extras/dockerfiles/ubuntu-17.10_install.sh
+++ b/extras/dockerfiles/ubuntu-17.10_install.sh
@@ -6,6 +6,8 @@
 
 apt-get install -y --allow-unauthenticated --no-install-recommends \
     g++-7 \
+    clang-3.8 \
+    clang-5.0 \
     python \
     python3-sh \
     python3-typed-ast \
diff --git a/extras/dockerfiles/ubuntu-18.04_custom.list b/extras/dockerfiles/ubuntu-18.04_custom.list
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-18.04_custom.list
diff --git a/extras/dockerfiles/ubuntu-18.04_install.sh b/extras/dockerfiles/ubuntu-18.04_install.sh
new file mode 100644
index 0000000..fa240d3
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-18.04_install.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+apt-get install -y --allow-unauthenticated --no-install-recommends \
+    g++-7 \
+    g++-8 \
+    clang-5.0 \
+    clang-6.0 \
+    python \
+    python3-sh \
+    python3-typed-ast \
+    clang-format
diff --git a/extras/dockerfiles/ubuntu-18.10_custom.list b/extras/dockerfiles/ubuntu-18.10_custom.list
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-18.10_custom.list
diff --git a/extras/dockerfiles/ubuntu-18.10_install.sh b/extras/dockerfiles/ubuntu-18.10_install.sh
new file mode 100644
index 0000000..f21f41d
--- /dev/null
+++ b/extras/dockerfiles/ubuntu-18.10_install.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+apt-get install -y --allow-unauthenticated --no-install-recommends \
+    g++-7 \
+    g++-8 \
+    clang-6.0 \
+    clang-7 \
+    python \
+    python3-sh \
+    python3-typed-ast \
+    clang-format
diff --git a/extras/dockerfiles/ubuntu_arm-16.04_custom.list b/extras/dockerfiles/ubuntu_arm-16.04_custom.list
new file mode 100644
index 0000000..0ba95de
--- /dev/null
+++ b/extras/dockerfiles/ubuntu_arm-16.04_custom.list
@@ -0,0 +1,10 @@
+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
diff --git a/extras/dockerfiles/ubuntu_arm-16.04_install.sh b/extras/dockerfiles/ubuntu_arm-16.04_install.sh
new file mode 100644
index 0000000..c04008d
--- /dev/null
+++ b/extras/dockerfiles/ubuntu_arm-16.04_install.sh
@@ -0,0 +1,12 @@
+#!/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 \
+    g++-6 \
+    python \
+    clang-format
diff --git a/extras/dockerfiles/ubuntu_arm-17.10_custom.list b/extras/dockerfiles/ubuntu_arm-17.10_custom.list
new file mode 100644
index 0000000..69c660b
--- /dev/null
+++ b/extras/dockerfiles/ubuntu_arm-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_arm-17.10_install.sh b/extras/dockerfiles/ubuntu_arm-17.10_install.sh
new file mode 100644
index 0000000..8883fd4
--- /dev/null
+++ b/extras/dockerfiles/ubuntu_arm-17.10_install.sh
@@ -0,0 +1,15 @@
+#!/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 \
+    clang-5.0 \
+    python \
+    python3-sh \
+    python3-typed-ast \
+    clang-format
+
+pip3 install typed_ast
diff --git a/extras/packaging/debian.control b/extras/packaging/debian.control
index 6fb8f7a..694b0f1 100644
--- a/extras/packaging/debian.control
+++ b/extras/packaging/debian.control
@@ -7,6 +7,7 @@
 Package: libfruit
 Architecture: any
 Depends: ${shlibs:Depends}
+Recommends: fruit-dev
 Description: Dependency Injection Framework For C++
  Fruit is a dependency injection framework for C++, loosely inspired by the
  Guice framework for Java.
diff --git a/extras/packaging/deploy_to_bintray.bat b/extras/packaging/deploy_to_bintray.bat
new file mode 100644
index 0000000..2473343
--- /dev/null
+++ b/extras/packaging/deploy_to_bintray.bat
@@ -0,0 +1,10 @@
+set FRUIT_VERSION=3.4.0
+
+for %%G in (Release Debug) DO CMD /C for %%H in (True False) DO CMD /C for %%I in (True False) DO conan create . google/stable -o fruit:shared=%%H -o fruit:use_boost=%%I -s build_type=%%G
+
+conan remote add fruit-bintray https://api.bintray.com/conan/google/fruit
+
+REM To authenticate:
+REM conan user -p BINTRAY_API_KEY_HERE -r fruit-bintray polettimarco
+
+conan upload fruit/%FRUIT_VERSION%@google/stable --all -r fruit-bintray
diff --git a/extras/packaging/deploy_to_bintray.sh b/extras/packaging/deploy_to_bintray.sh
new file mode 100644
index 0000000..31a80c7
--- /dev/null
+++ b/extras/packaging/deploy_to_bintray.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+FRUIT_VERSION=3.4.0
+
+# To authenticate:
+# conan user -p <BINTRAY_API_KEY_HERE> -r fruit-bintray polettimarco
+
+for build_type in Release Debug
+do
+    for is_shared in True False
+    do
+        for use_boost in True False
+        do
+            conan create . google/stable -o fruit:shared=$is_shared -o fruit:use_boost=$use_boost -s build_type=$build_type
+        done
+    done
+done
+
+conan remote update fruit-bintray https://api.bintray.com/conan/google/fruit
+conan upload fruit/${FRUIT_VERSION}@google/stable --all -r fruit-bintray
diff --git a/extras/packaging/libfruit.spec b/extras/packaging/libfruit.spec
index b84e178..378abe5 100644
--- a/extras/packaging/libfruit.spec
+++ b/extras/packaging/libfruit.spec
@@ -13,6 +13,7 @@
 
 BuildRequires:  cmake
 BuildRequires:  boost-devel
+Suggests:       libfruit-devel = %{version}
 
 %if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version}
 BuildRequires:  gcc-c++ >= 5.0.0
diff --git a/extras/scripts/postsubmit-helper.sh b/extras/scripts/postsubmit-helper.sh
index 4f6fcaa..8188681 100755
--- a/extras/scripts/postsubmit-helper.sh
+++ b/extras/scripts/postsubmit-helper.sh
@@ -27,6 +27,11 @@
     export CXX=g++-7
     ;;
 
+gcc-8)
+    export CC=gcc-8
+    export CXX=g++-8
+    ;;
+
 clang-3.5)
     export CC=clang-3.5
     export CXX=clang++-3.5
@@ -66,6 +71,21 @@
     esac
     ;;
 
+clang-5.0)
+    export CC=clang-5.0
+    export CXX=clang++-5.0
+    ;;
+
+clang-6.0)
+    export CC=clang-6.0
+    export CXX=clang++-6.0
+    ;;
+
+clang-7.0)
+    export CC=clang-7
+    export CXX=clang++-7
+    ;;
+
 clang-default)
     export CC=clang
     export CXX=clang++
@@ -90,14 +110,14 @@
     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) ;;
+    DebugPlain)           CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug   -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -O2") ;;
+    DebugPlainNoPch)      CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug   -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -O2" -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+    DebugAsan)            CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug   -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -O0 -fsanitize=address") ;;
+    DebugAsanNoPch)       CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug   -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -O0 -fsanitize=address" -DFRUIT_TESTS_USE_PRECOMPILED_HEADERS=OFF) ;;
+    DebugAsanUbsan)       CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug   -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -O0 -fsanitize=address,undefined") ;;
+    DebugAsanUbsanNoPch)  CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug   -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -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=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -O2"     -DRUN_TESTS_UNDER_VALGRIND=TRUE) ;;
+    DebugValgrindNoPch)   CMAKE_ARGS=(-DCMAKE_BUILD_TYPE=Debug   -DCMAKE_CXX_FLAGS="$STLARG -Werror -pedantic -DFRUIT_DEBUG=1 -DFRUIT_EXTRA_DEBUG=1 -D_GLIBCXX_DEBUG=1 -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) ;;
diff --git a/extras/scripts/postsubmit.sh b/extras/scripts/postsubmit.sh
index 96c277a..a3e4716 100755
--- a/extras/scripts/postsubmit.sh
+++ b/extras/scripts/postsubmit.sh
@@ -10,16 +10,21 @@
 fi
 
 case $OS in
-linux)
+linux*)
+    case $OS in
+    linux)     DOCKER_IMAGE="polettimarco/fruit-basesystem:ubuntu-$UBUNTU" ;;
+    linux-arm) docker run --rm --privileged multiarch/qemu-user-static:register --reset
+               DOCKER_IMAGE="polettimarco/fruit-basesystem:ubuntu_arm-$UBUNTU" ;;
+    esac
     docker rm -f fruit &>/dev/null || true
-    docker run -d -it --name fruit --privileged polettimarco/fruit-basesystem:ubuntu-$UBUNTU
+    docker run -d -it --name fruit --privileged "${DOCKER_IMAGE}"
     docker exec fruit mkdir fruit
     docker cp . fruit:/fruit
-    
+
     docker exec fruit bash -c "
-        export COMPILER=$COMPILER; 
+        export COMPILER=$COMPILER;
         export N_JOBS=$N_JOBS;
-        export STLARG=$STLARG; 
+        export STLARG=$STLARG;
         export ASAN_OPTIONS=$ASAN_OPTIONS;
         export OS=$OS;
         cd fruit; extras/scripts/postsubmit-helper.sh $1"
diff --git a/extras/scripts/travis_ci_install_osx.sh b/extras/scripts/travis_ci_install_osx.sh
index bf4524b..cb80087 100755
--- a/extras/scripts/travis_ci_install_osx.sh
+++ b/extras/scripts/travis_ci_install_osx.sh
@@ -39,7 +39,7 @@
 *) echo "Compiler not supported: ${COMPILER}. See travis_ci_install_osx.sh"; exit 1 ;;
 esac
 
-time brew upgrade python
+install_brew_package python
 time pip3 install pytest
 time pip3 install pytest-xdist
 time pip3 install sh
diff --git a/extras/scripts/travis_yml_generator.py b/extras/scripts/travis_yml_generator.py
index 514c2c4..9188269 100755
--- a/extras/scripts/travis_yml_generator.py
+++ b/extras/scripts/travis_yml_generator.py
@@ -67,7 +67,7 @@
 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,
+def add_ubuntu_tests(ubuntu_version, compiler, os='linux', stl=None, asan=True, ubsan=True,
                      use_precompiled_headers_in_tests=True, smoke_tests=[], exclude_tests=[], include_only_tests=None):
   env = {
     'UBUNTU': ubuntu_version,
@@ -76,7 +76,7 @@
   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)
+  export_statements = 'export OS=' + os + '; ' + 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,
@@ -138,8 +138,9 @@
     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_ubuntu_tests(ubuntu_version='18.10', compiler='gcc-8', asan=False, ubsan=False, smoke_tests=['DebugPlain', 'ReleasePlain'])
+add_ubuntu_tests(ubuntu_version='18.10', compiler='clang-4.0', stl='libstdc++')
+add_ubuntu_tests(ubuntu_version='18.10', compiler='clang-7.0', stl='libstdc++', smoke_tests=['DebugPlain', 'DebugAsanUbsan', 'ReleasePlain'])
 
 add_bazel_tests(ubuntu_version='16.04', smoke_tests=['DebugPlain'])
 
@@ -158,17 +159,15 @@
 # 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'])
+add_osx_tests(compiler='clang-default', xcode_version='8.2', stl='libc++', ubsan=False)
+
+add_osx_tests(compiler='clang-default', xcode_version='9.4', stl='libc++')
+add_osx_tests(compiler='clang-default', xcode_version='10', stl='libc++', smoke_tests=['DebugPlain'])
 
 # ** Disabled combinations **
 #
diff --git a/include/fruit/component.h b/include/fruit/component.h
index 5fbfdc6..98eb727 100644
--- a/include/fruit/component.h
+++ b/include/fruit/component.h
@@ -892,6 +892,37 @@
   install(fruit::Component<OtherComponentParams...> (*)(FormalArgs...), Args&&... args);
 
   /**
+   * Similar to install(), but allows to install a variable number of component functions instead of just 1. This
+   * additional flexibility is sometimes useful in templated `get*Component` functions and for other advanced use-cases.
+   *
+   * To use this method, wrap each get*Component function with its args in a fruit::ComponentFunction<...> object (using
+   * the helper function fruit::componentFunction), then pass all the fruit::ComponentFunction<...> objects (which can
+   * potentially have different params) to this method.
+   *
+   * For example:
+   *
+   * fruit::Component<Foo, Bar> getBarBazComponent() {
+   *   return fruit::createComponent()
+   *       .installComponentFunctions(
+   *           fruit::componentFunction(getFooComponent, a, b, c),
+   *           fruit::componentFunction(getBarComponent, x, y));
+   * }
+   *
+   * Is equivalent to:
+   *
+   * fruit::Component<Foo, Bar> getBarBazComponent() {
+   *   return fruit::createComponent()
+   *       .install(getFooComponent, a, b, c)
+   *       .install(getBarComponent, x, y);
+   * }
+   * 
+   * This is a simple example to show the idea, however in a simple case like this it's easier to just use install().
+   */
+  template <typename... ComponentFunctions>
+  PartialComponent<fruit::impl::InstallComponentFunctions<ComponentFunctions...>, Bindings...>
+  installComponentFunctions(ComponentFunctions... componentFunctions);
+
+  /**
    * This class is returned by PartialComponent::replace, see the documentation of that method for more information.
    */
   template <typename ReplacedComponent, typename... GetReplacedComponentFormalArgs>
@@ -965,7 +996,7 @@
    * 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
+   * to replace component functions 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).
    *
diff --git a/include/fruit/component_function.h b/include/fruit/component_function.h
new file mode 100644
index 0000000..56b77a1
--- /dev/null
+++ b/include/fruit/component_function.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_COMPONENT_FUNCTION_H
+#define FRUIT_COMPONENT_FUNCTION_H
+
+#include <fruit/impl/fruit_internal_forward_decls.h>
+
+namespace fruit {
+
+/**
+ * See fruit::componentFunction() helper for how to construct a ComponentFunction, and see
+ * PartialComponent::installComponentFunctions() for more information on using ComponentFunction objects.
+ */
+template <typename ComponentType, typename... ComponentFunctionArgs>
+class ComponentFunction {
+private:
+    ComponentType (*getComponent)(ComponentFunctionArgs...);
+    std::tuple<ComponentFunctionArgs...> args_tuple;
+
+    /**
+     * This is (intentionally) private, use fruit::componentFunction() to construct ComponentFunction objects.
+     */
+    ComponentFunction(ComponentType (*getComponent)(ComponentFunctionArgs...), ComponentFunctionArgs... args);
+
+    friend struct fruit::impl::ComponentStorageEntry;
+
+public:
+	// Prefer using the simpler componentFunction() below instead of this.
+	template <typename... ActualArgs>
+    static ComponentFunction<ComponentType, ComponentFunctionArgs...> create(
+		ComponentType (*getComponent)(ComponentFunctionArgs...), ActualArgs&&... args);
+
+    ComponentFunction(const ComponentFunction&) = default;
+    ComponentFunction(ComponentFunction&&) = default;
+
+    ComponentFunction& operator=(const ComponentFunction&) = default;
+    ComponentFunction& operator=(ComponentFunction&&) = default;
+
+    ComponentType operator()();
+};
+
+
+/**
+ * This function allows to easily construct a ComponentFunction without explicitly mentioning its type.
+ * See PartialComponent::installComponentFunctions() for more information on using ComponentFunction.
+ */
+template <typename... ComponentParams, typename... FormalArgs, typename... ActualArgs>
+ComponentFunction<fruit::Component<ComponentParams...>, FormalArgs...> componentFunction(
+    fruit::Component<ComponentParams...> (*getComponent)(FormalArgs...),
+    ActualArgs&&... args);
+
+}
+
+#include <fruit/impl/component_function.defn.h>
+
+#endif // FRUIT_COMPONENT_FUNCTION_H
diff --git a/include/fruit/fruit.h b/include/fruit/fruit.h
index ff208e7..901736f 100644
--- a/include/fruit/fruit.h
+++ b/include/fruit/fruit.h
@@ -25,6 +25,7 @@
 #include <fruit/impl/injection_errors.h>
 
 #include <fruit/component.h>
+#include <fruit/component_function.h>
 #include <fruit/fruit_forward_decls.h>
 #include <fruit/injector.h>
 #include <fruit/macro.h>
diff --git a/include/fruit/fruit_forward_decls.h b/include/fruit/fruit_forward_decls.h
index af02706..a12982f 100644
--- a/include/fruit/fruit_forward_decls.h
+++ b/include/fruit/fruit_forward_decls.h
@@ -60,6 +60,9 @@
 template <typename... P>
 class Injector;
 
+template <typename ComponentType, typename... ComponentFunctionArgs>
+class ComponentFunction;
+
 } // namespace fruit
 
 #endif // FRUIT_FRUIT_FORWARD_DECLS_H
diff --git a/include/fruit/impl/bindings.h b/include/fruit/impl/bindings.h
index c8694e5..ce6ca49 100644
--- a/include/fruit/impl/bindings.h
+++ b/include/fruit/impl/bindings.h
@@ -124,6 +124,12 @@
 struct InstallComponent {};
 
 /**
+ * Installs all the specified ComponentFunction objects.
+ */
+template <typename... ComponentFunctions>
+struct InstallComponentFunctions {};
+
+/**
  * An in-progress ReplaceComponent operation, where we don't have all the required information yet.
  */
 template <typename GetReplacedComponent>
diff --git a/include/fruit/impl/component.defn.h b/include/fruit/impl/component.defn.h
index ce3fb90..382ecac 100644
--- a/include/fruit/impl/component.defn.h
+++ b/include/fruit/impl/component.defn.h
@@ -21,6 +21,7 @@
 
 #include <fruit/impl/component_storage/component_storage.h>
 #include <fruit/impl/injection_errors.h>
+#include <fruit/impl/component_install_arg_checks.h>
 
 #include <memory>
 
@@ -54,7 +55,7 @@
   using Op = typename fruit::impl::meta::OpForComponent<Bindings...>::template ConvertTo<Comp>;
   (void)typename fruit::impl::meta::CheckIfError<Op>::type();
 
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !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
@@ -242,26 +243,6 @@
 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...)>,
@@ -269,7 +250,7 @@
 PartialComponent<Bindings...>::install(fruit::Component<OtherComponentParams...> (*getComponent)(FormalArgs...),
                                        Args&&... args) {
   using IntCollector = int[];
-  (void)IntCollector{0, checkAcceptableComponentInstallArg<FormalArgs>()...};
+  (void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<FormalArgs>()...};
 
   using Op = OpFor<fruit::impl::InstallComponent<fruit::Component<OtherComponentParams...>(FormalArgs...)>>;
   (void)typename fruit::impl::meta::CheckIfError<Op>::type();
@@ -280,13 +261,26 @@
 }
 
 template <typename... Bindings>
+template <typename... ComponentFunctions>
+inline PartialComponent<fruit::impl::InstallComponentFunctions<ComponentFunctions...>, Bindings...>
+PartialComponent<Bindings...>::installComponentFunctions(ComponentFunctions... componentFunctions) {
+
+  using Op = OpFor<fruit::impl::InstallComponentFunctions<ComponentFunctions...>>;
+  (void)typename fruit::impl::meta::CheckIfError<Op>::type();
+
+  std::tuple<ComponentFunctions...> component_functions_tuple{std::move(componentFunctions)...};
+
+  return {{storage, std::move(component_functions_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>()...};
+  (void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<FormalArgs>()...};
 
   std::tuple<FormalArgs...> args_tuple{std::forward<Args>(args)...};
 
@@ -303,7 +297,7 @@
     PartialComponentWithReplacementInProgress<OtherComponent, GetReplacedComponentFormalArgs...>::with(
         OtherComponent (*getReplacementComponent)(GetReplacementComponentFormalArgs...), Args&&... args) {
   using IntCollector = int[];
-  (void)IntCollector{0, checkAcceptableComponentInstallArg<GetReplacementComponentFormalArgs>()...};
+  (void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<GetReplacementComponentFormalArgs>()...};
 
   std::tuple<GetReplacementComponentFormalArgs...> args_tuple{std::forward<Args>(args)...};
 
diff --git a/include/fruit/impl/component_function.defn.h b/include/fruit/impl/component_function.defn.h
new file mode 100644
index 0000000..e006198
--- /dev/null
+++ b/include/fruit/impl/component_function.defn.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_COMPONENT_FUNCTION_DEFN_H
+#define FRUIT_COMPONENT_FUNCTION_DEFN_H
+
+#include <fruit/component_function.h>
+#include <fruit/impl/util/call_with_tuple.h>
+#include <fruit/impl/component_install_arg_checks.h>
+
+namespace fruit {
+
+template <typename ComponentType, typename... ComponentFunctionArgs>
+inline ComponentFunction<ComponentType, ComponentFunctionArgs...>::ComponentFunction(
+        ComponentType (*getComponent)(ComponentFunctionArgs...), ComponentFunctionArgs... args)
+    : getComponent(getComponent), args_tuple{args...} {
+    using IntCollector = int[];
+    (void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<ComponentFunctionArgs>()...};
+}
+
+template <typename ComponentType, typename... ComponentFunctionArgs>
+template <typename... ActualArgs>
+inline ComponentFunction<ComponentType, ComponentFunctionArgs...>
+ComponentFunction<ComponentType, ComponentFunctionArgs...>::create(
+	ComponentType(*getComponent)(ComponentFunctionArgs...), ActualArgs&&... args) {
+  return ComponentFunction<ComponentType, ComponentFunctionArgs...>(getComponent, std::forward<ActualArgs>(args)...);
+}
+
+template <typename ComponentType, typename... ComponentFunctionArgs>
+inline ComponentType ComponentFunction<ComponentType, ComponentFunctionArgs...>::operator()() {
+    return fruit::impl::callWithTuple(getComponent, args_tuple);
+}
+
+template <typename... ComponentParams, typename... FormalArgs, typename... ActualArgs>
+inline ComponentFunction<fruit::Component<ComponentParams...>, FormalArgs...> componentFunction(
+        fruit::Component<ComponentParams...> (*getComponent)(FormalArgs...),
+        ActualArgs&&... args) {
+    return ComponentFunction<fruit::Component<ComponentParams...>, FormalArgs...>::create(
+        getComponent, std::forward<ActualArgs>(args)...);
+}
+
+}
+
+#endif // FRUIT_COMPONENT_FUNCTION_DEFN_H
diff --git a/include/fruit/impl/component_functors.defn.h b/include/fruit/impl/component_functors.defn.h
index c65f953..e03b0a8 100644
--- a/include/fruit/impl/component_functors.defn.h
+++ b/include/fruit/impl/component_functors.defn.h
@@ -138,7 +138,7 @@
   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
+#if !FRUIT_NO_LOOP_CHECK
                            typename Comp::Deps,
 #endif
                            PushFront(typename Comp::InterfaceBindings, Pair<AnnotatedI, AnnotatedC>),
@@ -635,7 +635,7 @@
         Eval<RealOp>()(entries);
       }
       std::size_t numEntries() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if 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());
@@ -667,7 +667,7 @@
         Eval<RealOp>()(entries);
       };
       std::size_t numEntries() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
         auto provider = [](NakedArgs... args) {
           return std::unique_ptr<NakedT>(new NakedT(std::forward<NakedArgs>(args)...));
         };
@@ -688,7 +688,7 @@
     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
+#if !FRUIT_NO_LOOP_CHECK
     using new_Deps = ConcatVectors(typename OtherComp::Deps, typename Comp::Deps);
 #endif
     FruitStaticAssert(IsSame(typename OtherComp::InterfaceBindings, Vector<>));
@@ -698,7 +698,7 @@
     using new_DeferredBindingFunctors = typename Comp::DeferredBindingFunctors;
 
     using R = ConsComp(new_RsSuperset, new_Ps, new_NonConstRsPs,
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !FRUIT_NO_LOOP_CHECK
                        new_Deps,
 #endif
                        new_InterfaceBindings, new_DeferredBindingFunctors);
@@ -740,6 +740,31 @@
   };
 };
 
+struct InstallComponentFunctions {
+    template <typename Comp, typename... ComponentFunctions>
+    struct apply;
+
+    template <typename Comp>
+    struct apply<Comp> {
+      using type = ComponentFunctorIdentity(Comp);
+    };
+
+    template <typename Comp, typename... ComponentParams, typename... ComponentFunctionArgs, typename... ComponentFunctions>
+    struct apply<Comp, Type<fruit::ComponentFunction<fruit::Component<ComponentParams...>, ComponentFunctionArgs...>>, ComponentFunctions...> {
+      using type =
+          Call(
+              Compose2ComponentFunctors(
+                  ComponentFunctor(InstallComponent, ConstructComponentImpl(Type<ComponentParams>...)),
+                  ComponentFunctor(InstallComponentFunctions, ComponentFunctions...)),
+              Comp);
+    };
+
+    template <typename Comp, typename T, typename... ComponentFunctions>
+    struct apply<Comp, T, ComponentFunctions...> {
+        using type = ConstructError(IncorrectArgTypePassedToInstallComponentFuntionsErrorTag, T);
+    };
+};
+
 // CatchAll(PropagateError(Expr, Bool<false>), IsErrorExceptionHandler) evaluates to Bool<true> if Expr throws an error,
 // and Bool<false> otherwise.
 struct IsErrorExceptionHandler {
@@ -781,7 +806,7 @@
 
 // Not needed, just double-checking.
 // Uses FruitStaticAssert instead of FruitDelegateCheck so that it's checked only in debug mode.
-#ifdef FRUIT_EXTRA_DEBUG
+#if 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.
@@ -795,18 +820,18 @@
   struct apply;
 
   template <typename RsSupersetParam, typename PsParam, typename NonConstRsPsParam,
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !FRUIT_NO_LOOP_CHECK
             typename DepsParam,
 #endif
             typename InterfaceBindingsParam, typename DeferredBindingFunctors>
   struct apply<Comp<RsSupersetParam, PsParam, NonConstRsPsParam,
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !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
+#if !FRUIT_NO_LOOP_CHECK
                            DepsParam,
 #endif
                            InterfaceBindingsParam, EmptyList);
@@ -903,7 +928,7 @@
         Eval<RealOp>()(entries);
       }
       std::size_t numEntries() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
         using NakedC = UnwrapType<Eval<C>>;
         auto provider = [](const UnwrapType<Eval<CFunctor>>& fun) {
           return UnwrapType<Eval<IFunctor>>([=](typename TypeUnwrapper<Args>::type... args) {
@@ -965,7 +990,7 @@
         Eval<RealOp>()(entries);
       }
       std::size_t numEntries() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if 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...));
@@ -1249,6 +1274,11 @@
     using type = ComponentFunctor(InstallComponentHelper, Type<Params>...);
   };
 
+  template <typename... ComponentFunctions>
+  struct apply<fruit::impl::InstallComponentFunctions<ComponentFunctions...>> {
+    using type = ComponentFunctor(InstallComponentFunctions, Type<ComponentFunctions>...);
+  };
+
   template <typename GetReplacedComponent, typename GetReplacementComponent>
   struct apply<fruit::impl::ReplaceComponent<GetReplacedComponent, GetReplacementComponent>> {
     using type = ComponentFunctorIdentity;
diff --git a/include/fruit/impl/component_install_arg_checks.defn.h b/include/fruit/impl/component_install_arg_checks.defn.h
new file mode 100644
index 0000000..781848f
--- /dev/null
+++ b/include/fruit/impl/component_install_arg_checks.defn.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_COMPONENT_INSTALL_ARG_CHECKS_DEFN_H
+#define FRUIT_COMPONENT_INSTALL_ARG_CHECKS_DEFN_H
+
+#include <fruit/impl/component_install_arg_checks.h>
+
+#include <utility>
+#include <functional>
+
+namespace fruit {
+namespace impl {
+
+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;
+}
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_COMPONENT_INSTALL_ARG_CHECKS_DEFN_H
diff --git a/include/fruit/impl/component_install_arg_checks.h b/include/fruit/impl/component_install_arg_checks.h
new file mode 100644
index 0000000..dd52f05
--- /dev/null
+++ b/include/fruit/impl/component_install_arg_checks.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 FRUIT_COMPONENT_INSTALL_ARG_CHECKS_H
+#define FRUIT_COMPONENT_INSTALL_ARG_CHECKS_H
+
+#include <fruit/impl/fruit-config.h>
+
+namespace fruit {
+namespace impl {
+
+template <typename T>
+int checkAcceptableComponentInstallArg();
+
+} // namespace impl
+} // namespace fruit
+
+#include <fruit/impl/component_install_arg_checks.defn.h>
+
+#endif // FRUIT_COMPONENT_INSTALL_ARG_CHECKS_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
index a9d139c..08d6640 100644
--- a/include/fruit/impl/component_storage/component_storage_entry.defn.h
+++ b/include/fruit/impl/component_storage/component_storage_entry.defn.h
@@ -20,6 +20,7 @@
 #include <fruit/impl/component_storage/component_storage_entry.h>
 #include <fruit/impl/util/call_with_tuple.h>
 #include <fruit/impl/util/hash_codes.h>
+#include <fruit/component_function.h>
 
 namespace fruit {
 namespace impl {
@@ -53,7 +54,7 @@
   case Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS:
   case Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
     lazy_component_with_args.destroy();
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
     kind = Kind::INVALID;
 #endif
     break;
@@ -119,6 +120,12 @@
   return result;
 }
 
+template <typename Component, typename Arg, typename... Args>
+inline ComponentStorageEntry ComponentStorageEntry::LazyComponentWithArgs::create(
+    fruit::ComponentFunction<Component, Arg, Args...> component_function) {
+  return LazyComponentWithArgs::create(component_function.getComponent, component_function.args_tuple);
+}
+
 template <typename Component, typename... Args>
 inline ComponentStorageEntry
 ComponentStorageEntry::LazyComponentWithArgs::createReplacedComponentEntry(Component (*fun)(Args...),
@@ -177,6 +184,12 @@
 }
 
 template <typename Component>
+inline ComponentStorageEntry ComponentStorageEntry::LazyComponentWithNoArgs::create(
+        fruit::ComponentFunction<Component> component_function) {
+  return LazyComponentWithNoArgs::create(component_function.getComponent);
+}
+
+template <typename Component>
 inline ComponentStorageEntry
 ComponentStorageEntry::LazyComponentWithNoArgs::createReplacedComponentEntry(Component (*fun)()) {
   FruitAssert(fun != nullptr);
diff --git a/include/fruit/impl/component_storage/component_storage_entry.h b/include/fruit/impl/component_storage/component_storage_entry.h
index a320b49..80e1e48 100644
--- a/include/fruit/impl/component_storage/component_storage_entry.h
+++ b/include/fruit/impl/component_storage/component_storage_entry.h
@@ -34,7 +34,7 @@
  */
 struct ComponentStorageEntry {
   enum class Kind {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
     INVALID,
 #endif
     BINDING_FOR_CONSTRUCTED_OBJECT,
@@ -66,7 +66,7 @@
     COMPONENT_WITHOUT_ARGS_END_MARKER,
   };
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   mutable
 #endif
       Kind kind;
@@ -89,7 +89,7 @@
     // 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
+#if FRUIT_EXTRA_DEBUG
     bool is_nonconst;
 #endif
   };
@@ -111,7 +111,7 @@
     // The type IDs that this type depends on.
     const BindingDeps* deps;
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
     bool is_nonconst;
 #endif
   };
@@ -203,6 +203,9 @@
     static ComponentStorageEntry create(Component (*fun)());
 
     template <typename Component>
+    static ComponentStorageEntry create(fruit::ComponentFunction<Component> component_function);
+
+    template <typename Component>
     static ComponentStorageEntry createReplacedComponentEntry(Component (*fun)());
 
     template <typename Component>
@@ -258,6 +261,9 @@
     template <typename Component, typename... Args>
     static ComponentStorageEntry create(Component (*fun)(Args...), std::tuple<Args...> args_tuple);
 
+    template <typename Component, typename Arg, typename... Args>
+    static ComponentStorageEntry create(fruit::ComponentFunction<Component, Arg, Args...> component_function);
+
     template <typename Component, typename... Args>
     static ComponentStorageEntry createReplacedComponentEntry(Component (*fun)(Args...),
                                                               std::tuple<Args...> args_tuple);
@@ -322,7 +328,7 @@
 };
 
 // We can't have this assert in debug mode because we add debug-only fields that increase the size.
-#ifndef FRUIT_EXTRA_DEBUG
+#if !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),
diff --git a/include/fruit/impl/component_storage/partial_component_storage.defn.h b/include/fruit/impl/component_storage/partial_component_storage.defn.h
index 09e0c66..18e65e5 100644
--- a/include/fruit/impl/component_storage/partial_component_storage.defn.h
+++ b/include/fruit/impl/component_storage/partial_component_storage.defn.h
@@ -370,6 +370,62 @@
   }
 };
 
+template <std::size_t i, typename ComponentFunctionsTuple>
+struct AddAllComponentStorageEntries {
+    inline void operator()(FixedSizeVector<ComponentStorageEntry>& entries,
+                           ComponentFunctionsTuple& component_functions_tuple) {
+      AddAllComponentStorageEntries<i - 1, ComponentFunctionsTuple>()(entries, component_functions_tuple);
+      entries.push_back(createEntry(std::move(std::get<i - 1>(component_functions_tuple))));
+    }
+
+    template <typename ComponentType>
+    inline ComponentStorageEntry createEntry(
+        fruit::ComponentFunction<ComponentType> component_function) {
+      return ComponentStorageEntry::LazyComponentWithNoArgs::create(std::move(component_function));
+    }
+
+    template <typename ComponentType, typename Arg, typename... Args>
+    inline ComponentStorageEntry createEntry(
+        fruit::ComponentFunction<ComponentType, Arg, Args...> component_function) {
+      return ComponentStorageEntry::LazyComponentWithArgs::create(std::move(component_function));
+    }
+};
+
+template <typename ComponentFunctionsTuple>
+struct AddAllComponentStorageEntries<0, ComponentFunctionsTuple> {
+    inline void operator()(FixedSizeVector<ComponentStorageEntry>&,
+                           ComponentFunctionsTuple&) {}
+};
+
+template <typename ComponentFunctionsTuple>
+void addAllComponentStorageEntries(FixedSizeVector<ComponentStorageEntry>& entries,
+                                   ComponentFunctionsTuple&& component_functions_tuple) {
+  AddAllComponentStorageEntries<std::tuple_size<ComponentFunctionsTuple>::value,
+                                ComponentFunctionsTuple>()(
+      entries, component_functions_tuple);
+}
+
+template <typename... ComponentFunctions, typename... PreviousBindings>
+class PartialComponentStorage<InstallComponentFunctions<ComponentFunctions...>, PreviousBindings...> {
+private:
+  PartialComponentStorage<PreviousBindings...>& previous_storage;
+  std::tuple<ComponentFunctions...> component_functions_tuple;
+
+public:
+  PartialComponentStorage(PartialComponentStorage<PreviousBindings...>& previous_storage,
+                          std::tuple<ComponentFunctions...> component_functions_tuple)
+      : previous_storage(previous_storage), component_functions_tuple(std::move(component_functions_tuple)) {}
+
+  void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+    addAllComponentStorageEntries(entries, std::move(component_functions_tuple));
+    previous_storage.addBindings(entries);
+  }
+
+  std::size_t numBindings() const {
+    return previous_storage.numBindings() + sizeof...(ComponentFunctions);
+  }
+};
+
 template <typename OtherComponent, typename... PreviousBindings>
 class PartialComponentStorage<PartialReplaceComponent<OtherComponent()>, PreviousBindings...> {
 private:
diff --git a/include/fruit/impl/data_structures/fixed_size_allocator.defn.h b/include/fruit/impl/data_structures/fixed_size_allocator.defn.h
index 383b737..7bad811 100644
--- a/include/fruit/impl/data_structures/fixed_size_allocator.defn.h
+++ b/include/fruit/impl/data_structures/fixed_size_allocator.defn.h
@@ -21,7 +21,7 @@
 
 #include <cassert>
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 #include <iostream>
 #endif
 
@@ -44,7 +44,7 @@
 }
 
 inline void FixedSizeAllocator::FixedSizeAllocatorData::addType(TypeId typeId) {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   types[typeId]++;
 #endif
   if (!typeId.type_info->isTriviallyDestructible()) {
@@ -71,7 +71,7 @@
 
   char* p = storage_last_used;
   size_t misalignment = std::uintptr_t(p) % alignof(T);
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   FruitAssert(remaining_types[getTypeId<AnnotatedT>()] != 0);
   remaining_types[getTypeId<AnnotatedT>()]--;
 #endif
@@ -103,7 +103,7 @@
   // 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
+#if FRUIT_EXTRA_DEBUG
   remaining_types = allocator_data.types;
   std::cerr << "Constructing allocator for types:";
   for (auto x : remaining_types) {
@@ -117,7 +117,7 @@
   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
+#if FRUIT_EXTRA_DEBUG
   std::swap(remaining_types, x.remaining_types);
 #endif
 }
@@ -126,7 +126,7 @@
   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
+#if FRUIT_EXTRA_DEBUG
   std::swap(remaining_types, x.remaining_types);
 #endif
   return *this;
diff --git a/include/fruit/impl/data_structures/fixed_size_allocator.h b/include/fruit/impl/data_structures/fixed_size_allocator.h
index fa0480c..cbc8ca8 100644
--- a/include/fruit/impl/data_structures/fixed_size_allocator.h
+++ b/include/fruit/impl/data_structures/fixed_size_allocator.h
@@ -21,7 +21,7 @@
 #include <fruit/impl/meta/component.h>
 #include <fruit/impl/util/type_info.h>
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 #include <unordered_map>
 #endif
 
@@ -43,7 +43,7 @@
   // The chunk of memory that will be used for all allocations.
   char* storage_begin = nullptr;
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   std::unordered_map<TypeId, std::size_t> remaining_types;
 #endif
 
@@ -66,7 +66,7 @@
   private:
     std::size_t total_size = 0;
     std::size_t num_types_to_destroy = 0;
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
     std::unordered_map<TypeId, std::size_t> types;
 #endif
 
diff --git a/include/fruit/impl/data_structures/fixed_size_vector.defn.h b/include/fruit/impl/data_structures/fixed_size_vector.defn.h
index 79d0c97..ed8a533 100644
--- a/include/fruit/impl/data_structures/fixed_size_vector.defn.h
+++ b/include/fruit/impl/data_structures/fixed_size_vector.defn.h
@@ -84,12 +84,12 @@
 
 template <typename T, typename Allocator>
 inline void FixedSizeVector<T, Allocator>::push_back(T x) {
-#ifdef FRUIT_EXTRA_DEBUG
+#if 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
+#if FRUIT_EXTRA_DEBUG
   FruitAssert(v_end <= v_begin + capacity);
 #endif
 }
diff --git a/include/fruit/impl/data_structures/fixed_size_vector.templates.h b/include/fruit/impl/data_structures/fixed_size_vector.templates.h
index 5ce3959..d95953e 100644
--- a/include/fruit/impl/data_structures/fixed_size_vector.templates.h
+++ b/include/fruit/impl/data_structures/fixed_size_vector.templates.h
@@ -17,7 +17,7 @@
 #ifndef FRUIT_FIXED_SIZE_VECTOR_TEMPLATES_H
 #define FRUIT_FIXED_SIZE_VECTOR_TEMPLATES_H
 
-#ifndef IN_FRUIT_CPP_FILE
+#if !IN_FRUIT_CPP_FILE
 #error "Fruit .template.h file included in non-cpp file."
 #endif
 
diff --git a/include/fruit/impl/data_structures/memory_pool.defn.h b/include/fruit/impl/data_structures/memory_pool.defn.h
index 4d892d4..d4630f9 100644
--- a/include/fruit/impl/data_structures/memory_pool.defn.h
+++ b/include/fruit/impl/data_structures/memory_pool.defn.h
@@ -53,7 +53,7 @@
 
 template <typename T>
 FRUIT_ALWAYS_INLINE inline T* MemoryPool::allocate(std::size_t n) {
-#ifdef FRUIT_DISABLE_ARENA_ALLOCATION
+#if FRUIT_DISABLE_ARENA_ALLOCATION
   void* p = operator new(n * sizeof(T));
   allocated_chunks.push_back(p);
   return static_cast<T*>(p);
diff --git a/include/fruit/impl/data_structures/semistatic_graph.h b/include/fruit/impl/data_structures/semistatic_graph.h
index b48f1cf..26eaf46 100644
--- a/include/fruit/impl/data_structures/semistatic_graph.h
+++ b/include/fruit/impl/data_structures/semistatic_graph.h
@@ -20,7 +20,7 @@
 #include "memory_pool.h"
 #include <fruit/impl/data_structures/semistatic_map.h>
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 #include <iostream>
 #endif
 
@@ -67,7 +67,7 @@
   SemistaticMap<NodeId, InternalNodeId> node_index_map;
 
   struct NodeData {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
     NodeId key;
 #endif
 
@@ -93,7 +93,7 @@
   // The first element is unused.
   FixedSizeVector<InternalNodeId> edges_storage;
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   template <typename NodeIter>
   void printGraph(NodeIter first, NodeIter last);
 #endif
@@ -226,7 +226,7 @@
   node_iterator find(NodeId nodeId);
   const_node_iterator find(NodeId nodeId) const;
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   // Emits a runtime error if some node was not created but there is an edge pointing to it.
   void checkFullyConstructed();
 #endif
diff --git a/include/fruit/impl/data_structures/semistatic_graph.templates.h b/include/fruit/impl/data_structures/semistatic_graph.templates.h
index e55be4c..37e9484 100644
--- a/include/fruit/impl/data_structures/semistatic_graph.templates.h
+++ b/include/fruit/impl/data_structures/semistatic_graph.templates.h
@@ -17,7 +17,7 @@
 #ifndef SEMISTATIC_GRAPH_TEMPLATES_H
 #define SEMISTATIC_GRAPH_TEMPLATES_H
 
-#ifndef IN_FRUIT_CPP_FILE
+#if !IN_FRUIT_CPP_FILE
 #error "Fruit .template.h file included in non-cpp file."
 #endif
 
@@ -28,7 +28,7 @@
 #include <fruit/impl/data_structures/semistatic_map.templates.h>
 #include <fruit/impl/util/hash_helpers.h>
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 #include <iostream>
 #endif
 
@@ -48,9 +48,13 @@
   auto operator*() -> decltype(std::make_pair(*iter, SemistaticGraphInternalNodeId{index})) {
     return std::make_pair(*iter, SemistaticGraphInternalNodeId{index});
   }
+
+  bool operator==(const indexing_iterator<Iter, index_increment>& other) const {
+    return iter == other.iter;
+  }
 };
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 template <typename NodeId, typename Node>
 template <typename NodeIter>
 void SemistaticGraph<NodeId, Node>::printGraph(NodeIter first, NodeIter last) {
@@ -93,7 +97,10 @@
 
   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);
+      indexing_iterator<itr_t, sizeof(NodeData)>{node_ids.begin(), 0},
+      indexing_iterator<itr_t, sizeof(NodeData)>{node_ids.end(), node_ids.size() * sizeof(NodeData)},
+      node_ids.size(),
+      memory_pool);
 
   first_unused_index = node_ids.size();
 
@@ -101,7 +108,7 @@
 
   // Note that not all of these will be assigned in the loop below.
   nodes = FixedSizeVector<NodeData>(first_unused_index, NodeData{
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
                                                             NodeId(),
 #endif
                                                             1, Node()});
@@ -124,7 +131,7 @@
     }
   }
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   printGraph(first, last);
 #endif
 }
@@ -177,7 +184,7 @@
   // 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
+#if FRUIT_EXTRA_DEBUG
         NodeId(),
 #endif
         1, Node()});
@@ -201,12 +208,12 @@
     }
   }
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   printGraph(first, last);
 #endif
 }
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 template <typename NodeId, typename Node>
 void SemistaticGraph<NodeId, Node>::checkFullyConstructed() {
   for (NodeData& data : nodes) {
@@ -216,7 +223,7 @@
     }
   }
 }
-#endif // !NDEBUG
+#endif // !FRUIT_EXTRA_DEBUG
 
 // This is here so that we don't have to include fixed_size_vector.templates.h in fruit.h.
 template <typename NodeId, typename Node>
diff --git a/include/fruit/impl/data_structures/semistatic_map.h b/include/fruit/impl/data_structures/semistatic_map.h
index aa5bf3a..cd03cc2 100644
--- a/include/fruit/impl/data_structures/semistatic_map.h
+++ b/include/fruit/impl/data_structures/semistatic_map.h
@@ -92,7 +92,7 @@
    * 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);
+  SemistaticMap(Iter begin, Iter end, 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'.
diff --git a/include/fruit/impl/data_structures/semistatic_map.templates.h b/include/fruit/impl/data_structures/semistatic_map.templates.h
index f9034c3..7c62f24 100644
--- a/include/fruit/impl/data_structures/semistatic_map.templates.h
+++ b/include/fruit/impl/data_structures/semistatic_map.templates.h
@@ -17,7 +17,7 @@
 #ifndef SEMISTATIC_MAP_TEMPLATES_H
 #define SEMISTATIC_MAP_TEMPLATES_H
 
-#ifndef IN_FRUIT_CPP_FILE
+#if !IN_FRUIT_CPP_FILE
 #error "Fruit .template.h file included in non-cpp file."
 #endif
 
@@ -40,7 +40,8 @@
 
 template <typename Key, typename Value>
 template <typename Iter>
-SemistaticMap<Key, Value>::SemistaticMap(Iter values_begin, std::size_t num_values, MemoryPool& memory_pool) {
+SemistaticMap<Key, Value>::SemistaticMap(
+    Iter values_begin, Iter values_end, std::size_t num_values, MemoryPool& memory_pool) {
   NumBits num_bits = pickNumBits(num_values);
   std::size_t num_buckets = size_t(1) << num_bits;
 
@@ -57,8 +58,7 @@
   while (1) {
     hash_function.a = random_distribution(random_generator);
 
-    Iter itr = values_begin;
-    for (std::size_t i = 0; i < num_values; ++i, ++itr) {
+    for (Iter itr = values_begin; !(itr == values_end); ++itr) {
       Unsigned& this_count = count[hash((*itr).first)];
       ++this_count;
       if (this_count == beta) {
@@ -68,9 +68,7 @@
     break;
 
   pick_another:
-    for (std::size_t i = 0; i < num_buckets; ++i) {
-      count[i] = 0;
-    }
+    std::memset(count.data(), 0, num_buckets * sizeof(Unsigned));
   }
 
   values = FixedSizeVector<value_type>(num_values, value_type());
@@ -187,7 +185,7 @@
   while ((std::size_t(1) << result) < n) {
     ++result;
   }
-  return result;
+  return result + 1;
 }
 
 // This is here so that we don't have to include fixed_size_vector.templates.h in fruit.h.
diff --git a/include/fruit/impl/fruit_assert.h b/include/fruit/impl/fruit_assert.h
index b2b4b0d..b3f6fea 100644
--- a/include/fruit/impl/fruit_assert.h
+++ b/include/fruit/impl/fruit_assert.h
@@ -17,7 +17,7 @@
 #ifndef FRUIT_ASSERT_H
 #define FRUIT_ASSERT_H
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 #include <cassert>
 // Usage: FruitStaticAssert(MetaExpr)
 #define FruitStaticAssert(...) static_assert(fruit::impl::meta::Eval<__VA_ARGS__>::value, "")
diff --git a/include/fruit/impl/fruit_internal_forward_decls.h b/include/fruit/impl/fruit_internal_forward_decls.h
index 4883470..86ac747 100644
--- a/include/fruit/impl/fruit_internal_forward_decls.h
+++ b/include/fruit/impl/fruit_internal_forward_decls.h
@@ -34,6 +34,9 @@
 struct InjectorAccessorForTests;
 
 template <typename Component, typename... Args>
+class ComponentInterfaceImpl;
+
+template <typename Component, typename... Args>
 class LazyComponentImpl;
 
 namespace meta {
diff --git a/include/fruit/impl/injection_debug_errors.h b/include/fruit/impl/injection_debug_errors.h
index 3efdc5e..4b3c3d9 100644
--- a/include/fruit/impl/injection_debug_errors.h
+++ b/include/fruit/impl/injection_debug_errors.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef FRUIT_INJECTION_DEBUG_ERRORS
-#define FRUIT_INJECTION_DEBUG_ERRORS
+#ifndef FRUIT_INJECTION_DEBUG_ERRORS_H
+#define FRUIT_INJECTION_DEBUG_ERRORS_H
 
 #include <fruit/impl/injection_errors.h>
 
@@ -107,4 +107,4 @@
 } // namespace impl
 } // namespace fruit
 
-#endif // FRUIT_INJECTION_DEBUG_ERRORS
+#endif // FRUIT_INJECTION_DEBUG_ERRORS_H
diff --git a/include/fruit/impl/injection_errors.h b/include/fruit/impl/injection_errors.h
index 06a0344..a0e9def 100644
--- a/include/fruit/impl/injection_errors.h
+++ b/include/fruit/impl/injection_errors.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef FRUIT_INJECTION_ERRORS
-#define FRUIT_INJECTION_ERRORS
+#ifndef FRUIT_INJECTION_ERRORS_H
+#define FRUIT_INJECTION_ERRORS_H
 
 #include <fruit/impl/fruit_assert.h>
 #include <fruit/impl/meta/set.h>
@@ -366,6 +366,14 @@
       "destructor to the base class.");
 };
 
+template <typename Arg>
+struct IncorrectArgTypePassedToInstallComponentFuntionsError {
+    static_assert(
+        AlwaysFalse<Arg>::value,
+        "All arguments passed to installComponentFunctions() must be fruit::ComponentFunction<...> objects but an "
+        "argument with type Arg was passed instead.");
+};
+
 struct LambdaWithCapturesErrorTag {
   template <typename Lambda>
   using apply = LambdaWithCapturesError<Lambda>;
@@ -571,7 +579,12 @@
   using apply = FactoryBindingForUniquePtrOfClassWithNoVirtualDestructorError<BaseFactory, DerivedFactory>;
 };
 
+struct IncorrectArgTypePassedToInstallComponentFuntionsErrorTag {
+  template <typename Arg>
+  using apply = IncorrectArgTypePassedToInstallComponentFuntionsError<Arg>;
+};
+
 } // namespace impl
 } // namespace fruit
 
-#endif // FRUIT_INJECTION_ERRORS
+#endif // FRUIT_INJECTION_ERRORS_H
diff --git a/include/fruit/impl/injector.defn.h b/include/fruit/impl/injector.defn.h
index 2072db5..928c5c3 100644
--- a/include/fruit/impl/injector.defn.h
+++ b/include/fruit/impl/injector.defn.h
@@ -114,8 +114,7 @@
 
 template <typename... P>
 template <typename T>
-inline typename Injector<P...>::template RemoveAnnotations<T> Injector<P...>::get() {
-
+inline fruit::impl::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>();
@@ -129,8 +128,7 @@
 
 template <typename... P>
 template <typename AnnotatedC>
-inline const std::vector<typename fruit::Injector<P...>::template RemoveAnnotationsHelper<AnnotatedC>::type*>&
-Injector<P...>::getMultibindings() {
+inline const std::vector<fruit::impl::RemoveAnnotations<AnnotatedC>*>& Injector<P...>::getMultibindings() {
 
   using Op = fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
       fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>)>;
diff --git a/include/fruit/impl/injector/injector_accessor_for_tests.defn.h b/include/fruit/impl/injector/injector_accessor_for_tests.defn.h
index c74c930..c9ab227 100644
--- a/include/fruit/impl/injector/injector_accessor_for_tests.defn.h
+++ b/include/fruit/impl/injector/injector_accessor_for_tests.defn.h
@@ -23,7 +23,7 @@
 namespace impl {
 
 template <typename AnnotatedC, typename... Params>
-const typename fruit::Injector<Params...>::template RemoveAnnotations<AnnotatedC>*
+const fruit::impl::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>>)>;
diff --git a/include/fruit/impl/injector/injector_accessor_for_tests.h b/include/fruit/impl/injector/injector_accessor_for_tests.h
index b2c106c..cf7517a 100644
--- a/include/fruit/impl/injector/injector_accessor_for_tests.h
+++ b/include/fruit/impl/injector/injector_accessor_for_tests.h
@@ -46,7 +46,7 @@
    * Otherwise this method will return nullptr.
    */
   template <typename C, typename... Params>
-  static const typename fruit::Injector<Params...>::template RemoveAnnotations<C>*
+  static const fruit::impl::RemoveAnnotations<C>*
   unsafeGet(fruit::Injector<Params...>& injector);
 };
 }
diff --git a/include/fruit/impl/injector/injector_storage.defn.h b/include/fruit/impl/injector/injector_storage.defn.h
index b5de693..2474ada 100644
--- a/include/fruit/impl/injector/injector_storage.defn.h
+++ b/include/fruit/impl/injector/injector_storage.defn.h
@@ -70,7 +70,7 @@
 }
 
 inline bool InjectorStorage::BindingDataNodeIter::isTerminal() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if 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 &&
@@ -377,7 +377,7 @@
   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
+#if FRUIT_EXTRA_DEBUG
   binding.is_nonconst = true;
 #endif
   return result;
@@ -396,7 +396,7 @@
   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
+#if FRUIT_EXTRA_DEBUG
   binding.is_nonconst = false;
 #endif
   return result;
@@ -409,7 +409,7 @@
   result.type_id = getTypeId<AnnotatedC>();
   ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object;
   binding.object_ptr = &instance;
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   binding.is_nonconst = true;
 #endif
   return result;
@@ -422,7 +422,7 @@
   result.type_id = getTypeId<AnnotatedC>();
   ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object;
   binding.object_ptr = &instance;
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   binding.is_nonconst = false;
 #endif
   return result;
@@ -600,7 +600,7 @@
 
 template <typename AnnotatedSignature, typename Lambda>
 inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForProvider() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   using Signature =
       fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature(
           fruit::impl::meta::Type<AnnotatedSignature>)>>;
@@ -621,7 +621,7 @@
   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
+#if FRUIT_EXTRA_DEBUG
   binding.is_nonconst = true;
 #endif
   return result;
@@ -639,7 +639,7 @@
 
 template <typename AnnotatedSignature, typename Lambda, typename AnnotatedI>
 inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForCompressedProvider() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   using Signature =
       fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature(
           fruit::impl::meta::Type<AnnotatedSignature>)>>;
@@ -735,7 +735,7 @@
   ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct;
   binding.create = createInjectedObjectForConstructor<C, AnnotatedSignature>;
   binding.deps = getBindingDeps<NormalizedSignatureArgs<AnnotatedSignature>>();
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   binding.is_nonconst = true;
 #endif
   return result;
@@ -819,7 +819,7 @@
 
 template <typename AnnotatedSignature, typename Lambda>
 inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibindingProvider() {
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   using Signature =
       fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature(
           fruit::impl::meta::Type<AnnotatedSignature>)>>;
diff --git a/include/fruit/impl/injector/injector_storage.h b/include/fruit/impl/injector/injector_storage.h
index 6faa5f9..e9af2da 100644
--- a/include/fruit/impl/injector/injector_storage.h
+++ b/include/fruit/impl/injector/injector_storage.h
@@ -25,6 +25,7 @@
 #include <unordered_map>
 #include <vector>
 #include <mutex>
+#include <thread>
 
 namespace fruit {
 namespace impl {
diff --git a/include/fruit/impl/meta/component.h b/include/fruit/impl/meta/component.h
index 54aaf40..466ba29 100644
--- a/include/fruit/impl/meta/component.h
+++ b/include/fruit/impl/meta/component.h
@@ -520,7 +520,7 @@
 //********************************************************************************************************************************
 
 template <typename RsSupersetParam, typename PsParam, typename NonConstRsPsParam,
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !FRUIT_NO_LOOP_CHECK
           typename DepsParam,
 #endif
           typename InterfaceBindingsParam, typename DeferredBindingFunctorsParam>
@@ -536,7 +536,7 @@
   // - 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
+#if !FRUIT_NO_LOOP_CHECK
   using Deps = DepsParam;
 #endif
   using InterfaceBindings = InterfaceBindingsParam;
@@ -560,13 +560,13 @@
 // See ConsVector for more details.
 struct ConsComp {
   template <typename RsSupersetParam, typename PsParam, typename NonConstRsPsParam,
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !FRUIT_NO_LOOP_CHECK
             typename DepsParam,
 #endif
             typename InterfaceBindingsParam, typename DeferredBindingFunctorsParam>
   struct apply {
     using type = Comp<RsSupersetParam, PsParam, NonConstRsPsParam,
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !FRUIT_NO_LOOP_CHECK
                       DepsParam,
 #endif
                       InterfaceBindingsParam, DeferredBindingFunctorsParam>;
@@ -845,7 +845,7 @@
                        PropagateError(CheckNoRequiredTypesInComponentArguments(Vector<Ps...>),
                                       ConsComp(EmptySet, VectorToSetUnchecked(RemoveConstFromTypes(Vector<Ps...>)),
                                                RemoveConstTypes(Vector<Ps...>),
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !FRUIT_NO_LOOP_CHECK
                                                Vector<Pair<Ps, Vector<>>...>,
 #endif
                                                Vector<>, EmptyList))));
@@ -861,17 +861,17 @@
                                       ConsComp(VectorToSetUnchecked(RemoveConstFromTypes(Vector<Type<Rs>...>)),
                                                VectorToSetUnchecked(RemoveConstFromTypes(Vector<Ps...>)),
                                                RemoveConstTypes(Vector<Type<Rs>..., Ps...>),
-#ifndef FRUIT_NO_LOOP_CHECK
+#if !FRUIT_NO_LOOP_CHECK
                                                Vector<Pair<Ps, Vector<Type<Rs>...>>...>,
 #endif
                                                Vector<>, EmptyList))));
 
-#if !defined(FRUIT_NO_LOOP_CHECK) && defined(FRUIT_EXTRA_DEBUG)
+#if !FRUIT_NO_LOOP_CHECK && 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)
+#else  // FRUIT_NO_LOOP_CHECK || !FRUIT_EXTRA_DEBUG
     using type = type1;
-#endif // defined(FRUIT_NO_LOOP_CHECK) || !defined(FRUIT_EXTRA_DEBUG)
+#endif // FRUIT_NO_LOOP_CHECK || !FRUIT_EXTRA_DEBUG
   };
 };
 
@@ -897,7 +897,7 @@
   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
+#if !FRUIT_NO_LOOP_CHECK
                            typename Comp::Deps,
 #endif
                            typename Comp::InterfaceBindings, typename Comp::DeferredBindingFunctors);
@@ -913,7 +913,7 @@
         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
+#if !FRUIT_NO_LOOP_CHECK
         PushFront(typename Comp::Deps, Pair<C, CRequirements>),
 #endif
         typename Comp::InterfaceBindings, typename Comp::DeferredBindingFunctors);
@@ -941,7 +941,7 @@
   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
+#if !FRUIT_NO_LOOP_CHECK
                           typename Comp::Deps,
 #endif
                           typename Comp::InterfaceBindings, new_DeferredBindingFunctors);
@@ -956,7 +956,7 @@
   };
 };
 
-#if defined(FRUIT_EXTRA_DEBUG) || defined(FRUIT_IN_META_TEST)
+#if FRUIT_EXTRA_DEBUG || FRUIT_IN_META_TEST
 struct CheckComponentEntails {
   template <typename Comp, typename EntailedComp>
   struct apply {
@@ -991,7 +991,7 @@
     static_assert(true || sizeof(typename CheckIfError<Eval<type>>::type), "");
   };
 };
-#endif // defined(FRUIT_EXTRA_DEBUG) || defined(FRUIT_IN_META_TEST)
+#endif // FRUIT_EXTRA_DEBUG || FRUIT_IN_META_TEST
 
 // This calls ConstructError(NoBindingFoundErrorTag, ...) or
 // ConstructError(NoBindingFoundForAbstractClassErrorTag, ...) as appropriate.
diff --git a/include/fruit/impl/meta/errors.h b/include/fruit/impl/meta/errors.h
index 6f9de8a..a041f00 100644
--- a/include/fruit/impl/meta/errors.h
+++ b/include/fruit/impl/meta/errors.h
@@ -39,7 +39,7 @@
 struct ConstructError {
   template <typename ErrorTag, typename... Args>
   struct apply {
-#ifdef FRUIT_DEEP_TEMPLATE_INSTANTIATION_STACKTRACES_FOR_ERRORS
+#if 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...>;
diff --git a/include/fruit/impl/meta/eval.h b/include/fruit/impl/meta/eval.h
index 268b7dd..4a8eb94 100644
--- a/include/fruit/impl/meta/eval.h
+++ b/include/fruit/impl/meta/eval.h
@@ -33,7 +33,7 @@
 // General case, meta-constant.
 template <typename MetaExpr>
 struct DoEval {
-#ifdef FRUIT_TRACE_INSTANTIATIONS
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -51,7 +51,7 @@
   static constexpr bool value = true;
 };
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 
 // For debugging, we use a separate DoEvalFun so that we get longer (and more informative)
 // instantiation traces.
@@ -65,7 +65,7 @@
 
 template <typename MetaFun, typename... MetaExprs>
 struct DoEval<MetaFun(MetaExprs...)> {
-#ifdef FRUIT_TRACE_INSTANTIATIONS
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -78,7 +78,7 @@
 // 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
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -91,7 +91,7 @@
 
 template <typename MetaFun, typename... MetaExprs>
 struct DoEval<MetaFun(MetaExprs...)> {
-#ifdef FRUIT_TRACE_INSTANTIATIONS
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -106,7 +106,7 @@
 // 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
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -171,7 +171,7 @@
 
 template <typename ThenMetaExpr, typename ElseMetaExpr>
 struct EvalIf<Bool<true>, ThenMetaExpr, ElseMetaExpr> {
-#ifdef FRUIT_TRACE_INSTANTIATIONS
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -182,7 +182,7 @@
 
 template <typename ThenMetaExpr, typename ElseMetaExpr>
 struct EvalIf<Bool<false>, ThenMetaExpr, ElseMetaExpr> {
-#ifdef FRUIT_TRACE_INSTANTIATIONS
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -193,7 +193,7 @@
 
 template <typename CondMetaExpr, typename ThenMetaExpr, typename ElseMetaExpr>
 struct DoEval<If(CondMetaExpr, ThenMetaExpr, ElseMetaExpr)> {
-#ifdef FRUIT_TRACE_INSTANTIATIONS
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -206,7 +206,7 @@
 // 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
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -227,7 +227,7 @@
 
 template <typename MaybeErrorMetaExpr, typename ElseMetaExpr>
 struct DoEval<PropagateError(MaybeErrorMetaExpr, ElseMetaExpr)> {
-#ifdef FRUIT_TRACE_INSTANTIATIONS
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
@@ -240,7 +240,7 @@
 // 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
+#if FRUIT_TRACE_INSTANTIATIONS
   constexpr static bool static_warning() __attribute__((deprecated("static_warning"))) {
     return true;
   }
diff --git a/include/fruit/impl/meta/proof_tree_comparison.h b/include/fruit/impl/meta/proof_tree_comparison.h
index a8d1f91..e956a87 100644
--- a/include/fruit/impl/meta/proof_tree_comparison.h
+++ b/include/fruit/impl/meta/proof_tree_comparison.h
@@ -17,7 +17,7 @@
 #ifndef FRUIT_PROOF_TREE_COMPARISON_H
 #define FRUIT_PROOF_TREE_COMPARISON_H
 
-#if !defined(FRUIT_EXTRA_DEBUG) && !defined(FRUIT_IN_META_TEST)
+#if !FRUIT_EXTRA_DEBUG && !FRUIT_IN_META_TEST
 #error "This file should only be included in debug mode or in tests."
 #endif
 
diff --git a/include/fruit/impl/meta/proof_trees.h b/include/fruit/impl/meta/proof_trees.h
index ad6795f..1e6c24d 100644
--- a/include/fruit/impl/meta/proof_trees.h
+++ b/include/fruit/impl/meta/proof_trees.h
@@ -42,7 +42,7 @@
 // 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
+#if !FRUIT_NO_LOOP_CHECK
 
 using ProofForestFindHps = GraphFindNeighbors;
 
diff --git a/include/fruit/impl/meta_operation_wrappers.h b/include/fruit/impl/meta_operation_wrappers.h
new file mode 100644
index 0000000..c812161
--- /dev/null
+++ b/include/fruit/impl/meta_operation_wrappers.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_META_OPERATION_WRAPPERS_H
+#define FRUIT_META_OPERATION_WRAPPERS_H
+
+#include <fruit/impl/meta/basics.h>
+
+namespace fruit {
+namespace impl {
+
+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;
+
+} // namespace impl
+} // namespace fruit
+
+#endif // FRUIT_META_OPERATION_WRAPPERS_H
diff --git a/include/fruit/impl/normalized_component_storage/binding_normalization.h b/include/fruit/impl/normalized_component_storage/binding_normalization.h
index b5d395e..c50ec3f 100644
--- a/include/fruit/impl/normalized_component_storage/binding_normalization.h
+++ b/include/fruit/impl/normalized_component_storage/binding_normalization.h
@@ -17,7 +17,7 @@
 #ifndef FRUIT_BINDING_NORMALIZATION_H
 #define FRUIT_BINDING_NORMALIZATION_H
 
-#ifndef IN_FRUIT_CPP_FILE
+#if !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
diff --git a/include/fruit/impl/normalized_component_storage/binding_normalization.templates.h b/include/fruit/impl/normalized_component_storage/binding_normalization.templates.h
index 85888d7..1c9b485 100644
--- a/include/fruit/impl/normalized_component_storage/binding_normalization.templates.h
+++ b/include/fruit/impl/normalized_component_storage/binding_normalization.templates.h
@@ -17,7 +17,7 @@
 #ifndef FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
 #define FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
 
-#ifndef IN_FRUIT_CPP_FILE
+#if !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
@@ -143,7 +143,7 @@
       break;
 
     default:
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
       std::cerr << "Unexpected kind: " << (std::size_t)context.entries_to_process.back().kind << std::endl;
 #endif
       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
@@ -186,7 +186,7 @@
 
 // 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
+#if FRUIT_EXTRA_DEBUG
     entry_in_map.binding_for_constructed_object.is_nonconst |= entry.binding_for_constructed_object.is_nonconst;
 #endif
     return;
@@ -485,7 +485,7 @@
     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
   }
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   std::cout << "Expanding lazy component: " << entry.lazy_component_with_args.component->getFunTypeId() << std::endl;
 #endif
 
@@ -537,7 +537,7 @@
     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
   }
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   std::cout << "Expanding lazy component: " << entry.type_id << std::endl;
 #endif
 
@@ -574,7 +574,7 @@
       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
+#if FRUIT_EXTRA_DEBUG
         std::cout << "InjectorStorage: ignoring compressed binding for " << deps->deps[i]
                   << " because it's a dep of a multibinding." << std::endl;
 #endif
@@ -585,7 +585,7 @@
   // 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
+#if FRUIT_EXTRA_DEBUG
     std::cout << "InjectorStorage: ignoring compressed binding for " << type << " because it's an exposed type."
               << std::endl;
 #endif
@@ -605,7 +605,7 @@
         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
+#if FRUIT_EXTRA_DEBUG
           std::cout << "InjectorStorage: ignoring compressed binding for " << c_id << " because the type " << x_id
                     << " depends on it." << std::endl;
 #endif
@@ -644,13 +644,13 @@
     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
+#if 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
+#if FRUIT_EXTRA_DEBUG
     std::cout << "InjectorStorage: performing binding compression for the edge " << i_id << "->" << c_id << std::endl;
 #endif
   }
diff --git a/include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h b/include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h
index 07022c5..34047bd 100644
--- a/include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h
+++ b/include/fruit/impl/normalized_component_storage/normalized_bindings.defn.h
@@ -26,7 +26,7 @@
   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
+#if FRUIT_EXTRA_DEBUG
     is_nonconst = entry.binding_for_constructed_object.is_nonconst;
 #endif
     break;
@@ -35,13 +35,13 @@
   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
+#if FRUIT_EXTRA_DEBUG
     is_nonconst = entry.binding_for_object_to_construct.is_nonconst;
 #endif
     break;
 
   default:
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
     std::cerr << "Unexpected kind: " << (std::size_t)entry.kind << std::endl;
 #endif
     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
diff --git a/include/fruit/impl/normalized_component_storage/normalized_bindings.h b/include/fruit/impl/normalized_component_storage/normalized_bindings.h
index cf7876a..fe1d72b 100644
--- a/include/fruit/impl/normalized_component_storage/normalized_bindings.h
+++ b/include/fruit/impl/normalized_component_storage/normalized_bindings.h
@@ -33,7 +33,7 @@
     ComponentStorageEntry::BindingForObjectToConstruct::create_t create;
   };
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   bool is_nonconst;
 #endif
 
diff --git a/include/fruit/impl/normalized_component_storage/normalized_component_storage.h b/include/fruit/impl/normalized_component_storage/normalized_component_storage.h
index d580b59..22dc8b1 100644
--- a/include/fruit/impl/normalized_component_storage/normalized_component_storage.h
+++ b/include/fruit/impl/normalized_component_storage/normalized_component_storage.h
@@ -17,7 +17,7 @@
 #ifndef FRUIT_NORMALIZED_COMPONENT_STORAGE_H
 #define FRUIT_NORMALIZED_COMPONENT_STORAGE_H
 
-#ifndef IN_FRUIT_CPP_FILE
+#if !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
diff --git a/include/fruit/impl/util/hash_helpers.h b/include/fruit/impl/util/hash_helpers.h
index a060aac..49e0ec3 100644
--- a/include/fruit/impl/util/hash_helpers.h
+++ b/include/fruit/impl/util/hash_helpers.h
@@ -20,7 +20,7 @@
 #include <fruit/impl/data_structures/arena_allocator.h>
 #include <fruit/impl/fruit-config.h>
 
-#ifndef IN_FRUIT_CPP_FILE
+#if !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
diff --git a/include/fruit/impl/util/type_info.defn.h b/include/fruit/impl/util/type_info.defn.h
index 8b8df69..d52fab0 100644
--- a/include/fruit/impl/util/type_info.defn.h
+++ b/include/fruit/impl/util/type_info.defn.h
@@ -33,7 +33,7 @@
   constexpr TypeInfo::ConcreteTypeInfo operator()() const {
     return TypeInfo::ConcreteTypeInfo{
         sizeof(T), alignof(T), std::is_trivially_destructible<T>::value,
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
         false /* is_abstract */,
 #endif
     };
@@ -48,7 +48,7 @@
   constexpr TypeInfo::ConcreteTypeInfo operator()() const {
     return TypeInfo::ConcreteTypeInfo{
         0 /* type_size */, 0 /* type_alignment */, false /* is_trivially_destructible */,
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
         true /* is_abstract */,
 #endif
     };
@@ -70,21 +70,21 @@
 }
 
 inline size_t TypeInfo::size() const {
-#ifdef FRUIT_EXTRA_DEBUG
+#if 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
+#if 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
+#if FRUIT_EXTRA_DEBUG
   FruitAssert(!concrete_type_info.is_abstract);
 #endif
   return concrete_type_info.is_trivially_destructible;
@@ -109,7 +109,7 @@
 template <typename T>
 struct GetTypeInfoForType {
   constexpr TypeInfo operator()() const {
-#ifdef FRUIT_HAS_TYPEID
+#if FRUIT_HAS_TYPEID
     return TypeInfo(typeid(T), GetConcreteTypeInfo<T>()());
 #else
     return TypeInfo(GetConcreteTypeInfo<T>()());
@@ -120,7 +120,7 @@
 template <typename Annotation, typename T>
 struct GetTypeInfoForType<fruit::Annotated<Annotation, T>> {
   constexpr TypeInfo operator()() const {
-#ifdef FRUIT_HAS_TYPEID
+#if FRUIT_HAS_TYPEID
     return TypeInfo(typeid(fruit::Annotated<Annotation, T>), GetConcreteTypeInfo<T>()());
 #else
     return TypeInfo(GetConcreteTypeInfo<T>()());
@@ -130,7 +130,7 @@
 
 template <typename T>
 inline TypeId getTypeId() {
-#if defined(FRUIT_HAS_TYPEID) && !defined(FRUIT_HAS_CONSTEXPR_TYPEID)
+#if FRUIT_HAS_TYPEID && !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>()();
@@ -156,7 +156,7 @@
   return GetTypeIdsForListHelper<L>()(memory_pool);
 }
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 
 inline std::ostream& operator<<(std::ostream& os, TypeId type) {
   return os << std::string(type);
diff --git a/include/fruit/impl/util/type_info.h b/include/fruit/impl/util/type_info.h
index 7ffc8b3..01fb647 100644
--- a/include/fruit/impl/util/type_info.h
+++ b/include/fruit/impl/util/type_info.h
@@ -36,7 +36,7 @@
     std::size_t type_alignment;
     bool is_trivially_destructible;
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
     bool is_abstract;
 #endif
   };
@@ -85,7 +85,7 @@
 } // namespace impl
 } // namespace fruit
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
 
 #include <ostream>
 
diff --git a/include/fruit/injector.h b/include/fruit/injector.h
index 8f8e5ae..a62f631 100644
--- a/include/fruit/injector.h
+++ b/include/fruit/injector.h
@@ -23,6 +23,7 @@
 #include <fruit/component.h>
 #include <fruit/normalized_component.h>
 #include <fruit/provider.h>
+#include <fruit/impl/meta_operation_wrappers.h>
 
 namespace fruit {
 
@@ -45,16 +46,6 @@
  */
 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;
@@ -176,7 +167,7 @@
    * 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();
+  fruit::impl::RemoveAnnotations<T> get();
 
   /**
    * This is a convenient way to call get(). E.g.:
@@ -209,7 +200,7 @@
    * With an annotated parameter AnnotatedT=Annotated<Annotation, T>, this returns a const std::vector<T*>&.
    */
   template <typename T>
-  const std::vector<RemoveAnnotations<T>*>& getMultibindings();
+  const std::vector<fruit::impl::RemoveAnnotations<T>*>& getMultibindings();
 
   /**
    * This method is deprecated since Fruit injectors can now be accessed concurrently by multiple threads. This will be
diff --git a/src/binding_normalization.cpp b/src/binding_normalization.cpp
index 5ffe8ef..c353687 100644
--- a/src/binding_normalization.cpp
+++ b/src/binding_normalization.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <algorithm>
 #include <cstdlib>
@@ -219,7 +219,7 @@
                                             FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
                                             const multibindings_vector_t& multibindingsVector) {
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   std::cout << "InjectorStorage: adding multibindings:" << std::endl;
 #endif
   // Now we must merge multiple bindings for the same type.
@@ -262,7 +262,7 @@
     } break;
 
     default:
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
       std::cerr << "Unexpected kind: " << (std::size_t)i->first.kind << std::endl;
 #endif
       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
@@ -410,7 +410,7 @@
     } break;
 
     default:
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
       std::cerr << "Unexpected kind: " << (std::size_t)entry.kind << std::endl;
 #endif
       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
@@ -439,7 +439,7 @@
     // 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
+#if FRUIT_EXTRA_DEBUG
     std::cout << "InjectorStorage: undoing binding compression for: " << binding_compression_itr->second.i_type_id
               << "->" << cTypeId << std::endl;
 #endif
diff --git a/src/component.cpp b/src/component.cpp
index 69e971b..f37f11c 100644
--- a/src/component.cpp
+++ b/src/component.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <fruit/component.h>
 
diff --git a/src/demangle_type_name.cpp b/src/demangle_type_name.cpp
index d0a72dc..bff4835 100644
--- a/src/demangle_type_name.cpp
+++ b/src/demangle_type_name.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <fruit/impl/fruit-config.h>
 #include <fruit/impl/util/demangle_type_name.h>
diff --git a/src/fixed_size_allocator.cpp b/src/fixed_size_allocator.cpp
index e1a89ab..b490828 100644
--- a/src/fixed_size_allocator.cpp
+++ b/src/fixed_size_allocator.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <fruit/impl/data_structures/fixed_size_allocator.h>
 #include <fruit/impl/data_structures/fixed_size_vector.templates.h>
diff --git a/src/injector_storage.cpp b/src/injector_storage.cpp
index 840416a..1e77702 100644
--- a/src/injector_storage.cpp
+++ b/src/injector_storage.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <algorithm>
 #include <cstdlib>
@@ -75,7 +75,7 @@
                (DummyNode<TypeId, NormalizedBinding>*)nullptr, memory_pool),
       multibindings(std::move(normalized_component_storage_ptr->multibindings)) {
 
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   bindings.checkFullyConstructed();
 #endif
 }
@@ -94,7 +94,7 @@
 
   bindings = Graph(normalized_component.bindings, BindingDataNodeIter{new_bindings_vector.begin()},
                    BindingDataNodeIter{new_bindings_vector.end()}, memory_pool);
-#ifdef FRUIT_EXTRA_DEBUG
+#if FRUIT_EXTRA_DEBUG
   bindings.checkFullyConstructed();
 #endif
 }
diff --git a/src/memory_pool.cpp b/src/memory_pool.cpp
index c4105b2..1311b2c 100644
--- a/src/memory_pool.cpp
+++ b/src/memory_pool.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <fruit/impl/data_structures/memory_pool.h>
 
diff --git a/src/normalized_component_storage.cpp b/src/normalized_component_storage.cpp
index b483326..7449ec5 100644
--- a/src/normalized_component_storage.cpp
+++ b/src/normalized_component_storage.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <algorithm>
 #include <cstdlib>
diff --git a/src/normalized_component_storage_holder.cpp b/src/normalized_component_storage_holder.cpp
index b138ebd..0629b99 100644
--- a/src/normalized_component_storage_holder.cpp
+++ b/src/normalized_component_storage_holder.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <fruit/impl/normalized_component_storage/normalized_component_storage.h>
 #include <fruit/impl/normalized_component_storage/normalized_component_storage_holder.h>
diff --git a/src/semistatic_graph.cpp b/src/semistatic_graph.cpp
index c018bbd..1b23d70 100644
--- a/src/semistatic_graph.cpp
+++ b/src/semistatic_graph.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <fruit/impl/data_structures/semistatic_graph.h>
 #include <fruit/impl/data_structures/semistatic_graph.templates.h>
diff --git a/src/semistatic_map.cpp b/src/semistatic_map.cpp
index 57811f1..281f34a 100644
--- a/src/semistatic_map.cpp
+++ b/src/semistatic_map.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define IN_FRUIT_CPP_FILE
+#define IN_FRUIT_CPP_FILE 1
 
 #include <fruit/impl/data_structures/semistatic_graph.h>
 #include <fruit/impl/data_structures/semistatic_map.h>
diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt
new file mode 100644
index 0000000..ad6dc6e
--- /dev/null
+++ b/test_package/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(PackageTest CXX)
+
+include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+conan_basic_setup()
+
+add_executable(example example.cpp)
+target_link_libraries(example ${CONAN_LIBS})
+
+# CTest is a testing tool that can be used to test your project.
+# enable_testing()
+# add_test(NAME example
+#          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin
+#          COMMAND example)
diff --git a/test_package/conanfile.py b/test_package/conanfile.py
new file mode 100644
index 0000000..38f0fc4
--- /dev/null
+++ b/test_package/conanfile.py
@@ -0,0 +1,25 @@
+import os
+
+from conans import ConanFile, CMake, tools
+
+
+class FruitTestConan(ConanFile):
+    settings = "os", "compiler", "build_type", "arch"
+    generators = "cmake"
+
+    def build(self):
+        cmake = CMake(self)
+        # Current dir is "test_package/build/<build_id>" and CMakeLists.txt is
+        # in "test_package"
+        cmake.configure()
+        cmake.build()
+
+    def imports(self):
+        self.copy("*.dll", dst="bin", src="bin")
+        self.copy("*.dylib*", dst="bin", src="lib")
+        self.copy('*.so*', dst='bin', src='lib')
+
+    def test(self):
+        if not tools.cross_building(self.settings):
+            os.chdir("bin")
+            self.run(".%sexample" % os.sep)
diff --git a/test_package/example.cpp b/test_package/example.cpp
new file mode 100644
index 0000000..14658b5
--- /dev/null
+++ b/test_package/example.cpp
@@ -0,0 +1,14 @@
+#include <fruit/fruit.h>
+
+struct X {
+  INJECT(X()) = default;
+};
+
+fruit::Component<X> getXComponent() {
+  return fruit::createComponent();
+}
+
+int main() {
+  fruit::Injector<X> injector(getXComponent);
+  injector.get<X*>();
+}
diff --git a/tests/build_defs.bzl b/tests/build_defs.bzl
index 6cc71c2..ec48f39 100644
--- a/tests/build_defs.bzl
+++ b/tests/build_defs.bzl
@@ -5,6 +5,7 @@
             name = filename[:-3],
             srcs = [filename],
             imports = ["."],
+            timeout = "long",
             deps = [
                 "//third_party/fruit/tests:fruit_test_common",
             ],
diff --git a/tests/data_structures/test_fixed_size_allocator.py b/tests/data_structures/test_fixed_size_allocator.py
index 7f73f1c..061ea6e 100644
--- a/tests/data_structures/test_fixed_size_allocator.py
+++ b/tests/data_structures/test_fixed_size_allocator.py
@@ -18,7 +18,7 @@
 COMMON_DEFINITIONS = '''
     #include "test_common.h"
     
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     #include <fruit/impl/data_structures/fixed_size_vector.templates.h>
     
     using namespace std;
diff --git a/tests/data_structures/test_fixed_size_vector.py b/tests/data_structures/test_fixed_size_vector.py
index 721598a..91058eb 100644
--- a/tests/data_structures/test_fixed_size_vector.py
+++ b/tests/data_structures/test_fixed_size_vector.py
@@ -18,7 +18,7 @@
 COMMON_DEFINITIONS = '''
     #include "test_common.h"
     
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     #include <fruit/impl/data_structures/fixed_size_vector.templates.h>
     
     using namespace std;
diff --git a/tests/data_structures/test_semistatic_graph.py b/tests/data_structures/test_semistatic_graph.py
index e51577a..f6db0e8 100644
--- a/tests/data_structures/test_semistatic_graph.py
+++ b/tests/data_structures/test_semistatic_graph.py
@@ -18,7 +18,7 @@
 COMMON_DEFINITIONS = '''
     #include "test_common.h"
     
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     #include <fruit/impl/data_structures/semistatic_graph.templates.h>
     
     using namespace std;
diff --git a/tests/data_structures/test_semistatic_map.py b/tests/data_structures/test_semistatic_map.py
index c8936af..e51f13e 100644
--- a/tests/data_structures/test_semistatic_map.py
+++ b/tests/data_structures/test_semistatic_map.py
@@ -18,7 +18,7 @@
 COMMON_DEFINITIONS = '''
     #include "test_common.h"
     
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     #include <fruit/impl/data_structures/semistatic_map.templates.h>
     
     using namespace std;
@@ -31,7 +31,7 @@
           MemoryPool memory_pool;
           vector<pair<int, std::string>> values{};
           
-          SemistaticMap<int, std::string> map(values.begin(), values.size(), memory_pool);
+          SemistaticMap<int, std::string> map(values.begin(), values.end(), values.size(), memory_pool);
           Assert(map.find(0) == nullptr);
           Assert(map.find(2) == nullptr);
           Assert(map.find(5) == nullptr);
@@ -48,7 +48,7 @@
           MemoryPool memory_pool;
           vector<pair<int, std::string>> values{{2, "foo"}};
           
-          SemistaticMap<int, std::string> map(values.begin(), values.size(), memory_pool);
+          SemistaticMap<int, std::string> map(values.begin(), values.end(), values.size(), memory_pool);
           Assert(map.find(0) == nullptr);
           Assert(map.find(2) != nullptr);
           Assert(map.at(2) == "foo");
@@ -66,7 +66,7 @@
           MemoryPool memory_pool;
           vector<pair<int, std::string>> values{};
           
-          SemistaticMap<int, std::string> old_map(values.begin(), values.size(), memory_pool);
+          SemistaticMap<int, std::string> old_map(values.begin(), values.end(), 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));
@@ -88,7 +88,7 @@
           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);
+          SemistaticMap<int, std::string> map(values.begin(), values.end(), values.size(), memory_pool);
           Assert(map.find(0) == nullptr);
           Assert(map.find(1) != nullptr);
           Assert(map.at(1) == "foo");
@@ -111,7 +111,7 @@
           MemoryPool memory_pool;
           vector<pair<int, std::string>> values{{1, "foo"}};
           
-          SemistaticMap<int, std::string> old_map(values.begin(), values.size(), memory_pool);
+          SemistaticMap<int, std::string> old_map(values.begin(), values.end(), 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));
@@ -137,7 +137,7 @@
         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);
+          SemistaticMap<int, std::string> old_map(values.begin(), values.end(), 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));
@@ -168,7 +168,7 @@
         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> map1(values.begin(), values.end(), values.size(), memory_pool);
           SemistaticMap<int, std::string> map = std::move(map1);
           Assert(map.find(0) == nullptr);
           Assert(map.find(1) != nullptr);
@@ -191,7 +191,7 @@
         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> map1(values.begin(), values.end(), values.size(), memory_pool);
           SemistaticMap<int, std::string> map;
           map = std::move(map1);
           Assert(map.find(0) == nullptr);
diff --git a/tests/fruit_test_common.py b/tests/fruit_test_common.py
index ac9b3bd..c7668af 100644
--- a/tests/fruit_test_common.py
+++ b/tests/fruit_test_common.py
@@ -234,6 +234,11 @@
         # This shouldn't cause the tests to fail, so we ignore the exception and go ahead.
         pass
 
+def normalize_error_message_lines(lines):
+    # 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.
+    return [line.replace(' ', '').replace('std::__1::', 'std::') for line in lines]
+
 def expect_compile_error_helper(
         check_error_fun,
         setup_source_code,
@@ -266,16 +271,44 @@
 
     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_lines = 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)
+    check_error_fun(e, error_message_lines, error_message_head)
 
     try_remove_temporary_file(source_file_name)
 
+def apply_any_error_context_replacements(error_string, following_lines):
+    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.
+        replacement_lines = []
+        if len(following_lines) >= 4 and following_lines[0].strip() == 'with':
+            assert following_lines[1].strip() == '[', 'Line was: ' + following_lines[1]
+            for line in itertools.islice(following_lines, 2, 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)
+            (type_variable, type_expression) = match.groups()
+            error_string = re.sub(r'\b' + type_variable + r'\b', type_expression, error_string)
+    return error_string
+
 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.
@@ -297,7 +330,13 @@
     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):
+    def check_error(e, error_message_lines, error_message_head):
+        error_message_lines_with_replacements = [
+            apply_any_error_context_replacements(line, error_message_lines[line_number + 1:])
+            for line_number, line in enumerate(error_message_lines)]
+
+        normalized_error_message_lines = normalize_error_message_lines(error_message_lines_with_replacements)
+
         for line in normalized_error_message_lines:
             if re.search(expected_error_regex, line):
                 return
@@ -310,7 +349,6 @@
 
     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,
@@ -348,41 +386,15 @@
     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):
+    def check_error(e, error_message_lines, error_message_head):
+        normalized_error_message_lines = normalize_error_message_lines(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')
+                actual_fruit_error = apply_any_error_context_replacements(actual_fruit_error, normalized_error_message_lines[line_number + 1:])
                 break
         else:
             raise Exception(textwrap.dedent('''\
diff --git a/tests/meta/common.h b/tests/meta/common.h
index f2e3078..bf7d0ad 100644
--- a/tests/meta/common.h
+++ b/tests/meta/common.h
@@ -2,7 +2,7 @@
 #ifndef FRUIT_TESTS_META_COMMON_H
 #define FRUIT_TESTS_META_COMMON_H
 
-#define FRUIT_IN_META_TEST
+#define FRUIT_IN_META_TEST 1
 
 #include <fruit/impl/injection_debug_errors.h>
 #include <fruit/impl/injection_errors.h>
diff --git a/tests/meta/test_algos.py b/tests/meta/test_algos.py
index 5ac1ced..17e1ffa 100644
--- a/tests/meta/test_algos.py
+++ b/tests/meta/test_algos.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/algos.h>
diff --git a/tests/meta/test_basics.py b/tests/meta/test_basics.py
index 1508eaf..d12a0ae 100644
--- a/tests/meta/test_basics.py
+++ b/tests/meta/test_basics.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/vector.h>
diff --git a/tests/meta/test_graph.py b/tests/meta/test_graph.py
index 91eb36f..8de01ba 100644
--- a/tests/meta/test_graph.py
+++ b/tests/meta/test_graph.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/graph.h>
diff --git a/tests/meta/test_list.py b/tests/meta/test_list.py
index b930838..609d74d 100644
--- a/tests/meta/test_list.py
+++ b/tests/meta/test_list.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/list.h>
diff --git a/tests/meta/test_map.py b/tests/meta/test_map.py
index e943b9e..4358a9e 100644
--- a/tests/meta/test_map.py
+++ b/tests/meta/test_map.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/map.h>
diff --git a/tests/meta/test_meta_component.py b/tests/meta/test_meta_component.py
index b8910dd..9aafb01 100644
--- a/tests/meta/test_meta_component.py
+++ b/tests/meta/test_meta_component.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/component.h>
diff --git a/tests/meta/test_metaprogramming.py b/tests/meta/test_metaprogramming.py
index 6e0bfab..1d7f29c 100644
--- a/tests/meta/test_metaprogramming.py
+++ b/tests/meta/test_metaprogramming.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/component.h>
diff --git a/tests/meta/test_proof_trees.py b/tests/meta/test_proof_trees.py
index 380b64a..dc14266 100644
--- a/tests/meta/test_proof_trees.py
+++ b/tests/meta/test_proof_trees.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/metaprogramming.h>
diff --git a/tests/meta/test_set.py b/tests/meta/test_set.py
index bc73672..5a10579 100644
--- a/tests/meta/test_set.py
+++ b/tests/meta/test_set.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/set.h>
diff --git a/tests/meta/test_vector.py b/tests/meta/test_vector.py
index 12a39e9..34dd066 100644
--- a/tests/meta/test_vector.py
+++ b/tests/meta/test_vector.py
@@ -16,7 +16,7 @@
 from fruit_test_common import *
 
 COMMON_DEFINITIONS = '''
-    #define IN_FRUIT_CPP_FILE
+    #define IN_FRUIT_CPP_FILE 1
     
     #include "meta/common.h"
     #include <fruit/impl/meta/vector.h>
diff --git a/tests/test_component_functions.py b/tests/test_component_functions.py
new file mode 100755
index 0000000..69ab6cf
--- /dev/null
+++ b/tests/test_component_functions.py
@@ -0,0 +1,560 @@
+#!/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"
+    '''
+
+def test_component_function_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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_no_args_success():
+    source = '''
+        struct X {
+          int n;
+          X(int n) : n(n) {}
+        };
+        
+        fruit::Component<X> getParentComponent() {
+          return fruit::createComponent()
+            .registerProvider([]() { return X(5); });
+        }
+
+        fruit::Component<X> getComponent() {
+          return fruit::createComponent()
+            .installComponentFunctions(fruit::componentFunction(getParentComponent));
+        }
+
+        int main() {
+          fruit::Injector<X> injector(getComponent);
+          X x = injector.get<X>();
+          Assert(x.n == 5);
+        }
+        '''
+    expect_success(COMMON_DEFINITIONS, source)
+
+def test_component_function_one_arg_success():
+    source = '''
+        struct X {
+          int n;
+          X(int n) : n(n) {}
+        };
+        
+        fruit::Component<X> getParentComponent(std::string) {
+          return fruit::createComponent()
+            .registerProvider([]() { return X(5); });
+        }
+
+        fruit::Component<X> getComponent() {
+          return fruit::createComponent()
+            .installComponentFunctions(fruit::componentFunction(getParentComponent, std::string("Hello")));
+        }
+
+        int main() {
+          fruit::Injector<X> injector(getComponent);
+          X x = injector.get<X>();
+          Assert(x.n == 5);
+        }
+        '''
+    expect_success(COMMON_DEFINITIONS, source)
+
+def test_component_function_error_not_move_constructible():
+    source = '''
+        struct X {};
+        
+        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()
+            .installComponentFunctions(fruit::componentFunction(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<Arg>. to .const Arg &.'
+        + '|.Arg::Arg\(Arg &&\).: attempting to reference a deleted function',
+        COMMON_DEFINITIONS,
+        source)
+
+def test_component_function_error_not_move_constructible_with_conversion():
+    source = '''
+        struct X {};
+        
+        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()
+            .installComponentFunctions(fruit::componentFunction(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<Arg>. to .int.'
+        + '|error: copying parameter of type .Arg. invokes deleted constructor',
+        COMMON_DEFINITIONS,
+        source)
+
+def test_component_function_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()
+            .installComponentFunctions(fruit::componentFunction(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'
+        # This is the error printed by MSVC. It's not great but I couldn't find a way to have it print
+        # a more useful error.
+        + '|cannot convert argument 1 from .int. to .std::allocator_arg_t.',
+        COMMON_DEFINITIONS,
+        source)
+
+def test_component_function_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()
+            .installComponentFunctions(fruit::componentFunction(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'
+        # This is the error printed by MSVC. It's not great but I couldn't find a way to have it print
+        # a more useful error.
+        + '|cannot convert argument 1 from .int. to .std::allocator_arg_t.',
+        COMMON_DEFINITIONS,
+        source)
+
+def test_component_function_error_not_move_assignable():
+    source = '''
+        struct X {};
+        
+        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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_error_not_move_assignable_with_conversion():
+    source = '''
+        struct X {};
+        
+        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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_error_not_hashable():
+    source = '''
+        struct X {};
+        
+        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()
+            .installComponentFunctions(fruit::componentFunction(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_component_function_error_not_hashable_with_conversion():
+    source = '''
+        struct X {};
+        
+        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()
+            .installComponentFunctions(fruit::componentFunction(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)
+
+if __name__== '__main__':
+    main(__file__)
diff --git a/tests/test_component_replacement.py b/tests/test_component_replacement.py
index f2f3e57..46c52a7 100755
--- a/tests/test_component_replacement.py
+++ b/tests/test_component_replacement.py
@@ -106,6 +106,46 @@
         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_installed_using_component_function_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)
+              .installComponentFunctions(fruit::componentFunction(ReplacedComponentInstallation));
+        }
+        
+        int main() {
+          fruit::Injector<int> injector(getRootComponent);
+          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) {
@@ -137,6 +177,37 @@
         source,
         locals())
 
+def test_replace_component_installed_using_component_function_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)
+              .installComponentFunctions(fruit::componentFunction(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'),
diff --git a/tests/test_install.py b/tests/test_install.py
index 2901da1..9ea4d48 100755
--- a/tests/test_install.py
+++ b/tests/test_install.py
@@ -401,7 +401,7 @@
     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 &.',
+            + '|.Arg::Arg\(Arg &&\).: cannot convert argument 1 from .std::_Tuple_val<Arg>. to .const Arg &.',
         COMMON_DEFINITIONS,
         source)
 
@@ -435,7 +435,7 @@
     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.',
+            + '|.Arg::Arg\(Arg &&\).: cannot convert argument 1 from .std::_Tuple_val<Arg>. to .int.',
         COMMON_DEFINITIONS,
         source)
 
diff --git a/tests/test_install_component_functions.py b/tests/test_install_component_functions.py
new file mode 100755
index 0000000..bd7647a
--- /dev/null
+++ b/tests/test_install_component_functions.py
@@ -0,0 +1,372 @@
+#!/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_install_component_functions_deduped_against_previous_install_no_args():
+    source = '''
+        int num_executions = 0;
+        
+        fruit::Component<int> getChildComponent() {
+          static int n = 5;
+          ++num_executions;
+          return fruit::createComponent()
+              .bindInstance(n);
+        }
+        
+        fruit::Component<> getMiddleComponent() {
+          return fruit::createComponent()
+              .install(getChildComponent);
+        }
+        
+        fruit::Component<int> getMainComponent() {
+          return fruit::createComponent()
+              .install(getMiddleComponent)
+              .installComponentFunctions(fruit::componentFunction(getChildComponent));
+        }
+
+        int main() {
+          fruit::Injector<int> injector(getMainComponent);
+          int n = injector.get<int>();
+          Assert(n == 5);
+          Assert(num_executions == 1);
+        }
+        '''
+    expect_success(
+        COMMON_DEFINITIONS,
+        source,
+        locals())
+
+def test_install_component_functions_deduped_against_following_install_no_args():
+    source = '''
+        int num_executions = 0;
+        
+        fruit::Component<int> getChildComponent() {
+          static int n = 5;
+          ++num_executions;
+          return fruit::createComponent()
+              .bindInstance(n);
+        }
+        
+        fruit::Component<> getMiddleComponent() {
+          return fruit::createComponent()
+              .installComponentFunctions(fruit::componentFunction(getChildComponent));
+        }
+        
+        fruit::Component<int> getMainComponent() {
+          return fruit::createComponent()
+              .install(getMiddleComponent)
+              .install(getChildComponent);
+        }
+
+        int main() {
+          fruit::Injector<int> injector(getMainComponent);
+          int n = injector.get<int>();
+          Assert(n == 5);
+          Assert(num_executions == 1);
+        }
+        '''
+    expect_success(
+        COMMON_DEFINITIONS,
+        source,
+        locals())
+
+def test_install_component_functions_deduped_against_previous_install_with_args():
+    source = '''
+        int num_executions = 0;
+        
+        fruit::Component<int> getChildComponent(int) {
+          static int n = 5;
+          ++num_executions;
+          return fruit::createComponent()
+              .bindInstance(n);
+        }
+        
+        fruit::Component<> getMiddleComponent() {
+          return fruit::createComponent()
+              .install(getChildComponent, 42);
+        }
+                
+        fruit::Component<int> getMainComponent() {
+          return fruit::createComponent()
+              .install(getMiddleComponent)
+              .installComponentFunctions(fruit::componentFunction(getChildComponent, 42));
+        }
+
+        int main() {
+          fruit::Injector<int> injector(getMainComponent);
+          int n = injector.get<int>();
+          Assert(n == 5);
+          Assert(num_executions == 1);
+        }
+        '''
+    expect_success(
+        COMMON_DEFINITIONS,
+        source,
+        locals())
+
+def test_install_component_functions_deduped_against_following_install_with_args():
+    source = '''
+        int num_executions = 0;
+        
+        fruit::Component<int> getChildComponent(int) {
+          static int n = 5;
+          ++num_executions;
+          return fruit::createComponent()
+              .bindInstance(n);
+        }
+        
+        fruit::Component<> getMiddleComponent() {
+          return fruit::createComponent()
+              .installComponentFunctions(fruit::componentFunction(getChildComponent, 42));
+        }
+                        
+        fruit::Component<int> getMainComponent() {
+          return fruit::createComponent()
+              .install(getMiddleComponent)
+              .install(getChildComponent, 42);
+        }
+
+        int main() {
+          fruit::Injector<int> injector(getMainComponent);
+          int n = injector.get<int>();
+          Assert(n == 5);
+          Assert(num_executions == 1);
+        }
+        '''
+    expect_success(
+        COMMON_DEFINITIONS,
+        source,
+        locals())
+
+def test_install_component_functions_same_as_with_previous_install_with_different_args():
+    source = '''
+        int num_executions = 0;
+        
+        fruit::Component<int> getChildComponent(int) {
+          static int n = 5;
+          ++num_executions;
+          return fruit::createComponent()
+              .bindInstance(n);
+        }
+
+        fruit::Component<> getMiddleComponent() {
+          return fruit::createComponent()
+              .install(getChildComponent, 42);
+        }
+        
+        fruit::Component<int> getMainComponent() {
+          return fruit::createComponent()
+              .install(getMiddleComponent)
+              .installComponentFunctions(fruit::componentFunction(getChildComponent, 2));
+        }
+
+        int main() {
+          fruit::Injector<int> injector(getMainComponent);
+          int n = injector.get<int>();
+          Assert(n == 5);
+          Assert(num_executions == 2);
+        }
+        '''
+    expect_success(
+        COMMON_DEFINITIONS,
+        source,
+        locals())
+
+def test_install_component_functions_same_as_following_install_with_different_args():
+    source = '''
+        int num_executions = 0;
+        
+        fruit::Component<int> getChildComponent(int) {
+          static int n = 5;
+          ++num_executions;
+          return fruit::createComponent()
+              .bindInstance(n);
+        }
+
+        fruit::Component<> getMiddleComponent() {
+          return fruit::createComponent()
+              .installComponentFunctions(fruit::componentFunction(getChildComponent, 42));
+        }
+                
+        fruit::Component<int> getMainComponent() {
+          return fruit::createComponent()
+              .install(getMiddleComponent)
+              .install(getChildComponent, 2);
+        }
+
+        int main() {
+          fruit::Injector<int> injector(getMainComponent);
+          (void)injector;
+          Assert(num_executions == 2);
+        }
+        '''
+    expect_success(
+        COMMON_DEFINITIONS,
+        source,
+        locals())
+
+def test_install_component_functions_no_component_functions():
+    source = '''
+        fruit::Component<> getComponent() {
+          return fruit::createComponent()
+            .installComponentFunctions();
+        }
+
+        int main() {
+          fruit::Injector<> injector(getComponent);
+          (void)injector;
+        }
+        '''
+    expect_success(COMMON_DEFINITIONS, source)
+
+def test_install_component_functions_one_component_function():
+    source = '''
+        struct X {
+          int n;
+          X(int n) : n(n) {}
+        };
+        
+        fruit::Component<X> getParentComponent(std::string) {
+          return fruit::createComponent()
+            .registerProvider([]() { return X(5); });
+        }
+
+        fruit::Component<X> getComponent() {
+          return fruit::createComponent()
+            .installComponentFunctions(fruit::componentFunction(getParentComponent, std::string("Hello")));
+        }
+
+        int main() {
+          fruit::Injector<X> injector(getComponent);
+          X x = injector.get<X>();
+          Assert(x.n == 5);
+        }
+        '''
+    expect_success(COMMON_DEFINITIONS, source)
+
+def test_install_component_functions_two_component_functions():
+    source = '''
+        struct X {
+          int n;
+          X(int n) : n(n) {}
+        };
+        
+        struct Y {
+          int n;
+          Y(int n) : n(n) {}
+        };
+        
+        fruit::Component<X> getParentComponent1(std::string) {
+          return fruit::createComponent()
+            .registerProvider([]() { return X(5); });
+        }
+
+        fruit::Component<Y> getParentComponent2(std::string) {
+          return fruit::createComponent()
+              .registerProvider([]() { return Y(42); });
+        }
+
+        fruit::Component<X, Y> getComponent() {
+          return fruit::createComponent()
+              .installComponentFunctions(
+                  fruit::componentFunction(getParentComponent1, std::string("Hello")),
+                  fruit::componentFunction(getParentComponent2, std::string("World")));
+        }
+
+        int main() {
+          fruit::Injector<X, Y> injector(getComponent);
+          X x = injector.get<X>();
+          Y y = injector.get<Y>();
+          Assert(x.n == 5);
+          Assert(y.n == 42);
+        }
+        '''
+    expect_success(COMMON_DEFINITIONS, source)
+
+def test_install_component_functions_with_template_parameter_pack_unpacking():
+    source = '''
+        template <typename T>
+        struct GetComponentHolder;
+    
+        struct X {
+          int n;
+          X(int n) : n(n) {}
+        };
+        
+        struct Y {
+          int n;
+          Y(int n) : n(n) {}
+        };
+        
+        template <>
+        struct GetComponentHolder<X> {
+            static fruit::Component<X> getComponent(std::string) {
+              return fruit::createComponent()
+                .registerProvider([]() { return X(5); });
+            }
+        };
+
+        template <>
+        struct GetComponentHolder<Y> {
+            static fruit::Component<Y> getComponent(std::string) {
+              return fruit::createComponent()
+                  .registerProvider([]() { return Y(42); });
+            } 
+        };
+
+        template <typename... Ts>
+        fruit::Component<Ts...> getComponent() {
+          return fruit::createComponent()
+              .installComponentFunctions(
+                  fruit::componentFunction(GetComponentHolder<Ts>::getComponent, std::string("Hello"))...);
+        }
+
+        int main() {
+          fruit::Injector<X, Y> injector(getComponent<X, Y>);
+          X x = injector.get<X>();
+          Y y = injector.get<Y>();
+          Assert(x.n == 5);
+          Assert(y.n == 42);
+        }
+        '''
+    expect_success(COMMON_DEFINITIONS, source)
+
+def test_install_component_functions_wrong_argument_type():
+    source = '''
+        fruit::Component<> getMainComponent() {
+          return fruit::createComponent()
+              .installComponentFunctions(42);
+        }
+        '''
+    expect_compile_error(
+        'IncorrectArgTypePassedToInstallComponentFuntionsError<int>',
+        'All arguments passed to installComponentFunctions.. must be fruit::ComponentFunction<...> objects but an '
+        'argument with type Arg was passed instead.',
+        COMMON_DEFINITIONS,
+        source,
+        locals())
+
+if __name__== '__main__':
+    main(__file__)
diff --git a/tests/test_macros.h b/tests/test_macros.h
index 8d850e1..3b35f76 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef FRUIT_TEST_MACROS
-#define FRUIT_TEST_MACROS
+#ifndef FRUIT_TEST_MACROS_H
+#define FRUIT_TEST_MACROS_H
 
 #include <iostream>
 
@@ -33,4 +33,4 @@
     (void)sizeof(__VA_ARGS__);                                                                                         \
   }
 
-#endif // FRUIT_TEST_MACROS
+#endif // FRUIT_TEST_MACROS_H