From b618883aa379f360d4fe1dc33c953a19ca4460dd Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Thu, 11 Feb 2016 21:18:25 +0100 Subject: [PATCH] [DEV] create first version of the lib (extract from ewol) --- esignal/Base.cpp | 45 +++++ esignal/Base.h | 52 ++++++ esignal/Interface.cpp | 52 ++++++ esignal/Interface.h | 46 +++++ esignal/Signal.h | 413 ++++++++++++++++++++++++++++++++++++++++++ esignal/debug.cpp | 14 ++ esignal/debug.h | 40 ++++ lutin_esignal.py | 50 +++++ 8 files changed, 712 insertions(+) create mode 100644 esignal/Base.cpp create mode 100644 esignal/Base.h create mode 100644 esignal/Interface.cpp create mode 100644 esignal/Interface.h create mode 100644 esignal/Signal.h create mode 100644 esignal/debug.cpp create mode 100644 esignal/debug.h create mode 100644 lutin_esignal.py diff --git a/esignal/Base.cpp b/esignal/Base.cpp new file mode 100644 index 0000000..f23a5aa --- /dev/null +++ b/esignal/Base.cpp @@ -0,0 +1,45 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2016, Edouard DUPIN, all right reserved + * + * @license APACHE v2.0 (see license file) + */ + +#include +#include +#include +#include + +#ifdef DEBUG + int32_t esignal::Base::m_uidSignal = 0; + int32_t esignal::Base::m_signalCallLevel = 0; +#endif + +esignal::Base::Base(esignal::Interface& _signalInterfaceLink, + const std::string& _name, + const std::string& _description, + bool _periodic) : + m_signalInterfaceLink(_signalInterfaceLink), + m_name(_name), + m_description(_description), + m_callInProgress(0), + m_someOneRemoveInCall(false), + m_periodic(_periodic) { + // add a reference on the current signal ... + m_signalInterfaceLink.signalAdd(this); +} + +std::ostream& esignal::operator <<(std::ostream& _os, const esignal::Base& _obj) { + _os << _obj.getName(); + return _os; +} + + +const char* esignal::logIndent(int32_t _iii) { + static const char g_val[] = " "; + if (_iii > 5) { + return g_val; + } + return g_val + (5-_iii)*4; +} \ No newline at end of file diff --git a/esignal/Base.h b/esignal/Base.h new file mode 100644 index 0000000..1365457 --- /dev/null +++ b/esignal/Base.h @@ -0,0 +1,52 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2016, Edouard DUPIN, all right reserved + * + * @license APACHE v2.0 (see license file) + */ +#pragma once + +#include + +namespace esignal { + class Base { + protected: + #ifdef DEBUG + static int32_t m_uidSignal; + static int32_t m_signalCallLevel; + #endif + esignal::Interface& m_signalInterfaceLink; + std::string m_name; + std::string m_description; + int32_t m_callInProgress; + bool m_someOneRemoveInCall; + bool m_periodic; + public: + /** + * @brief Create a parameter with a specific type. + * @param[in] _signalInterfaceLink reference on the signal list. + * @param[in] _name Static name of the parameter. + * @param[in] _description description of the parameter. + * @param[in] _periodic Customisation of the log display tag at true to down debug lebel at verbose. + */ + Base(esignal::Interface& _signalInterfaceLink, + const std::string& _name, + const std::string& _description = "", + bool _periodic = false); + /** + * @brief Destructor. + */ + virtual ~Base() { }; + + const std::string& getName() const { + return m_name; + } + const std::string& getDescription() const { + return m_description; + } + virtual void release(std::shared_ptr _obj) = 0; + }; + std::ostream& operator <<(std::ostream& _os, const esignal::Base& _obj); + const char* logIndent(int32_t _iii); +} diff --git a/esignal/Interface.cpp b/esignal/Interface.cpp new file mode 100644 index 0000000..a1a18a9 --- /dev/null +++ b/esignal/Interface.cpp @@ -0,0 +1,52 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2016, Edouard DUPIN, all right reserved + * + * @license APACHE v2.0 (see license file) + */ + +#include +#include +#include +#include + +esignal::Interface::Interface() { + +} + +esignal::Interface::~Interface() { + m_list.clear(); +} + +// note this pointer is not allocated and not free at the end of the class +void esignal::Interface::signalAdd(esignal::Base* _pointerOnSignal) { + if (_pointerOnSignal == nullptr) { + ESIGNAL_ERROR("Try to link a nullptr parameters"); + return; + } + m_list.push_back(_pointerOnSignal); +} + +std::vector esignal::Interface::signalGetAll() const { + std::vector out; + for (auto &it : m_list) { + if(it != nullptr) { + out.push_back(it->getName()); + } + } + return out; +} + +void esignal::Interface::signalUnBindAll(const std::shared_ptr& _object) { + if (_object == nullptr) { + ESIGNAL_ERROR("Input ERROR nullptr pointer Object ..."); + return; + } + for(auto &it : m_list) { + if (it == nullptr) { + continue; + } + it->release(_object); + } +} diff --git a/esignal/Interface.h b/esignal/Interface.h new file mode 100644 index 0000000..acd3f03 --- /dev/null +++ b/esignal/Interface.h @@ -0,0 +1,46 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2016, Edouard DUPIN, all right reserved + * + * @license APACHE v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace esignal { + class Base; + class Interface { + friend class esignal::Base; // to register parameter in the list. + private: + std::vector m_list; //!< list of availlable Parameters + public: + /** + * @brief Constructor. + */ + Interface(); + /** + * @brief Destructor. + */ + ~Interface(); + /** + * @brief Register a parameter class pointer in the List of parameters + * @note This class does not destroy the parameter pointer!!! + * @param[in] pointerOnParameter Pointer on the parameter that might be added. + */ + void signalAdd(esignal::Base* _pointerOnParameter); + /** + * @brief Get All the signal list: + * @return vector on all the signals names + */ + std::vector signalGetAll() const; + /** + * @brief Remove binding on all event class. + * @param[in] _sharedPtr sharedPtr to unlink (no type needed ...). + */ + void signalUnBindAll(const std::shared_ptr& _sharedPtr); + }; +} + diff --git a/esignal/Signal.h b/esignal/Signal.h new file mode 100644 index 0000000..2e7b8fb --- /dev/null +++ b/esignal/Signal.h @@ -0,0 +1,413 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2016, Edouard DUPIN, all right reserved + * + * @license APACHE v2.0 (see license file) + */ +#pragma once + +#include +#include +#include + +namespace esignal { + #undef __class__ + #define __class__ "Signal" + template class Signal : public esignal::Base { + private: + std::vector, + std::function>> m_callerList; // current list of binded element + std::vector, + std::function>> m_callerListInCallback; // temporaty list (when add one in call process) + std::vector> m_callerListDirect; // current list of binded element + std::vector> m_callerListDirectInCallback; // temporaty list (when add one in call process) + public: + /** + * @brief Create a signal with a specific type. + * @param[in] _signalInterfaceLink reference on the signal lister. + * @param[in] _name Static name of the signal. + * @param[in] _description Description of the signal. + * @param[in] _periodic Customisation of the log display tag at true to down debug lebel at verbose. + */ + Signal(esignal::Interface& _signalInterfaceLink, + const std::string& _name, + const std::string& _description = "", + bool _periodic = false) : + esignal::Base(_signalInterfaceLink, _name, _description, _periodic) { + + }; + /** + * @brief Destructor. + */ + virtual ~Signal() { }; + /** + * @brief Bind a callback function to the current signal (generic methis (simplest)) + * @param[in] _obj Shared pointer on the caller object + * @param[in] _func Link on the fuction that might be called (inside a class) + * @example signalXXXX.bind(shared_from_this(), &ClassName::onCallbackXXX); + */ + template + void bind(std::shared_ptr _obj, void (TYPE::*_func)(const T&, TArgs...), TArgs... _args2) { + std::shared_ptr obj2 = std::dynamic_pointer_cast(_obj); + if (obj2 == nullptr) { + ESIGNAL_ERROR("Can not bind signal ..."); + return; + } + if (m_callInProgress == 0) { + m_callerList.push_back(std::make_pair(std::weak_ptr(_obj), std::bind(_func, obj2.get(), std::placeholders::_1, std::forward(_args2)...))); + } else { + m_callerListInCallback.push_back(std::make_pair(std::weak_ptr(_obj), std::bind(_func, obj2.get(), std::placeholders::_1, std::forward(_args2)...))); + } + } + /** + * @brief Advanced binding a callback function to the current signal. + * @param[in] _obj Shared pointer on the caller object + * @param[in] _func functor to call (do it yourself) + * @example signalXXXX.connect(shared_from_this(), std::bind(&ClassName::onCallbackXXX, this, std::placeholders::_1)); + */ + void connect(std::shared_ptr _obj, std::function _function ) { + if (m_callInProgress == 0) { + m_callerList.push_back(std::make_pair(std::weak_ptr(_obj), _function)); + } else { + m_callerListInCallback.push_back(std::make_pair(std::weak_ptr(_obj), _function)); + } + } + //! @previous + void connect(std::function _function ) { + if (m_callInProgress == 0) { + m_callerListDirect.push_back(_function); + } else { + m_callerListDirectInCallback.push_back(_function); + } + } + /** + * @brief Check if an object is registered in the Signal + * @param[in] _obj shared pointer on the object + * @return true The object is connected at this signal. + * @return false The object is NOT connected on this signal. + */ + bool isRegistered(std::shared_ptr _obj) { + if (_obj == nullptr) { + return false; + } + for (auto &it : m_callerList) { + std::shared_ptr obj = it.first.lock(); + if (obj == _obj) { + return true; + } + } + for (auto &it : m_callerListInCallback) { + std::shared_ptr obj = it.first.lock(); + if (obj == _obj) { + return true; + } + } + return false; + } + /** + * @brief remove link on the signal. + * @param[in] _obj shared pointer on the removing object + */ + void release(std::shared_ptr _obj) { + if (m_callInProgress == 0) { + // Remove from the list : + auto it(m_callerList.begin()); + while(it != m_callerList.end()) { + if (it->first.lock() == _obj) { + it = m_callerList.erase(it); + } else { + ++it; + } + } + } else { + // just remove weak poointer + auto it(m_callerList.begin()); + while(it != m_callerList.end()) { + if (it->first.lock() == _obj) { + it->first.reset(); + } else { + ++it; + } + } + m_someOneRemoveInCall = true; + } + // remove from add list in callback progress + auto it = m_callerListInCallback.begin(); + while(it != m_callerListInCallback.end()) { + if (it->first.lock() == _obj) { + it = m_callerListInCallback.erase(it); + } else { + ++it; + } + } + } + /** + * @brief Generate a signal on all interface listening. + * @param[in] _data data to emit + */ + void emit(const T& _data) { + #ifdef DEBUG + m_signalCallLevel++; + int32_t tmpID = m_uidSignal++; + #endif + m_callInProgress++; + if (m_periodic == true) { + ESIGNAL_VERBOSE(esignal::logIndent(m_signalCallLevel-1) << "emit signal{" << tmpID << "} : signal='" << m_name << "' data='" << etk::to_string(_data) << "' to: " << m_callerList.size() << " element(s)"); + } else { + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << "emit signal{" << tmpID << "} : signal='" << m_name << "' data='" << etk::to_string(_data) << "' to: " << m_callerList.size() << " element(s)"); + } + { + auto it(m_callerList.begin()); + while (it != m_callerList.end()) { + std::shared_ptr destObject = it->first.lock(); + if (destObject == nullptr) { + it = m_callerList.erase(it); + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << " nullptr dest"); + continue; + } + if (m_periodic == true) { + ESIGNAL_VERBOSE(esignal::logIndent(m_signalCallLevel-1) << " signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } else { + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << " signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } + it->second(_data); + ++it; + } + } + { + auto it(m_callerListDirect.begin()); + while (it != m_callerListDirect.end()) { + if (m_periodic == true) { + ESIGNAL_VERBOSE(esignal::logIndent(m_signalCallLevel-1) << "X signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } else { + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << "X signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } + (*it)(_data); + ++it; + } + } + m_callInProgress--; + #ifdef DEBUG + m_signalCallLevel--; + #endif + // Remove element in call phase: + if (m_someOneRemoveInCall == true) { + m_someOneRemoveInCall = false; + // Remove from the list : + auto it(m_callerList.begin()); + while(it != m_callerList.end()) { + if (it->first.expired() == true) { + it = m_callerList.erase(it); + } else { + ++it; + } + } + } + // add element in call phase: + if (m_callerListInCallback.size() > 0) { + for (auto &it : m_callerListInCallback) { + m_callerList.push_back(it); + } + m_callerListInCallback.clear(); + } + if (m_callerListDirectInCallback.size() > 0) { + for (auto &it : m_callerListDirectInCallback) { + m_callerListDirect.push_back(it); + } + m_callerListDirectInCallback.clear(); + } + } + size_t getNumberConnected() { + return m_callerList.size() + m_callerListDirect.size(); + } + }; + #undef __class__ + #define __class__ "Signal" + template<> class Signal : public esignal::Base { + private: + std::vector, std::function>> m_callerList; + std::vector, std::function>> m_callerListInCallback; + std::vector> m_callerListDirect; + std::vector> m_callerListDirectInCallback; + public: + /** + * @brief Create a signal with a specific 'void' type. + * @param[in] _signalInterfaceLink reference on the signal lister. + * @param[in] _name Static name of the signal. + * @param[in] _description Description of the signal. + * @param[in] _periodic Customisation of the log display tag at true to down debug lebel at verbose. + */ + Signal(esignal::Interface& _signalInterfaceLink, + const std::string& _name, + const std::string& _description = "", + bool _periodic = false) : + esignal::Base(_signalInterfaceLink, _name, _description, _periodic) { + + }; + /** + * @brief Destructor. + */ + virtual ~Signal() { }; + + /** + * @brief Bind a callback function to the current signal (generic methis (simplest)) + * @param[in] _obj Shared pointer on the caller object + * @param[in] _func Link on the fuction that might be called (inside a class) + * @example signalXXXX.connect(shared_from_this(), &ClassName::onCallbackXXX); + */ + template + void bind(std::shared_ptr _obj, void (TYPE::*_func)(TArgs...), TArgs... args2) { + std::shared_ptr obj2 = std::dynamic_pointer_cast(_obj); + if (obj2 == nullptr) { + ESIGNAL_ERROR("Can not bind signal ..."); + return; + } + if (m_callInProgress == 0) { + m_callerList.push_back(std::make_pair(std::weak_ptr(_obj), std::bind(_func, obj2.get(), std::forward(args2)...))); + } else { + m_callerListInCallback.push_back(std::make_pair(std::weak_ptr(_obj), std::bind(_func, obj2.get(), std::forward(args2)...))); + } + } + /** + * @brief Advanced binding a callback function to the current signal. + * @param[in] _obj Shared pointer on the caller object + * @param[in] _func functor to call (do it yourself) + * @example signalXXXX.connect(shared_from_this(), std::bind(&ClassName::onCallbackXXX, this, std::placeholders::_1)); + */ + void connect(std::shared_ptr _obj, std::function _function ) { + if (m_callInProgress == 0) { + m_callerList.push_back(std::make_pair(std::weak_ptr(_obj), _function)); + } else { + m_callerListInCallback.push_back(std::make_pair(std::weak_ptr(_obj), _function)); + } + } + //! @previous + void connect(std::function _function ) { + if (m_callInProgress == 0) { + m_callerListDirect.push_back(_function); + } else { + m_callerListDirectInCallback.push_back(_function); + } + } + /** + * @brief remove link on the signal. + * @param[in] _obj shared pointer on the removing object + */ + void release(std::shared_ptr _obj) { + auto it(m_callerList.begin()); + if (m_callInProgress == 0) { + // Remove from the list : + while(it != m_callerList.end()) { + if (it->first.lock() == _obj) { + //ESIGNAL_DEBUG(" unbind : " << _obj->getObjectType() << " signal='" << m_name << "'"); + it = m_callerList.erase(it); + } else { + ++it; + } + } + } else { + // just remove weak poointer + while(it != m_callerList.end()) { + if (it->first.lock() == _obj) { + //ESIGNAL_DEBUG(" unbind : " << _obj->getObjectType() << " signal='" << m_name << "' (delayed)"); + it->first.reset(); + } else { + ++it; + } + } + m_someOneRemoveInCall = true; + } + // remove from add list in callback progress + it = m_callerListInCallback.begin(); + while(it != m_callerListInCallback.end()) { + if (it->first.lock() == _obj) { + //ESIGNAL_DEBUG(" unbind : " << _obj->getObjectType() << " signal='" << m_name << "' (notActive)"); + it = m_callerListInCallback.erase(it); + } else { + ++it; + } + } + } + void emit() { + #ifdef DEBUG + m_signalCallLevel++; + int32_t tmpID = m_uidSignal++; + #endif + m_callInProgress++; + if (m_periodic == true) { + ESIGNAL_VERBOSE(esignal::logIndent(m_signalCallLevel-1) << "emit signal{" << tmpID << "} : signal='" << m_name << "' to: " << m_callerList.size() << " element(s)"); + } else { + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << "emit signal{" << tmpID << "} : signal='" << m_name << "' to: " << m_callerList.size() << " element(s)"); + } + { + auto it(m_callerList.begin()); + while (it != m_callerList.end()) { + std::shared_ptr destObject = it->first.lock(); + if (destObject == nullptr) { + it = m_callerList.erase(it); + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << " nullptr dest"); + continue; + } + if (m_periodic == true) { + ESIGNAL_VERBOSE(esignal::logIndent(m_signalCallLevel-1) << " signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } else { + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << " signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } + it->second(); + ++it; + } + } + { + auto it(m_callerListDirect.begin()); + while (it != m_callerListDirect.end()) { + if (m_periodic == true) { + ESIGNAL_VERBOSE(esignal::logIndent(m_signalCallLevel-1) << " signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } else { + ESIGNAL_DEBUG(esignal::logIndent(m_signalCallLevel-1) << " signal{" << tmpID << "} :");// [" << destObject->getId() << "]" << destObject->getObjectType()); + } + (*it)(); + ++it; + } + } + m_callInProgress--; + #ifdef DEBUG + m_signalCallLevel--; + #endif + // Remove element in call phase: + if (m_someOneRemoveInCall == true) { + m_someOneRemoveInCall = false; + // Remove from the list : + auto it(m_callerList.begin()); + while(it != m_callerList.end()) { + if (it->first.expired() == true) { + it = m_callerList.erase(it); + } else { + ++it; + } + } + } + // add element in call phase: + if (m_callerListInCallback.size() > 0) { + for (auto &it : m_callerListInCallback) { + if (it.first.expired() == false) { + m_callerList.push_back(it); + } + } + m_callerListInCallback.clear(); + } + if (m_callerListDirectInCallback.size() > 0) { + for (auto &it : m_callerListDirectInCallback) { + m_callerListDirect.push_back(it); + } + m_callerListDirectInCallback.clear(); + } + } + size_t getNumberConnected() { + return m_callerList.size() + m_callerListDirect.size();; + } + }; + #undef __class__ + #define __class__ nullptr +} + diff --git a/esignal/debug.cpp b/esignal/debug.cpp new file mode 100644 index 0000000..74a6f81 --- /dev/null +++ b/esignal/debug.cpp @@ -0,0 +1,14 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2016, Edouard DUPIN, all right reserved + * + * @license APACHE v2.0 (see license file) + */ + +#include + +int32_t esignal::getLogId() { + static int32_t g_val = etk::log::registerInstance("esignal"); + return g_val; +} diff --git a/esignal/debug.h b/esignal/debug.h new file mode 100644 index 0000000..1bc5735 --- /dev/null +++ b/esignal/debug.h @@ -0,0 +1,40 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2016, Edouard DUPIN, all right reserved + * + * @license APACHE v2.0 (see license file) + */ +#pragma once + +#include + +namespace esignal { + int32_t getLogId(); +}; +#define ESIGNAL_BASE(info,data) TK_LOG_BASE(esignal::getLogId(),info,data) + +#define ESIGNAL_PRINT(data) ESIGNAL_BASE(-1, data) +#define ESIGNAL_CRITICAL(data) ESIGNAL_BASE(1, data) +#define ESIGNAL_ERROR(data) ESIGNAL_BASE(2, data) +#define ESIGNAL_WARNING(data) ESIGNAL_BASE(3, data) +#ifdef DEBUG + #define ESIGNAL_INFO(data) ESIGNAL_BASE(4, data) + #define ESIGNAL_DEBUG(data) ESIGNAL_BASE(5, data) + #define ESIGNAL_VERBOSE(data) ESIGNAL_BASE(6, data) + #define ESIGNAL_TODO(data) ESIGNAL_BASE(4, "TODO : " << data) +#else + #define ESIGNAL_INFO(data) do { } while(false) + #define ESIGNAL_DEBUG(data) do { } while(false) + #define ESIGNAL_VERBOSE(data) do { } while(false) + #define ESIGNAL_TODO(data) do { } while(false) +#endif + +#define ESIGNAL_ASSERT(cond,data) \ + do { \ + if (!(cond)) { \ + ESIGNAL_CRITICAL(data); \ + assert(!#cond); \ + } \ + } while (0) + diff --git a/lutin_esignal.py b/lutin_esignal.py new file mode 100644 index 0000000..8228a7b --- /dev/null +++ b/lutin_esignal.py @@ -0,0 +1,50 @@ +#!/usr/bin/python +import lutin.module as module +import lutin.tools as tools +import lutin.debug as debug +import os +import lutin.multiprocess as lutinMultiprocess + + +def get_type(): + return "LIBRARY" + +def get_desc(): + return "esignal is signal management for all class" + +def get_licence(): + return "APACHE-2" + +def get_compagny_type(): + return "com" + +def get_compagny_name(): + return "atria-soft" + +def get_maintainer(): + return ["Mr DUPIN Edouard "] + +def get_version(): + return [0,1,"dev"] + +def create(target, module_name): + my_module = module.Module(__file__, module_name, get_type()) + my_module.add_extra_compile_flags() + my_module.add_src_file([ + 'esignal/debug.cpp', + 'esignal/Interface.cpp', + 'esignal/Base.cpp' + ]) + my_module.add_header_file([ + 'esignal/debug.h', + 'esignal/Interface.h', + 'esignal/Base.h', + 'esignal/Signal.h' + ]) + my_module.add_module_depend(['etk']) + my_module.add_path(tools.get_current_path(__file__)) + my_module.compile_flags('c++', [ + "-DESIGNAL_VERSION=\"\\\"" + tools.version_to_string(get_version()) + "\\\"\"" + ]) + return my_module +