#pragma once #include #include namespace cserver { struct SeparatedComponent {}; template struct Options : utempl::TypeList {}; template struct ComponentConfig {}; namespace impl { template struct NamedValue { T value; }; template using GetTypeFromComponentConfig = decltype( [](const ComponentConfig&) -> TT { }(std::declval())); template inline constexpr utempl::ConstexprString kNameFromComponentConfig = decltype([](const ComponentConfig&) { return utempl::Wrapper{}; }(std::declval()))::kValue; template inline constexpr auto TransformIfOk(T&& value, auto&& f) { if constexpr(requires{f(std::forward(value));}) { return f(std::forward(value)); } else { return value; }; }; } // namespace impl template struct ConstexprConfig { utempl::Tuple data; template inline constexpr auto Get() const -> auto { return [&](const ConstexprConfig...>&){ constexpr auto list = utempl::TypeList...>{}; constexpr std::size_t I = Find>(list); if constexpr(I < sizeof...(Ts)) { return utempl::Get(this->data).value; }; }(*this); }; template inline constexpr auto Append(T&& value) const -> ConstexprConfig>> { return {.data = TupleCat(this->data, utempl::MakeTuple(impl::NamedValue>{std::forward(value)}))}; }; }; template struct ServiceContext { static constexpr auto kConfig = config; static constexpr auto kList = utempl::TypeList{}; static constexpr auto kThreadsCount = config.template Get<"threads">(); utempl::Tuple storage; inline constexpr ServiceContext() : storage{ [&](std::index_sequence) -> utempl::Tuple { return {[&](utempl::Wrapper) { return [&] -> std::remove_cvref_t(storage))> { return {utempl::Wrapper(names)>{}, *this}; }; }(utempl::Wrapper{})...}; }(std::index_sequence_for()) } {}; inline constexpr auto Run() { [&](std::index_sequence) { ([](auto& component){ if constexpr(requires{component.Run();}) { component.Run(); }; }(Get(this->storage)), ...); }(std::index_sequence_for()); }; template inline constexpr auto FindComponent() -> auto& { constexpr auto I = utempl::Find(names, name); return Get(this->storage); }; template inline constexpr auto FindComponent() -> T& { constexpr auto I = utempl::Find>(utempl::kTypeList); return Get(this->storage); }; }; namespace impl { namespace loopholes { template struct Getter { friend constexpr auto Magic(Getter); }; template struct Injector { friend constexpr auto Magic(Getter) {return Value;}; }; template concept Injected = requires{Magic(Getter{});}; template struct TagWithTalue {}; template {}>{}> constexpr auto CounterImpl(...) { return I; }; template consteval auto CounterImpl(std::size_t arg) requires Injected{}, Ts...> { return CounterImpl(arg); }; template < typename Tag, typename... Ts, auto R = CounterImpl<0, Tag, Ts...>(std::size_t{}) > consteval auto Counter(auto...) { return R; }; } // namespace loopholes template struct DependencyInfoKey {}; template inline constexpr auto Use() { std::ignore = T{Args...}; }; template consteval auto Ignore() {}; template struct DependencyInfoInjector { static constexpr ConstexprConfig kConfig = config; private: template static consteval auto FindComponentTypeImpl(ComponentConfig...) { constexpr auto I = utempl::Find(utempl::Tuple{std::string_view{names}...}, std::string_view{name}); return [] -> decltype(utempl::Get(utempl::TypeList{})) {}(); }; public: template using FindComponentType = decltype(FindComponentTypeImpl(Ts{}...)); template static constexpr auto FindComponentName = [] (ComponentConfig...) { return Get(utempl::TypeList{})>(utempl::Tuple{names...}); }(Ts{}...); template < utempl::ConstexprString name, typename..., std::size_t I = loopholes::Counter>(), auto = loopholes::Injector< DependencyInfoKey< Current, I >{}, name >{} > static constexpr auto FindComponent() -> FindComponentType&; template < typename T, typename..., std::size_t I = loopholes::Counter>>(), auto = loopholes::Injector< DependencyInfoKey< Current, I >{}, FindComponentName >{} > static constexpr auto FindComponent() -> T&; template static consteval auto GetDependencies() requires (I == 0 || requires {Magic(loopholes::Getter{}>{});}) { if constexpr(I == 0 && !requires {Magic(loopholes::Getter{}>{});}) { return utempl::Tuple{}; } else { if constexpr(requires{GetDependencies();}) { return GetDependencies{}>{}))::Type>(); } else { return utempl::Tuple{names..., Magic(loopholes::Getter{}>{})}; }; }; }; template static inline consteval auto Inject() { Ignore{}, DependencyInfoInjector{}>())>(); }; }; } // namespace impl template struct DependencyGraphElement {}; template struct DependencyGraph { static constexpr auto kValue = utempl::TypeList{}; }; namespace impl { template struct CompileTimeStack { std::array, Size> data{}; std::size_t current{}; inline constexpr auto Push(T value) { if(this->current == Size) { throw std::out_of_range{0}; }; this->data[current].emplace(std::move(value)); this->current++; }; inline constexpr auto Pop() { auto tmp = this->current; this->current--; return std::move(*this->data[tmp]); }; }; template struct RangeView { T* beginIterator; T* endIterator; template inline constexpr auto begin(this Self&& self) { return std::forward(self).beginIterator; }; template inline constexpr auto end(this Self&& self) { return std::forward(self).endIterator; }; inline constexpr RangeView(std::ranges::range auto&& range) : beginIterator(std::ranges::begin(range)), endIterator(std::ranges::end(range)) {}; }; RangeView(std::ranges::range auto&& range) -> RangeView>; } // namespace impl template consteval auto TopologicalSort(const DependencyGraph...>&) { const utempl::Tuple storage = {[&](utempl::Wrapper) { return [&](std::index_sequence) -> std::array> { return {utempl::Find(utempl::Tuple{Names...}, Get(DDependencies))...}; }(std::make_index_sequence>{}); }(utempl::Wrapper{})...}; constexpr auto Size = utempl::kTupleSize; const std::array adj = [&](std::index_sequence){ return std::array{impl::RangeView{Get(storage)}...}; }(std::make_index_sequence{}); std::array visited{}; constexpr auto ResultSize = sizeof...(Dependencies); impl::CompileTimeStack response{}; for(std::size_t i = 0; i < Size; i++) { if(visited[i] == true) { continue; }; [&](this auto&& self, std::size_t v) -> void { visited[v] = true; for(const auto& element : adj[v]) { if(!visited[element]) { self(element); }; }; response.Push(v); }(i); }; return utempl::Transform(std::move(response.data), [](auto&& data){ return *data; }); }; template struct ServiceContextBuilder { static constexpr auto kList = utempl::kTypeList; static constexpr auto kConfig = config; template static consteval auto Append(Options = {}) -> decltype(T::template Adder{}>(ServiceContextBuilder{}>>{})) { return {}; }; template static consteval auto Append(Options = {}) -> ServiceContextBuilder{}>> requires (!requires(ServiceContextBuilder{}>> builder) {T::template Adder{}>(builder);}) { return {}; }; template static consteval auto AppendConfigParam() -> ServiceContextBuilder(Value), ComponentConfigs...> { return {}; }; template static consteval auto TransformComponents(F&& f) -> ServiceContextBuilder { return {}; }; template static consteval auto FindComponent() { return [] (const ServiceContextBuilder...>&) -> decltype(utempl::Get(utempl::TypeList{})) { std::unreachable(); }(ServiceContextBuilder{}); }; static consteval auto Config() { return config; }; static consteval auto GetDependencyGraph() { return [&] (ComponentConfig...){ return DependencyGraph (ComponentConfig) { impl::DependencyInfoInjector injector; injector.template Inject(); return injector.GetDependencies(); }(ComponentConfigs{})>...>{}; }(ComponentConfigs{}...); }; static consteval auto Sort() { constexpr auto sorted = TopologicalSort(GetDependencyGraph()); return [&](std::index_sequence){ constexpr auto list = utempl::TypeList{}; return ServiceContextBuilder(list))...>{}; }(std::index_sequence_for{}); }; static constexpr auto Run() -> void { [] (utempl::TypeList...>) { ServiceContext context; context.Run(); for(;;) { std::this_thread::sleep_for(std::chrono::minutes(1)); }; }(utempl::TypeList{}); }; }; } // namespace cserver