I’m trying to build an injection class that allows any other class to be injected under a name, for the purpose of automatically passing said injection class to the injected class’s constructor. The reasoning for doing this is because the injection class contains some variables that are needed to set up the injected class that I’d like to have managed.
Currently I have some code like this:
#include <memory>
#include <string>
#include <stdout>
#include <typeindex>
class Injector {
private:
std::map<std::type_index, std::map<std::string, std::shared_ptr<void>>> instances;
public:
template<class T, typename... Args>
void inject(std::string name, Args&&... args) {
static_assert(std::is_constructible_v<T, Args..., Injector*>, "Cannot construct type with given args");
auto resourcePtr = std::make_shared<T>(std::forward<Args>(args)..., this);
instances[typeid(T)][name] = resourcePtr;
}
template<class T>
T get(std::string name) {
auto nameToTMap = instances.find(typeid(T));
if (nameToTMap == instances.end()) {
return std::nullopt;
}
auto mapInstance = nameToTMap->second;
auto instance = mapInstance.find(name);
if (instance == mapInstance.end()) {
return std::nullopt;
}
// Here is my problem
auto typedInstance = std::dynamic_pointer_cast<T>(instance->second);
if (!typedInstance) {
return std::nullopt;
}
return typedInstance;
}
};
class Foo {
public:
Foo(int x, int y, Injector injector) {
printf("Hello from Foo, %i, %i", x, y);
}
}
int main() {
Injector injector;
injector.inject<Foo>("foo", 1, 2); // Prints "Hello from Foo, 1, 2"
std::shared_ptr<Foo> myFoo = injector.get<Foo>("foo");
return 0;
}
When compiling (clang++) I get the error:
error: 'void' is not a class type
_ET* __p = dynamic_cast<_ET*>(__r.get());
^ ~~~~~~~~~
note: in instantiation of function template specialization 'std::__1::dynamic_pointer_cast<Foo, void>' requested here
auto typedInstance = std::dynamic_pointer_cast<T>(instance->second);
I’ve tried several different things (like having an Injectable
class) but nothing works so I’m wondering if something like this even works in C++? I know Casting from shared_ptr<void>
to shared_ptr<T>
will always have the correct result because the void
ptr is stored in a map
under the key of T
If this won’t work are there any other ways I can automatically pass a variable to instantiations / function calls in a reliable way?
>Solution :
.get()
doesn’t belong there. std::dynamic_pointer_cast
applies to a shared_ptr
directly, not a raw pointer.
Also, you want std::static_pointer_cast
instead. That’s equivalent to static_cast
being the correct choice if you were to use raw pointers instead of std::shared_ptr
. dynamic_cast
casts between pointers to different (sub)objects in a polymorphic class hierarchy. static_cast
casts from void*
to any other pointer type without affecting to which object the pointer points (assuming the pointer’s address is suitably aligned for T
).
You are responsible to assure that the std::shared_ptr<void>
actually points to an object of type T
. Otherwise the cast with std::static_pointer_cast<T>
will (with very few exceptions) result in undefined behavior when the resulting pointer is used.
In particular if (!typedInstance)
doesn’t do anything useful in regards to the cast. It can only fail if the original std::shared_ptr<void>
was already empty to begin with. If the std::shared_ptr<void>
doesn’t point to an object of type T
, then the result of the cast will still not be (guaranteed to be) a null pointer.