Skip to content

Instantly share code, notes, and snippets.

@Frityet
Created February 12, 2026 17:39
Show Gist options
  • Select an option

  • Save Frityet/d301fb33a6e3189e894c69097ab5defb to your computer and use it in GitHub Desktop.

Select an option

Save Frityet/d301fb33a6e3189e894c69097ab5defb to your computer and use it in GitHub Desktop.
#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