[boost] [boost-ext] Looking for Review Manager/s

252 views
Skip to first unread message

Krzysztof Jusiak via Boost

unread,
Feb 18, 2021, 2:58:06 PM2/18/21
to boost@lists.boost.org List, Krzysztof Jusiak
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

Richard Hodges via Boost

unread,
Feb 18, 2021, 3:02:13 PM2/18/21
to boost@lists.boost.org List, Richard Hodges
Oh! I've been interested in SML for some time. Great!
--
Richard Hodges
hodg...@gmail.com
office: +442032898513
home: +376841522
mobile: +376380212

Edward Diener via Boost

unread,
Feb 18, 2021, 4:08:10 PM2/18/21
to bo...@lists.boost.org, Edward Diener
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.

Robert Ramey via Boost

unread,
Feb 18, 2021, 7:36:17 PM2/18/21
to bo...@lists.boost.org, Robert Ramey
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

Niall Douglas via Boost

unread,
Feb 19, 2021, 3:40:07 AM2/19/21
to bo...@lists.boost.org, Niall Douglas
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

Richard Hodges via Boost

unread,
Feb 19, 2021, 9:54:16 AM2/19/21
to boost@lists.boost.org List, Richard Hodges
On Thu, 18 Feb 2021 at 20:58, Krzysztof Jusiak via Boost <
bo...@lists.boost.org> wrote:

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
--
Richard Hodges
hodg...@gmail.com
office: +442032898513
home: +376841522
mobile: +376380212

Krzysztof Jusiak via Boost

unread,
Feb 19, 2021, 10:43:27 AM2/19/21
to boost@lists.boost.org List, Krzysztof Jusiak
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 <krzy...@jusiak.net>
wrote:

Peter Dimov via Boost

unread,
Feb 19, 2021, 11:20:37 AM2/19/21
to bo...@lists.boost.org, Peter Dimov
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. :-)

Peter Koch Larsen via Boost

unread,
Feb 19, 2021, 2:42:08 PM2/19/21
to bo...@lists.boost.org, Peter Koch Larsen
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

Andrzej Krzemienski via Boost

unread,
Feb 19, 2021, 3:27:59 PM2/19/21
to Boost mailing list, Andrzej Krzemienski
czw., 18 lut 2021 o 20:58 Krzysztof Jusiak via Boost <bo...@lists.boost.org>
napisał(a):


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;

Krzysztof Jusiak via Boost

unread,
Feb 19, 2021, 5:32:41 PM2/19/21
to boost@lists.boost.org List, Krzysztof Jusiak

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_objects_tree.cpp
- 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)?

Niall Douglas via Boost

unread,
Feb 19, 2021, 9:10:27 PM2/19/21
to bo...@lists.boost.org, Niall Douglas
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

Richard Hodges via Boost

unread,
Feb 20, 2021, 1:28:22 AM2/20/21
to bo...@lists.boost.org, Richard Hodges
On Sat, 20 Feb 2021 at 03:10, Niall Douglas via Boost <bo...@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.

--
Richard Hodges
hodg...@gmail.com
office: +442032898513
home: +376841522
mobile: +376380212

_______________________________________________

Peter Dimov via Boost

unread,
Feb 20, 2021, 11:27:59 AM2/20/21
to bo...@lists.boost.org, Peter Dimov
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.

Peter Dimov via Boost

unread,
Feb 20, 2021, 11:44:36 AM2/20/21
to bo...@lists.boost.org, Peter Dimov
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?

Niall Douglas via Boost

unread,
Feb 20, 2021, 1:17:03 PM2/20/21
to bo...@lists.boost.org, Niall Douglas
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

Robert Ramey via Boost

unread,
Feb 20, 2021, 2:23:31 PM2/20/21
to bo...@lists.boost.org, Robert Ramey
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

Andrzej Krzemienski via Boost

unread,
Feb 20, 2021, 5:15:36 PM2/20/21
to Boost mailing list, Andrzej Krzemienski
sob., 20 lut 2021 o 03:10 Niall Douglas via Boost <bo...@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".

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;

Peter Dimov via Boost

unread,
Feb 20, 2021, 6:11:17 PM2/20/21
to bo...@lists.boost.org, Peter Dimov
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".

Krzysztof Jusiak via Boost

unread,
Feb 20, 2021, 6:27:35 PM2/20/21
to boost@lists.boost.org List, Krzysztof Jusiak, Peter Dimov
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 <bo...@lists.boost.org>
wrote:

Krzysztof Jusiak via Boost

unread,
Feb 20, 2021, 6:28:03 PM2/20/21
to boost@lists.boost.org List, Krzysztof Jusiak
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 <bo...@lists.boost.org>
wrote:

Richard Hodges via Boost

unread,
Feb 21, 2021, 2:49:04 AM2/21/21
to bo...@lists.boost.org, Richard Hodges


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?

--
Richard Hodges
hodg...@gmail.com
office: +442032898513
home: +376841522
mobile: +376380212

_______________________________________________

Niall Douglas via Boost

unread,
Feb 21, 2021, 10:17:59 AM2/21/21
to bo...@lists.boost.org, Niall Douglas
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.

Peter Dimov via Boost

unread,
Feb 21, 2021, 10:39:56 AM2/21/21
to bo...@lists.boost.org, Peter Dimov
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.

Niall Douglas via Boost

unread,
Feb 21, 2021, 1:13:00 PM2/21/21
to bo...@lists.boost.org, Niall Douglas
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

Peter Dimov via Boost

unread,
Feb 21, 2021, 1:31:45 PM2/21/21
to bo...@lists.boost.org, Peter Dimov
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

Edward Diener via Boost

unread,
Feb 21, 2021, 2:48:19 PM2/21/21
to bo...@lists.boost.org, Edward Diener
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.

Robert Ramey via Boost

unread,
Feb 21, 2021, 8:44:06 PM2/21/21
to bo...@lists.boost.org, Robert Ramey
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

Edward Diener via Boost

unread,
Feb 21, 2021, 10:51:48 PM2/21/21
to bo...@lists.boost.org, Edward Diener

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.

Andrzej Krzemienski via Boost

unread,
Feb 22, 2021, 2:44:51 AM2/22/21
to Boost mailing list, Andrzej Krzemienski
niedz., 21 lut 2021 o 00:27 Krzysztof Jusiak via Boost <
bo...@lists.boost.org> napisał(a):

> Let me try to explain DI (manual and automatic) via example.

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;

Andrzej Krzemienski via Boost

unread,
Feb 22, 2021, 4:08:35 AM2/22/21
to Boost mailing list, Andrzej Krzemienski
niedz., 21 lut 2021 o 16:18 Niall Douglas via Boost <bo...@lists.boost.org>
napisał(a):

> On 20/02/2021 22:15, Andrzej Krzemienski via Boost wrote:

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.

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;

Dominique Devienne via Boost

unread,
Feb 22, 2021, 4:12:21 AM2/22/21
to bo...@lists.boost.org, Dominique Devienne
On Sun, Feb 21, 2021 at 4:40 PM Peter Dimov via Boost <bo...@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

Andrzej Krzemienski via Boost

unread,
Feb 22, 2021, 4:25:58 AM2/22/21
to Boost mailing list, Andrzej Krzemienski
pon., 22 lut 2021 o 10:12 Dominique Devienne via Boost <
bo...@lists.boost.org> napisał(a):

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;

Niall Douglas via Boost

unread,
Feb 22, 2021, 8:46:19 AM2/22/21
to bo...@lists.boost.org, Niall Douglas
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

Richard Hodges via Boost

unread,
Feb 22, 2021, 9:09:59 AM2/22/21
to bo...@lists.boost.org, Richard Hodges
On Mon, 22 Feb 2021 at 14:46, Niall Douglas via Boost <bo...@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
hodg...@gmail.com
office: +442032898513
home: +376841522
mobile: +376380212

Dominique Devienne via Boost

unread,
Feb 22, 2021, 11:08:35 AM2/22/21
to bo...@lists.boost.org, Dominique Devienne
On Mon, Feb 22, 2021 at 3:10 PM Richard Hodges via Boost <
bo...@lists.boost.org> wrote:

> On Mon, 22 Feb 2021 at 14:46, Niall Douglas via Boost <
> bo...@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

Niall Douglas via Boost

unread,
Feb 22, 2021, 11:31:32 AM2/22/21
to bo...@lists.boost.org, Niall Douglas
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.

Cem Bassoy via Boost

unread,
Feb 23, 2021, 2:37:17 AM2/23/21
to bo...@lists.boost.org, Cem Bassoy
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 <
bo...@lists.boost.org>:

Niall Douglas via Boost

unread,
Feb 23, 2021, 5:44:59 AM2/23/21
to boost@lists.boost.org List, Niall Douglas
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.

Julien Blanc via Boost

unread,
Feb 23, 2021, 7:07:37 AM2/23/21
to bo...@lists.boost.org, Julien Blanc
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

Krzysztof Jusiak via Boost

unread,
Feb 23, 2021, 8:59:50 AM2/23/21
to boost@lists.boost.org List, Krzysztof Jusiak
>
> 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

Richard Hodges via Boost

unread,
Feb 23, 2021, 9:49:33 AM2/23/21
to boost@lists.boost.org List, Richard Hodges
On Tue, 23 Feb 2021 at 14:59, Krzysztof Jusiak via Boost <
bo...@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.
--
Richard Hodges
hodg...@gmail.com
office: +442032898513
home: +376841522
mobile: +376380212

Krzysztof Jusiak via Boost

unread,
Feb 23, 2021, 10:15:18 AM2/23/21
to boost@lists.boost.org List, Krzysztof Jusiak
>
> 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
);

Joaquin M López Muñoz via Boost

unread,
Feb 24, 2021, 1:37:10 PM2/24/21
to Krzysztof Jusiak via Boost, Joaquin M López Muñoz
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

Reply all
Reply to author
Forward
0 new messages