Open Tarjei400 opened 2 years ago
you can use [di::override]
in any module to force resovling to the specific binding. For example,
di::bind<ICacheDriver>().to<MemCacheDriver>() [di::override]
should do it
I need to have both of these to be instantiated when application starts though, and I think there is no other way of achieving this other than using separate injectors right?
you could use annotations for that - https://github.com/boost-ext/di/blob/cpp14/example/annotations.cpp or strong type interfaces
For having both instances (RedisServer and MemcacheServer) in same injector, yes I can use named injections, thats whats actually I am using in code, however even with [di::override] in order to instantiate those objects, I still need to have 2 seperate injectors right? That also mean that an object I am injecting as shared (Logger) will inject be instantiated 2 times aswell.
Hey so as @krzysztof-jusiak using annotations was part of it. I also dug up a little bit into extensible injectors, and shared_factory, which turned out to be a solution to my problem, when using shared_factory, even if using factories elsewhere, I can mark a deeper dependencies to be shared, apparently. I couldn't find it well described in docs I will leave below a full example of this, as I think docs a little insufficient on that matter. Thanks for help here!
#include <iostream>
#include <optional>
#include <memory>
#include <boost/di.hpp>
#include <boost/di/extension/bindings/contextual_bindings.hpp>
#include <boost/di/extension/injections/extensible_injector.hpp>
#include <boost/di/extension/injections/shared_factory.hpp>
#include <boost/di/extension/injections/factory.hpp>
#include <boost/di/extension/injections/assisted_injection.hpp>
using namespace boost::ext;
namespace di = boost::di;
class Logger;
class RedisCacheDriver;
class MemCacheDriver;
class ICacheDriver;
class Logger;
struct Server {
std::shared_ptr<ICacheDriver> cache{nullptr};
BOOST_DI_INJECT(Server,
const std::shared_ptr<ICacheDriver>& cache
): cache(cache) {
}
Server() {}
};
struct ICacheDriver {
std::shared_ptr<Logger> log{nullptr};
virtual void set() = 0;
ICacheDriver(const std::shared_ptr<Logger>& log): log(log) {}
};
struct NoCacheDriver: public ICacheDriver {
void set() {};
};
struct RedisCacheDriver: public ICacheDriver{
BOOST_DI_INJECT(RedisCacheDriver,
const std::shared_ptr<Logger>& log
): ICacheDriver(log) {
}
void set() { std::cout << "Redis" << std::endl;}
};
struct MemCacheDriver: public ICacheDriver {
BOOST_DI_INJECT(MemCacheDriver,
const std::shared_ptr<Logger>& log,
int v
): ICacheDriver(log) {
}
void set() { std::cout << "Memcache" << std::endl;}
};
class Logger {
public:
BOOST_DI_INJECT(Logger,
) {
}
};
struct RedisServer: public Server {
RedisServer() {};
};
struct MemcacheServer: public Server {
MemcacheServer() {};
};
struct TcpServer: public Server {
TcpServer() = default;
};
auto Redis = [](){};
auto Memcache = [](){};
class Application {
public:
std::shared_ptr<Server> tst;
std::shared_ptr<Server> tst2;
std::shared_ptr<Server> redis;
std::shared_ptr<Server> memcache;
BOOST_DI_INJECT(Application,
(named = Redis) const std::shared_ptr<di::extension::ifactory<Server>>& factory,
(named = Memcache) const std::shared_ptr<di::extension::ifactory<Server>>& factory2,
(named = Redis) const std::shared_ptr<Server> redis,
(named = Memcache) const std::shared_ptr<Server> memcache
): redis(redis), memcache(memcache){
tst = factory->create();
tst2 = factory2->create();
}
};
int main() {
auto commonInjector = di::make_injector<di::extension::contextual_bindings>(
di::bind<Logger>().to(di::extension::shared_factory<Logger>([](const auto& injector) {
auto ctx = di::extension::context(injector);
std::cout << "new logger: "<< ctx << std::endl;
return std::make_shared<Logger>();
}))
//di::bind<ICacheDriver>.to<NoCacheDriver>(),
// di::bind<Application>.to<Application>()
);
auto redis_injector = di::make_injector<di::extension::contextual_bindings>(
di::extension::make_extensible(commonInjector),
di::bind<ICacheDriver>.to<RedisCacheDriver>()[di::override],
di::bind<di::extension::ifactory<Server>>().to(di::extension::factory<Server>{})
);
auto mem_cache_injector = di::make_injector<di::extension::contextual_bindings>(
di::extension::make_extensible(commonInjector),
di::bind<ICacheDriver>.to<MemCacheDriver>()[di::override],
di::bind<di::extension::ifactory<Server>>().to(di::extension::factory<Server>{})
);
auto f = di::create<di::extension::factory<Server>>(mem_cache_injector);
using ServerInjector = std::function<std::unique_ptr<Server>()>;
auto bootstrapInjector = di::make_injector<di::extension::contextual_bindings>(
di::extension::make_extensible(commonInjector),
di::bind<di::extension::ifactory<Server>>().named(Redis).to([&](){
return di::create<std::shared_ptr<di::extension::ifactory<Server>>>(redis_injector);
}),
di::bind<di::extension::ifactory<Server>>().named(Memcache).to([&](){
return di::create<std::shared_ptr<di::extension::ifactory<Server>>>(mem_cache_injector);
}),
di::bind<Server>().named(Redis).to([&](const auto& _) -> std::shared_ptr<Server>{
auto ret = di::create<std::shared_ptr<Server>>(redis_injector);
return ret;
}),
di::bind<Server>().named(Memcache).to([&](const auto& _) -> std::shared_ptr<Server>{
auto ret = di::create<std::shared_ptr<Server>>(mem_cache_injector);
return ret;
})
);
// auto srv = di::create<std::shared_ptr<Server>>(redis_injector);
// auto srv2 = di::create<std::shared_ptr<Server>>(mem_cache_injector);
auto app = di::create<std::shared_ptr<Application>>(bootstrapInjector);
std::cout << "Created " << (app->redis->cache->log == app->memcache->cache->log) << std::endl;
std::cout << "created 2 " << (app->tst->cache->log == app->memcache->cache->log) << std::endl;
std::cout << "created 3 " << (app->tst->cache->log == app->redis->cache->log) << std::endl;
std::cout << "created 4 " << (app->tst->cache->log == app->tst2->cache->log) << std::endl;
assert(app->tst != app->redis);
assert(app->tst2 != app->memcache);
assert(app->tst->cache->log == app->redis->cache->log);
assert(app->tst->cache->log == app->memcache->cache->log);
assert(app->tst2->cache->log == app->redis->cache->log);
assert(app->tst2->cache->log == app->memcache->cache->log);
app->tst->cache->set();
app->tst2->cache->set();
app->redis->cache->set();
app->memcache->cache->set();
}
Hello, I could not find any meaningfull answer to my problem so I decided to ask a question here. This is just a simple example to ilustrate a problem, but I will refer to it for more clarity as if I wanted to use it.
So my problem is, I wanted to have multiple instances of Server class, however specialised in a way I am changing ICacheDriver interface (sometimes it might not be direct dependency, but even deeper in a tree, lets say I wanted to change ILogger upon instantiation). Below I am posting only solution I could come up with however it feels a little bit like workaround. More over if there is same interface (in this case ILogger) even though I expect shared instance to be injected, I always end up with new instances of ILogger.
Is below example only way of achieving this? I think there are no examples in documentation touching this subject. I hope description was clear enough, thank you for any clarifications on this matter.