[boost-ext] Looking for Review Manager/s

Hi, I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them? [boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies ATM, [boost::ext] consists of four core libraries: * DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/ * SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/ * UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut * TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well. If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks. Thank you very much, -Kris

On Thu, 18 Feb 2021 at 20:58, Krzysztof Jusiak via Boost < boost@lists.boost.org> wrote:
Hi,
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
[boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies
ATM, [boost::ext] consists of four core libraries:
* DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/
* SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/
Oh! I've been interested in SML for some time. Great!
* UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut
* TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te
All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks.
Thank you very much, -Kris
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212

On 2/18/2021 2:57 PM, Krzysztof Jusiak via Boost wrote:
Hi,
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
[boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies
ATM, [boost::ext] consists of four core libraries:
* DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/
* SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/
* UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut
* TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te
All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks.
Thank you very much, -Kris
I like the testing framework, even if it is for C ++20 only. While I have used both Boost Test and lightweight test I admit that your more "natural" testing syntax is quite appealing. I will admit that your example-driven documentation approach does not appeal to me in general, but evidently most programmers like it. I would, however, suggest you attempt to explain the reason why anyone would want to use your DI, SML, and TE libraries, ie. how does using any of these libraries actually benefit a C++ programmer. Other than the "wow" factor I need a well thought out reason that I can understand to use a library in my own code. I am glad to see you are using the features of C++14 on up to provide different versions of state machine and type erasure than are currently in Boost.

On 2/18/21 11:57 AM, Krzysztof Jusiak via Boost wrote:
* DI - Dependency Injection Library
* SML - State Machine Library overview: * UT - Unit Testing Framework
* TE - Run-time polymorphism (type erasure) library overview: All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks.
It sounds like you are proposing admission to boost of all these libraries as a group. That is, I would expect this to be 4 separate libraries, each with it's own review, documentation etc. Am I missing something here? Robert Ramey

On 18/02/2021 19:57, Krzysztof Jusiak via Boost wrote:
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
I think you've waited long enough. I volunteer to review manage proposed Boost.DI. Niall

On Thu, 18 Feb 2021 at 20:58, Krzysztof Jusiak via Boost < boost@lists.boost.org> wrote:
Hi,
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
[boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies
ATM, [boost::ext] consists of four core libraries:
* DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/
* SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/
I have experimented with SML in the past and liked it. If it were *proposed* for inclusion, I would be happy to *second* the proposal in line with the published review process (link below). https://www.boost.org/development/submissions.html#Seconded
* UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut
* TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te
All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks.
Thank you very much, -Kris
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212

Hi, Just wanted to apologize for all the headache/additional work I've caused for the Boost Steering Committee and for all the confusion I caused to the users because of using the boost name. It never was my intention to do so and I'm sorry for screwing it up. After talking with Glen Fernandes we have found a path forward and I already started working on it to fix as much damage as possible - The boost logo has been removed from https://github.com/boost-ext - All advertisements/talks won't include boost name - Additionally, all libraries already have ext namespace and disclaimer that they aren't part of Boost libraries - External packages will not include boost as well (existing packages will be either removed or changed) Conan - di - https://conan.io/center/di - seems good - ut - https://conan.io/center/boost-ext-ut - (to be changed) - sml - https://github.com/conan-io/conan-center-index/pull/4591 (in progress, added a comment to ensure it will be called sml without boost) Vcpkg - di - https://github.com/microsoft/vcpkg/tree/master/ports/boost-di (to be changed) - ut - not added - sml - not added My apologies again and please let me know what else I can do and/or see any other resource which has to be changed. Thank you, Kris On Thu, Feb 18, 2021 at 12:57 PM Krzysztof Jusiak <krzysztof@jusiak.net> wrote:
Hi,
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
[boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies
ATM, [boost::ext] consists of four core libraries:
* DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/
* SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/
* UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut
* TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te
All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks.
Thank you very much, -Kris

Krzysztof Jusiak wrote: ...
* DI - Dependency Injection Library
* SML - State Machine Library
* UT - Unit Testing Framework
* TE - Run-time polymorphism (type erasure) library
I'm not sure how I feel about two-letter library (hence directory, repo) names. SML is fine, but maybe we can have proper names (or at least TLAs) for the other three? E.g. Boost.Inject for DI, or something like that. :-)

I do not believe I have the expertise to be a review manager on any of these, but I have toyed with SML and looked at TE and find both to be of very good quality. Having those libraries in would be a nice boost for boost. /Peter On Thu, Feb 18, 2021 at 8:57 PM Krzysztof Jusiak via Boost <boost@lists.boost.org> wrote:
Hi,
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
[boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies
ATM, [boost::ext] consists of four core libraries:
* DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/
* SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/
* UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut
* TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te
All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks.
Thank you very much, -Kris
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

czw., 18 lut 2021 o 20:58 Krzysztof Jusiak via Boost <boost@lists.boost.org> napisał(a):
Hi,
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
[boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies
ATM, [boost::ext] consists of four core libraries:
* DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/
Regarding the DI library, I read the introduction and the tutorial part, and it does not help me understand what problem this library solves or why I should use it. Is there some other way to explain what this library is for? Or maybe I need to switch my mindset to Java-like OO patterns a la GoF in order to appreciate this? Is there a lot of theory that I have to learn prior to be able to understand what it is for? The docs give this example: auto injector = di::make_injector(); injector.create<app>(); Does this create a temporary object of type `app` that vanishes momentarily? Or is there some magical object lifetime management going on behind the scenes? Also, it looks like it will only work if the leaves of this dependency tree are default-constructible and if I am happy with calling default constructors. But usually I am not: I need to pass arguments to my objects, even if they are leaves in the dependency tree. I would appreciate help in understanding the motivation. Regards, &rzej;
* SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/
* UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut
* TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te
All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to address any issues/requests before the review as well as help with any process-related tasks.
Thank you very much, -Kris
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Fri, Feb 19, 2021 at 1:27 PM Andrzej Krzemienski via Boost < boost@lists.boost.org> wrote:
czw., 18 lut 2021 o 20:58 Krzysztof Jusiak via Boost < boost@lists.boost.org> napisał(a):
Hi,
I just wanted to share, potentially useful for the Boost and C++ community, libraries and ask if anyone would be willing to become a Review Manager for any of them?
[boost-ext] (https://github.com/boost-ext) is a collection of C++ libraries with aspirations to be included in Boost. Libraries can be characterized by: * Modern C++ (>= C++14) * Header/Single Module only * No dependencies
ATM, [boost::ext] consists of four core libraries:
* DI - Dependency Injection Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.2.0 (first release - 2014) key features: - supports macro-free constructor injection - supports templates injection - quick compilation-times - highly optimized code generation try it online: https://godbolt.org/z/5qTKhf source code: https://github.com/boost-ext/di documentation: https://boost-ext.github.io/di/
Regarding the DI library, I read the introduction and the tutorial part, and it does not help me understand what problem this library solves or why I should use it. Is there some other way to explain what this library is for?
I was hoping that the explantation from https://boost-ext.github.io/di/boost is good enough but I understand it might be confusing. DI is a very simple concept, actually. The main point is to make the code less coupled by injecting dependencies which is usually done by passing them through constructors. I'd hope that the following talk - https://www.youtube.com/watch?v=yVogS4NbL6U is a good explanation of DI as well. I'm happy to follow-up in any form, though.
Or maybe I need to switch my mindset to Java-like OO patterns a la GoF in order to appreciate this?
Right, Dependency Injection is related to OO patterns and especially the SOLI-D (Dependency Inversion) principle.
Is there a lot of theory that I have to learn prior to be able to understand what it is for?
Not sure, OO, SOLID should be more than enough, IMHO.
The docs give this example:
auto injector = di::make_injector(); injector.create<app>();
Does this create a temporary object of type `app` that vanishes momentarily? Or is there some magical object lifetime management going on behind the scenes?
That will depend on the scope. By default, it's a singleton scope so the app will have the same lifetime as the program. Some useful information can be found: - https://boost-ext.github.io/di/user_guide.html#scopes - https://boost-ext.github.io/di/tutorial.html
Also, it looks like it will only work if the leaves of this dependency tree are default-constructible and if I am happy with calling default constructors. But usually I am not: I need to pass arguments to my objects, even if they are leaves in the dependency tree.
DI deduces constructor parameters automatically so it works with non-default-constructible types as well. DI is capable of creating the whole dependency tree.
Potential examples which can maybe help - https://github.com/boost-ext/di/blob/cpp14/example/tutorial/basic_create_obj... - https://github.com/boost-ext/di/blob/cpp14/example/motivation.cpp - https://github.com/boost-ext/di/tree/cpp14/example/polymorphism
I would appreciate help in understanding the motivation.
I hope that the examples and explanation helps a bit, but if not I'm also more than happy/available to have a follow-up (via zoom or similar) if that helps (feedback could be used to improve the documentation)?
Regards, &rzej;
* SML - State Machine Library overview: standard: C++14 single header with no dependencies (neither STL nor Boost is required) release: 1.1.4 (first release - 2016) key features: - much faster compilation-times than Boost.MSM (up to 60x faster) - highly optimized and customizable code generation - suitable for embedded systems try it online: https://godbolt.org/z/y99L50 source code: https://github.com/boost-ext/sml documentation: https://boost-ext.github.io/sml/
* UT - Unit Testing Framework overview: standard: C++20 single header with no dependencies (STL required) release: 1.1.8 (first release - 2019) key features: - macro-free - minimal boilerplate - fast compilation-times and execution try it online: https://godbolt.org/z/Jqb5Ye source code: https://github.com/boost-ext/ut documentation: https://boost-ext.github.io/ut
* TE - Run-time polymorphism (type erasure) library overview: standard: C++17 single header with no dependencies (STL required) release: - key features: - simple interface - highly optimized code generation try it online: https://godbolt.org/z/xY9MEq source code: https://github.com/boost-ext/te documentation: https://github.com/boost-ext/te
All libraries (except TE) were successfully deployed in the production systems. Some, such as DI and SML are used by well known/big IT companies as well.
If anyone is interested in becoming a Review Manager for any of the libraries, I'd really appreciate it and I'll more than happy to
address
any issues/requests before the review as well as help with any process-related tasks.
Thank you very much, -Kris
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 19/02/2021 22:20, Krzysztof Jusiak via Boost wrote:
I hope that the examples and explanation helps a bit, but if not I'm also more than happy/available to have a follow-up (via zoom or similar) if that helps (feedback could be used to improve the documentation)?
Kris I speak from experience with Outcome that you're going to have to be much more hand holdy than that description you just gave. You'll need to explain things in small, baby steps, because DI is one of those things that nobody understands until the lightbulb switches on, and then they totally get it. Given that Mr. Dimov and several others have indicated they don't understand the motivation behind DI, if you want to see DI get into Boost, you're going to have to write a lot of personalised responses to people and keep repeating yourself to each individual in turn until people twig what DI is and why it's useful. I did this for Outcome, and whilst it was laborious at the time, I think it paid off in the end for everybody here. (Incidentally, I chose DI to review manage for a reason, I think this is the first vocabulary library we've seen presented here since Outcome. I am *very* much hoping its review isn't going to turn into thousands of emails ...) Andrzej, I'll give you my description of DI (which is not that of Boost.DI). For certain kinds of problem solution, it can make sense to turn inside out the traditional design of a C++ program i.e. place the innards on the outside, and put what is normally outside into the innards. You thus get an "inverted design" program. Normally that's a bad idea, because maintenance is harder, it's anti social to other engineers, and so on. Also, in C++, we typically use template techniques such as CRTP for DI, we don't typically use polymorphic techniques in this, but that's peculiar to C++ and not what most languages do because they don't have powerful enough compile time facilities. For complex use cases, or where a fixed ABI is absolutely needed, template based techniques are too painful to use to do DI, and for this niche use case, a common runtime framework such as that of Boost.DI lets arbitrary pieces of DI based code interoperate with one another in a composable fashion. This use case is probably even more niche than that for Outcome, which maybe 15% of C++ developers would ever need to consider using. For non-template DI, I would estimate less than 5% of C++ developers would ever need to consider using it. *However*, for where you do actually need runtime polymorphic DI, there are very few good solutions to hand in C++, unlike in other major languages. I think proposed Boost.DI could really fill that empty gap, and for those few who need that gap filled, they _badly_ need it filled. A standardised solution in this space would be particularly valuable. Just to be clear, I was talking about DI in general above, I don't know if Boost.DI is the correct, or sufficient, or best approach to solving non-template DI in C++. That's for the review here to decide. Niall

On Sat, 20 Feb 2021 at 03:10, Niall Douglas via Boost <boost@lists.boost.org> wrote:
On 19/02/2021 22:20, Krzysztof Jusiak via Boost wrote:
I hope that the examples and explanation helps a bit, but if not I'm also
I’ve looked at the examples in the documentation. DI seems to allow me to build an arbitrary tuple of objects and pass that tuple as the argument to a constructor. DI will then match off items in my tuple to constructor arguments by interface type. In that sense it seems to allow me to use an arbitrary “bag” of parameters to build another object provided sufficient arguments have a corresponding item in the bag (by interface type). Since this matching is at compile time, what do I gain by this rather than simply passing the arguments directly? Can you show an example where use of DI allows me to express code more clearly than a hand-written call? I can think of uses of a dynamic (run time) bag of argument objects, but not compile time. Thanks in advance.
more than happy/available to have a follow-up (via zoom or similar) if that helps (feedback could be used to improve the documentation)?
Kris I speak from experience with Outcome that you're going to have to be much more hand holdy than that description you just gave. You'll need to explain things in small, baby steps, because DI is one of those things that nobody understands until the lightbulb switches on, and then they totally get it.
Given that Mr. Dimov and several others have indicated they don't understand the motivation behind DI, if you want to see DI get into Boost, you're going to have to write a lot of personalised responses to people and keep repeating yourself to each individual in turn until people twig what DI is and why it's useful. I did this for Outcome, and whilst it was laborious at the time, I think it paid off in the end for everybody here.
(Incidentally, I chose DI to review manage for a reason, I think this is the first vocabulary library we've seen presented here since Outcome. I am *very* much hoping its review isn't going to turn into thousands of emails ...)
Andrzej, I'll give you my description of DI (which is not that of Boost.DI). For certain kinds of problem solution, it can make sense to turn inside out the traditional design of a C++ program i.e. place the innards on the outside, and put what is normally outside into the innards. You thus get an "inverted design" program.
Normally that's a bad idea, because maintenance is harder, it's anti social to other engineers, and so on. Also, in C++, we typically use template techniques such as CRTP for DI, we don't typically use polymorphic techniques in this, but that's peculiar to C++ and not what most languages do because they don't have powerful enough compile time facilities.
For complex use cases, or where a fixed ABI is absolutely needed, template based techniques are too painful to use to do DI, and for this niche use case, a common runtime framework such as that of Boost.DI lets arbitrary pieces of DI based code interoperate with one another in a composable fashion.
This use case is probably even more niche than that for Outcome, which maybe 15% of C++ developers would ever need to consider using. For non-template DI, I would estimate less than 5% of C++ developers would ever need to consider using it.
*However*, for where you do actually need runtime polymorphic DI, there are very few good solutions to hand in C++, unlike in other major languages. I think proposed Boost.DI could really fill that empty gap, and for those few who need that gap filled, they _badly_ need it filled. A standardised solution in this space would be particularly valuable.
Just to be clear, I was talking about DI in general above, I don't know if Boost.DI is the correct, or sufficient, or best approach to solving non-template DI in C++. That's for the review here to decide.
Niall
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212

Niall Douglas wrote:
Andrzej, I'll give you my description of DI (which is not that of Boost.DI). For certain kinds of problem solution, it can make sense to turn inside out the traditional design of a C++ program i.e. place the innards on the outside, and put what is normally outside into the innards. You thus get an "inverted design" program.
[...] I think what I'm missing most here is concrete examples of specific problems that DI in general, and Boost.DI in particular, solve.

On 20/02/2021 16:27, Peter Dimov via Boost wrote:
Niall Douglas wrote:
Andrzej, I'll give you my description of DI (which is not that of Boost.DI). For certain kinds of problem solution, it can make sense to turn inside out the traditional design of a C++ program i.e. place the innards on the outside, and put what is normally outside into the innards. You thus get an "inverted design" program.
[...]
I think what I'm missing most here is concrete examples of specific problems that DI in general, and Boost.DI in particular, solve.
I agree that it's on the library proposer to convince everybody here of the value add of the technique, and then his proposed solution. Vocab libraries often struggle with communication, because what they solve is so fundamental. It's a steep hill to climb, but necessary to get such a library into Boost. Kris I don't suppose you can rustle of somebody who is using DI in production and would be willing to either open source that code, or come here to describe how DI solved a pain point for them? Niall

sob., 20 lut 2021 o 03:10 Niall Douglas via Boost <boost@lists.boost.org> napisał(a):
On 19/02/2021 22:20, Krzysztof Jusiak via Boost wrote:
I hope that the examples and explanation helps a bit, but if not I'm also more than happy/available to have a follow-up (via zoom or similar) if that helps (feedback could be used to improve the documentation)?
Kris I speak from experience with Outcome that you're going to have to be much more hand holdy than that description you just gave. You'll need to explain things in small, baby steps, because DI is one of those things that nobody understands until the lightbulb switches on, and then they totally get it.
Given that Mr. Dimov and several others have indicated they don't understand the motivation behind DI, if you want to see DI get into Boost, you're going to have to write a lot of personalised responses to people and keep repeating yourself to each individual in turn until people twig what DI is and why it's useful. I did this for Outcome, and whilst it was laborious at the time, I think it paid off in the end for everybody here.
Yes. My recollection of the adventure with Outcome is similar. I guess it takes time and energy to explain one's idea to others. I imagine it is frustrating too. One writes a library, makes an effort to share it with the people, and the only response back is, "do more work, convince us".
(Incidentally, I chose DI to review manage for a reason, I think this is the first vocabulary library we've seen presented here since Outcome. I am *very* much hoping its review isn't going to turn into thousands of emails ...)
Andrzej, I'll give you my description of DI (which is not that of Boost.DI). For certain kinds of problem solution, it can make sense to turn inside out the traditional design of a C++ program i.e. place the innards on the outside, and put what is normally outside into the innards. You thus get an "inverted design" program.
Normally that's a bad idea, because maintenance is harder, it's anti social to other engineers, and so on. Also, in C++, we typically use template techniques such as CRTP for DI, we don't typically use polymorphic techniques in this, but that's peculiar to C++ and not what most languages do because they don't have powerful enough compile time facilities.
For complex use cases, or where a fixed ABI is absolutely needed, template based techniques are too painful to use to do DI, and for this niche use case, a common runtime framework such as that of Boost.DI lets arbitrary pieces of DI based code interoperate with one another in a composable fashion.
This use case is probably even more niche than that for Outcome, which maybe 15% of C++ developers would ever need to consider using. For non-template DI, I would estimate less than 5% of C++ developers would ever need to consider using it.
*However*, for where you do actually need runtime polymorphic DI, there are very few good solutions to hand in C++, unlike in other major languages. I think proposed Boost.DI could really fill that empty gap, and for those few who need that gap filled, they _badly_ need it filled. A standardised solution in this space would be particularly valuable.
Just to be clear, I was talking about DI in general above, I don't know if Boost.DI is the correct, or sufficient, or best approach to solving non-template DI in C++. That's for the review here to decide.
So far, my understanding is the following. Please correct me, if I am mischaracterizing the situation. This library is not so much about Dependency Injection. Dependency Injection is just a style of programming. The DI-library is rather about managing the complexity that results from choosing to use Dependency Injection. Suppose I have a class like the following: ``` struct Point { int x; int y; }; struct Rect { Point lowerLeft; Point upperRight; }; class State { Rect area; Point location; // invariant: location is inside area; public: State(int left, int lo, int right, int hi, int x, int y) : area{{left, lo}, {right, hi}}, location{x, y} {} }; int main() { State s{0, 0, 2, 2, 1, 1}; } ``` I do not like that I have to pass so many parameters, that in this configuration can be easily confused. So, I want to apply the Dependency Injection philosophy. I get: class State { Rect area; Point location; // invariant: location is inside area; public: State(Rect a, Point loc) : area{a}, location{loc} {} }; int main() { State s{Rect{{0, 0}, {2, 2}}, Point{1, 1}}; } ``` Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?). I get the following result: ``` int main() { State s = di::make_injector().create<State>(0, 0, 2, 2, 1, 1); } ``` And now I get back to the situation where I am passing six ints and can easily confuse which int represents what. I am pretty sure I am now unfairly mischaracterizing the library. But this is what I get from the motivation and tutorial sections, and the explanations I have seen so far. You cannot see this behavior in the tutorial example that is using class `app`, because most of the types in there are default-constructed or constructed from values that were themselves (perhaps recursively) default-constructed. So it looks to me that in order to appreciate this library, I have to make most of my types default-constructible. At which point am I missing something? Regards, &rzej;
Niall
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Andrzej Krzemienski wrote: ...
So, I want to apply the Dependency Injection philosophy. I get:
class State { Rect area; Point location; // invariant: location is inside area; public: State(Rect a, Point loc) : area{a}, location{loc} {} };
int main() { State s{Rect{{0, 0}, {2, 2}}, Point{1, 1}}; } ```
Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?).
You are thinking in C++ (value semantics) but you need to be thinking in Java (reference semantics, OOP, object DAGs.) Suppose you need to have a logger object shared_ptr<ILogger> pl; // new FileLogger( log_file_name fn ); and a database connector object (which uses a logger to log things) shared_ptr<IDbConn> pdb; // new MysqlDb( mysql_db_info mdi, shared_ptr<ILogger> pl ) and an abstract configuration query interface, that can use an .ini file, or a JSON file, or a database shared_ptr<IConfig> pc; // new DbConfig( shared_ptr<IDbConn> pdb, shared_ptr<ILogger> pl ); Now when you need to create the config query object, you need to give it a database connector and a logger, and you also need to give the same logger to the database connector. So with [NotYetBoost].DI you'll write something like auto injector = make_injector( bind<log_file_name>.to( "something.log" ), bind<ILogger>.to<FileLogger>(), bind<>.to( mysql_db_info{ ... } ), bind<IDbConn>.to<MysqlDb>(), bind<IConfig>.to<DbConfig>() ); auto cfg = injector.create<IConfig>(); although I don't really know if this will compile or work. :-) Now imagine that same thing but with 10 objects, or 20 objects. Also imagine that your day to day vocabulary includes "business logic", "enterprise", "UML" and "XML".

Let me try to explain DI (manual and automatic) via example. DI vs NO-DI is all about less coupling by injecting dependencies instead of. It's often referred as a Hollywood prince - Don't call us we will call you! Example struct coupled_no_di { void api() { d.call(); } depedency d{singleton::get()}; }; - Coupled design (hard to test) struct not_coupled_di { not_coupled_di(dependency& d) : d_{d} {} void api() { d.call(); } depedency& d; }; - Much better - separation of business logic and object creation. But we can do even better by applying - Dependency Inversion (relay on abstractions and not concrete implementations) - Abstractions can be done in many different ways (type erasure, templates, abstract classes, etc.) template<DependencyConcept TDependency> struct not_coupled_di { not_coupled_di(TDependency& d) : d_{d} {} void api() { d.call(); } TDependency& d; }; The above is much better because - It's not coupled to any specific implementation - Can be changed for testing (mocks/fakes/stubs) Okay, now let's take at the main. A good design will apply a Composition Root (unique place in the app when dependencies are being created) int main() { my_depedency dependency{...}; not_coupled_di di{dependency}; di.api(); } - The above is an example of manual DI which is cool already but may lead to what is called a Wiring Mess. Let's imagine that we have a big dependency tree because we are following SOLID principle and we especially apply the Single Responsibility Principle (we will have a lot of dependencies). int main() { my_dependency_1 d1{}; my_dependency_2 d2{}; my_dependency_3 d2{d1, d2}; my_dependency_4 d3{d1, 42, d3}; app a{d3, ...}; ... // Order of the above is important and with bigger projects might be easily 10k LOC+ } - Well, we can maintain the above if we want, but Automatic DI will let us actually focus on the business logic instead! - Any change in the constructors (a reference to shared_pointer, the order of parameters) will require us to change the above :( Boost.DI library can help with removing the Wiring Mess for us, though! int main() { auto injector = make_injector(); app = create<app>(injector); return app.api(); } Right now the benefits of using the framework instead of manual DI - If we change any constructor parameter or order of any dependency we DON't have to change anything with DI framework - Before not_coupled_di(TDependency& d); - Refactor not_coupled_di(std::shared_ptr<TDependency>); With manual DI the wiring has to be changed to pass the shared_ptr with Boost.DI we don't have to change the wiring code at all. Okay, but what about the polymorphism behaviour. Boost.DI allows a different type of polymorphism. One can inject templates, abstract classes, variant, type erasure etc. More can be found here - https://github.com/boost-ext/di/tree/cpp14/example/polymorphism. Why that's important? Because it's a better design and makes testing easier too. How to do it with Boost.DI? // configuration auto module = make_injector( bind<interface>.to<implementation> // I'm not going to go into details, but Boost.DI allows to inject TEMPLATES, abstract classes, etc... ); // production, release int main() { app = create<app>(module); return app.api(); } // end 2 end testing int main() { auto injector = di::make_injector( module(), // production wiring di::bind<interface>.to<mock> [override] ); app = create<app>(module); return app.api(); } - Great, we can test end 2 end with overriding some dependencies such as time, database, networking, logging etc... - We have a loosely coupled design! - We don't have to change ANYTHING in the wiring code when we change the dependency tree and/or any constructor parameters/order I hope that helps a bit? I also really encourage to take a quick look at which summaries concepts behind DI. - https://www.youtube.com/watch?v=yVogS4NbL6U Thanks, Kris On Sat, Feb 20, 2021 at 4:10 PM Peter Dimov via Boost <boost@lists.boost.org> wrote:
Andrzej Krzemienski wrote: ...
So, I want to apply the Dependency Injection philosophy. I get:
class State { Rect area; Point location; // invariant: location is inside area; public: State(Rect a, Point loc) : area{a}, location{loc} {} };
int main() { State s{Rect{{0, 0}, {2, 2}}, Point{1, 1}}; } ```
Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?).
You are thinking in C++ (value semantics) but you need to be thinking in Java (reference semantics, OOP, object DAGs.) Suppose you need to have a logger object
shared_ptr<ILogger> pl; // new FileLogger( log_file_name fn );
and a database connector object (which uses a logger to log things)
shared_ptr<IDbConn> pdb; // new MysqlDb( mysql_db_info mdi, shared_ptr<ILogger> pl )
and an abstract configuration query interface, that can use an .ini file, or a JSON file, or a database
shared_ptr<IConfig> pc; // new DbConfig( shared_ptr<IDbConn> pdb, shared_ptr<ILogger> pl );
Now when you need to create the config query object, you need to give it a database connector and a logger, and you also need to give the same logger to the database connector. So with [NotYetBoost].DI you'll write something like
auto injector = make_injector( bind<log_file_name>.to( "something.log" ), bind<ILogger>.to<FileLogger>(), bind<>.to( mysql_db_info{ ... } ), bind<IDbConn>.to<MysqlDb>(), bind<IConfig>.to<DbConfig>() );
auto cfg = injector.create<IConfig>();
although I don't really know if this will compile or work. :-)
Now imagine that same thing but with 10 objects, or 20 objects. Also imagine that your day to day vocabulary includes "business logic", "enterprise", "UML" and "XML".
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Sun, 21 Feb 2021 at 00:27, Krzysztof Jusiak via Boost < boost@lists.boost.org> wrote:
Let me try to explain DI (manual and automatic) via example.
DI vs NO-DI is all about less coupling by injecting dependencies instead of. It's often referred as a Hollywood prince - Don't call us we will call you!
Example
struct coupled_no_di { void api() { d.call(); } depedency d{singleton::get()}; };
- Coupled design (hard to test)
struct not_coupled_di { not_coupled_di(dependency& d) : d_{d} {}
void api() { d.call(); } depedency& d; };
- Much better - separation of business logic and object creation.
But we can do even better by applying - Dependency Inversion (relay on abstractions and not concrete implementations) - Abstractions can be done in many different ways (type erasure, templates, abstract classes, etc.)
template<DependencyConcept TDependency> struct not_coupled_di { not_coupled_di(TDependency& d) : d_{d} {}
void api() { d.call(); } TDependency& d; };
The above is much better because - It's not coupled to any specific implementation - Can be changed for testing (mocks/fakes/stubs)
Okay, now let's take at the main. A good design will apply a Composition Root (unique place in the app when dependencies are being created)
int main() { my_depedency dependency{...}; not_coupled_di di{dependency};
di.api(); }
- The above is an example of manual DI which is cool already but may lead to what is called a Wiring Mess.
Let's imagine that we have a big dependency tree because we are following SOLID principle and we especially apply the Single Responsibility Principle (we will have a lot of dependencies).
int main() { my_dependency_1 d1{}; my_dependency_2 d2{}; my_dependency_3 d2{d1, d2}; my_dependency_4 d3{d1, 42, d3}; app a{d3, ...}; ...
// Order of the above is important and with bigger projects might be easily 10k LOC+ }
- Well, we can maintain the above if we want, but Automatic DI will let us actually focus on the business logic instead! - Any change in the constructors (a reference to shared_pointer, the order of parameters) will require us to change the above :(
Boost.DI library can help with removing the Wiring Mess for us, though!
int main() { auto injector = make_injector(); app = create<app>(injector); return app.api(); }
Right now the benefits of using the framework instead of manual DI - If we change any constructor parameter or order of any dependency we DON't have to change anything with DI framework
- Before not_coupled_di(TDependency& d);
- Refactor not_coupled_di(std::shared_ptr<TDependency>);
With manual DI the wiring has to be changed to pass the shared_ptr with Boost.DI we don't have to change the wiring code at all.
Okay, but what about the polymorphism behaviour. Boost.DI allows a different type of polymorphism. One can inject templates, abstract classes, variant, type erasure etc. More can be found here - https://github.com/boost-ext/di/tree/cpp14/example/polymorphism.
Why that's important? Because it's a better design and makes testing easier too. How to do it with Boost.DI?
// configuration auto module = make_injector( bind<interface>.to<implementation> // I'm not going to go into details, but Boost.DI allows to inject TEMPLATES, abstract classes, etc... );
// production, release int main() { app = create<app>(module); return app.api(); }
// end 2 end testing int main() { auto injector = di::make_injector( module(), // production wiring di::bind<interface>.to<mock> [override] ); app = create<app>(module); return app.api(); }
- Great, we can test end 2 end with overriding some dependencies such as time, database, networking, logging etc... - We have a loosely coupled design! - We don't have to change ANYTHING in the wiring code when we change the dependency tree and/or any constructor parameters/order
I hope that helps a bit?
Extremely helpful, thank you. So in summary it’s a clever bag of arguments that are matched by type to any constructor it’s applied to. The motivating use case is to reduce the burden of maintaining the dependencies of application objects. Is that a succinct, if simplistic, summary?
I also really encourage to take a quick look at which summaries concepts behind DI. - https://www.youtube.com/watch?v=yVogS4NbL6U
Thanks, Kris
On Sat, Feb 20, 2021 at 4:10 PM Peter Dimov via Boost < boost@lists.boost.org> wrote:
Andrzej Krzemienski wrote: ...
So, I want to apply the Dependency Injection philosophy. I get:
class State { Rect area; Point location; // invariant: location is inside area; public: State(Rect a, Point loc) : area{a}, location{loc} {} };
int main() { State s{Rect{{0, 0}, {2, 2}}, Point{1, 1}}; } ```
Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?).
You are thinking in C++ (value semantics) but you need to be thinking in Java (reference semantics, OOP, object DAGs.) Suppose you need to have a logger object
shared_ptr<ILogger> pl; // new FileLogger( log_file_name fn );
and a database connector object (which uses a logger to log things)
shared_ptr<IDbConn> pdb; // new MysqlDb( mysql_db_info mdi, shared_ptr<ILogger> pl )
and an abstract configuration query interface, that can use an .ini file, or a JSON file, or a database
shared_ptr<IConfig> pc; // new DbConfig( shared_ptr<IDbConn> pdb, shared_ptr<ILogger> pl );
Now when you need to create the config query object, you need to give it a database connector and a logger, and you also need to give the same logger to the database connector. So with [NotYetBoost].DI you'll write something like
auto injector = make_injector( bind<log_file_name>.to( "something.log" ), bind<ILogger>.to<FileLogger>(), bind<>.to( mysql_db_info{ ... } ), bind<IDbConn>.to<MysqlDb>(), bind<IConfig>.to<DbConfig>() );
auto cfg = injector.create<IConfig>();
although I don't really know if this will compile or work. :-)
Now imagine that same thing but with 10 objects, or 20 objects. Also imagine that your day to day vocabulary includes "business logic", "enterprise", "UML" and "XML".
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212

niedz., 21 lut 2021 o 00:27 Krzysztof Jusiak via Boost < boost@lists.boost.org> napisał(a):
Let me try to explain DI (manual and automatic) via example.
DI vs NO-DI is all about less coupling by injecting dependencies instead of. It's often referred as a Hollywood prince - Don't call us we will call you!
Example
struct coupled_no_di { void api() { d.call(); } depedency d{singleton::get()}; };
- Coupled design (hard to test)
struct not_coupled_di { not_coupled_di(dependency& d) : d_{d} {}
void api() { d.call(); } depedency& d; };
- Much better - separation of business logic and object creation.
But we can do even better by applying - Dependency Inversion (relay on abstractions and not concrete implementations) - Abstractions can be done in many different ways (type erasure, templates, abstract classes, etc.)
template<DependencyConcept TDependency> struct not_coupled_di { not_coupled_di(TDependency& d) : d_{d} {}
void api() { d.call(); } TDependency& d; };
The above is much better because - It's not coupled to any specific implementation - Can be changed for testing (mocks/fakes/stubs)
Okay, now let's take at the main. A good design will apply a Composition Root (unique place in the app when dependencies are being created)
int main() { my_depedency dependency{...}; not_coupled_di di{dependency};
di.api(); }
- The above is an example of manual DI which is cool already but may lead to what is called a Wiring Mess.
Let's imagine that we have a big dependency tree because we are following SOLID principle and we especially apply the Single Responsibility Principle (we will have a lot of dependencies).
int main() { my_dependency_1 d1{}; my_dependency_2 d2{}; my_dependency_3 d2{d1, d2}; my_dependency_4 d3{d1, 42, d3}; app a{d3, ...}; ...
// Order of the above is important and with bigger projects might be easily 10k LOC+ }
- Well, we can maintain the above if we want, but Automatic DI will let us actually focus on the business logic instead! - Any change in the constructors (a reference to shared_pointer, the order of parameters) will require us to change the above :(
Can you demonstrate how this library works with types that are not default-constructible? E.g., the tutorial example has this code: ``` int main() { logger logger_; renderer renderer_; view view_{renderer_, logger_}; model model_{logger_}; controller controller_{model_, view_, logger_}; user user_{logger_}; app app_{controller_, user_}; } ``` Suppose I want all my objects to have labels so that I can easily identify them. I introduce a type label, which actually only stores a std:string, following the advice from the tutorial, but it serves only one purpose: to designate labels: ``` class label { std::string _s; public: label() = delete; // no empty labels label(std::string_view s); }; label operator ""_l(const char * s, size_t l ) { return label(std::string_view(s, l); } // to use "label"_l ``` Now, suppose that all the classes in the example -- logger, renderer, view, model, controller, user, app -- only expose constructors that take such a label as the first argument. Additionally, suppose that classes model and controller need a distinct logger. So when I manually initialize this herd, I do it like this: ``` int main() { logger log1{"app.logger.lo"_l}; logger log2{"app.logger.hi"_l}; renderer renderer_{"main_renderer"_l}; view view_{"main_view"_l, renderer_, log1}; model model_{"main_model"_l, log2}; // note: the other logger controller controller_{"main_controller"_l, model_, view_, log2}; // note: the other logger user user_{"main_user"_l, log1}; app app_{"main_app"_l, controller_, user_}; } ``` How is it going to look like when replaced with the proposed DI-library? Regards, &rzej;
Boost.DI library can help with removing the Wiring Mess for us, though!
int main() { auto injector = make_injector(); app = create<app>(injector); return app.api(); }
Right now the benefits of using the framework instead of manual DI - If we change any constructor parameter or order of any dependency we DON't have to change anything with DI framework
- Before not_coupled_di(TDependency& d);
- Refactor not_coupled_di(std::shared_ptr<TDependency>);
With manual DI the wiring has to be changed to pass the shared_ptr with Boost.DI we don't have to change the wiring code at all.
Okay, but what about the polymorphism behaviour. Boost.DI allows a different type of polymorphism. One can inject templates, abstract classes, variant, type erasure etc. More can be found here - https://github.com/boost-ext/di/tree/cpp14/example/polymorphism.
Why that's important? Because it's a better design and makes testing easier too. How to do it with Boost.DI?
// configuration auto module = make_injector( bind<interface>.to<implementation> // I'm not going to go into details, but Boost.DI allows to inject TEMPLATES, abstract classes, etc... );
// production, release int main() { app = create<app>(module); return app.api(); }
// end 2 end testing int main() { auto injector = di::make_injector( module(), // production wiring di::bind<interface>.to<mock> [override] ); app = create<app>(module); return app.api(); }
- Great, we can test end 2 end with overriding some dependencies such as time, database, networking, logging etc... - We have a loosely coupled design! - We don't have to change ANYTHING in the wiring code when we change the dependency tree and/or any constructor parameters/order
I hope that helps a bit?
I also really encourage to take a quick look at which summaries concepts behind DI. - https://www.youtube.com/watch?v=yVogS4NbL6U
Thanks, Kris
On Sat, Feb 20, 2021 at 4:10 PM Peter Dimov via Boost < boost@lists.boost.org> wrote:
Andrzej Krzemienski wrote: ...
So, I want to apply the Dependency Injection philosophy. I get:
class State { Rect area; Point location; // invariant: location is inside area; public: State(Rect a, Point loc) : area{a}, location{loc} {} };
int main() { State s{Rect{{0, 0}, {2, 2}}, Point{1, 1}}; } ```
Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?).
You are thinking in C++ (value semantics) but you need to be thinking in Java (reference semantics, OOP, object DAGs.) Suppose you need to have a logger object
shared_ptr<ILogger> pl; // new FileLogger( log_file_name fn );
and a database connector object (which uses a logger to log things)
shared_ptr<IDbConn> pdb; // new MysqlDb( mysql_db_info mdi, shared_ptr<ILogger> pl )
and an abstract configuration query interface, that can use an .ini file, or a JSON file, or a database
shared_ptr<IConfig> pc; // new DbConfig( shared_ptr<IDbConn> pdb, shared_ptr<ILogger> pl );
Now when you need to create the config query object, you need to give it a database connector and a logger, and you also need to give the same logger to the database connector. So with [NotYetBoost].DI you'll write something like
auto injector = make_injector( bind<log_file_name>.to( "something.log" ), bind<ILogger>.to<FileLogger>(), bind<>.to( mysql_db_info{ ... } ), bind<IDbConn>.to<MysqlDb>(), bind<IConfig>.to<DbConfig>() );
auto cfg = injector.create<IConfig>();
although I don't really know if this will compile or work. :-)
Now imagine that same thing but with 10 objects, or 20 objects. Also imagine that your day to day vocabulary includes "business logic", "enterprise", "UML" and "XML".
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 20/02/2021 22:15, Andrzej Krzemienski via Boost wrote:
Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?). I get the following result:
``` int main() { State s = di::make_injector().create<State>(0, 0, 2, 2, 1, 1); } ``` And now I get back to the situation where I am passing six ints and can easily confuse which int represents what.
I am pretty sure I am now unfairly mischaracterizing the library. But this is what I get from the motivation and tutorial sections, and the explanations I have seen so far. You cannot see this behavior in the tutorial example that is using class `app`, because most of the types in there are default-constructed or constructed from values that were themselves (perhaps recursively) default-constructed. So it looks to me that in order to appreciate this library, I have to make most of my types default-constructible.
At which point am I missing something?
I think the other answers answered most of your points, so I'll add to those only two: 1. It is hard to see the wood from the trees for C++ devs because C++ uses dependency injection all over the place in its standard library, its standard idioms, and commonplace practice. The classic example is Allocators: std::vector<int, MyAllocator<int>> std::vector delegates the responsibility of allocating, creating, destroying and deallocating arrays of int to the user supplied service MyAllocator. This is literally Dependency Injection, and it is so commonplace in C++ as a design pattern that we don't call it that. Purists from other languages will point out that std::vector knows the *concrete type* of the delegated service. But other than that it has no idea what the implementation is, other than it promises the side effects guaranteed by the Allocator Concept. If you remove knowledge of _concrete_ type, and replace that with _abstract_ type, you get what we would call a visitor class in C++ - basically a bunch of pure virtual functions. This corresponds to std::pmr::vector<int> whereby the concrete implementation type for allocating, creating, destroying and deallocating arrays of int is no longer known to vector, only that there is an abstract API which promises the side effects guaranteed by the Allocator Concept. Now, imagine that your program has some memory problem, and it only ever uses std::vector<int>. Thanks to the Dependency Injection, you get two degrees of freedom: a) If you chose the concrete type MyAllocator, through a recompile you can inject a mock MyAllocator for testing and debug. b) If you chose the abstract type MyPmrAllocator, you don't need to recompile your code, you simply swap the MyPmrAllocator instance you construct at the beginning which is injected into all your classes with a mock MyPmrAllocator for testing and debug. Option a) is tractable in codebases < 100M lines of code. Option b) becomes worth it in codebases > 100M lines of code. Note that as my codebase grows, I can proactively take a decision to move from degree of freedom a) to b) without breaking all my source code i.e. I can choose for my runtime to be slower in exchange for radically reduced recompile times. 2. I just gave a specific example of the value of the two typical forms of Dependency Injection in C++, and I'm going to assume it's uncontroversial (I actually think it's an exemplar of all that's wrong with Allocators, but that is off topic for here). Something peculiar about how we typically do Dependency Injection in C++ is that it's always *specific* and not *generalised*. If we have a problem e.g. delegation of memory allocation, we design a _specific_ dependency injected solution. What we don't do in C++ is design a _generalised_ dependency injection solution which is universal (unlike say in Java). The advantage of a universal DI which exists everywhere is very much like why choosing Outcome is better than rolling your own result<T> type. Yes anybody can roll their own result<T> type, indeed probably most people do. But when library A has resultA<T>, and library B has resultB<T>, and library C has resultC<T>, how is a codebase dependent on all three libraries supposed to interoperate between those three libraries easily? Most of Outcome's complexity stems from being friendly to third party resultX<T> types. I myself I have deployed Outcome in foreign codebases each using their own Result types, and Outcome can (usually) capture all of those seamlessly without loss of original information fidelity. Thus Outcome becomes "the ring to rule them all", which is its exact value proposition and why I would suppose Outcome was accepted into Boost. What I would like to see of any Boost.DependencyInjection is the exact same "one ring to rule them all" in that it should be easy to integrate *any* bespoke third party Dependency Injection mechanism or framework into Boost.DependencyInjection such that one can *seamlessly* compose library A, library B and library C in a single application, and it all "just works". I'll be frank in saying that I don't believe the current proposed Boost.DI does this. Unless I and most other people here can be convinced otherwise, my personal current expectation is that the proposed Boost.DI will be rejected, but hopefully with ample feedback on what to do for a Boost.DI v2, assuming Kris has the stamina and will. In my personal opinion, if Boost.DI _can_ do things like seamlessly compose arbitrary Allocators, both concrete and abstract, and arbitrary other custom bespoke Dependency Injection designs from across the C++ standard library and the Boost libraries, then its tutorial ought to describe that integration just like the end of the Outcome tutorial shows three separate, independent, different error handling strategies in three separate library dependencies being seamlessly integrated into one application with Outcome doing all the donkey work between those dependencies. I think that if the tutorial demonstrated that seamless composure in action, that would be compelling. Niall

Niall Douglas wrote:
I'll be frank in saying that I don't believe the current proposed Boost.DI does this. Unless I and most other people here can be convinced otherwise, my personal current expectation is that the proposed Boost.DI will be rejected, but hopefully with ample feedback on what to do for a Boost.DI v2, assuming Kris has the stamina and will.
By the look of it, what's being submitted is already a v3, if not v4. It's fairly obvious that (and how) the design has evolved from runtime-based to compile-time, for instance. There's a lot of functionality there that has clearly been added to address issues arising from practical use. I'll be surprised if, after you ask a specific question, the library does not already have an answer for it.

On 21/02/2021 15:39, Peter Dimov via Boost wrote:
Niall Douglas wrote:
I'll be frank in saying that I don't believe the current proposed Boost.DI does this. Unless I and most other people here can be convinced otherwise, my personal current expectation is that the proposed Boost.DI will be rejected, but hopefully with ample feedback on what to do for a Boost.DI v2, assuming Kris has the stamina and will.
By the look of it, what's being submitted is already a v3, if not v4. It's fairly obvious that (and how) the design has evolved from runtime-based to compile-time, for instance.
My memory is that the C++ 14 edition from five years ago did both, and I don't see much difference now. I do agree it's gained the patina of production aging, which is a good thing.
There's a lot of functionality there that has clearly been added to address issues arising from practical use. I'll be surprised if, after you ask a specific question, the library does not already have an answer for it.
I _suspect_ that as well, but for me personally, the current introduction page and the current tutorial aren't doing it for me. - The introduction page reads like a hectoring blog post on why you are designing your code all wrong. This does not leave a person feeling welcome, or drawn into reading more. - By the end of the tutorial, I don't feel "bought in" to the design pattern. I think it's hard in a vocab library to generate buy in for a specific library, but I think by the end of the tutorial the reader ought to be bought into "something like this vocab library", typically a hand written clone by the reader. I would count the tutorial as successful if that is achieved. This tutorial, it just somehow feels unfocused. It swings between telling me implementation specifics to hectoring me about my code being wrong to being simultaneously too hand holdy in parts, and too sweeping in others. I reach the end of the tutorial page not really sure whether this design pattern is useful to my problem, and not without a certain amount of confusion as to what all this is about anyway. And I'm someone who is a fan of inverted responsibility design patterns, and I irritate my work colleagues constantly with lots of "weird" inversions in the code I design, so I _ought_ to be an easy conversion. I think I need to reach the end of that tutorial convinced that a real problem I encounter often is being solved here. Niall

Niall Douglas wrote:
I _suspect_ that as well, but for me personally, the current introduction page and the current tutorial aren't doing it for me.
The introduction and the tutorial didn't resonate with me either. Watching the talk was significantly more informative. https://www.youtube.com/watch?v=yVogS4NbL6U

On 2/21/2021 1:12 PM, Niall Douglas via Boost wrote:
On 21/02/2021 15:39, Peter Dimov via Boost wrote:
Niall Douglas wrote:
I'll be frank in saying that I don't believe the current proposed Boost.DI does this. Unless I and most other people here can be convinced otherwise, my personal current expectation is that the proposed Boost.DI will be rejected, but hopefully with ample feedback on what to do for a Boost.DI v2, assuming Kris has the stamina and will.
By the look of it, what's being submitted is already a v3, if not v4. It's fairly obvious that (and how) the design has evolved from runtime-based to compile-time, for instance.
My memory is that the C++ 14 edition from five years ago did both, and I don't see much difference now. I do agree it's gained the patina of production aging, which is a good thing.
There's a lot of functionality there that has clearly been added to address issues arising from practical use. I'll be surprised if, after you ask a specific question, the library does not already have an answer for it.
I _suspect_ that as well, but for me personally, the current introduction page and the current tutorial aren't doing it for me.
- The introduction page reads like a hectoring blog post on why you are designing your code all wrong. This does not leave a person feeling welcome, or drawn into reading more.
- By the end of the tutorial, I don't feel "bought in" to the design pattern. I think it's hard in a vocab library to generate buy in for a specific library, but I think by the end of the tutorial the reader ought to be bought into "something like this vocab library", typically a hand written clone by the reader. I would count the tutorial as successful if that is achieved.
This tutorial, it just somehow feels unfocused. It swings between telling me implementation specifics to hectoring me about my code being wrong to being simultaneously too hand holdy in parts, and too sweeping in others. I reach the end of the tutorial page not really sure whether this design pattern is useful to my problem, and not without a certain amount of confusion as to what all this is about anyway. And I'm someone who is a fan of inverted responsibility design patterns, and I irritate my work colleagues constantly with lots of "weird" inversions in the code I design, so I _ought_ to be an easy conversion.
I think I need to reach the end of that tutorial convinced that a real problem I encounter often is being solved here.
Libraries need more than a tutorial. I find tutorials helpful, but if I can not understand the various functionalities of a library, no amount of tutorial explanation helps me. By functionality I do not mean just a reference, but an actual written out explanation of the basic concepts of the library, how they are embodied in classes, and how they fit together, as well as a basic explanation of the pros and cons of using the functionality. I gather that I am old-fashioned, but I just do not want to have to do the work of figuring things out for myself through tutorial examples and a reference, because inevitably I will want to do things which are not explained by some example and I will be lost without understanding what the various pieces of a library actually do. This is not a knock against the DI library but just a general appeal for explanations rather than just the examples/reference type of docs.

On 2/21/21 11:47 AM, Edward Diener via Boost wrote:
Libraries need more than a tutorial. I find tutorials helpful, but if I can not understand the various functionalities of a library, no amount of tutorial explanation helps me. By functionality I do not mean just a reference, but an actual written out explanation of the basic concepts of the library, how they are embodied in classes, and how they fit together, as well as a basic explanation of the pros and cons of using the functionality. I gather that I am old-fashioned, but I just do not want to have to do the work of figuring things out for myself through tutorial examples and a reference, because inevitably I will want to do things which are not explained by some example and I will be lost without understanding what the various pieces of a library actually do. This is not a knock against the DI library but just a general appeal for explanations rather than just the examples/reference type of docs.
Edward, You've made this points several times in the past. I've had a lot to say about C++ library documentation: a)what it should and should not contain b)it's relationship to the header and implementation code c)it's relationship to C++ concepts d)structural organization e) ... other stuff I even gave a presentation of my views at CPPCon: https://www.youtube.com/watch?v=YxmdCxX9dMk An example of faithful application of these ideas can be found by looking at the documentation of the safe numerics library. I'd be curious to hear your thoughts on this work. Robert Ramey

On 2/21/2021 8:43 PM, Robert Ramey via Boost wrote:
On 2/21/21 11:47 AM, Edward Diener via Boost wrote:
Libraries need more than a tutorial. I find tutorials helpful, but if I can not understand the various functionalities of a library, no amount of tutorial explanation helps me. By functionality I do not mean just a reference, but an actual written out explanation of the basic concepts of the library, how they are embodied in classes, and how they fit together, as well as a basic explanation of the pros and cons of using the functionality. I gather that I am old-fashioned, but I just do not want to have to do the work of figuring things out for myself through tutorial examples and a reference, because inevitably I will want to do things which are not explained by some example and I will be lost without understanding what the various pieces of a library actually do. This is not a knock against the DI library but just a general appeal for explanations rather than just the examples/reference type of docs.
Edward,
You've made this points several times in the past. I've had a lot to say about C++ library documentation:
a)what it should and should not contain b)it's relationship to the header and implementation code c)it's relationship to C++ concepts d)structural organization e) ... other stuff
I even gave a presentation of my views at CPPCon: https://www.youtube.com/watch?v=YxmdCxX9dMk
An example of faithful application of these ideas can be found by looking at the documentation of the safe numerics library.
I'd be curious to hear your thoughts on this work.
I like your Safe Numerics documentation. I am certainly not against Boost-ext docs, but was just reflecting that the confusion about understanding DI might be solved by a different documentation approach.

On Sun, Feb 21, 2021 at 4:40 PM Peter Dimov via Boost <boost@lists.boost.org> wrote:
By the look of it, what's being submitted is already a v3, if not v4. It's fairly obvious that (and how) the design has evolved from runtime-based to compile-time, for instance.
My own experience with DI comes from the Java world. And there, from what I saw, it's all about wiring various components at runtime from configuration files, thus I'm confused about Boost.DI being compile-time only. In Java, any piece of code can be a "plugin", given the dynamic nature of the runtime itself. So lookup, instantiation, and wiring of components at runtime is easy and natural. You can assemble at runtime (often server) programs from disparate pieces, from completely unrelated libraries (possibly even implemented in different languages compiling to the JVM). DI has been part of Java since 2009: https://jcp.org/en/jsr/detail?id=330 And has many concrete implementations: Commons-Inject, Guice, Spring, Dagger, etc... But in C++, you can't arbitrarily instantiate any class w/o jumping through some hoops, and some runtime factory mechanism, so how can it be fully compile-time? True DI a-la-Java needs more that Boost.DI, no? To the point where I can specify the logger or DB backend of my server app at runtime. Is that use-case not part of Boost.DI? Thanks, --DD

pon., 22 lut 2021 o 10:12 Dominique Devienne via Boost < boost@lists.boost.org> napisał(a):
On Sun, Feb 21, 2021 at 4:40 PM Peter Dimov via Boost < boost@lists.boost.org> wrote:
By the look of it, what's being submitted is already a v3, if not v4. It's fairly obvious that (and how) the design has evolved from runtime-based to compile-time, for instance.
My own experience with DI comes from the Java world.
And there, from what I saw, it's all about wiring various components at runtime from configuration files, thus I'm confused about Boost.DI being compile-time only.
In Java, any piece of code can be a "plugin", given the dynamic nature of the runtime itself. So lookup, instantiation, and wiring of components at runtime is easy and natural. You can assemble at runtime (often server) programs from disparate pieces, from completely unrelated libraries (possibly even implemented in different languages compiling to the JVM).
DI has been part of Java since 2009: https://jcp.org/en/jsr/detail?id=330 And has many concrete implementations: Commons-Inject, Guice, Spring, Dagger, etc...
But in C++, you can't arbitrarily instantiate any class w/o jumping through some hoops, and some runtime factory mechanism, so how can it be fully compile-time? True DI a-la-Java needs more that Boost.DI, no? To the point where I can specify the logger or DB backend of my server app at runtime. Is that use-case not part of Boost.DI? Thanks, --DD
Interesting. Maybe what Boost.DI library needs is a clear formulation of the problem that it addresses. Of course, it should be more specific than "Provide Dependency Injection for C++". Regards, &rzej;
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 22/02/2021 09:12, Dominique Devienne via Boost wrote:
And there, from what I saw, it's all about wiring various components at runtime from configuration files, thus I'm confused about Boost.DI being compile-time only.
The very first item in the Examples part of the docs is dynamically wiring together components using a runtime-supplied XML file. Yes proposed Boost.DI is compile time, but it can also do runtime. I agree that the docs don't make this as obvious as they could. Niall

On Mon, 22 Feb 2021 at 14:46, Niall Douglas via Boost <boost@lists.boost.org> wrote:
On 22/02/2021 09:12, Dominique Devienne via Boost wrote:
And there, from what I saw, it's all about wiring various components at runtime from configuration files, thus I'm confused about Boost.DI being compile-time only.
The very first item in the Examples part of the docs is dynamically wiring together components using a runtime-supplied XML file.
Yes proposed Boost.DI is compile time, but it can also do runtime. I agree that the docs don't make this as obvious as they could.
Comprehensive documentation around the runtime I use case might make it compelling for me.
Niall
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212

On Mon, Feb 22, 2021 at 3:10 PM Richard Hodges via Boost < boost@lists.boost.org> wrote:
On Mon, 22 Feb 2021 at 14:46, Niall Douglas via Boost < boost@lists.boost.org> wrote:
The very first item in the Examples part of the docs is dynamically wiring together components using a runtime-supplied XML file.
Yes proposed Boost.DI is compile time, but it can also do runtime. I agree that the docs don't make this as obvious as they could.
Comprehensive documentation around the runtime I use case might make it compelling for me.
Especially if the assembled components are across shared-libraries. For true Java-like runtime wiring of components picked up from the classpath. Imagine a core no-dependency statically linked exe, with embedded SQLite backend, and simple built-in logger. Useful on its own, can be shared / open sourced. But which can be configured at runtime via CLI or file configs, to use a more sofisticated Logstash logger, a different backend, a different WebSocket impl, etc... With those parts closed sourced, and for a fee. Like a freemium model. --DD

niedz., 21 lut 2021 o 16:18 Niall Douglas via Boost <boost@lists.boost.org> napisał(a):
On 20/02/2021 22:15, Andrzej Krzemienski via Boost wrote:
Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?). I get the following result:
``` int main() { State s = di::make_injector().create<State>(0, 0, 2, 2, 1, 1); } ``` And now I get back to the situation where I am passing six ints and can easily confuse which int represents what.
I am pretty sure I am now unfairly mischaracterizing the library. But this is what I get from the motivation and tutorial sections, and the explanations I have seen so far. You cannot see this behavior in the tutorial example that is using class `app`, because most of the types in there are default-constructed or constructed from values that were themselves (perhaps recursively) default-constructed. So it looks to me that in order to appreciate this library, I have to make most of my types default-constructible.
At which point am I missing something?
I think the other answers answered most of your points, so I'll add to those only two:
1. It is hard to see the wood from the trees for C++ devs because C++ uses dependency injection all over the place in its standard library, its standard idioms, and commonplace practice. The classic example is Allocators:
std::vector<int, MyAllocator<int>>
std::vector delegates the responsibility of allocating, creating, destroying and deallocating arrays of int to the user supplied service MyAllocator. This is literally Dependency Injection, and it is so commonplace in C++ as a design pattern that we don't call it that.
Purists from other languages will point out that std::vector knows the *concrete type* of the delegated service. But other than that it has no idea what the implementation is, other than it promises the side effects guaranteed by the Allocator Concept.
If you remove knowledge of _concrete_ type, and replace that with _abstract_ type, you get what we would call a visitor class in C++ - basically a bunch of pure virtual functions. This corresponds to std::pmr::vector<int> whereby the concrete implementation type for allocating, creating, destroying and deallocating arrays of int is no longer known to vector, only that there is an abstract API which promises the side effects guaranteed by the Allocator Concept.
Now, imagine that your program has some memory problem, and it only ever uses std::vector<int>. Thanks to the Dependency Injection, you get two degrees of freedom:
a) If you chose the concrete type MyAllocator, through a recompile you can inject a mock MyAllocator for testing and debug.
b) If you chose the abstract type MyPmrAllocator, you don't need to recompile your code, you simply swap the MyPmrAllocator instance you construct at the beginning which is injected into all your classes with a mock MyPmrAllocator for testing and debug.
Option a) is tractable in codebases < 100M lines of code. Option b) becomes worth it in codebases > 100M lines of code. Note that as my codebase grows, I can proactively take a decision to move from degree of freedom a) to b) without breaking all my source code i.e. I can choose for my runtime to be slower in exchange for radically reduced recompile times.
Thanks Niall. Maybe this explains the bad reception of the DI-library. If injecting dependencies is so natural to C++ and the library documentation starts from convincing me that I do not know what it is and I am STUPID (rather than SOLID), then this builds a confusion that makes it more difficult to conume the rest.
2. I just gave a specific example of the value of the two typical forms of Dependency Injection in C++, and I'm going to assume it's uncontroversial (I actually think it's an exemplar of all that's wrong with Allocators, but that is off topic for here).
Something peculiar about how we typically do Dependency Injection in C++ is that it's always *specific* and not *generalised*. If we have a problem e.g. delegation of memory allocation, we design a _specific_ dependency injected solution. What we don't do in C++ is design a _generalised_ dependency injection solution which is universal (unlike say in Java).
The advantage of a universal DI which exists everywhere is very much like why choosing Outcome is better than rolling your own result<T> type. Yes anybody can roll their own result<T> type, indeed probably most people do. But when library A has resultA<T>, and library B has resultB<T>, and library C has resultC<T>, how is a codebase dependent on all three libraries supposed to interoperate between those three libraries easily?
Most of Outcome's complexity stems from being friendly to third party resultX<T> types. I myself I have deployed Outcome in foreign codebases each using their own Result types, and Outcome can (usually) capture all of those seamlessly without loss of original information fidelity. Thus Outcome becomes "the ring to rule them all", which is its exact value proposition and why I would suppose Outcome was accepted into Boost.
What I would like to see of any Boost.DependencyInjection is the exact same "one ring to rule them all" in that it should be easy to integrate *any* bespoke third party Dependency Injection mechanism or framework into Boost.DependencyInjection such that one can *seamlessly* compose library A, library B and library C in a single application, and it all "just works".
I am having difficulties with mapping the library-interop offered by Outcome onto the similar library-interop that would be gained from such a hypothetical dependency injection library. The only way I can interpret your words is a situation where one library creates a recipe for injecting parameters to my class widget and another library uses this recipe to actually create objects of my type: ``` // library one: std::function<app> make_app = [] { logger log1{"app.logger.lo"_l}; logger log2{"app.logger.hi"_l}; renderer renderer_{"main_renderer"_l}; view view_{"main_view"_l, renderer_, log1}; model model_{"main_model"_l, log2}; // note: the other logger controller controller_{"main_controller"_l, model_, view_, log2}; // note: the other logger user user_{"main_user"_l, log1}; return app {"main_app"_l, controller_, user_}; }; // library two: void some_fun(std::function<app> make_app) { app app1 = make_app(); app1.run(); app app2 = make_app(); app2.run(); } ``` But did you have something like this in mind when you wrote the above description? And if so, what is the value added by a dedicated library, if one can achieve the same foal with std::function? Is the only motivation that in some subset of cases the library can try to deduce (hopefully correctly) from the number of arguments, how I wanted to initialize the arguments? It looks like a more adequate name for such a library is "factory", because what it does is create objects. Or maybe I am still missing something? Regards, &rzej;
I'll be frank in saying that I don't believe the current proposed Boost.DI does this. Unless I and most other people here can be convinced otherwise, my personal current expectation is that the proposed Boost.DI will be rejected, but hopefully with ample feedback on what to do for a Boost.DI v2, assuming Kris has the stamina and will.
In my personal opinion, if Boost.DI _can_ do things like seamlessly compose arbitrary Allocators, both concrete and abstract, and arbitrary other custom bespoke Dependency Injection designs from across the C++ standard library and the Boost libraries, then its tutorial ought to describe that integration just like the end of the Outcome tutorial shows three separate, independent, different error handling strategies in three separate library dependencies being seamlessly integrated into one application with Outcome doing all the donkey work between those dependencies.
I think that if the tutorial demonstrated that seamless composure in action, that would be compelling.
Niall
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 22/02/2021 09:08, Andrzej Krzemienski via Boost wrote:
What I would like to see of any Boost.DependencyInjection is the exact same "one ring to rule them all" in that it should be easy to integrate *any* bespoke third party Dependency Injection mechanism or framework into Boost.DependencyInjection such that one can *seamlessly* compose library A, library B and library C in a single application, and it all "just works".
I am having difficulties with mapping the library-interop offered by Outcome onto the similar library-interop that would be gained from such a hypothetical dependency injection library. The only way I can interpret your words is a situation where one library creates a recipe for injecting parameters to my class widget and another library uses this recipe to actually create objects of my type
That's not exactly what I meant. What I meant is that if library A uses DI mechanism A, can I describe to a Boost.DependencyInjection that mechanism A, such that mechanism A maps 100% into Boost.DependencyInjection. To put it back into terms of Allocators, how do I tell a Boost.DependencyInjection that for all std::vector<T, X> where X meets the Allocator Concept, std::vector<T, X> can have an instance X injected into its construction such that one can now write: const auto injector = di::make_injector( di::bind<std::vector>.to<MyAllocator>() ); injector.create<std::vector<int>>() ... and that makes me a std::vector<int, MyAllocator<int>>. I'm sure it's probably possible, it's just I from my best reading of the docs, I think it's quite awkward to do right now. And I think it ought to not be awkward in anything Boost approves.
But did you have something like this in mind when you wrote the above description? And if so, what is the value added by a dedicated library, if one can achieve the same foal with std::function? Is the only motivation that in some subset of cases the library can try to deduce (hopefully correctly) from the number of arguments, how I wanted to initialize the arguments?
I think you have correctly identified that arguments handling appears to be a weak point of the current proposed library. Right now it appears to match on most argument count first, but I'm wondering how std::vector's non-uniform placement of the allocator instance in its constructors could be handled without typing out a mapping function for every std::vector constructor overload which takes an allocator instance. I guess what I'd like to tell it is "allocator instances can appear at argument offset 2, 5, and 6 only of the constructor", or failing that, a constexpr initialiser list of how to map constructor args. Obviously, completely automated deduction would be best, but without Reflection it would murder compile times. Niall

That's not exactly what I meant.
What I meant is that if library A uses DI mechanism A, can I describe to a Boost.DependencyInjection that mechanism A, such that mechanism A maps 100% into Boost.DependencyInjection.
To put it back into terms of Allocators, how do I tell a Boost.DependencyInjection that for all std::vector<T, X> where X meets the Allocator Concept, std::vector<T, X> can have an instance X injected into its construction such that one can now write:
const auto injector = di::make_injector( di::bind<std::vector>.to<MyAllocator>() ); injector.create<std::vector<int>>()
... and that makes me a std::vector<int, MyAllocator<int>>.
I'm sure it's probably possible, it's just I from my best reading of the docs, I think it's quite awkward to do right now. And I think it ought to not be awkward in anything Boost approves.
With DI that can be already achieved with constructor/named template deduction. Let's try it then, shall we? For example, we have a custom allocator my_allocator template<class T> struct my_allocator : std::allocator<T> {}; And 2 dependencies dep1, dep2 were we want a custom allocator to be injected (for vector and set) template<class TAllocator = class Allocator> struct dep1 { explicit(true) dep1(std::vector<int, TAllocator>) {} }; template<class TAllocator = class Allocator> struct dep2 { dep2(const std::set<int, std::less<int>, TAllocator>&, std::unique_ptr<interface> i) { assert(dynamic_cast<implementation*>(i.get())); } }; and the application app1, app2 (notice different constructor parameters) template<class TAllocator = class Allocator> struct app1 { app1(dep1<TAllocator>, dep2<TAllocator>) {} }; template<class TAllocator = class Allocator> struct app2 { app2(std::shared_ptr<dep2<TAllocator>>, const dep1<TAllocator>&) {} }; Let's make the injector and override Allocator with my_allocator auto injector = di::make_injector( di::bind<class Allocator>.to<my_allocator<int>>() // won't compile if missing (that's what means compile-time DI, it won't be an exception it will be a compilation error , di::bind<interface>.to<implementation>() // won't compile if missing ); int main() { auto app1 = injector.create<app1>(); // Okay will create app1 with my_allocator injected into all dependencies which are using class Allocator auto app2 = injector.create<app2>(); // Okay will create app2, just a show-case that the order and types of constructor parameters doesn't matter for DI } Full example -> https://godbolt.org/z/sPszoG Right, now for 2e2 testing we would like to change the allocator for test_allocator but keep all other dependencies auto test_injector = di::make_injector( std::move(injector), // old injector di::bind<class Allocator>.to<test_allocator>() [di::override] ); int main() { auto app1 = test_injector.create<app1>(); // Okay will create app1 with test_allocator injected into all dependencies which are using class Allocator }

On 23/02/2021 07:49, Krzysztof Jusiak wrote:
With DI that can be already achieved with constructor/named template deduction.
Let's try it then, shall we?
If you combined what you described with the ability for a runtime parsed config file to select which Allocator to inject (e.g. performance/debug/leakcheck), I'd personally find that motivating. I think your introduction and tutorial need to be convincing people based on examples like that, as well. I think you need three convincing examples, each very different from the other, to be persuasive. Niall

Le 2021-02-23 11:44, Niall Douglas via Boost a écrit :
On 23/02/2021 07:49, Krzysztof Jusiak wrote:
With DI that can be already achieved with constructor/named template deduction.
Let's try it then, shall we?
If you combined what you described with the ability for a runtime parsed config file to select which Allocator to inject (e.g. performance/debug/leakcheck), I'd personally find that motivating.
I think your introduction and tutorial need to be convincing people based on examples like that, as well. I think you need three convincing examples, each very different from the other, to be persuasive.
Talking about the introduction page, a few do/don't do are a bit odd from my point of view: * in the “Carrying dependencies” sample, public inheritance is replaced by composition. Unless the code was badly broken in the first place (ie, using public inheritance where it shouldn't have), it's very unlikely that such a change can usually be applied. A possible fix would at least be to switch to private inheritance in the initial (bad) code. * in the “Carrying injector” sample, it is stated that “Service locator is consider to be an anti-pattern because its instance is required to be passed as the ONLY constructor parameter into all constructors”. I don't know where that statement come from – and some code bases i've worked with disagree with that statement. There's much to say about the service locator pattern, though, and DI is probably a better alternative, but i need to be convinced on solid bases. * the “Manual DI - Wiring Mess” is the part i'd like to see expanded. Typically, with the given example, i don't get a clue on how i replace “view” with another implementation. Which, as far as i'm concerned, is the strong point of DI. The mocks provider sample gives an answer, IMHO it ought to be in the introduction. Besides, the library looks indeed very interesting. Regards, Julien

On Tue, 23 Feb 2021 at 14:59, Krzysztof Jusiak via Boost < boost@lists.boost.org> wrote:
That's not exactly what I meant.
What I meant is that if library A uses DI mechanism A, can I describe to a Boost.DependencyInjection that mechanism A, such that mechanism A maps 100% into Boost.DependencyInjection.
To put it back into terms of Allocators, how do I tell a Boost.DependencyInjection that for all std::vector<T, X> where X meets the Allocator Concept, std::vector<T, X> can have an instance X injected into its construction such that one can now write:
const auto injector = di::make_injector( di::bind<std::vector>.to<MyAllocator>() ); injector.create<std::vector<int>>()
... and that makes me a std::vector<int, MyAllocator<int>>.
I'm sure it's probably possible, it's just I from my best reading of the docs, I think it's quite awkward to do right now. And I think it ought to not be awkward in anything Boost approves.
With DI that can be already achieved with constructor/named template deduction.
Let's try it then, shall we?
This is clearly stupendously clever. What I don't understand is what specific thing is linking the bound Allocator type in the injector to the specialisation point in the app's constructor. Is it this: template<class TAllocator = class Allocator> ? This is going to need to be meticulously documented and spelled out in small words for people like me who obviously live in your intellectual shadow.
For example, we have a custom allocator my_allocator
template<class T> struct my_allocator : std::allocator<T> {};
And 2 dependencies dep1, dep2 were we want a custom allocator to be injected (for vector and set)
template<class TAllocator = class Allocator> struct dep1 { explicit(true) dep1(std::vector<int, TAllocator>) {} };
template<class TAllocator = class Allocator> struct dep2 { dep2(const std::set<int, std::less<int>, TAllocator>&, std::unique_ptr<interface> i) { assert(dynamic_cast<implementation*>(i.get())); } };
and the application app1, app2 (notice different constructor parameters)
template<class TAllocator = class Allocator> struct app1 { app1(dep1<TAllocator>, dep2<TAllocator>) {} };
template<class TAllocator = class Allocator> struct app2 { app2(std::shared_ptr<dep2<TAllocator>>, const dep1<TAllocator>&) {} };
Let's make the injector and override Allocator with my_allocator
auto injector = di::make_injector( di::bind<class Allocator>.to<my_allocator<int>>() // won't compile if missing (that's what means compile-time DI, it won't be an exception it will be a compilation error , di::bind<interface>.to<implementation>() // won't compile if missing );
int main() { auto app1 = injector.create<app1>(); // Okay will create app1 with my_allocator injected into all dependencies which are using class Allocator auto app2 = injector.create<app2>(); // Okay will create app2, just a show-case that the order and types of constructor parameters doesn't matter for DI }
Full example -> https://godbolt.org/z/sPszoG
Right, now for 2e2 testing we would like to change the allocator for test_allocator but keep all other dependencies
auto test_injector = di::make_injector( std::move(injector), // old injector di::bind<class Allocator>.to<test_allocator>() [di::override] );
int main() { auto app1 = test_injector.create<app1>(); // Okay will create app1 with test_allocator injected into all dependencies which are using class Allocator }
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212

This is clearly stupendously clever.
What I don't understand is what specific thing is linking the bound Allocator type in the injector to the specialisation point in the app's constructor. Is it this: template<class TAllocator = class Allocator> ?
This is going to need to be meticulously documented and spelled out in small words for people like me who obviously live in your intellectual shadow.
Yeah, templates are bound by default types names (notice that it also allow concepts - AllocatorConcept = class Allocator) whilst constructor parameters are deduced. Firstly, types are rebound and then proper constructor parameters are being injected. If anything is missing in the wiring compilation error will be triggered. template<class TAllocator = class Allocator> auto injector = di::make_injector( di::bind<class Allocator>.to<my_allocator<int>>() // rebind class Allocator to my_allocator );

El 23/02/2021 a las 16:03, Krzysztof Jusiak via Boost escribió:
This is clearly stupendously clever.
What I don't understand is what specific thing is linking the bound Allocator type in the injector to the specialisation point in the app's constructor. Is it this: template<class TAllocator = class Allocator> ?
This is going to need to be meticulously documented and spelled out in small words for people like me who obviously live in your intellectual shadow.
Yeah, templates are bound by default types names (notice that it also allow concepts - AllocatorConcept = class Allocator) whilst constructor parameters are deduced. Firstly, types are rebound and then proper constructor parameters are being injected. If anything is missing in the wiring compilation error will be triggered.
Does the library also support binding to class *template* types? Sort of: auto injector = di::make_injector( di::bind_class_template<std::allocator>.to<my_allocator>() ); Joaquín M López Muñoz

FWIW I did look at the documentation for DI. I was quite pleased with it. Here are the things I liked: a) there was a lot effort invested in organizing the docs. b) they were quite readable c) I liked the small examples help me understand this. I've been exposed to the idea of DI in the past and I could never figure out what it was and what the purpose was. d) I liked the physical layout - very consistent with other boost libraries. (I've become skeptical of the usage of other system. They don't really add much in my view - other than one more of way of doing things.) All this is very good. BUT a) I was left with the feeling that I sort of understood it but could figure it out if I spent more time at it. This is generally better than I respond to most library documentation. b) Same goes for the motivation / use case for the library. What problem does it solve, how does it compare with other solutions, etc. So I felt we were 90% there. My personal experience in the past has made me a little gun shy. When I get to this point in my own work, I sometimes find getting that last 10% ends up being impossible. This usually turns out that that my "solution" - though it looks good - has some flaws that I didn't really understand myself. I was blind to them. Only when I try to reconcile the anomalies in the documentation does it become apparent that I've over looked some subtle aspect in the library. Could be conceptual side effects, conflict with other ideas that the library uses, or perhaps it adds more conceptual overhead then the library itself eliminates. Or something. This is the basis of my recommendation that ping-pong between writing code, examples, tests is under-appreciated as a contributor to good software. Sorry I can't be more helpful - the whole subject is sort of confusing to me. Robert Ramey

Krzysztof Jusiak wrote:
documentation: https://boost-ext.github.io/di/
This brings up one pretty obvious question, what if I have two constructor parameters of the same type: struct X { std::string name; }; struct Y { std::string title; }; struct Z { X x; Y y; }; How do I inject specific values for X::name and Y::title?

Great question. There are multiple ways of distinguishing them but IMHO strong types would be the most elegant/encouraged when used with DI. struct X { name_t name; }; struct Y { title_t title }; auto injector = di::make_injector( bind<name_t>.to("name"), bind<title_t>.to("title") ); Alternatively, named annotations can be used too - https://boost-ext.github.io/di/tutorial.html (search named or annotations) Or what it's called a context injection auto injector = di::make_injector( bind<std::string>.when<X>.to("name"), bind<std::string>.when<Y>.to("title") ); On Sat, Feb 20, 2021 at 9:44 AM Peter Dimov via Boost <boost@lists.boost.org> wrote:
Krzysztof Jusiak wrote:
documentation: https://boost-ext.github.io/di/
This brings up one pretty obvious question, what if I have two constructor parameters of the same type:
struct X { std::string name; };
struct Y { std::string title; };
struct Z { X x; Y y; };
How do I inject specific values for X::name and Y::title?
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

I have also been waiting for SML! I'd be happy to help and assist review managers! Cem Am So., 21. Feb. 2021 um 00:28 Uhr schrieb Krzysztof Jusiak via Boost < boost@lists.boost.org>:
Great question. There are multiple ways of distinguishing them but IMHO strong types would be the most elegant/encouraged when used with DI.
struct X { name_t name; };
struct Y { title_t title };
auto injector = di::make_injector( bind<name_t>.to("name"), bind<title_t>.to("title") );
Alternatively, named annotations can be used too - https://boost-ext.github.io/di/tutorial.html (search named or annotations)
Or what it's called a context injection
auto injector = di::make_injector( bind<std::string>.when<X>.to("name"), bind<std::string>.when<Y>.to("title") );
On Sat, Feb 20, 2021 at 9:44 AM Peter Dimov via Boost < boost@lists.boost.org> wrote:
Krzysztof Jusiak wrote:
documentation: https://boost-ext.github.io/di/
This brings up one pretty obvious question, what if I have two constructor parameters of the same type:
struct X { std::string name; };
struct Y { std::string title; };
struct Z { X x; Y y; };
How do I inject specific values for X::name and Y::title?
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (12)
-
Andrzej Krzemienski
-
Cem Bassoy
-
Dominique Devienne
-
Edward Diener
-
Joaquin M López Muñoz
-
Julien Blanc
-
Krzysztof Jusiak
-
Niall Douglas
-
Peter Dimov
-
Peter Koch Larsen
-
Richard Hodges
-
Robert Ramey