I often use the "dependency injection" pattern in my projects. In C++ it is easiest to implement by passing around raw pointers, but now with C++11, everything in high-level code should be doable with smart pointers. But what is the best practice for this case? Performance is not critical, a clean and understandable code matters more to me now.
Let me show a simplified example. We have an algorithm that uses distance calculations inside. We want to be able to replace this calculation with different distance metrics (Euclidean, Manhattan, etc.). Our goal is to be able to say something like:
SomeAlgorithm algorithmWithEuclidean(new EuclideanDistanceCalculator());
SomeAlgorithm algorithmWithManhattan(new ManhattanDistanceCalculator());
but with smart pointers to avoid manual new
and delete
.
This is a possible implementation with raw pointers:
class DistanceCalculator {
public:
virtual double distance(Point p1, Point p2) = 0;
};
class EuclideanDistanceCalculator {
public:
virtual double distance(Point p1, Point p2) {
return sqrt(...);
}
};
class ManhattanDistanceCalculator {
public:
virtual double distance(Point p1, Point p2) {
return ...;
}
};
class SomeAlgorithm {
DistanceCalculator* distanceCalculator;
public:
SomeAlgorithm(DistanceCalculator* distanceCalculator_)
: distanceCalculator(distanceCalculator_) {}
double calculateComplicated() {
...
double dist = distanceCalculator->distance(p1, p2);
...
}
~SomeAlgorithm(){
delete distanceCalculator;
}
};
Let's assume that copying is not really an issue, and if we didn't need polymorphism we would just pass the DistanceCalculator
to the constructor of SomeAlgorithm
by value (copying). But since we need to be able to pass in different derived instances (without slicing), the parameter must be either a raw pointer, a reference or a smart pointer.
One solution that comes to mind is to pass it in by reference-to-const and encapsulate it in a std::unique_ptr<DistanceCalculator>
member variable. Then the call would be:
SomeAlgorithm algorithmWithEuclidean(EuclideanDistance());
But this stack-allocated temporary object (rvalue-reference?) will be destructed after this line. So we'd need some copying to make it more like a pass-by-value. But since we don't know the runtime type, we cannot construct our copy easily.
We could also use a smart pointer as the constructor parameter. Since there is no issue with ownership (the DistanceCalculator
will be owned by SomeAlgorithm
) we should use std::unique_ptr
. Should I really replace all of such constructor parameters with unique_ptr
? it seems to reduce readability. Also the user of SomeAlgorithm
must construct it in an awkward way:
SomeAlgorithm algorithmWithEuclidean(std::unique_ptr<DistanceCalculator>(new EuclideanDistance()));
Or should I use the new move semantics (&&, std::move) in some way?
It seems to be a pretty standard problem, there must be some succinct way to implement it.
In most cases you don't want or need ownership transfer, it makes code harder to understand and less flexible (moved-from objects can't be reused). The typical case would be to keep ownership with the caller:
Raw pointers are fine to express non-ownership. If you prefer you can use a reference in the constructor argument, it makes no real difference. However, don't use a reference as data member, it makes the class unnecessarily unassignable.
The down side of just using any pointer (smart or raw), or even an ordinary C++ reference, is that they allow calling non-const methods from a const context.
For stateless classes with a single method that is a non-issue, and
std::function
is a good alternative, but for the general case of classes with state or multiple methods I propose a wrapper similar but not identical tostd::reference_wrapper
(which lacks the const safe accessor).usage:
Replace
T*
withunique_ptr
orshared_ptr
to get owning versions. In this case, also add move construction, and construction from anyunique_ptr<T2>
orshared_ptr<T2>
).If I wanted to do this, the first thing I'd do is kill your interface, and instead use this:
type erased invocation object.
I could do a drop-in replacement using your
EuclideanDistanceCalculator
like this:but as distance calculators rarely require state, we could do away with the object.
With C++1y support, this shortens to:
which as it no longer requires a local variable, can be used inline:
but again, the
EuclideanDistance
doesn't have any real state, so instead we can justIf we really don't need movement but we do need state, we can write a
unique_function< R(Args...) >
type that does not support non-move based assignment, and store one of those instead.The core of this is that the interface
DistanceCalculator
is noise. The name of the variable is usually enough.std::function< double(Point,Point) > m_DistanceCalculator
is clear in what it does. The creator of the type-erasure objectstd::function
handles any lifetime management issues, we just store thefunction
object by value.If your actual dependency injection is more complicated (say multiple different related callbacks), using an interface isn't bad. If you want to avoid copy requirements, I'd go with this:
then, write up your own
make_unique<T>(Args&&...)
(astd
one is coming in C++1y), and use it like this:Interface:
Use:
If you don't want
virtual ~InterfaceForDependencyStuff()
and want to useunique_ptr
, you have to use aunique_ptr
that stores its deleter (by passing in a stateful deleter).On the other hand, if
std::shared_ptr
already comes with amake_shared
, and it stores its deleter statefully by default. So if you go withshared_ptr
storage of your interface, you get:and
and
make_shared
will store a pointer-to-function that deletesImplementationForDependencyStuff
that will not be lost when you convert it to astd::shared_ptr<InterfaceForDependencyStuff>
, so you can safely lack avirtual
destructor inInterfaceForDependencyStuff
. I personally would not bother, and leavevirtual ~InterfaceForDependencyStuff
there.