File indexing completed on 2024-06-26 07:05:12
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include <fmt/format.h>
0012 #include <fmt/ranges.h>
0013 #include <functional>
0014 #include <map>
0015 #include <string>
0016
0017 #include <algorithms/error.h>
0018 #include <algorithms/name.h>
0019 #include <algorithms/property.h>
0020
0021
0022
0023
0024
0025 #define ALGORITHMS_DEFINE_SERVICE(className) \
0026 protected: \
0027 className() : Service<className>(#className) {} \
0028 \
0029 public: \
0030 friend class Service<className>; \
0031 className(const className&) = delete; \
0032 void operator=(const className&) = delete; \
0033 constexpr static const char* kName = #className;
0034
0035 namespace algorithms {
0036
0037 class ServiceError : public Error {
0038 public:
0039 ServiceError(std::string_view msg) : Error{msg, "algorithms::ServiceError"} {}
0040 };
0041
0042 class ServiceBase : public PropertyMixin {
0043 public:
0044
0045 bool ready() const { return m_ready; }
0046 void ready(bool state) { m_ready = state; }
0047
0048 private:
0049 bool m_ready{false};
0050 };
0051
0052
0053
0054
0055
0056
0057
0058 class ServiceSvc : public NameMixin {
0059 public:
0060 using ServiceMapType = std::map<std::string_view, ServiceBase*>;
0061 static ServiceSvc& instance() {
0062
0063 static ServiceSvc svc;
0064 return svc;
0065 }
0066
0067
0068
0069
0070 template <class Svc>
0071 void add(
0072 ServiceBase* svc, const std::function<void(Svc&)>& init = [](Svc& s) { s.init(); }) {
0073 m_keys.push_back(Svc::kName);
0074 m_services[Svc::kName] = svc;
0075
0076
0077 if (m_initializers.count(Svc::kName) == 0) {
0078 setInit(init);
0079 }
0080
0081 if (m_init) {
0082 initSingle<Svc>();
0083 validate();
0084 }
0085 }
0086
0087 template <class Svc> bool has() const { return m_services.count(Svc::kName); }
0088 template <class Svc> void setInit(const std::function<void(Svc&)>& init) {
0089 m_initializers[Svc::kName] = [init]() { init(Svc::instance()); };
0090 }
0091
0092
0093
0094 void init() {
0095
0096 if (m_init) {
0097 throw ServiceError("Cannot initialize services twice");
0098 }
0099
0100 for (const auto& name : m_keys) {
0101 try {
0102 m_initializers.at(name)();
0103 } catch (const std::exception& e) {
0104
0105 break;
0106 }
0107 auto svc = m_services.at(name);
0108
0109 if (svc->missingProperties().size() > 0) {
0110 break;
0111 }
0112 svc->ready(true);
0113 }
0114
0115
0116
0117 validate();
0118
0119
0120 m_init = true;
0121 }
0122
0123 template <class Svc> void initSingle() {
0124 std::string_view name = Svc::kName;
0125 if (!m_init) {
0126 throw ServiceError(
0127 fmt::format("initSingle<{}>() should not be called before/instead of init()", name));
0128 }
0129 auto svc = m_services.at(name);
0130
0131 if (svc->ready()) {
0132 throw ServiceError(fmt::format("Cannot initialize service {} - already initialized", name));
0133 }
0134
0135 m_initializers.at(name)();
0136
0137 svc->ready(true);
0138 }
0139
0140 template <class Svc = ServiceBase> Svc* service(std::string_view name) const {
0141 return static_cast<Svc*>(m_services.at(name));
0142 }
0143
0144
0145 void validate() const {
0146 std::map<std::string_view, std::vector<std::string_view>> missing_props;
0147 std::vector<std::string_view> not_initialized;
0148 for (std::string_view name : m_keys) {
0149 const auto svc = m_services.at(name);
0150 auto missing = svc->missingProperties();
0151 if (!missing.empty()) {
0152 missing_props[name] = missing;
0153 }
0154 if (!svc->ready()) {
0155 not_initialized.push_back(name);
0156 }
0157 std::string err = "";
0158 if (missing_props.size() > 0) {
0159 err += fmt::format("Encountered missing service properties: {}\n", missing_props);
0160 }
0161 if (not_initialized.size() > 0) {
0162 err += fmt::format("Encountered uninitialized services: {}\n", not_initialized);
0163 }
0164 if (err.size() > 0) {
0165 throw ServiceError(fmt::format("Error initializing all services:\n{}", err));
0166 }
0167 }
0168 }
0169 const auto& services() const { return m_services; }
0170 bool ready() const {
0171 for (const auto& key : m_keys) {
0172 if (!m_services.at(key)->ready()) {
0173 return false;
0174 }
0175 }
0176 return true;
0177 }
0178
0179 private:
0180 ServiceSvc() : NameMixin{"ServiceSvc", "Special service that keeps track of all services"} {}
0181
0182 public:
0183 ServiceSvc(const ServiceSvc&) = delete;
0184 void operator=(const ServiceSvc&) = delete;
0185
0186 private:
0187 std::vector<std::string_view> m_keys;
0188 std::map<std::string_view, ServiceBase*> m_services;
0189 std::map<std::string_view, std::function<void()>> m_initializers;
0190 bool m_init = false;
0191 };
0192
0193
0194
0195
0196
0197 template <class SvcType> class Service : public ServiceBase, public NameMixin {
0198 public:
0199 static SvcType& instance() {
0200
0201 static SvcType svc;
0202 return svc;
0203 }
0204
0205
0206
0207 Service(std::string_view name) : NameMixin{name, name} {
0208 ServiceSvc::instance().add<SvcType>(this);
0209 }
0210 };
0211
0212 }
0213