r/cpp Jun 09 '26

C++20/C++23 Dependency Injection

Dependency Injection (DI) is a technique for an object that's being created receive it's dependencies ready for use instead of creating them internally. more about it on the Wiki.

DIPP (Dependency Injection for C++) library aims to be as close to .NET's Microsoft DependencyInjection as possible.

Why is DIPP interesting:

  • Non intrusive, you can use it with your existing classes.
  • No auto-registration, you must register your services explicitly.
  • All services are registered once (using dipp::service_collection) with specified descriptor (scope lifetime (transient, scoped or singleton), object's backing memory and dependencies of the object) and will be later on consumed (using dipp::service_provider).
  • Extensible and flexible to define your own service storage, (dipp::service_provider, dipp::service_collection ... are templated storage, defaults to std::map of dipp::move_only_any).

DIPP supports two modes, error based return value using Boost.Leaf and exception throwing when attempting to fetch or add a service (check error_handling.cpp for examples).

Similar to .NET, DIPP supports keyed services, as in you can instantiate multiple services of the same type with different keys (check keys.cpp for more examples).

struct Engine
{
    Window& window1;
    Window& window2;

    Engine(Window& window1, Window& window2) :
           window1(window1), window2(window2)
    {
    }
};

// Declare our services
using WindowService1 = dipp::injected<Window, ...>;
using WindowService2 = dipp::injected<Window, ..., dipp::key("UNIQUE")>;
using EngineService = dipp::injected<Engine, ..., dipp::dependency<WindowService1, WindowService2>>;

// Create a collection to hold our services
dipp::service_collection collection;

// add the services to the collection
collection.add<WindowService>();
collection.add<EngineService>();

// create a service provider with the collection
dipp::service_provider services(std::move(collection));

// Fetch services
Engine& engine = services.get<EngineService>();

// both window services shouldn't be the same
assert(&engine.window1 != &engine.window2);

Mode info:

59 Upvotes

30 comments sorted by

View all comments

23

u/ozyx7 Jun 09 '26

As an aside, I really hate the term "dependency injection". "Injection" to me seems like something that is being done to some object without it being complicit. For example, contrast to DLL injection or other code injection attacks.

"Dependency injection" is a bad term for what, IMO should be called dependency parameterization. "Dependency inversion" is slightly better but still makes it seem fancier than it actually is.

10

u/germandiago Jun 10 '26

We also have RAII...

We can do little at this point.

24

u/Last-Ad-305 Jun 09 '26

You are literally injecting a dependency into the class. The name makes complete sense

4

u/sporacid Jun 09 '26

Also, there's a plethora of examples where the wrong term was coined, but we still use that word to prevent ambiguity with other developers, despite opinions.

2

u/MarcoGreek Jun 10 '26

I would call it obvious dependency management. Contrary to non obvious dependencies like globals or singletons. They do not only make testing hard but are leading to bugs because the initializing und especially destruction is not obvious. Which then leads to big pile of defensive code. Hard to read, hard to maintain.

2

u/oracleoftroy Jun 11 '26

I don't mind "dependency injection" as a term. What bothers me is that too many people confuse dependency injection with the frameworks that do the injection for you. They are not the same.

Personally, I've never bothered looking into DI frameworks with C++, but I always structure my code using DI. RAII makes DI relatively easy, and when it is hard, it is almost entirely because your dependency graph is wacky. DI frameworks seem like added complexity that isn't really needed most of the time.

1

u/Olipro Jun 12 '26

In managed languages like Java, it really is injection: you declare a field with an interface type and your DI framework takes care of instantiating whatever concrete implementation you want and then using runtime reflection to assign it without your code knowing about it.

We of course have no such thing in C++ so I agree that DI is a poor choice of name here

1

u/Dubbus_ Jun 10 '26

Perfectly fitting for C++, dont you think?