diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 7018df9..0000000 --- a/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.kdev4 -/*.kdev4 -/.idea -/cmake-build-debug diff --git a/Benchmarks.md b/Benchmarks.md index f1f3475..ec3f4bf 100644 --- a/Benchmarks.md +++ b/Benchmarks.md @@ -29,8 +29,8 @@ destroying all injected objects). | Full injection time | 100 classes | 1000 classes | |---------------------|-------------|---------------------------| -| Clang 4.0.0 | 81-84 us | 2000-2100 us (2.0-2.1 ms) | -| GCC 7.0.1 | 69-71 us | 1800 us (1.8 ms) | +| Clang 4.0.0 | 81-84 μs | 2000-2100 μs (2.0-2.1 ms) | +| GCC 7.0.1 | 69-71 μs | 1800 μs (1.8 ms) | #### Injection time when using NormalizedComponent @@ -42,8 +42,8 @@ calling the various `get*Component` functions). | Fruit normalization time | 100 classes | 1000 classes | |--------------------------|-------------|------------------| -| Clang 4.0.0 | 80-82 us | 2000 us (2 ms) | -| GCC 7.0.1 | 62-65 us | 1800 us (1.8 ms) | +| Clang 4.0.0 | 80-82 μs | 2000 μs (2 ms) | +| GCC 7.0.1 | 62-65 μs | 1800 μs (1.8 ms) | And this is the time required to create an `Injector` from the `NormalizedComponent`, to inject the root class of the dependency graph (which involves injecting all the other ones too, as dependencies) and finally the time to destroy the @@ -51,8 +51,8 @@ injector (which involves destroying all injected objects). | Per-injector time | 100 classes | 1000 classes | |-------------------|-------------|--------------| -| Clang 4.0.0 | 2.1-2.4 us | 94-95 us | -| GCC 7.0.1 | 2-2.1 us | 96 us | +| Clang 4.0.0 | 2.1-2.4 μs | 94-95 μs | +| GCC 7.0.1 | 2-2.1 μs | 96 μs | Here we assume that all classes need to be injected. However, in a real example we would use Fruit `Provider`s to only inject the classes that are actually needed, so the time will be much lower. You can see an example of how to do that in @@ -68,8 +68,8 @@ table of the "Per-injector time" (if you're using `NormalizedComponent`). | New/delete time | 100 classes | 1000 classes | |-----------------|-------------|--------------| -| Clang 4.0.0 | 2.6 us | 45 us | -| GCC 7.0.1 | 2.7-2.8 us | 37 us | +| Clang 4.0.0 | 2.6 μs | 45 μs | +| GCC 7.0.1 | 2.7-2.8 μs | 37 μs | Note that in the 100 classes case, Fruit manages to be faster than raw new/delete when using `NormalizedComponent`. This is thanks to Fruit's custom allocation strategy. diff --git a/FAQ.md b/FAQ.md deleted file mode 100644 index c3bce35..0000000 --- a/FAQ.md +++ /dev/null @@ -1,80 +0,0 @@ -#### How mature is this project? - -This project was started in May 2014, Fruit 1.0.0 was released in November 2014 and Fruit 3.0.0 was released in -August 2017. Fruit now has extensive unit and integration tests. - -At the time of writing, Fruit is the most popular dependency injection library for C++ (at least, it's the first result -in a [Google search](https://www.google.co.uk/search?q=C%2B%2B+dependency+injection) for "C++ dependency injection" and -it's -[the top C++ dependency injection library](https://github.com/search?l=&o=desc&q=dependency+injection+language%3AC%2B%2B&ref=advsearch&s=stars&type=Repositories) -by number of stars on Github). - -#### Why the name "Fruit"? - -Fruit is loosely inspired by the [Guice](https://github.com/google/guice) library for Java (pronounced as "juice"), -which uses run-time checks (unlike Fruit where most checks occur at compile-time). - -_If you'd like some Guice but don't want to wait (for run-time errors), have some Fruit._ - -It also hints to the fact that Fruit components have clearly-defined boundaries while Guice modules don't (all Guice -modules have the same type) and to the fact that both Fruit and Guice are 100% natural (no need to run any code -generation tools). - -#### Does Fruit use Run-Time Type Identification (RTTI)? - -Fruit **optionally** uses compile-time RTTI (i.e., it calls `typeid(T)` at compile time) to print type names in error -messages. -However, Fruit never calls `typeid` on an object, and never calls it at all at runtime. - -For the `typeid(T)` calls to work RTTI has to be enabled for the build, but you should not expect any performance -degradation due to RTTI, since it's only used at compile time. - -If RTTI is disabled, Fruit will still compile and work, but if an injection error occurs Fruit won't be able to print -the affected types as it otherwise would. It's therefore reasonable to have RTTI disabled in release builds, but -enabled in debug builds. If you disable RTTI when building code that uses Fruit, you'll need to link against a Fruit -build that also has RTTI disabled (note that the pre-packaged Fruit binaries have RTTI enabled). - -#### Fruit uses templates heavily. Will this have an impact on the executable size? - -Fruit uses templates heavily for metaprogramming, but the storage that backs injectors and components is *not* -templated. - -Most of the templated methods are just wrappers and are written in such a way that they can be inlined by the compiler; -so you should not expect a significant increase in the executable size due to the use of templates. - -See also the next question. - -#### Does Fruit have an impact on the executable size? - -If before using Fruit you were already using virtual classes, the impact on the executable size will be negligible. - -Otherwise, there will be a (likely small, but noticeable) increase due to the RTTI information for classes with virtual -methods (if RTTI is enabled). Note that [RTTI can be disabled](faq#does-fruit-use-run-time-type-identification-rtti). - -In addition, for each binding there will be up to 4 additional symbols in the executable (one function to create the -object, one function to destroy it and two static constants). The two functions are small forwarding functions, so the -constructor/destructor of the injected type might get inlined into them; in this case 1 or 2 symbols can be saved. - -An example codebase with 100 interfaces, 100 (empty) classes, 100 component functions and 900 dependency edges between -classes takes around `400 KB` (when compiled with GCC, with RTTI enabled). - -See also the [benchmarks page](benchmarks) and the next question. - -#### Does Fruit have an impact on performance? - -If before using Fruit you were already using virtual classes allocated with `new`, injection shouldn't have a noticeable -impact on performance. - -Otherwise, there will be the cost of the memory allocations for implementation classes (if they were allocated on the -stack before), and some speedup that was gained by inlining functions into their callers might also be lost. - -There are benchmarks results for various compilers in the [benchmarks page](benchmarks). The summary is that with 100 -injected interfaces, 100 implementation classes and 900 dependency edges between them, the injection time (creation of -the injector + injection of all the 100 classes) is around 70 us (when using GCC). - -When using `NormalizedComponent` to pre-collect the bindings (while still having a separate injector for each request), -you can pay the 70 us cost once and then pay only 2 us at injection time (creation of the injector + injection of all -100 classes). - -So creating an injector for each request (in addition to the single injector created during the server startup) can be -reasonable, depending of course on your performance requirements. diff --git a/Home.md b/Home.md deleted file mode 100644 index 6bd06e6..0000000 --- a/Home.md +++ /dev/null @@ -1,101 +0,0 @@ -## What is Fruit? - -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. The code has also been optimized so that using injection adds very little overhead -(more details in the [benchmarks page](benchmarks)). - -It allows to split the implementation code in "components" (aka modules) that can be assembled to form other components. - -From a component it's then possible to create an injector, that can create instances of the classes registered in the -component. - -The code is on github at [github.com/google/fruit](https://github.com/google/fruit) and -[prebuilt packages](https://github.com/google/fruit/wiki/install#prebuilt-packages) are available. - -## Features - -* Main features: - * Binding of a type to an interface, [details here](quick-reference#bindings) - * Inject annotations for constructors, [details here](quick-reference#inject-macro) - * Binding to a provider (lambda), [details here](quick-reference#providers) - * Binding to an instance/value, [details here](quick-reference#binding-instances) - * Annotated bindings, [details here](quick-reference#annotated-injection) - * Assisted injection, [details here](quick-reference#factories-and-assisted-injection) - * Automatic registration of factories for a type I if there is a factory for C and I is bound to C, - [details here](quick-reference#bindings) -* Unlike most DI frameworks, most checks are done at **compile time**, so that errors can be caught early. Some - examples of checks done at compile time: - * Checking that all required types are bound (implicitly or explicitly) - * Checking that there are no dependency loops in the bound types. -* The only injection errors that can't be detected at compile time (and will be detected at run-time) are: - * When a type has multiple inconsistent bindings in different components. - * When two `Component` functions `install()` each other. - * When a user-defined lambda passed to `registerProvider` or `registerMultibindingProvider` returns `nullptr`. - * When attempting to replace a `Component` (using `replace(...).with(...)` after the replaced componed is - installed). -* No code generation. Just include `fruit/fruit.h` and link with the `fruit` library. -* Not intrusive. You can bind interfaces and classes without modifying them (e.g. from a third-party library that - doesn't use Fruit). -* Reduces the need of `#include`s. The header file of a component only includes the interfaces exposed by the - component. The implementation classes and the interfaces that the component doesn't expose (for example, private - interfaces that the client code doesn't need to know about) don't need to be included. So after changing the binding - of a type in a component (as long as the interfaces exposed by the component remain the same) only the component - itself needs to be re-compiled. Any components and injectors that use that component **don't** need to. This makes - compilation of large projects much faster than an include-all-the-classes-I-need-to-inject approach. -* Helps with binary compatibility: as a consequence of the previous point, since the client code doesn't include the - implementation classes (not even the header files) if the interfaces exported by the component didn't change the - compiled client code is binary compatible with the new implementation. -* No internal static data. This allows the creation of separate injectors in different parts of a system (even - concurrently), which might bind the same type in different ways. -* Conditional injection based on runtime conditions. This allows to decide what to inject based on e.g. flags passed - to the executable or an XML file loaded at runtime. - * Note that you don't need special support in Fruit for the way that you use to decide what to inject. For - example, if you'd like to determine the classes to inject based on the result of an RPC to a server sent using a - proprietary RPC framework, you can do this and you don't need to modify Fruit. -* The combination of the previous two features means that at runtime you can decide to create a separate injector with - a different configuration. E.g. think of a web server that receives a notification to reconfigure itself, creates a - component with the new configuration for new requests and then deletes the old one when there are no more requests - using it, never stopping serving requests. -* Optional eager injection: after calling a specific method on the injector, multiple threads can use the same - injector concurrently with no locking. -* Multi-bindings: unlike the typical binding when in an injector there's a single binding for each type, - multi-bindings allow components to specify several bindings and the collection of bound instances can be retrieved - from the injector. This can be useful for e.g. plugin loading/hooks, or to register request handlers in a server. - -Eager to get started? Jump to the [Getting started](https://github.com/google/fruit/wiki/tutorial:-getting-started) -page of the tutorial. - -Look at the [examples/](https://github.com/google/fruit/tree/master/examples) directory in the source tree for example -code, or see the [FAQ page](faq) for more information. - -#### Rejected features - -* Compile-time detection of multiple inconsistent bindings. This feature has been rejected because it would interfere - with some of the features above that are considered more important (conditional injection, binary compatibility, - few includes). -* Injection scopes, e.g. binding a type/value only for the duration of a request. This feature was implemented and - then removed, replaced by the use of `NormalizedComponent`. If you need to create many injectors that have most of - the bindings in common, `NormalizedComponent` allows to save most of work involved in the injector creation (but - there will still be separate injectors). See - [the server page in the tutorial](https://github.com/google/fruit/wiki/tutorial:-server) for an example use of - `NormalizedComponent` with per-request injectors. - -Do you have a feature in mind that's not in the above list? Drop me an email -([poletti.marco@gmail.com](mailto:poletti.marco@gmail.com)), I'm interested to hear your idea and I'll implement it if -feasible. - -### License - -The code is released under the Apache 2.0 license. See the -[COPYING](https://github.com/google/fruit/blob/master/COPYING) file for more details. - -This project is not an official Google project. It is not supported by Google and Google specifically disclaims all -warranties as to its quality, merchantability, or fitness for a particular purpose. - -### Contact information - -Currently [Marco Poletti](https://github.com/poletti-marco) ([poletti.marco@gmail.com](mailto:poletti.marco@gmail.com)) -is the only regular developer of this project. - -Occasional contributors: [Jason Mealler](https://github.com/jmealler), [Maxwell Koo](https://github.com/mjkoo) diff --git a/Install.md b/Install.md deleted file mode 100644 index d69a87f..0000000 --- a/Install.md +++ /dev/null @@ -1,120 +0,0 @@ -### Dependencies - -Fruit's only dependency is on the Boost hashset/hashmap implementation. Even that is optional: you can switch to the -STL implementation by passing the flag `-DFRUIT_USES_BOOST=False` to CMake. In any case, the Boost library is only -required when building Fruit itself and not when building code that uses Fruit. - -Also, Fruit uses many C++11 features so you need to use a relatively recent compiler in C++11 mode (or later). -**This is not just to build Fruit itself, but also to build any code that uses Fruit.** - -The supported compiler/STL/OS combinations are: - -* GCC 5.0.0 or later on Linux and OS X -* MinGW's GCC 5.0.0 or later on Windows -* Clang 3.5 or later using stdlibc++ (GCC's STL) on Linux -* Clang 3.5 or later using libc++ on Linux and OS X -* MSVC 2017 or later on Windows - -That said, any C++11-compliant compiler should work on any platform. - -Compilers known _not_ to work due to a lacking C++11 support and/or compiler bugs: - -* Intel compiler ([bug1](https://software.intel.com/en-us/comment/1862049), - [bug2](https://software.intel.com/en-us/comment/1854501)) - -If you find any other compiler/platform where Fruit doesn't work, feel free to -[contact me](mailto:poletti.marco@gmail.com) and I can take a look. - -### Prebuilt packages - -Prebuilt packages for Fedora, openSUSE, Ubuntu, Debian and various other distributions are available. These packages are -built using GCC, and **they may not work if you want to compile your project with Clang or other compilers**. In that -case, see the section below on how to compile Fruit manually. - -**[Choose your distribution here](http://software.opensuse.org/download.html?project=home%3Apoletti_marco&package=libfruit)** - -After following the instructions there, to install the headers (necessary to compile programs that use Fruit): - -* For Fedora, openSUSE, RHEL, CentOS, SLE: install the `libfruit-devel` package. -* For *Ubuntu, Debian: install the `fruit-dev` package. -* For Arch: nothing to do, the headers are already installed. - -I will write packages or build files for other distributions on request. Or if you write one, please send it to me and I -will publish it here. - -### Building Fruit manually - -In most cases you can (and should) use the prebuilt packages instead, see the previous section. - -#### With CMake, on Linux/OS X - -For building Fruit, you'll need to have `cmake` and `make` installed, together with a recent version of GCC/Clang (see -above). - -First, get the code from Github: [https://github.com/google/fruit/releases](https://github.com/google/fruit/releases) - -To configure and build: - - cmake -DCMAKE_BUILD_TYPE=Release . && make -j - -Or if you don't want to use Boost: - - cmake -DCMAKE_BUILD_TYPE=Release -DFRUIT_USES_BOOST=False . && make -j - -To install (under Linux this uses `/usr/local`): - - sudo make install - -To configure for installation in a specific directory, e.g. `/usr`: - - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr . && make -j - -The above instructions are the simplest to get started, but out-of-source builds are also supported. - -#### With MSVC, on Windows - -You can import Fruit in Visual Studio (2017 and later) as a CMake project. You need to set the relevant CMake flags in -the `CMakeSettings.json` file that Visual Studio will create. -For example, if you installed Boost in `C:\boost\boost_1_62_0`, you can put this configuration in your -`CMakeSettings.json`: - - { - "configurations": [ - { - "name": "x64-Release", - "generator": "Visual Studio 15 2017 Win64", - "configurationType": "Release", - "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", - "cmakeCommandArgs": "-DBOOST_DIR=C:\\boost\\boost_1_62_0 -DCMAKE_BUILD_TYPE=Release", - "buildCommandArgs": "-m -v:minimal" - } - ] - } - -Or if you don't want to use Boost, you can use: - - { - "configurations": [ - { - "name": "x64-Release", - "generator": "Visual Studio 15 2017 Win64", - "configurationType": "Release", - "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", - "cmakeCommandArgs": "-DFRUIT_USES_BOOST=False -DCMAKE_BUILD_TYPE=Release", - "buildCommandArgs": "-m -v:minimal" - } - ] - } - -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). - -For more information (e.g. how to run Fruit tests in MSVC) see the -[CONTRIBUTING.md](https://github.com/google/fruit/blob/master/CONTRIBUTING.md) file. - - -#### With Bazel, on Linux - -Building with [Bazel](http://bazel.io) is also supported. You should import the `fruit/extras/bazel_root` directory, -not the Fruit root. See the [CONTRIBUTING.md](https://github.com/google/fruit/blob/master/CONTRIBUTING.md) file -for more details. diff --git a/Quick-reference.md b/Quick-reference.md deleted file mode 100644 index f808a28..0000000 --- a/Quick-reference.md +++ /dev/null @@ -1,1007 +0,0 @@ - -This page is a quick reference for Fruit features, it's intended for people that already know the basics of Fruit. If -you're just starting to learn Fruit, read the [tutorial](https://github.com/google/fruit/wiki/tutorial:-getting-started) -first. - -#### Table of contents - -* [Constructor injection](#constructor-injection) -* [Bindings](#bindings) -* [Multibindings](#multibindings) -* [Providers](#providers) -* [Binding instances](#binding-instances) -* [Annotated injection](#annotated-injection) -* [Factories and assisted injection](#factories-and-assisted-injection) -* [Type of a component, components with requirements and automatic bindings](#type-of-a-component-components-with-requirements-and-automatic-bindings) -* [Injector](#injector) -* [Normalized components](#normalized-components) -* [Injection scopes](#injection-scopes) -* [Eager injection](#eager-injection) -* [Lazy injection](#lazy-injection) -* [Templated and parametrized components](#templated-and-parametrized-components) - - -## Constructor injection - -#### INJECT macro - -The simplest way to define that a class should be injected using a constructor is to wrap the prototype of that -constructor in the class definition with the INJECT macro. - - class GreeterImpl : public Greeter { - private: - Writer* writer; - - public: - INJECT(GreeterImpl(Writer* writer)) - : writer(writer) { - } - - // ... - }; - -

- -

- -The constructor can then be defined inside the class definition (as in the snippet above) or just declared there and -defined out of the class (for example, in a `.cpp` file if the class definition is in a header file). If the constructor -is defined elsewhere, only the prototype in the class definition should be wrapped in `INJECT()`, the constructor -definition shouldn't be. - -Note that where constructor injection is desired, even 0-argument constructors must be explicitly registered, whether or -not they are user-defined. Fruit never uses a constructor unless that constructor has been explicitly marked as the one -that should be used for injection. This is to avoid undesired constructor injection of value types; e.g. if -`std::vector` is not bound it's likely that the programmer forgot and Fruit reports an error if this binding -is missing, rather than silently constructing an empty vector. - - class StdoutWriter : public Writer { - public: - INJECT(StdoutWriter()) = default; - - // ... - }; - -

- -

- -The INJECT macro can only be used when the constructor has a simple signature. It can't be used with any of the -following: - -* An explicit `inline` specifier at the point of declaration of the constructor. In this case, the specifier could be - removed since for constructors defined in the class definition it's implicit, while for constructors defined outside - the class it's the `inline` specifier at the point of definition that counts. However if it's present it interferes - with the `INJECT()` macro. -* Templated constructors. In this case, you should use `registerProvider` instead. -* Constructors with default values for parameters. In this case, you should use an `Inject` typedef or - `registerConstructor` instead. - -Note that the restriction on templates is only when the constructor itself is templated. `INJECT()` **can** be used for -templated classes, as long as the constructor is not also templated. - - template - class GreeterImpl : public Greeter { - private: - W* writer; - - public: - INJECT(GreeterImpl(W* writer)) - : writer(writer) { - } - - // ... - }; - -

- -

- -The `INJECT()` macro can also be used when only some of the constructor parameters should be injected, see -[assisted injection](quick-reference#factories-and-assisted-injection) for more details. - -#### Inject typedef - -When the `INJECT()` macro can't be used (or as an alternative to avoid using macros) an `Inject` typedef can be defined -inside the class instead, with the constructor signature. The two examples in the INJECT section can be rewritten as: - - class GreeterImpl : public Greeter { - private: - Writer* writer; - - public: - GreeterImpl(Writer* writer) - : writer(writer) { - } - - typedef GreeterImpl(Writer*) Inject; - // Or: - // using Inject = GreeterImpl(Writer*); - - // ... - }; - -

- -

- - class StdoutWriter : public Writer { - public: - StdoutWriter() = default; // Optional if no other constructors are defined - - typedef StdoutWriter() Inject; - // Or: - // using Inject = StdoutWriter(); - - // ... - }; - -

- -

- -And these are two examples that couldn't be written using the `INJECT()` macro: - - class StdoutWriter : public Writer { - public: - StdoutWriter(const char* line_terminator = "\n"); - - typedef StdoutWriter() Inject; - // Or: - // using Inject = StdoutWriter(); - - // ... - }; - -

- -

- - class GreeterImpl : public Greeter { - private: - Writer* writer; - - public: - template - GreeterImpl(W* writer) - : writer(writer) { - } - - template - GreeterImpl(std::unique_ptr writer) - : writer(writer.get()) { - } - - typedef GreeterImpl(Writer*) Inject; - // Or: - // using Inject = GreeterImpl(Writer*); - - // ... - }; - -

- -

- -In the last example the first templated constructor will be used, with `W`=`Writer`. The usual overloading resolution -(with template parameter deduction) is performed to determine the constructor to use for injection. - -As the `INJECT` macro, the `Inject` typedef can also be used when only some of the constructor parameters should be -injected, [see assisted injection](quick-reference#factories-and-assisted-injection) for more details. - -#### registerConstructor() - -While the `Inject` typedef doesn't have some of the limitations of the `INJECT()` macro, there are some situations where -even the typedef is not flexible enough: - -* If you can't modify the class definition. This is typically because the class is a third-party class that is not - under our control, and the third-party library doesn't use Fruit. Also, a similar situation would occur if you're - trying Fruit in an existing codebase and you want to keep Fruit-specific code separate from the existing code. -* If the class should be injected using that constructor only in some cases. For example, in other cases you want a - different constructor for that class, or you want to use a provider instead of constructor injection. - -The solution here is the `registerConstructor()` method available when constructing a component. - - class GreeterImpl : public Greeter { - private: - Writer* writer; - - public: - GreeterImpl(Writer* writer) - : writer(writer) { - } - - // ... - }; - - fruit::Component, Greeter> getGreeterComponent() { - return fruit::createComponent() - .registerConstructor() - .bind(); - } - - -

- -

- -The `registerConstructor()` alone can be modelled as: - -

- -

- -If only some of the constructor parameters should be injected, see -[the section on assisted injection](quick-reference#factories-and-assisted-injection). - -If none of the constructors does the desired initializations, see the [section on providers](quick-reference#providers) -to see how to bind a lambda function instead. - -## Bindings - -`bind()` specifies that `Interface` can be injected from an object of type `Impl`. `Impl` must be a -class derived from `Interface`. -It can also bind factories, i.e. bind `std::function(T1,...,Tn)>` to -`std::function(T1,...,Tn)>` or `std::function`, for any parameter types -`T1,...,Tn`. - -See the section on [factories and assisted injection](quick-reference#factories-and-assisted-injection) to see a -convenient way how to bind a `std::function(T1,...,Tn)>` in the first place. - -

- -

- -## Multibindings - -Multibindings are a feature of Fruit (inspired by Guice) that allows to bind the same type multiple times, in different -ways. They can be used to bind plugins, listeners, etc. - -Also, since there are no constraints to the number of bindings (even 0 is allowed), they can be used for optional -injection: «if T is bound, then get an instance of T and ...», however in this case T must be bound as a multibinding -and not as a normal binding. See [lazy injection](quick-reference#lazy-injection) instead if you want something like -«if (...) then inject T» with T always bound but expensive to inject. - -The multibindings for a given type are completely separate from the normal bindings; in most cases a type will have -either multibindings or bindings, but not both. - -Multibindings can depend on normal bindings (e.g. using constructor injection) but they can't depend on other -multibindings. - -Unlike normal bindings, multibindings do **not** contribute to a Component type. E.g. a component with multibindings for -Foo and a binding for Bar will have type `fruit::Component`, not `fruit::Component`. This allows to add -multibindings for any type in any component without exposing this in the component type. - -Unlike normal bindings, Fruit will not attempt to remove duplicate multibindings, even if they are -"the same multibinding". - -Example usage: - - fruit::Component<> getListenerComponent() { - return fruit::createComponent() - .addMultibinding() - .addMultibinding(); - } - - const std::vector& listeners = injector.getMultibindings(); - for (Listener* listener : listeners) { - listener->notify(); - } - -Note that here we specify all multibindings in a single `Component`, but this is not necessary; as for normal bindings, -Fruit will collect the multibindings in all components used to create the injector. - -## Providers - -This section is about registering providers. For `fruit::Provider`, see the section on -[lazy injection](quick-reference#lazy-injection). - -The simplest way to register a class for injection is using constructor injection. However, sometimes there is no -constructor that does the desired initialization, and it's not possible to add one (e.g for third-party classes) or it's -not reasonable to add one in that class (e.g. for value types, or if the constructor would have to do a too-complex -task). In these cases, Fruit allows to construct the object using an arbitrary lambda function. - - class Database { - public: - static Database* open(std::string db_name, DbConnectionPool* pool); - - void setConnectionTimeout(int seconds); - ... - }; - - fruit::Component getDatabaseComponent() { - return fruit::createComponent() - ... // Bind DbConnectionPool here - .registerProvider([](DbConnectionPool* pool) { - Database* db = Database::open("MyDb", pool); - db->setConnectionTimeout(12); - return db; - }); - } - -

- -

- -If the lambda returns a pointer, as in the example above, Fruit will **take ownership** of that pointer and will call -`delete` on that object when the injector is destroyed. If you don't want Fruit to take ownership, wrap the pointer in a -holder struct or tuple (but note that you'll need to change the places where that type is used) or consider binding an -instance. - -Instead, if the lambda returns a value, Fruit will move the value into the injector and then call the class destructor -when the injector is destroyed. Note that this saves a memory allocation/deallocation, so it's preferable to -return a value if possible. - -`registerProvider()` supports only lambdas with no captured variables (aka stateless lambdas). Plain functions are not -supported (but you can wrap the function in a lambda). - -If you would like to bind a lambda with captures or a user-defined functor, it can still be done with a workaround. - -To use this user-defined functor: - - struct Functor { - Functor(int n) {...} - MyClass operator()(Foo* foo) {...} - }; - -Instead of: - - Component getMyClassComponent() { - static const Functor aFunctor(42); - return fruit::createComponent() - ... // Bind Foo - // Not allowed !! - .registerProvider([=aFunctor](Foo* foo) { return aFunctor(foo); }); - } - -Write: - - Component getMyClassComponent() { - static const Functor aFunctor(42); - return fruit::createComponent() - ... // Bind Foo - .bindInstance(aFunctor) - .registerProvider([](Functor functor, Foo* foo) { return functor(foo); }); - } - -

- -

- -## Binding instances - -In most cases, it's easier and less bug-prone to let Fruit manage the lifetime of injected objects. However, in some -cases it's necessary to give Fruit access to some object external to the injector, and for which Fruit has no ownership. - -Binding instances is also a way to bind values that are constant during the lifetime of the injector, but vary between -injectors; see the section on [normalized components](quick-reference#normalized-components). - - fruit::Component getFooComponent() { - static Foo foo = createFoo(); - return fruit::createComponent() - .bindInstance(foo); - } - -

- -

- -It's also possible to bind constant values, however they then need to be marked as `const` in the `Component` type too: - - fruit::Component getFooComponent() { - const static Foo foo = createFoo(); - return fruit::createComponent() - .bindInstance(foo); - } - -## Annotated injection - -There are cases when several implementation types might have the same "natural interface" they implement, except that -only one of these types can be bound to it within a single injector. Annotated injection allows to have multiple -bindings for the same type and to specify which binding is desired at each point of use. See the -[tutorial page](https://github.com/google/fruit/wiki/tutorial:-annotated-injection) for a complete example. - -To bind a type using annotated injection, use `fruit::Annotated` instead of `T`, where `MarkerType` is -any type you want to use to mark this binding for `T` (typically `MarkerType` is defined as an empty struct, for -simplicity). - -Example binding: - - class MainBrakeImpl : public Brake { - public: - INJECT(MainBrakeImpl()) = default; - - // ... - }; - - fruit::Component> getMainBrakeComponent() { - return fruit::createComponent() - .bind, MainBrakeImpl>(); - } - -Example use: - - class CarImpl : public Car { - private: - Brake* mainBrake; - - public: - INJECT(CarImpl(ANNOTATED(MainBrake, Brake*) mainBrake) - : mainBrake(mainBrake) { - } - // Or: - // using Inject = CarImpl(fruit::Annotated); - // CarImpl(Brake* mainBrake) - // : mainBrake(mainBrake) { - // } - - // ... - }; - -Note that the `ANNOTATED` macro is only meant to be used within `INJECT`. Everywhere else you should write -`fruit::Assisted<>` instead, both to define bindings that use annotated injection and to use them. - -Fruit supports annotated types instead of plain types everywhere it would make sense to use them (e.g. you can use -`ASSISTED` and `ANNOTATED` parameters in the same constructor, you can use an annotated type in `bindInstance`). - -The only notable place where you should _not_ use annotated types is to annotate the class type in a constructor: - - class FooImpl : public Foo { - private: - Bar* bar; - public: - // Error, no need to use ANNOTATED here - INJECT(ANNOTATED(MyAnnotation, FooImpl)(Bar* bar)) - : bar(bar) { - } - }; - -When auto-injecting a class, Fruit already knows the correct annotation, so providing the annotation here would be -redundant. - -Another thing to note when using annotated injection is that if you bind an implementation class to an interface using 2 -different annotations there will be only _one_ instance of the implementation class: - - class FooImpl : public Foo { - public: - INJECT(Foo()) = default; - }; - - struct First {}; - struct Second {}; - Component, fruit::Annotated> - getFooComponent() { - - return fruit::createComponent() - .bind, FooImpl>() - .bind, FooImpl>(); // Allowed, but misleading! - } - -That's because there is only one instance of every type in an injector (unless that type is annotated with multiple -annotations, but `FooImpl` isn't). This is probably not what you want. In this case, you should also annotate the two -implementation classes, so that the injector will contain a different instance for each annotation: - - class FooImpl : public Foo { - public: - INJECT(Foo()) = default; - }; - - struct First {}; - struct Second {}; - Component, fruit::Annotated> - getFooComponent() { - - return fruit::createComponent() - .bind, fruit::Annotated>() - .bind, fruit::Annotated>(); - } - -Note that we did not change the definition of `FooImpl`, we only need to specify the annotation in the `bind()` calls. -Fruit will then auto-inject `fruit::Annotated` and `fruit::Annotated`, both using -`FooImpl`'s constructor. - -## Factories and assisted injection - -There are cases when within a single injector, multiple instances of a given class need to be created, either by -repeatedly calling a single factory function (e.g. for threads in a thread pool) or because there is no single "correct" -instance (different instances are required in different places). Instead of injecting a type `MyClass`, a -`std::function()>` or `std::function(T1,...,Tn)>` can be injected -instead. - -Example factory binding: - - class MyClass { - public: - MyClass(Foo* foo, int n); - ... - }; - - Component(int)>> getMyClassComponent() { - fruit::createComponent() - ... // Bind Foo - .registerFactory(Foo*, fruit::Assisted)>( - [](Foo* foo, int n) { - return std::unique_ptr(new MyClass(foo, n)); - }); - } - -

- -

- -Example use of the binding from the injector: - - std::function(int)> myClassFactory(injector); - std::unique_ptr x = myClassFactory(15); - -Example use of the binding to inject another class: - - class Bar { - public: - INJECT(Bar(std::function(int)> myClassFactory)) { - std::unique_ptr x = myClassFactory(15); - ... - } - }; - -

- -

- -Note the `fruit::Assisted<>` wrapping the type `int` where `registerFactory()` is called. This allows to distinguish -types that should be parameters of the factory ("assisted injection", like `int` in this example) from ones that should -be injected by Fruit (normal, non-assisted injection, like `Foo` in this example). - -While Fruit will only create one instance of the factory in each injector, every call to the factory potentially returns -another value; Fruit does not memoize the return value. So two calls to `myClassFactory(15)` will return different -instances. - -If memoization is desired, it can be performed in the lambda passed to `registerFactory()`. - -Also, for factories, Fruit does **not** take ownership of the values created by the factory. So it's preferable to -return a `std::unique_ptr` in the lambda instead of a plain pointer to give ownership of the object to the caller of the -factory. - -It's possible to bind factories using `bind<>`, see the [section on bind](quick-reference#bindings) for more info. - -In simple cases, where the lambda passed to `registerFactory()` just calls `std::unique_ptr(new MyClass(...))` -(as in the example above) the `INJECT()` macro or an `Inject` typedef can be used in the created class itself, instead -of writing the `registerFactory()` call explicitly in the component definition. - - class MyClass { - public: - INJECT(MyClass(Foo* foo, ASSISTED(int) n)); - ... - }; - -

- -

- -Note that when using `INJECT()` for assisted injection, the assisted types are wrapped with the `ASSISTED()` macro -instead of `fruit::Assisted<>`. - - class MyClass { - public: - MyClass(Foo* foo, int n); - - using Inject = MyClass(Foo*, fruit::Assisted); - ... - }; - -Instead, if an `Inject` typedef is used, the types that require assisted injection should be wrapped in -`fruit::Assisted<>` (as in the original `registerFactory()` call) instead of `ASSISTED()`. - -In both cases, the constructor definition (in case the constructor is defined outside the class definition) does **not** -need to wrap the types in any way; this is obvious when using the `Inject` typedef, but less obvious when using the -`ASSISTED()` macro. - -The `INJECT()` macro (and the equivalent Inject typedef) can also be used to inject factories in the special case when -there are 0 assisted arguments; in this case they will bind a type of the form -`std::function()>` consistently with the cases with >0 assisted arguments. - -## Type of a component, components with requirements and automatic bindings - -When using Fruit, every component has a type that declares what type bindings the component exposes and which ones (if -any) the component requires. - -For example, if a component has type `fruit::Component, U1, U2>`, that component exposes the -types `U1` and `U2` but requires bindings for `T1`, `T2` and `T3`. In the common case where a component has no -requirements, the `fruit::Required<...>` part can be omitted, so we would have just `fruit::Component` in this -case. - -When combining components, requirements can be satisfied with a matching binding, or by installing a component that -provides those types. - -For example: - - fruit::Component, U1, U2> getU1U2Component(); - fruit::Component, T1> getT1Component(); - - class SomeType : public T2 { - public: - INJECT(SomeType(T3*)) {...} - }; - - fruit::Component, U1> getU1Component() { - return fruit::createComponent() - .install(getU1U2Component) // Now U1,U2 are provided, T1,T2,T3 are required - .install(getT1Component) // Now U1,U2,T1 are provided, T2,T3 are required - .bind(); // Now U1,U2,T1,T2 are provided, - // T3,SomeType are required - } - -

- -

- -When Fruit compares the provides/requires with the return type of the `getT1Component()` function, it realizes that all -types declared to be provided are already provided, but the type `SomeType` is currently required even though it's not -declared as required in the resulting Component. At this point Fruit looks for an `Inject` typedef in SomeType (the -`INJECT()` macro is just a shorthand for an `Inject` typedef), finds it and adds the binding to the component (automatic -binding). Note that in order to add the automatic binding Fruit needs to look at the class definition of `SomeType`. -Instead, for other types (e.g. `T3`) Fruit doesn't require the definition, a forward declaration is enough to make the -above function compile. - -When calculating the required and provided types, Fruit also ensures that there are no dependency loops between bindings -and/or components. For example, the following code produces a (compile time) dependency loop error: - - fruit::Component, T1> getT1Component(); - fruit::Component, T2> getT2Component(); - - fruit::Component getT1T2Component() { - return fruit::createComponent() - .install(getT1Component) - .install(getT2Component); // Error, dependency loop. - } - -

- -

- -## Injector - -A Fruit injector is an object of type `fruit::Injector`. Unlike components, injectors can't have -requirements. The types `T1,...Tn` are the types exposed by the injector, i.e. the types that can be used in calls to -`get<>`. Even though the injector most likely contains bindings for other types, only the types explicitly declared in -the injector type can be directly obtained by the injector itself. Other types will be injected as needed using the -bindings in the injector, but behind the scenes. - -Multibindings behave differently, see the [section on multibindings](quick-reference#multibindings) for more -information. - -For each type `T` exposed by the injector, the following `get()` calls are allowed: - -* `injector.get()` -* `injector.get()` -* `injector.get()` -* `injector.get()` -* `injector.get()` -* `injector.get>()` -* `injector.get>()` -* `injector.get>()` - -See the [section on lazy injection](quick-reference#lazy-injection) to see what `fruit::Provider` is; the other types -should be clear. - -For constant bindings (e.g. the `T` in `fruit::Injector`) only the following `get()` calls are allowed: - -* `injector.get()` -* `injector.get()` -* `injector.get()` -* `injector.get>()` - -Note that we don't mention any `std::function` here. This is because the injector treats `std::function` as any other -type. - -If `T` is `std::function(T1,...Tn)>` the above get calls are allowed as for any other `T`. Note that -those are allowed for the `std::function` type, but not for `X` itself. - -For convenience, the injector also has a conversion operator to all types that can be retrieved from it using `get()`. -For example, this get call: - - MyClass* myClass = injector.get(); - -can be abbreviated as: - - MyClass* myClass(injector); - -## Normalized components - -Normalized components are a feature of Fruit that allows for fast creation of multiple injectors that share most (or -all) the bindings. - -This is an advanced feature of Fruit that allows to reduce injection time in some cases; if you're just starting to -use Fruit you might want to ignore this for now (just construct an Injector from your root Component function). - -Using a NormalizedComponent only helps if: - -* You create multiple injectors during the lifetime of a process. E.g. if you only create one injector at startup you - won't benefit from using `NormalizedComponent`. - -* Some of those injectors share all (or almost all) their bindings. - -When both of those requirements apply, you can switch to using `NormalizedComponent` in the "similar" injectors by -first refactoring the injectors' root components to be of the form: - - fruit::Component<...> getRootComponent(...) { - return fruit::createComponent() - // This contains the bindings common to the group of similar injectors. - .install(getSharedComponent, ...) - // This contains the bindings specific to this injector. - .install(getSpecificComponent, ...); - } - -Then you can change your injector construction from: - - fruit::Injector<...> injector(getRootComponent, ...); - -To: - - fruit::NormalizedComponent, ...> normalized_component(getSharedComponent, ...); - fruit::Injector<...> injector(normalized_component, getSpecificComponent, ...); - -This splits the work of constructing the Injector in two phases: normalization (where Fruit will call the `Component` -functions to collect all the bindings and check for some classes of runtime errors) and the actual creation of the -injector, during which Fruit will also collect/check the additional bindings specific to that injector. - -Then you can share the same normalized_component object across all those injectors (also in different threads, -`NormalizedComponent` is thread-safe), so that the normalization step only occurs once (i.e., you should only construct -`NormalizedComponent` from `getSharedComponent` once, otherwise you'd pay the normalization cost multiple times). - -Creating an `Injector` from a `NormalizedComponent` and injecting separate instances is very cheap, on the order of 2 us -for an injection graph with 100 interfaces, 100 implementation classes and 900 edges (for more details see -[the Benchmarks page](https://github.com/google/fruit/wiki/benchmarks)). - -This might (depending of course on your performance requirements) allow you to create injectors where it would -otherwise be unthinkable, e.g. creating a separate injector for each request in a server. - -Injectors that share the same `NormalizedComponent` are still independent; for example, if you call -`injector.get()` in two injectors, each injector will construct its own instance of `Foo`. - -Example usage: - - fruit::Component, X> getXComponent(); // Lots of bindings here - - int main() { - fruit::NormalizedComponent, X> normalizedComponent( - getXComponent()); - - for (...) { - fruit::Component yComponent = ...; // Few injector-specific bindings - - Injector injector(normalizedComponent, yComponent); - X* x = injector.get(); - ... - } - } - - -Constructing an injector from a `NormalizedComponent` is much faster than constructing it from a Component, because most -of the processing (aka normalization) has been done in advance when the `Component` object was converted to -`NormalizedComponent`. It's also possible to specify some additional bindings at the point where the injector is -created; however, this should be limited to very few bindings (<5 is best, <10 is still ok). If too many of these -bindings are added, the injector construction can become even slower than it would have been without using the -normalized component. - -As mentioned before, normalized components are best suited if the vast majority of the bindings are common. A typical -use case is in a server, where a separate injector is created for every request, but the bindings of the handler classes -are common to all requests; in that case a normalized component can be used, and the request itself (and possibly some -metadata about the request) can be added to each injector at the point of construction. See the -[section on injection scopes](quick-reference#injection-scopes) for a more detailed example. - -## Injection scopes - -This section is meant for readers familiar with Guice. If you don't know what injection scopes are, feel free to skip -this section. - -Unlike Guice, Fruit does **not** have injection scopes. However, Fruit's normalized components can be used in situations -where with Guice you would have used injection scopes. - -As an example, let's consider a situation in which there are 3 scopes, with the types `X`, `Y` and `Z` part of the three -scopes respectively: - - // Outer scope - class X { - public: - INJECT(X()) = default; - ... - }; - - fruit::Component getXComponent() { - return fruit::createComponent(); - } - - // Middle scope - class Y { - public: - INJECT(Y(X* x)); - ... - }; - - fruit::Component, Y> getYComponent() { - return fruit::createComponent(); - } - - // Inner scope - class Z { - public: - INJECT(Z(X* x, Y* y)); - ... - }; - - fruit::Component, Z> getZComponent() { - return fruit::createComponent(); - } - -Then the code that creates the injectors can be written as: - - fruit::Component<> getEmptyComponent() { - return fruit::createComponent(); - } - - fruit::Component getXInstanceComponent(X* x) { - return fruit::createComponent() - .bindInstance(*x); - } - - fruit::Component getXYInstanceComponent(X* x, Y* y) { - return fruit::createComponent() - .bindInstance(*x) - .bindInstance(*y); - } - - fruit::NormalizedComponent outerScopeNormalizedComponent( - getXComponent); - fruit::NormalizedComponent, Y> middleScopeNormalizedComponent( - getYComponent); - fruit::NormalizedComponent, Z> innerScopeNormalizedComponent( - getZComponent); - - for (...) { - // We want to enter the outer scope here. - fruit::Injector outerScopeInjector( - outerScopeNormalizedComponent, getEmptyComponent); - X* x = outerScopeInjector.get(); - for (...) { - // We want to enter the middle scope here. - fruit::Injector middleScopeInjector( - middleScopeNormalizedComponent, getXInstanceComponent, x); - Y* y = middleScopeInjector.get(); - for (...) { - // We want to enter the inner scope here. - fruit::Injector innerScopeInjector( - innerScopeNormalizedComponent, getXYInstanceComponent, x, y); - Z* z = innerScopeInjector.get(); - ... - } - } - } - -The instance of `X` is owned by `outerScopeInjector` so it lives for a full iteration of the outer loop. The instance of -`Y` is owned by `middleScopeInjector` so it lives for an iteration of the middle loop. Finally, the instance of `Z` is -owned by `innerScopeInjector` so it only lives for an iteration of the inner loop. - -See the [section on normalized components](quick-reference#normalized-components) and the -[section on bindInstance](quick-reference#binding-instances) if the code above is not clear. - -## Eager injection - -If you create an injector that will be used concurrently by multiple threads, you either need to synchronize the calls -(using a lock) or you might want to inject everything to start with, so that all `get` and `getMultibindings` calls on -the injector don't need to modify it (as the objects have already been constructed). The `Injector` class provides a -method to do this: `eagerlyInjectAll()`. After calling this method, the `Injector` object can be shared by multiple -threads with no locking. - -However, this does not perform lazy injection; so if you're only injecting a `Provider`, `Foo` will not be -constructed. This means that even after calling this method, `get()` calls on `Provider` objects are still **not** -thread-safe (unless you're sure that that class has already been injected). - -## Lazy injection - -Lazy injection is a feature that can be used when a type `Foo` depends on a type `Bar` only for some methods, and `Bar` -is expensive to inject. This may not necessarily be because the `Bar` constructor is slow, it might be expensive because -it triggers the injection of many other types. In this case, `Foo` can depend on `fruit::Provider` instead of -directly depending on `Bar`. This delays the injection of `Bar` (and of the types that need to be injected in order to -inject the `Bar` object) until the `get` method of the provider is called. - -Let's see some example code for this situation: - - class Bar { - public: - INJECT(Bar(ReallyExpensiveClass x, SomeOtherReallyExpensiveClass y)); - ... - }; - - class Foo { - private: - fruit::Provider barProvider; - - public: - INJECT(Foo(fruit::Provider barProvider)) - : barProvider(barProvider) { - } - - void doSomething() { - Bar* bar = barProvider.get(); - ... - } - }; - -`Bar`, `ReallyExpensiveClass` and `SomeOtherReallyExpensiveClass` are only injected when `foo->doSomething()` is called, -not when `Foo` itself is injected. This can give a sizable performance win if `foo->doSomething()` is not always called. - -Every call to `get()` (within the same injector) returns the same `Bar` instance, and that's also the same instance that -will be used to inject other classes (if there are other classes that depend on `Bar`). - -This means that the call to `get()` contains a branch (basically an `if (is_already_initialized) {...}`) so once the -`Bar*` has been retrieved you might want to store it in a variable or field instead of calling `get()` multiple times. - -## Templated and parametrized components - -`get*Component()` functions are just normal functions; therefore they can be templated: - - template - class FooInterface { ... }; - - template - class FooImpl : public FooInterface> { - public: - INJECT(FooImpl()) = default; - ... - }; - - template - fruit::Component> getFooComponent() { - return createComponent() - .bind>, FooImpl>(); - } - -

- -

- -And they can parametrized: - - class FooInterface { ... }; - - class OldFooImpl : public FooInterface { - public: - INJECT(OldFooImpl()) = default; - ... - }; - - class NewFooImpl : public FooInterface { - public: - INJECT(NewFooImpl()) = default; - ... - }; - - fruit::Component getFooComponent(bool use_old_impl) { - if (use_old_impl) { - return fruit::createComponent() - .bind(); - } else { - return fruit::createComponent() - .bind(); - } - } - -

- -

- -The last example also shows how to do conditional injection. - -When parameterizing them, however, there are some constraints on the argument types. Specifically, they must be: - - * Copy-constructible - * Move-constructible - * Assignable - * Move-assignable - * Equality-comparable (i.e., `operator==` must be defined for two values of that type) - * Hashable (i.e., `std::hash` must be defined for values of that type) - -Note that this only applies to the arguments of the function, not the types declared by the returned `Component`. In the -example above, this means that `bool` must satisfy these requirements (and it does), but `FooInterface`, `NewFooImpl` -and `OldFooImpl` don't need to. diff --git a/Tutorial:-Annotated-injection.md b/Tutorial:-Annotated-injection.md deleted file mode 100644 index 0374c7d..0000000 --- a/Tutorial:-Annotated-injection.md +++ /dev/null @@ -1,143 +0,0 @@ - -In some situations several implementation types might have the same "natural interface" they implement, except that only -one of these types can be bound to it within a single injector. For example, a car might have a main brake and an -emergency brake, which both implement the same `Brake` interface. - -Intuitively, we'd want a component like: - - Component getBrakesComponent() { - return fruit::createComponent() - .bind() - // This doesn't actually work, Brake is already bound - .bind(); - } - -And a way to inject `MainBrakeImpl` and `EmergencyBrakeImpl` instead of just `Brake`. While it's of course possible to -inject the two concrete classes, doing so requires including their definitions (that would otherwise be in the `.cpp` -files that define the components) into all places that need to inject one or the other. - -Assisted injection is a feature of Fruit that helps with these situations (if you're familiar with Guice, this should -sound familiar). Let's see how we can use it to solve this example. The full sources are available at -[examples/annotated_injection](https://github.com/google/fruit/tree/master/examples/annotated_injection). - -Let's start with the `Brake` interface, which is straightforward (no sign of annotated injection yet): - - // brake.h - class Brake { - public: - // Activates the brake. Throws an exception if braking failed. - virtual void activate() = 0; - }; - -Then we can write an implementation of `Brake` marked as the main brake as follows: - - // main_brake.h - struct MainBrake {}; - - fruit::Component> getMainBrakeComponent(); - -
- - // main_brake.cpp - class MainBrakeImpl : public Brake { - public: - INJECT(MainBrakeImpl()) = default; - - void activate() override { - // ... - } - }; - - fruit::Component> getMainBrakeComponent() { - return fruit::createComponent() - .bind, MainBrakeImpl>(); - } - -`fruit::Annotated` is a way to mark the type `Brake` with the "annotation" `MainBrake`. The annotation -type can be any type, but to avoid confusion it's preferable to define a type just for this purpose, for example as an -empty struct as we do here. - -Fruit accepts annotated types in many places. For example here we see it passed as a parameter to `Component` and -`bind()`. Note that it's not necessary to annotate `MainBrakeImpl` too: there's only one binding of that type in the -injector, and when binding types with `bind()` the two types don't need to have the same annotation. It would be -possible though (it's just unnecessary in this case). - -We can then define an emergency brake class on the exact same way: - - // emergency_brake.h - struct EmergencyBrake {}; - - fruit::Component> - getEmergencyBrakeComponent(); - -
- - // emergency_brake.cpp - class EmergencyBrakeImpl : public Brake { - public: - INJECT(EmergencyBrakeImpl()) = default; - - void activate() override { - // ... - } - }; - - fruit::Component> - getEmergencyBrakeComponent() { - - return fruit::createComponent() - .bind, EmergencyBrakeImpl>(); - } - -We can then inject the two brakes in our `Car` class, as follows: - - // car.cpp - class CarImpl : public Car { - private: - Brake* mainBrake; - Brake* emergencyBrake; - - public: - INJECT(CarImpl(ANNOTATED(MainBrake, Brake*) mainBrake, - ANNOTATED(EmergencyBrake, Brake*) emergencyBrake)) - : mainBrake(mainBrake), emergencyBrake(emergencyBrake) { - } - // Or: - // using Inject = CarImpl(fruit::Annotated, - // fruit::Annotated); - // CarImpl(Brake* mainBrake, Brake* emergencyBrake) - // : mainBrake(mainBrake), emergencyBrake(emergencyBrake) { - // } - - void brake() override { - try { - mainBrake->activate(); - } catch (...) { - // The main brake failed! - emergencyBrake->activate(); - } - } - }; - - fruit::Component getCarComponent() { - return fruit::createComponent() - .bind() - .install(getMainBrakeComponent) - .install(getEmergencyBrakeComponent); - } - -Note the use of the `ANNOTATED` macro inside `INJECT`, the equivalent `Inject` typedef, and the fact that the two -parameters of the constructor are of type `Brake*` (i.e., `ANNOTATED` only adds the annotation to the parameter type in -the `Inject` typedef, but not to the constructor parameter). - -In this case both parameters were marked with `ANNOTATED`, but it's of course possible to mix annotated parameters with -non-annotated ones in the same class. - -This diagram models the Car component above: - -

- -

- -In the [next part of the tutorial](https://github.com/google/fruit/wiki/tutorial:-errors) we'll see how Fruit reports -injection errors. diff --git a/Tutorial:-Assisted-injection.md b/Tutorial:-Assisted-injection.md deleted file mode 100644 index 1af2d2b..0000000 --- a/Tutorial:-Assisted-injection.md +++ /dev/null @@ -1,122 +0,0 @@ - -In this part of the tutorial, we'll build a component that scales `double` values by a constant factor. The full source -is available in [examples/scaling_doubles](https://github.com/google/fruit/tree/master/examples/scaling_doubles). -To warm up, let's write a `Multiplier` component. - - // multiplier.h - class Multiplier { - public: - // Returns the product of x and y. - virtual double multiply(double x, double y) = 0; - }; - - fruit::Component getMultiplierComponent(); - -
- - // multiplier.cpp - #include "multiplier.h" - - class MultiplierImpl : public Multiplier { - public: - double multiply(double x, double y) override { - return x * y; - } - }; - - fruit::Component getMultiplierComponent() { - return fruit::createComponent() - .bind() - .registerConstructor(); - } - -

- -

- -When there is a canonical implementation of an interface, as in this case, we can put the definition of the interface -and the `get*Component()` function declaration in the same header file, to avoid having two separate header files. - -Note that the `MultiplierImpl` constructor was _not_ wrapped with `INJECT()`. It's a convenient way to tell Fruit what -injector to use, but it can't be used in some cases, for example if `MultiplierImpl` was provided by another project -that doesn't use Fruit. Here we could use it, but we don't just to show the (equivalent) explicit `registerConstructor` -call with the constructor signature. - - // scaler.h - class Scaler { - public: - virtual double scale(double x) = 0; - }; - - using ScalerFactory = std::function(double)>; - - fruit::Component getScalerComponent(); - -At first, one might be tempted to write `Component`. However, there is no single `Scaler` implementation, there -is a `Scaler` for each `double` value (the scaling factor). So we expose this in the component signature. When returning -a value type we can just return it by value, but for interfaces we must return a `unique_ptr`. Fruit can inject any -class, but there is special support for types of the form `std::function(...)>` as we'll see shortly. -Let's write the implementation now. - - // scaler.cpp - #include "scaler.h" - #include "multiplier.h" - - class ScalerImpl : public Scaler { - private: - Multiplier* multiplier; - double factor; - - public: - INJECT(ScalerImpl(ASSISTED(double) factor, Multiplier* multiplier)) - : multiplier(multiplier), factor(factor) { - } - - double scale(double x) override { - return multiplier->multiply(x, factor); - } - }; - - fruit::Component getScalerComponent() { - return fruit::createComponent() - .bind() - .install(getMultiplierComponent()); - } - -

- -

- -Here we see for the first time the use of the `ASSISTED()` macro. It's used to mark types in a constructor wrapped with -`INJECT()` that don't have to be injected, but will become the parameters of an injected factory function. - -Note that here we installed the multiplier component directly, instead of declaring it as a requirement and then writing -a component that composes `ScalerImplComponent` and `MultiplierComponent`. It's a tradeoff: this slightly reduces -modularity, but makes the code more concise (it avoids 2 files). There is no definite answer on what's better; this -tradeoff must be evaluated on a case-by-case basis. - -Note also that the `bind` operation now is doing something different: instead of binding `Scaler` to `ScalerImpl`, it's -binding an `std::function(double)>` to a `std::function(double)>`, -which is then injected using constructor injection. - - // main.cpp - #include "scaler.h" - - int main() { - fruit::Injector injector(getScalerComponent); - ScalerFactory scalerFactory(injector); - - std::unique_ptr scaler = scalerFactory(12.1); - std::cout << scaler->scale(3) << std::endl; - - return 0; - } - -Here is the `main()` function, which should be easy to understand at this point. We get an instance of the factory from -the injector, and then we call the factory ourselves to get an instance of `Scaler`. - -Now you know the basics of how to use Fruit. To get more familiar with it, try rewriting the above system by yourself or -any other program that you like, and experiment a bit by changing the code. - -In the [next part of the tutorial](https://github.com/google/fruit/wiki/tutorial:-server) we'll see how to use Fruit to -write a server. diff --git a/Tutorial:-Errors.md b/Tutorial:-Errors.md deleted file mode 100644 index 5cf002e..0000000 --- a/Tutorial:-Errors.md +++ /dev/null @@ -1,78 +0,0 @@ - -One of the key features of Fruit is that errors are reported at compile time. However, these error messages might be -confusing at first. In this part of the tutorial we'll learn how Fruit error messages look like and what are the -important parts to look for. - -In this tutorial we'll see the error messages emitted by GCC (specifically, GCC 7.0.1). Clang error messages are -similar. - -Let's consider this example: - - struct X { - }; - - fruit::Component getComponent() { - return fruit::createComponent(); - } - -Here we expect an error, because `X` is not explicitly bound and can't be automatically bound because it has no -constructor wrapped in `INJECT` nor an `Inject` typedef. - - In file included from /usr/local/include/fruit/fruit.h:25:0, - from main.cpp:2: - /usr/local/include/fruit/impl/injection_errors.h: In instantiation of ‘struct fruit::impl::NoBindingFoundError’: - /usr/local/include/fruit/impl/component.defn.h:59:55: required from ‘fruit::Component::Component(fruit::PartialComponent) [with Bindings = {}; Params = {X}]’ - main.cpp:8:35: required from here - /usr/local/include/fruit/impl/injection_errors.h:33:3: error: static assertion failed: No explicit binding nor C::Inject definition was found for T. - static_assert( - ^~~~~~~~~~~~~ - In file included from /usr/local/include/fruit/component.h:705:0, - from /usr/local/include/fruit/fruit.h:28, - from main.cpp:2: - /usr/local/include/fruit/impl/component.defn.h: In instantiation of ‘fruit::Component::Component(fruit::PartialComponent) [with Bindings = {}; Params = {X}]’: - main.cpp:8:35: required from here - /usr/local/include/fruit/impl/component.defn.h:62:135: error: no type named ‘Result’ in ‘fruit::impl::meta::OpForComponent<>::ConvertTo, fruit::impl::meta::Vector >, fruit::impl::meta::Vector >, fruit::impl::meta::Vector, fruit::impl::meta::Vector<> > >, fruit::impl::meta::Vector<>, fruit::impl::meta::EmptyList> > {aka struct fruit::impl::meta::Error}’ - (void)typename fruit::impl::meta::CheckIfError>::type(); - ^~~~~~ - /usr/local/include/fruit/impl/component.defn.h:65:68: error: ‘using Op = fruit::impl::meta::OpForComponent<>::ConvertTo, fruit::impl::meta::Vector >, fruit::impl::meta::Vector >, fruit::impl::meta::Vector, fruit::impl::meta::Vector<> > >, fruit::impl::meta::Vector<>, fruit::impl::meta::EmptyList> > {aka struct fruit::impl::meta::Error}’ has no member named ‘numEntries’ - std::size_t num_entries = component.storage.numBindings() + Op().numEntries(); - ~~~~~^~~~~~~~~~ - /usr/local/include/fruit/impl/component.defn.h:68:7: error: no match for call to ‘(Op {aka fruit::impl::meta::Error}) (fruit::impl::FixedSizeVector&)’ - Op()(entries); - ~~~~^~~~~~~~~ - -When you receive Fruit error messages, you should only look at the first error. Different compilers have varying amount -of success in reporting useful errors after the first one. So let's focus on the first error: - - In file included from /usr/local/include/fruit/fruit.h:25:0, - from main.cpp:2: - /usr/local/include/fruit/impl/injection_errors.h: In instantiation of ‘struct fruit::impl::NoBindingFoundError’: - /usr/local/include/fruit/impl/component.defn.h:59:55: required from ‘fruit::Component::Component(fruit::PartialComponent) [with Bindings = {}; Params = {X}]’ - main.cpp:8:35: required from here - /usr/local/include/fruit/impl/injection_errors.h:33:3: error: static assertion failed: No explicit binding nor C::Inject definition was found for T. - static_assert( - ^~~~~~~~~~~~~ - -From here you can see that: - -* the error is a `NoBindingFoundError` for the type `X` (third line) -* it was caused by `main.cpp:8` -* The detailed error message is `No explicit binding nor C::Inject definition was found for T.`. Note that in this - message, `T` refers to the type in the `*Error<>` instantiation (in this case, `T` is `X`). - -If we now change the code above to: - - struct X { - INJECT(X()) = default; - }; - - fruit::Component getComponent() { - return fruit::createComponent(); - } - -Then the file will compile. - -This is how Fruit reports missing bindings, one of the most common injection errors. - -In the [next part of the tutorial](https://github.com/google/fruit/wiki/tutorial:-assisted-injection) we'll learn how to -use assisted injection. diff --git a/Tutorial:-Getting-started.md b/Tutorial:-Getting-started.md deleted file mode 100644 index 1e036b5..0000000 --- a/Tutorial:-Getting-started.md +++ /dev/null @@ -1,86 +0,0 @@ -Let's look at a simple example: a Hello world implementation written using Fruit. Using Fruit is probably overkill for -such a simple situation, but it allows us to see the basic features and syntax of Fruit. - - #include - #include - - class Writer { - public: - virtual void write(std::string s) = 0; - }; - - class StdoutWriter : public Writer { - public: - // Like "StdoutWriter() = default;" but also marks this constructor as the one to use for injection. - INJECT(StdoutWriter()) = default; - - virtual void write(std::string s) override { - std::cout << s; - } - }; - - class Greeter { - public: - virtual void greet() = 0; - }; - - class GreeterImpl : public Greeter { - private: - Writer* writer; - - public: - // Like "GreeterImpl(Writer* writer) : ... {...}" but also marks this constructor as the one to use for injection. - INJECT(GreeterImpl(Writer* writer)) - : writer(writer) { - } - - virtual void greet() override { - writer->write("Hello world!\n"); - } - }; - - fruit::Component getGreeterComponent() { - return fruit::createComponent() - .bind() - .bind(); - } - - int main() { - fruit::Injector injector(getGreeterComponent); - Greeter* greeter = injector.get(); - greeter->greet(); - return 0; - } - -The structure of the above program can be modelled with the following component diagram. - -

- -

- -The `StdoutWriter` class requires nothing and provides a `StdoutWriter` instance. The binding between `StdoutWriter` and -`Writer` requires a `StdoutWriter` instance and provides a `Writer` instance. The `GreeterImpl` class requires a -`Writer` instance and provides a `GreeterImpl` instance. The binding between `GreeterImpl` and `Greeter` requires a -`GreeterImpl` instance and provides a `Greeter` instance. Finally, `GreeterComponent` requires nothing and provides a -`Greeter` instance. - -When using Fruit, composite components are typically defined in a `get*Component()` function that starts by creating an -empty component with a call to `fruit::createComponent()`, then uses a chain of operations to add sub-components. Here -we only see the `bind()` operation but there are several more. The result of the chain of operations is then -converted with an implicit conversion to the desired component type (in this case, `Component`); during this -conversion Fruit looks for constructors in the bound classes that are wrapped in `INJECT()` and this is how the other -two components above were added. Note that a component does _not_ have to expose all bound interfaces, for example in -this case we haven't exposed the `Writer` interface, and we haven't exposed any implementation classes. An alternative -signature for `getGreeterComponent()` that returns a `Component` would also -be allowed. - -It's usually useful to hide information on concrete classes, so when a class is bound to an interface the implementation -class is usually not provided. For simplicity, in the rest of the tutorial we'll model the component of the -implementation class together with all the relevant bindings as a single component. The above diagram will then become: - -

- -

- -In the [next part of the tutorial](https://github.com/google/fruit/wiki/tutorial:-simple-system) we'll see a simple -system built using Fruit. diff --git a/Tutorial:-Server.md b/Tutorial:-Server.md deleted file mode 100644 index b8040ef..0000000 --- a/Tutorial:-Server.md +++ /dev/null @@ -1,375 +0,0 @@ - -In this chapter of the tutorial, we will see how to use Fruit's normalized components to write an HTTP-like server. We -want to write a "server" that reads request URLs from standard input, and dispatches the request to the right handler -based on the URL, handling each request in a separate thread. - -The full source is available in [examples/server](https://github.com/google/fruit/tree/master/examples/server). - -First, let's define the `Request` type. For this simple server, a request is just a URL. - - // request.h - struct Request { - std::string path; - }; - -We'll also have a `ServerContext` struct, to show how request handlers can access non-request-specific information. - -This class is not strictly necessary in our case, but we still use it to show how such information would be passed to -the handlers in a real server. - - // server_context.h - struct ServerContext { - std::string startupTime; - }; - -Now let's write two request handlers: one for URLs starting with `/foo/`: - - // foo_handler.h - #include "request.h" - #include "server_context.h" - - class FooHandler { - public: - // Handles a request for a subpath of "/foo/". - // The request is injected, no need to pass it directly here.` - virtual void handleRequest() = 0; - }; - - fruit::Component, FooHandler> - getFooHandlerComponent(); - -
- - // foo_handler.cpp - #include "foo_handler.h" - - class FooHandlerImpl : public FooHandler { - private: - const Request& request; - const ServerContext& serverContext; - - public: - INJECT(FooHandlerImpl(const Request& request, const ServerContext& serverContext)) - : request(request), serverContext(serverContext) { - } - - void handleRequest() override { - cout << "FooHandler handling request on server started at " - << serverContext.startupTime << " for path: " << request.path << endl; - } - }; - - fruit::Component, FooHandler> getFooHandlerComponent() { - return fruit::createComponent() - .bind(); - } - -

- -

- -And, as you might have expected, one for URLs starting with `/bar/`: - - // bar_handler.h - #include "request.h" - #include "server_context.h" - - class BarHandler { - public: - // Handles a request for a subpath of "/bar/". - // The request is injected, no need to pass it directly here. - virtual void handleRequest() = 0; - }; - - fruit::Component, BarHandler> - getBarHandlerComponent(); - -
- - // bar_handler.cpp - #include "bar_handler.h" - - class BarHandlerImpl : public BarHandler { - private: - const Request& request; - const ServerContext& serverContext; - - public: - INJECT(BarHandlerImpl(const Request& request, const ServerContext& serverContext)) - : request(request), serverContext(serverContext) { - } - - void handleRequest() override { - cout << "BarHandler handling request on server started at " - << serverContext.startupTime << " for path: " << request.path << endl; - } - }; - - fruit::Component, BarHandler> getBarHandlerComponent() { - return fruit::createComponent() - .bind(); - } - -

- -

- -The interfaces of the two components require a `Request` and a `ServerContext` to be bound externally. This approach is -more flexible than just passing them as parameter to `handleRequest()` for two reasons: first, the `FooHandler` and -`BarHandler` interfaces don't need to depend on `ServerContext`. Also, this allows them to inject other classes that -require `Request` and `ServerContext`, not just to inject `Request` and `ServerContext` themselves. - -Now we need a class that dispatches the requests to the right handler: - - // request_dispatcher.h - #include "request.h" - #include "server_context.h" - - class RequestDispatcher { - public: - // Handles the current request. - // The request is injected, no need to pass it directly here. - virtual void handleRequest() = 0; - }; - - fruit::Component, RequestDispatcher> - getRequestDispatcherComponent(); - -
- - // request_dispatcher.cpp - #include "request_dispatcher.h" - - #include "foo_handler.h" - #include "bar_handler.h" - - class RequestDispatcherImpl : public RequestDispatcher { - private: - const Request& request; - Provider fooHandler; - Provider barHandler; - - public: - INJECT(RequestDispatcherImpl( - const Request& request, - Provider fooHandler, - Provider barHandler)) - : request(request), - fooHandler(fooHandler), - barHandler(barHandler) { - } - - void handleRequest() override { - if (stringStartsWith(request.path, "/foo/")) { - fooHandler.get()->handleRequest(); - } else if (stringStartsWith(request.path, "/bar/")) { - barHandler.get()->handleRequest(); - } else { - cerr << "Error: no handler found for request path: '" - << request.path << "' , ignoring request." << endl; - } - } - - private: - static bool stringStartsWith(const string& s, const string& candidatePrefix) { - return s.compare(0, candidatePrefix.size(), candidatePrefix) == 0; - } - }; - - fruit::Component, RequestDispatcher> - getRequestDispatcherComponent() { - - return fruit::createComponent() - .bind() - .install(getFooHandlerComponent) - .install(getBarHandlerComponent); - } - -

- -

- -Note that `RequestDispatcherImpl` holds providers of the handlers, not instances. This delays the injection of -`FooHandler` and `BarHandler` to the point of use (when we call `get()` on the injector). - -We do this because we only want to inject the handler that is actually used for the request. -In a large system, there will likely be many handlers, and many of those will have lots of dependencies that would also -end up being injected if we injected all handler classes directly in `RequestDispatcherImpl` instead of injecting -`Provider`s. - -And now we just need the `Server` class: - - // server.h - #include "request.h" - #include "server_context.h" - #include "request_dispatcher.h" - - class Server { - public: - virtual void run(fruit::Component, - RequestDispatcher> - (*)()) = 0; - }; - - fruit::Component getServerComponent(); - -Note that the `run()` method of the server takes a `Component` function. This is because we will use two injectors - one -for the server startup (that will be used to inject the `Server` instance) and one for each request, that will be used -to inject the request dispatcher. - -

- -

- - // server.cpp - #include "server.h" - - #include "server_context.h" - #include "request_dispatcher.h" - - class ServerImpl : public Server { - private: - std::vector threads; - - public: - INJECT(ServerImpl()) { - } - - ~ServerImpl() { - for (std::thread& t : threads) { - t.join(); - } - } - - void run(fruit::Component, - RequestDispatcher> - (*getRequestDispatcherComponent)()) - override { - ServerContext serverContext; - serverContext.startupTime = getTime(); - - const fruit::NormalizedComponent, RequestDispatcher> - requestDispatcherNormalizedComponent( - getRequestDispatcherComponentWithContext, - getRequestDispatcherComponent, - &serverContext); - - cerr << "Server started." << endl; - - while (1) { - cerr << endl; - cerr << "Enter the request (absolute path starting with \"/foo/\" or " - << "\"/bar/\"), or an empty line to exit." << endl; - Request request; - getline(cin, request.path); - cerr << "Server received request: " + request.path << endl; - if (request.path.empty()) { - cerr << "Server received empty line, shutting down." << endl; - break; - } - - threads.push_back(std::thread( - worker_thread_main, - std::ref(requestDispatcherNormalizedComponent), request)); - } - } - - private: - static void worker_thread_main( - const fruit::NormalizedComponent< - fruit::Required, - RequestDispatcher>& - requestDispatcherNormalizedComponent, - Request request) { - - fruit::Injector injector( - requestDispatcherNormalizedComponent, getRequestComponent, &request); - - RequestDispatcher* requestDispatcher(injector); - requestDispatcher->handleRequest(); - } - - static string getTime() { - time_t now = time(nullptr); - tm* localTime = localtime(&now); - string result = asctime(localTime); - if (result.size() != 0 && result.back() == '\n') { - result.pop_back(); - } - return result; - } - - static fruit::Component getRequestComponent(Request* request) { - return fruit::createComponent() - .bindInstance(*request); - } - - static fruit::Component, RequestDispatcher> - getRequestDispatcherComponentWithContext( - fruit::Component, RequestDispatcher>( - *getRequestDispatcherComponent)(), - ServerContext* serverContext) { - return fruit::createComponent() - .install(getRequestDispatcherComponent) - .bindInstance(*serverContext); - } - }; - - fruit::Component getServerComponent() { - return fruit::createComponent() - .bind(); - } - -

- -

- -The `run()` method is a main-like method that is called at startup and will only terminate when the server is shut down. - -For every request (i.e. a line read from standard input) a new worker thread is created. `worker_thread_main()` creates -the injector that will handle the request and calls `RequestDispatcher` to determine the correct handler. -Note the use of `NormalizedComponent` instead of `Component` here. A normalized component is a transformed version of a -component from which it's very efficient to create an injector; most of the computation that would otherwise take place -when the injector is created is done in advance when the `NormalizedComponent` is created. In this example, this allows -the server to do that computation only once, at startup, instead of repeating it in every request. - -It's still allowed to add some bindings to a `NormalizedComponent` before creating an injector. This is what happens in -`worker_thread_main()`, that has to bind a different `Request` in each injector. The two-argument constructor of -`Injector` takes a `NormalizedComponent` and a `Component` and creates an injector with the union of the bindings. -Creating an injector for each request is faster than you might expect; look at -[the benchmarks page](https://sites.google.com/site/fruitlib/benchmarks) for the details. - -Finally, we have the `main()` function, that creates the outer injector and calls `Server::run()`. - - // main.cpp - #include "server.h" - #include "request_dispatcher.h" - - int main() { - Injector injector(getServerComponent); - - Server* server(injector); - server->run(getRequestDispatcherComponent); - - return 0; - } - -These are the dependencies between the source files: - -

- -

- -Note that changes to a handler (say, `bar_handler.cpp`) only require recompilation of that translation unit. When adding -a handler, we need to recompile the new handler and `request_dispatcher.cpp` (that will likely need changes anyway); we -still don't need to recompile the server nor the other handlers, as there's no dependency from `main.cpp` and -`server.cpp` to the handlers, not even to their header files. - -This concludes the tutorial. Now you should be able to use Fruit effectively. - -For more details on each feature of Fruit, see the [Quick reference](quick-reference) or the [FAQ](faq). If something is -still unclear, don't hesitate to [send me an email](mailto:poletti.marco@gmail.com). - -

- -

diff --git a/Tutorial:-Simple-system.md b/Tutorial:-Simple-system.md deleted file mode 100644 index 0a5fa66..0000000 --- a/Tutorial:-Simple-system.md +++ /dev/null @@ -1,293 +0,0 @@ - -In this second part of the tutorial we'll implement a simple command line utility that reads a number and outputs the -number incremented by 1. In a real-world system such a simple functionality would be in a single class (or even all in -`main()`). Here we instead (over)use dependency injection to split functionality as much as possible, showing how Fruit -can help building a complex system from several components, while limiting the dependencies between components. - -In the following, we omit the parts of the code that are not relevant (e.g. include guards, system includes). The full -source is available in -[examples/simple_injection](https://github.com/google/fruit/tree/master/examples/simple_injection). - -We start by writing the interface for the increment: - - // incrementer.h - class Incrementer { - public: - // Returns x + 1. - virtual int increment(int x) = 0; - }; - -An increment is a special case of addition, so let's also write an interface for a class that does addition: - - // adder.h - class Adder { - public: - // Returns the sum of x and y. - virtual int add(int x, int y) = 0; - }; - -Now we want a component that, given an implementation of `Adder`, provides an implementation of `Incrementer`. - - // incrementer_impl.h - #include "incrementer.h" - #include "adder.h" - - fruit::Component, Incrementer> getIncrementerImplComponent(); - - // incrementer_impl.cpp - #include "incrementer_impl.h" - - class IncrementerImpl : public Incrementer { - private: - Adder* adder; - - public: - INJECT(IncrementerImpl(Adder* adder)) - : adder(adder) { - } - - virtual int increment(int x) override { - return adder->add(x, 1); - } - }; - - fruit::Component, Incrementer> getIncrementerImplComponent() { - return fruit::createComponent() - .bind(); - } - -

- -

- -This is our first encounter with a component that has requirements. A Fruit component can have required types, and these -types are specified using `fruit::Required` as the first type argument of a component. If the signature of -`getIncrementerImplComponent()` didn't specify the requirement, Fruit would have looked for a binding for `Adder` and, -not finding it, it would have aborted the compilation with an error. All required types that a component doesn't bind -must be declared in the `Component` type. - -Note that the only information exposed in the header file is what the module requires and provides. The -`IncrementerImpl` class is defined in the .cpp file only. This is similar to what happens using the -[Pimpl idiom](http://en.wikipedia.org/wiki/Pimpl). - -Side note: some readers might have expected an anonymous namespace wrapping the implementation class. Adding it is -reasonable, as the class is only used within the .cpp file. We have omitted the anonymous namespace here to make the -code easier to understand. - -The lack of virtual destructors is also intended, as Fruit handles the destruction of the objects in the injector, and -it destroys the concrete classes directly (not through a pointer to the base class), so the compiler will report no -warnings (not even with `-W -Wall`). However you can still have a virtual destructor if you want to. - -Now, let's implement `Adder`. - - // simple_adder.h - #include "adder.h" - - fruit::Component getSimpleAdderComponent(); - - // simple_adder.cpp - #include "simple_adder.h" - - class SimpleAdder : public Adder { - public: - INJECT(SimpleAdder()) = default; - - virtual int add(int x, int y) override { - return x + y; - } - }; - - fruit::Component getSimpleAdderComponent() { - return fruit::createComponent() - .bind(); - } - -

- -

- -This component is very simple, it should be self-explanatory at this point. - -So, we have a component that provides `Adder` and one that requires `Adder` and provides `Incrementer`. Now we want to -combine the two. - - // simple_incrementer.h - #include "incrementer.h" - - fruit::Component getSimpleIncrementerComponent(); - - // simple_incrementer.cpp - #include "simple_incrementer.h" - - #include "incrementer_impl.h" - #include "simple_adder.h" - - fruit::Component getSimpleIncrementerComponent() { - return fruit::createComponent() - .install(getIncrementerImplComponent) - .install(getSimpleAdderComponent); - } - -

- -

- -`install()` is an operation that is used to "install" a sub-component inside the current component. As we've already -seen in the previous page of the tutorial, note that here we are not exposing all the interfaces that we could. The -`Adder` interface is considered an implementation detail, so we don't want to expose it. Note that it's not even -included in the header file, only the .cpp file depends on it (indirectly, through `simple_adder.h`). This is an example -of how Fruit helps reduce the number of includes (and therefore also the compilation time) of large projects. Without -dependency injection, in order to expose an implementation of `Incrementer` we would have included `IncrementerImpl`, -and the `IncrementerImpl` implementation would have included `SimpleAdder`. With dependency injection but without Fruit, -the `IncrementerImpl` implementation would no longer include `SimpleAdder`, but the client code (e.g. `main()`) would -have to include both `IncrementerImpl` and `SimpleAdder`. - -Phew! So much for incrementing a number. - -Now that the implementation part is complete, we just need to write the `main()` function. - - #include "simple_incrementer.h" - - int main() { - fruit::Injector injector(getSimpleIncrementerComponent); - Incrementer* incrementer = injector.get(); - - int x; - std::cin >> x; - std::cout << incrementer->increment(x) << std::endl; - - return 0; - } - -We construct an `Injector` from the component function, then get an instance of `Incrementer` from the injector. No need -to include any class definition here, besides `Incrementer` itself. - -After releasing the above program with a hefty price tag (or open-source, depending on your taste :-) ) we get some -customer feedback. Some customers are happy but some noticed that incrementing a big number sometimes yields a negative -number. They would like us to add a `--checked` option that enables overflow checking. - -Our implementation code is modular (thanks to dependency injection), so we don't need to modify any of the above -components. Also, the `Incrementer` component delegates the work to `Adder`, so the only thing that we need is a checked -implementation of `Adder`. - - // checked_adder.h - #include "adder.h" - - fruit::Component getCheckedAdderComponent(); - - // checked_adder.cpp - #include "checked_adder.h" - - class CheckedAdder : public Adder { - private: - bool add_overflows(int x, int y) { - ... // Implementation here - } - - public: - INJECT(CheckedAdder()) = default; - - virtual int add(int x, int y) override { - if (add_overflows(x, y)) { - std::cerr << "CheckedAdder: detected overflow during addition of " - << x << " and " << y << std::endl; - abort(); - } - return x + y; - } - }; - - fruit::Component getCheckedAdderComponent() { - return fruit::createComponent() - .bind(); - } - - -

- -

- -Nothing new here. Now we assemble the new component with the `IncrementComponent` that we've written before. - - // checked_incrementer.h - #include "incrementer.h" - - fruit::Component getCheckedIncrementerComponent(); - - // checked_incrementer.cpp - #include "checked_incrementer.h" - #include "incrementer_impl.h" - #include "checked_adder.h" - - fruit::Component getCheckedIncrementerComponent() { - return fruit::createComponent() - .install(getIncrementerImplComponent) - .install(getCheckedAdderComponent); - } - -

- -

- -Except the reuse of the same `IncrementerComponent`, there's nothing interesting to note here. - - // incrementer_component.h - #include "incrementer.h" - - fruit::Component getIncrementerComponent(bool checked); - - // incrementer_component.cpp - #include "incrementer_component.h" - #include "simple_incrementer.h" - #include "checked_incrementer.h" - - fruit::Component getIncrementerComponent(bool checked) { - if (checked) { - return fruit::createComponent() - .install(getCheckedIncrementerComponent); - } else { - return fruit::createComponent() - .install(getSimpleIncrementerComponent); - } - } - -

- -

- -A `get*Component()` function is just a normal function, so we can have parameterized components just by adding -parameters and we can do conditional injection by using an `if` inside the function and having multiple returns. - -Let's now rewrite `main()` to accept the new argument and to use the new parametrized component. - - // main.cpp - #include "incrementer_component.h" - - using fruit::Component; - using fruit::Injector; - - // Try e.g.: - // echo 5 | ./incrementer - // echo 2147483647 | ./incrementer - // echo 2147483647 | ./incrementer --checked - int main(int argc, const char* argv[]) { - bool checked = false; - if (argc == 2 && std::string(argv[1]) == "--checked") - checked = true; - - Injector injector(getIncrementerComponent, checked); - Incrementer* incrementer(injector); - - int x; - std::cin >> x; - std::cout << incrementer->increment(x) << std::endl; - - return 0; - } - -Here we see an alternative syntax for obtaining a class instance from an injector. Instead of calling `get` on the -injector, we convert the injector to the type that we want. This is convenient to avoid repeating the type twice -(compare with the main function above). - -In the [next part of the tutorial](https://github.com/google/fruit/wiki/tutorial:-annotated-injection) we'll learn how -to use annotated injection. diff --git a/_Sidebar.md b/_Sidebar.md deleted file mode 100644 index f8e20a9..0000000 --- a/_Sidebar.md +++ /dev/null @@ -1,12 +0,0 @@ -* [Home](home) -* [Benchmarks](benchmarks) -* [FAQ](faq) -* [Install](install) -* [Quick reference](quick-reference) -* Tutorial - * [Getting started](https://github.com/google/fruit/wiki/tutorial:-getting-started) - * [Simple system](https://github.com/google/fruit/wiki/tutorial:-simple-system) - * [Annotated injection](https://github.com/google/fruit/wiki/tutorial:-annotated-injection) - * [Errors](https://github.com/google/fruit/wiki/tutorial:-errors) - * [Assisted injection](https://github.com/google/fruit/wiki/tutorial:-assisted-injection) - * [Server](https://github.com/google/fruit/wiki/tutorial:-server) diff --git a/bar_handler.png b/bar_handler.png deleted file mode 100644 index a931223..0000000 Binary files a/bar_handler.png and /dev/null differ diff --git a/bind.png b/bind.png deleted file mode 100644 index a777551..0000000 Binary files a/bind.png and /dev/null differ diff --git a/bind_instance.png b/bind_instance.png deleted file mode 100644 index 7ad3ce9..0000000 Binary files a/bind_instance.png and /dev/null differ diff --git a/car_component.png b/car_component.png deleted file mode 100644 index 6057e2c..0000000 Binary files a/car_component.png and /dev/null differ diff --git a/checked_adder.png b/checked_adder.png deleted file mode 100644 index e659624..0000000 Binary files a/checked_adder.png and /dev/null differ diff --git a/checked_incrementer.png b/checked_incrementer.png deleted file mode 100644 index 4e02a70..0000000 Binary files a/checked_incrementer.png and /dev/null differ diff --git a/component_composition.png b/component_composition.png deleted file mode 100644 index 9180cd9..0000000 Binary files a/component_composition.png and /dev/null differ diff --git a/component_dep_loop.png b/component_dep_loop.png deleted file mode 100644 index 16e8b95..0000000 Binary files a/component_dep_loop.png and /dev/null differ diff --git a/foo_handler.png b/foo_handler.png deleted file mode 100644 index afa4314..0000000 Binary files a/foo_handler.png and /dev/null differ diff --git a/greeter.png b/greeter.png deleted file mode 100644 index 7a7c065..0000000 Binary files a/greeter.png and /dev/null differ diff --git a/hello_world-deps.png b/hello_world-deps.png deleted file mode 100644 index c5a164c..0000000 Binary files a/hello_world-deps.png and /dev/null differ diff --git a/incrementer.png b/incrementer.png deleted file mode 100644 index 4dd4a88..0000000 Binary files a/incrementer.png and /dev/null differ diff --git a/incrementer_component.png b/incrementer_component.png deleted file mode 100644 index 1cc427b..0000000 Binary files a/incrementer_component.png and /dev/null differ diff --git a/inject_macro.png b/inject_macro.png deleted file mode 100644 index 86e3ab8..0000000 Binary files a/inject_macro.png and /dev/null differ diff --git a/inject_macro_no_args.png b/inject_macro_no_args.png deleted file mode 100644 index 63f71d1..0000000 Binary files a/inject_macro_no_args.png and /dev/null differ diff --git a/inject_macro_template.png b/inject_macro_template.png deleted file mode 100644 index f4f58bf..0000000 Binary files a/inject_macro_template.png and /dev/null differ diff --git a/inject_typedef_greeter.png b/inject_typedef_greeter.png deleted file mode 100644 index 86e3ab8..0000000 Binary files a/inject_typedef_greeter.png and /dev/null differ diff --git a/inject_typedef_templated_constructor.png b/inject_typedef_templated_constructor.png deleted file mode 100644 index 86e3ab8..0000000 Binary files a/inject_typedef_templated_constructor.png and /dev/null differ diff --git a/inject_typedef_writer.png b/inject_typedef_writer.png deleted file mode 100644 index 63f71d1..0000000 Binary files a/inject_typedef_writer.png and /dev/null differ diff --git a/inject_typedef_writer2.png b/inject_typedef_writer2.png deleted file mode 100644 index 63f71d1..0000000 Binary files a/inject_typedef_writer2.png and /dev/null differ diff --git a/multibindings-deps.png b/multibindings-deps.png deleted file mode 100644 index c5a164c..0000000 Binary files a/multibindings-deps.png and /dev/null differ diff --git a/multiplier.png b/multiplier.png deleted file mode 100644 index a3df4d1..0000000 Binary files a/multiplier.png and /dev/null differ diff --git a/parametrized_component.png b/parametrized_component.png deleted file mode 100644 index c96c4e3..0000000 Binary files a/parametrized_component.png and /dev/null differ diff --git a/provider.png b/provider.png deleted file mode 100644 index 5ef808e..0000000 Binary files a/provider.png and /dev/null differ diff --git a/provider_functor.png b/provider_functor.png deleted file mode 100644 index dbf5cd5..0000000 Binary files a/provider_functor.png and /dev/null differ diff --git a/register_constructor.png b/register_constructor.png deleted file mode 100644 index 5199027..0000000 Binary files a/register_constructor.png and /dev/null differ diff --git a/register_constructor_component.png b/register_constructor_component.png deleted file mode 100644 index a25629e..0000000 Binary files a/register_constructor_component.png and /dev/null differ diff --git a/register_factory.png b/register_factory.png deleted file mode 100644 index 251763b..0000000 Binary files a/register_factory.png and /dev/null differ diff --git a/register_factory_macro.png b/register_factory_macro.png deleted file mode 100644 index ae8837e..0000000 Binary files a/register_factory_macro.png and /dev/null differ diff --git a/register_factory_use.png b/register_factory_use.png deleted file mode 100644 index 2ad24a9..0000000 Binary files a/register_factory_use.png and /dev/null differ diff --git a/request_dispatcher.png b/request_dispatcher.png deleted file mode 100644 index f3aed89..0000000 Binary files a/request_dispatcher.png and /dev/null differ diff --git a/request_injector.png b/request_injector.png deleted file mode 100644 index 9d5562b..0000000 Binary files a/request_injector.png and /dev/null differ diff --git a/scaler.png b/scaler.png deleted file mode 100644 index 97a2456..0000000 Binary files a/scaler.png and /dev/null differ diff --git a/scaling_doubles-deps.png b/scaling_doubles-deps.png deleted file mode 100644 index 5a1e16e..0000000 Binary files a/scaling_doubles-deps.png and /dev/null differ diff --git a/server-deps.png b/server-deps.png deleted file mode 100644 index a47c0a3..0000000 Binary files a/server-deps.png and /dev/null differ diff --git a/server.png b/server.png deleted file mode 100644 index 283624e..0000000 Binary files a/server.png and /dev/null differ diff --git a/simple_adder.png b/simple_adder.png deleted file mode 100644 index fe1f371..0000000 Binary files a/simple_adder.png and /dev/null differ diff --git a/simple_greeter.png b/simple_greeter.png deleted file mode 100644 index fe54e84..0000000 Binary files a/simple_greeter.png and /dev/null differ diff --git a/simple_incrementer.png b/simple_incrementer.png deleted file mode 100644 index 64dcf68..0000000 Binary files a/simple_incrementer.png and /dev/null differ diff --git a/simple_injection-deps.png b/simple_injection-deps.png deleted file mode 100644 index 0de3c16..0000000 Binary files a/simple_injection-deps.png and /dev/null differ diff --git a/templated_component.png b/templated_component.png deleted file mode 100644 index 3094d9e..0000000 Binary files a/templated_component.png and /dev/null differ