Last active
December 11, 2022 17:49
-
-
Save antcolag/c2c946e51f085fbcf344e8119b736d4a to your computer and use it in GitHub Desktop.
Possible implementation of a dependency injection framework in C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <system_error> | |
#include <memory> | |
template<typename T> | |
class di : public std::shared_ptr<T> | |
{ | |
static std::shared_ptr<T> ptr; | |
public: | |
static void reset() | |
{ | |
di<T>::ptr.reset(); | |
} | |
static std::shared_ptr<T> replace(std::shared_ptr<T> ptr) | |
{ | |
di<T>::ptr = ptr; | |
return di<T>::ptr; | |
} | |
template<typename ... args_t> | |
static std::shared_ptr<T> emplace(args_t && ... args) | |
{ | |
return di<T>::replace(std::make_shared<T>( | |
std::forward<args_t>(args) ... | |
)); | |
} | |
bool is_linked() const | |
{ | |
return *this && di<T>::ptr.get() == this->get(); | |
} | |
static di<T> instance() requires std::is_abstract<T>::value | |
{ | |
return di<T>::ptr; | |
} | |
template<typename ... args_t> | |
static di<T> instance(args_t && ... args) requires (!std::is_abstract<T>::value) | |
{ | |
if(!di<T>::ptr) { | |
di<T>::emplace(std::forward<args_t>(args) ...); | |
} | |
return di<T>::ptr; | |
} | |
template<typename ... args_t> | |
di(args_t && ... ptr) : std::shared_ptr<T>(std::forward<args_t>(ptr) ...) | |
{} | |
~di() | |
{ | |
if(this->is_linked() && di<T>::ptr.use_count() <= 2){ | |
di<T>::ptr.reset(); | |
} | |
} | |
}; | |
template<typename T> | |
std::shared_ptr<T> di<T>::ptr {}; | |
// some test cases | |
struct logger_interface | |
{ | |
virtual void log(std::string) = 0; | |
virtual ~logger_interface() = default; | |
}; | |
struct some_service_interface | |
{ | |
virtual void serve() = 0; | |
virtual ~some_service_interface() = default; | |
}; | |
struct logger_with_id : logger_interface | |
{ | |
static int counter; | |
int id = ++counter; | |
void log(std::string s) { | |
std::cout << id << ") " << s << std::endl; | |
} | |
}; | |
int logger_with_id::counter = 0; | |
struct some_service : some_service_interface | |
{ | |
di<logger_interface> logger; | |
some_service( | |
di<logger_interface> logger = di<logger_interface>::instance() | |
) : | |
logger(logger) | |
{} | |
void serve() { | |
logger->log("serving..."); | |
} | |
}; | |
std::error_condition app = []() { | |
di<logger_interface>::replace(di<logger_with_id>::emplace()); | |
di<some_service_interface>::replace(di<some_service>::emplace()); | |
std::cout << "running app"<< std::endl; | |
di<logger_interface>::instance()->log("app"); | |
di<some_service_interface>::instance()->serve(); | |
std::cout << std::endl; | |
return std::error_condition{}; | |
}(); | |
struct decorated_logger : logger_interface { | |
di<logger_interface> logger; | |
decorated_logger( | |
di<logger_interface> logger = di<logger_interface>::instance() | |
) : | |
logger(logger) | |
{} | |
void log(std::string s) { | |
logger->log("decorating..."); | |
logger->log(s); | |
} | |
}; | |
std::error_condition app_with_custom_logger_on_service = []( | |
di<logger_interface> logger, | |
di<some_service_interface> service | |
) { | |
std::cout << "running app_with_custom_logger_on_service"<< std::endl; | |
logger->log("app"); | |
service->serve(); | |
std::cout << std::endl; | |
return std::error_condition{}; | |
}( | |
di<logger_interface>::replace(std::make_shared<logger_with_id>()), | |
di<some_service_interface>::replace(std::make_shared<some_service>( | |
std::make_shared<decorated_logger>(std::make_shared<logger_with_id>()) | |
)) | |
); | |
struct mock_logger : logger_interface { | |
void log(std::string) { | |
std::cout << "mock_logger" << std::endl; | |
} | |
}; | |
struct mock_some_service : some_service_interface { | |
void serve() { | |
std::cout << "mock_some_service" << std::endl; | |
} | |
}; | |
std::error_condition test = []( | |
di<logger_interface> logger, | |
di<some_service_interface> service | |
) { | |
std::cout << "running test"<< std::endl; | |
logger->log("app"); | |
service->serve(); | |
std::cout << std::endl; | |
return std::error_condition{}; | |
}( | |
di<logger_interface>::replace(std::make_shared<mock_logger>()), | |
di<some_service_interface>::replace(std::make_shared<mock_some_service>()) | |
); | |
std::error_condition test2 = []( | |
) { | |
std::cout << "running test2"<< std::endl; | |
di<logger_interface>::replace(di<logger_with_id>::emplace()); | |
di<some_service_interface>::replace(di<some_service>::emplace()); | |
di<logger_interface>::instance()->log("app"); | |
di<some_service_interface>::instance()->serve(); | |
std::cout << std::endl; | |
return std::error_condition{}; | |
}(); | |
struct my_s { | |
int i; | |
my_s(int i) : i(i){} | |
my_s() : i(0){} | |
}; | |
int test3 = []( | |
) { | |
//di<my_s>::emplace(); | |
std::cout << "running test3"<< std::endl; | |
std::cout << "!! " << di<my_s>::instance().is_linked() << " " << di<my_s>::instance().get() << std::endl; | |
std::cout << std::endl; | |
return 0; | |
}(); | |
int main(){ | |
// ... | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment