#pragma once #include #include #include #include #include #include #include "util/logger.hpp" #define DEFAULT_DELEGATE_FUNCTOR_ALLOC 5 namespace hep { class expired_weak_object : public std::runtime_error { public: explicit expired_weak_object(const std::string& message) : std::runtime_error(message) {} }; /** * Creates a weak function that safely calls a method if the * object is alive. * * @tparam O Class owning the method * @tparam A Variadic template parameters for the method arguments * * @param method Pointer to the static method to be called * @param obj Shared pointer to the object instance * @returns std::function that calls method on obj if alive, or throws if * expired * @throws std::invalid_argument If obj is null. * @throws expired_weak_object If the object has expired when called * * @note Could investigate removing throwing exceptions, and instead return a * bool if the weak pointer is alive. This may improve performance */ template std::function createWeakFunction(void (O::*method)(A...), std::shared_ptr obj) { if (!obj) { throw std::invalid_argument("method and obj must not be null"); } std::weak_ptr weakObj = obj; return std::function([method = std::move(method), weakObj = std::move(weakObj)](A... args) { if (auto lockedObj = weakObj.lock()) { (lockedObj.get()->*method)(std::forward(args)...); } else { throw expired_weak_object("Attempting to call function of a dead class"); } }); } template class Delegate { public: using Functor = std::function; Delegate() { this->functors.reserve(DEFAULT_DELEGATE_FUNCTOR_ALLOC); } void add(const Functor& func) { functors.push_back(func); } template void addMethod(void (O::*memberFn)(A...), std::shared_ptr obj) { functors.push_back(createWeakFunction(memberFn, obj)); } void clear() { this->functors.clear(); } void invoke(A... args) { if (this->functors.empty()) { log::warning("invoking empty delegate"); return; } std::vector deadFunctorIndexs; for (size_t i = 0; i < functors.size(); i++) { if (this->functors[i]) { try { this->functors[i](std::forward(args)...); } catch (const expired_weak_object& e) { log::error("delegate invoke error: ", e.what()); deadFunctorIndexs.push_back(i); } } } for (auto it = deadFunctorIndexs.rbegin(); it != deadFunctorIndexs.rend(); ++it) { functors.erase(functors.begin() + *it); } } void operator=(Functor c) { clear(); if (c != nullptr) { add(c); } } void operator+=(Functor c) { add(c); } void operator()() { invoke(); } private: std::vector functors; }; } // namespace hep