Adam Bienkunski’s Post

In embedded C++, DI looks simple right up to the moment the object graph starts growing. Then it stops being a pattern discussion and becomes a lifetime problem. People often say, “Just use constructor injection.” And yes, that works. But as the system grows, dependency injection stops being only about how dependencies are passed in. It becomes about who creates them, who owns them, how long they live, and how much wiring the design can absorb. I am talking mainly about embedded-style C++ codebases. Not template-heavy compile-time DI, but systems that value explicit object graphs, dynamic polymorphism, predictable behavior, and code that is easy to debug, review, and maintain. In that world, injecting a dependency is rarely just “pass it into the constructor.” You are also deciding whether the dependency is owned or borrowed, whether unique_ptr stays at the composition root or spreads through the graph, whether you need factories to keep construction testable, and whether shared_ptr appears simply because composition got painful. That is why DI often feels harder in C++ than in languages with framework-managed lifecycles. In Java with Spring, the container creates objects, resolves dependencies, and manages lifecycle. In C++, those decisions stay in your code. They are explicit, local, and impossible to ignore. That is both the strength and the cost. C++ gives us precise control over lifetime. What gets harder is scaling that control as the object graph grows. In small systems, explicit wiring feels clean. In larger ones, it can turn into plumbing. In practice, many embedded C++ codebases end up mixing approaches. Constructor injection for clear dependencies. References for borrowed collaborators. unique_ptr where ownership is real. Factory seams where tests need substitution. And only occasionally a container or custom injector when wiring becomes too large to manage by hand. So for me, DI in embedded C++ is not mainly about patterns. It is about how much lifetime and wiring complexity a design can carry before the composition model starts fighting the codebase. How do you handle this in real projects? Do you keep unique_ptr at the composition root? Inject factories for tests? Build your own injector? Or just accept some plumbing as the price of clarity? #EmbeddedSystems #Cpp #SoftwareEngineering #SoftwareArchitecture #EmbeddedSoftware

  • No alternative text description for this image

Dependency Injection (DI) in C++ is a VERY simple concept that many people erroneous confabulate by trying to apply an overly complicated definition of Dependency Injection from different languages upon C++. DI in C++ is nothing more than the use of polymorphic interface, either: dynamic-polymorphism at runtime using the virtual mechanism or static-polymorphism at compile time using templates or concepts. In other languages DI is a Factory Pattern that creates a set of objects with a predetermined internal architectural/relationships for example potentially something like a Model-View-Controller. There is nothing in C++ that stops that type of factory and it is frequently done. It is not paradigm or feature built-into the language but it is very easily done, as it is in multiple library/frameworks in C++. Anytime that an object or mechanism accepts an interface that can be fulfilled by a different object type that is Dependency Injection. Examples of Dependency Injection: inheritance C++89: godbolt.org/z/hx6sG5Prh templated C++89: godbolt.org/z/5GfeTrhbK concepts C++20: godbolt.org/z/vsnnaWzqW Here is a slide of an example/comparison between dynamic and static polymorphic DI from a Design Patterns talk I gave years ago.

  • No alternative text description for this image

Really good take. The shift from “how to inject” to “who owns what and for how long” is exactly where things get hard in C++.

Well, you know my answer to that :)

See more comments

To view or add a comment, sign in

Explore content categories