Created
February 12, 2026 17:39
-
-
Save Frityet/d301fb33a6e3189e894c69097ab5defb to your computer and use it in GitHub Desktop.
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 <cstdio> | |
| #include <string_view> | |
| #include <string> | |
| #include <memory> | |
| #include <unordered_map> | |
| #include <typeindex> | |
| #include <variant> | |
| #include <optional> | |
| #include <functional> | |
| #include <type_traits> | |
| class IService { | |
| public: | |
| IService(IService &&) = default; | |
| virtual ~IService() = 0; | |
| protected: | |
| IService() = default; | |
| }; | |
| IService::~IService() {} | |
| template<typename T> | |
| concept is_service = std::is_base_of_v<IService, T>; | |
| template<typename TImpl, typename TInterface> | |
| concept is_concrete_service = is_service<TInterface> and std::is_base_of_v<TInterface, TImpl> and not std::is_abstract_v<TImpl>; | |
| template <typename TBase, typename TDerived> requires std::is_base_of_v<TBase, TDerived> | |
| constexpr inline auto unique_ptr_downcast(std::unique_ptr<TDerived> &&p) | |
| { | |
| return std::unique_ptr<TBase>(static_cast<TBase *>(p.release())); | |
| } | |
| template<typename TFn, typename ...TArgs> | |
| struct CachedResult { | |
| using Args = std::tuple<TArgs...>; | |
| using Result = std::decay_t<std::invoke_result_t<TFn, TArgs...>>; | |
| std::variant<TFn, Result, std::monostate> result; | |
| constexpr CachedResult() : result(std::monostate()) {} | |
| constexpr CachedResult &operator=(TFn &&fn) | |
| { | |
| result = std::forward<TFn>(fn); | |
| return *this; | |
| } | |
| constexpr Result &operator()(TArgs &&...args) | |
| { | |
| if (std::holds_alternative<std::monostate>(result)) | |
| throw std::runtime_error("no"); | |
| if (result.index() == 0) { | |
| auto &fn = std::get<0>(result); | |
| result.template emplace<1> ( | |
| std::invoke(std::move(fn), std::forward<TArgs>(args)...) | |
| ); | |
| } | |
| return std::get<1>(result); | |
| } | |
| }; | |
| template<typename ...TBuilders> | |
| class ServiceProvider { | |
| public: | |
| using Self = ServiceProvider<TBuilders...>; | |
| constexpr ServiceProvider() = default; | |
| template<is_service TService, is_service ...TDependencies> | |
| constexpr auto register_service(this auto &&self, auto &&fn) //requires is_concrete_service<TService, std::invoke_result<decltype(fn), decltype(self)>> | |
| { | |
| using TBuilder = CachedResult<std::function<std::unique_ptr<TService>(TDependencies &...)>, TDependencies &...>; | |
| auto s = ServiceProvider<TBuilders..., TBuilder>(); | |
| ((std::get<TBuilders>(s._storage) = std::move(std::get<TBuilders>(self._storage))), ...); | |
| std::get<TBuilder>(s._storage) = [fn](TDependencies &...deps) -> std::unique_ptr<TService> { | |
| using TRes = std::remove_cvref_t<decltype(std::invoke(fn, deps...))>; | |
| auto ptr = std::make_unique<TRes>(std::invoke(fn, deps...)); | |
| return unique_ptr_downcast<TService>(std::move(ptr)); | |
| }; | |
| return s; | |
| } | |
| template<typename T> | |
| struct DepsFetcher { | |
| constexpr inline T &operator()(ServiceProvider &sp) | |
| { return sp.get<std::decay_t<T>>(); } | |
| }; | |
| template<typename ...TArgs> | |
| struct DepsFetcher<std::tuple<TArgs...>> { | |
| constexpr inline std::tuple<TArgs...> operator()(ServiceProvider &sp) | |
| { | |
| return std::forward_as_tuple(DepsFetcher<TArgs>()(sp)...); | |
| } | |
| }; | |
| template<is_service TService> | |
| constexpr auto &get() | |
| { | |
| using Target = std::unique_ptr<TService>; | |
| constexpr size_t Index = []() { | |
| size_t idx = -1; | |
| size_t current = 0; | |
| ((std::is_same_v<typename TBuilders::Result, Target> ? (idx = current) : 0, current++), ...); | |
| return idx; | |
| }(); | |
| auto &builder = std::get<Index>(_storage); | |
| return *std::apply(builder, DepsFetcher<typename std::decay_t<decltype(builder)>::Args>()(*this)); | |
| } | |
| std::tuple<TBuilders...> _storage; | |
| }; | |
| //---// | |
| class IWriter : public IService { | |
| public: | |
| IWriter() = default; | |
| IWriter(IWriter &&) = default; | |
| virtual ~IWriter() = 0; | |
| virtual void write(std::string_view data) = 0; | |
| }; | |
| IWriter::~IWriter() {} | |
| class IGreeter : public IService { | |
| public: | |
| virtual ~IGreeter() = 0; | |
| virtual void greet() = 0; | |
| }; | |
| IGreeter::~IGreeter() {} | |
| class IGreeter2 : public IService { | |
| public: | |
| virtual ~IGreeter2() = 0; | |
| virtual void greet() = 0; | |
| }; | |
| IGreeter2::~IGreeter2() {} | |
| //---// | |
| class StandardOutputWriter final : public IWriter { | |
| public: | |
| StandardOutputWriter() = default; | |
| StandardOutputWriter(StandardOutputWriter &&) = default; | |
| void write(std::string_view data) | |
| { | |
| std::puts(data.data()); | |
| } | |
| ~StandardOutputWriter() = default; | |
| }; | |
| static_assert(is_concrete_service<StandardOutputWriter, IWriter>); | |
| class DefaultGreeter final : public IGreeter { | |
| public: | |
| DefaultGreeter(IWriter &writer, std::string_view to): _writer(writer), _to(to) {} | |
| DefaultGreeter(DefaultGreeter &&other): _writer(other._writer), _to(std::move(other._to)) | |
| {} | |
| void greet() | |
| { | |
| _writer.write("Hello, " + _to); | |
| } | |
| ~DefaultGreeter() = default; | |
| private: | |
| IWriter &_writer; | |
| std::string _to; | |
| }; | |
| class OtherGreeter final : public IGreeter2 { | |
| public: | |
| OtherGreeter(IWriter &writer, std::string_view to): _writer(writer), _to(to) {} | |
| OtherGreeter(OtherGreeter &&other): _writer(other._writer), _to(std::move(other._to)) | |
| {} | |
| void greet() | |
| { | |
| _writer.write("Goodbye, " + _to); | |
| } | |
| ~OtherGreeter() = default; | |
| private: | |
| IWriter &_writer; | |
| std::string _to; | |
| }; | |
| int main() | |
| { | |
| auto s = ServiceProvider() | |
| .register_service<IWriter>([]() { return StandardOutputWriter(); }) | |
| .register_service<IGreeter, IWriter>([](IWriter &writer) { | |
| return DefaultGreeter(writer, "Frityet"); | |
| }) | |
| .register_service<IGreeter2, IWriter>([](IWriter &writer){ | |
| return OtherGreeter(writer, "Frityet"); | |
| }); | |
| s.get<IGreeter>().greet(); | |
| s.get<IGreeter2>().greet(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment