Hello everyone,, I've been looking into a dependency injection style framework for a project I'm working on and was wondering what was possible currently (C++17 and cmake). The basic requirements are that it provides access to non-virtual type erased objects, the wrapper for which can be made with something like dyno, and the implementations of those can be changed just by changing the references in the cmake project. That should be implementable with static lambda registrations that it can use to build the dependency graph by their arguments and return types and call as needed to avoid static initialization order fiascos.
There are some things I'd like to improve about that though. In general the possible registrations shouldn't change at runtime so all the information to build the dependency graph should be available at compile time and should be able to transform it to be effectively direct calls to the implementation with optimizations(1). The ability to register multiple implementations and choose between them(2). Enable automatic creation of the registration lambda from a type by looking at its constructor(3). The ability to work with some degree of generic providers, for example I could give it a compose provider that takes std::function and std::function as dependencies to provide a std::function and it could determine if there is a B it would be able to provide those dependencies for(4).
For (1) the problem I see is that you'd have to find a way to get the dependencies into the same translation unit. The best I could see was to have the projects add information so can generate sufficient forward declarations to build the graph, I'd be a little worried about how much that could add to the translation units though. Conceptually what it provides would be really nice at least at runtime. Even then I don't quite understand how to do the registrations without something like the friend type loophole. Don't need it to change, just be able to discover all of them and put them in an array would work.
For (2) that should be relatively easy to add to the internals, just add registrations to a list instead of overriding.
For (3) the closest that seems possible currently is the friend type loophole with this specific case showcased here https://godbolt.org/z/FxPDgU from stack overflow.
(4) seems to be similarly difficult since I don't know any way to do it besides defining a templated alias and checking each registered service/wrapper type to see if it is an instantiation with something like https://github.com/ruler501/linq/blob/8b5fe4b2e4121c8c1a55f92f11800ab86275726d/util.h#L240 and build up the graph that way. Can provide the template information with specializations of a template that defines a type list of the required arguments with a wrapper around the template aliases where needed.
Are there any libraries that already implement some of this? If not how much of this seems feasible to do beyond toy examples?
Ultimately what I'm looking for is to see if it's something that could be done rather than being it to solve specific problems. I've mainly focused on trying for 0-cost in runtime performance, 0-cost in code to change implementations, supporting type erasure so there's no virtual calls, having no reliance on specific implementations, extensible to more interfaces than just what my project needs without requiring code modifications, and being able to automatically determine if compositions/transformations can be used. The last one is what I've had the most trouble trying to do by hand, but I can do all of it with boilerplate currently so they aren't strictly needed for functionality.
asked Aug 14 '19 at 06:10 AM in C++ Programming
Follow this question
Once you sign in you will be able to subscribe for any updates here