/* * EMS-ESP - https://github.com/emsesp/EMS-ESP * Copyright 2020-2025 emsesp.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EMSESP_EMSFACTORY_H_ #define EMSESP_EMSFACTORY_H_ #include #include // for unique_ptr #include // Forward declaration namespace emsesp { class EMSdevice; } // Macro for class registration #define REGISTER_FACTORY(derivedClass, device_type) \ namespace { \ auto registry_##derivedClass = ConcreteEMSFactory(device_type); \ } namespace emsesp { class EMSFactory { public: virtual ~EMSFactory() = default; using FactoryMap = std::map; // Register factory object of derived class using the device_type as the unique identifier static inline auto registerFactory(const uint8_t device_type, EMSFactory * factory) -> void { getRegister()[device_type] = factory; } // returns all registered classes (really only for debugging) static inline auto device_handlers() -> const FactoryMap & { return getRegister(); } // Construct derived class returning an unique ptr static auto add(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * default_name, uint8_t flags, uint8_t brand) -> std::unique_ptr { if (auto * ptr = makeRaw(device_type, device_id, product_id, version, default_name, flags, brand)) { return std::unique_ptr(ptr); } return nullptr; } virtual auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) const -> EMSdevice * = 0; private: // Force global variable to be initialized, thus it avoids the "initialization order fiasco" static inline auto getRegister() -> FactoryMap & { static FactoryMap classRegister{}; return classRegister; } // Construct derived class returning a raw pointer // find which EMS device it is and use that class static auto makeRaw(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) -> EMSdevice * { const auto & reg = getRegister(); const auto it = reg.find(device_type); return (it != reg.end()) ? it->second->construct(device_type, device_id, product_id, version, name, flags, brand) : nullptr; } }; template class ConcreteEMSFactory : EMSFactory { public: // Register this global object on the EMSFactory register explicit ConcreteEMSFactory(const uint8_t device_type) { EMSFactory::registerFactory(device_type, this); } auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) const -> EMSdevice * { return new DerivedClass(device_type, device_id, product_id, version, name, flags, brand); } }; } // namespace emsesp #endif