[DEV] can remove signal befor handle

This commit is contained in:
Edouard DUPIN 2016-02-19 22:04:27 +01:00
parent 9003745811
commit 1fcf22bf03
4 changed files with 205 additions and 283 deletions

View File

@ -14,15 +14,13 @@
namespace esignal {
#undef __class__
#define __class__ "Signal<T>"
template<typename T> class Signal : public esignal::Base {
private:
std::vector<std::pair<std::weak_ptr<void>,
std::function<void(const T&)>>> m_callerList; // current list of binded element
std::vector<std::pair<std::weak_ptr<void>,
std::function<void(const T&)>>> m_callerListInCallback; // temporaty list (when add one in call process)
std::vector<std::function<void(const T&)>> m_callerListDirect; // current list of binded element
std::vector<std::function<void(const T&)>> m_callerListDirectInCallback; // temporaty list (when add one in call process)
#define __class__ "ISignal<T>"
template<class... Args>
class ISignal : public Signal<Args...> {
protected:
esignal::Interface& m_signalInterfaceLink;
std::string m_name;
std::string m_description;
public:
/**
* @brief Create a signal with a specific type.
@ -31,10 +29,10 @@ namespace esignal {
* @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);
ISignal(esignal::Interface& _signalInterfaceLink,
const std::string& _name,
const std::string& _description = "",
bool _periodic = false);
/**
* @brief Destructor.
*/

View File

@ -14,27 +14,172 @@
#include <functional>
#include <type_traits>
#include <utility>
#include <mutex>
namespace esignal {
extern size_t s_uid;
template<class TYPE>
class RefCount {
public:
std::mutex m_lock;
int64_t m_count;
TYPE* m_data;
public:
RefCount(TYPE* _data) :
m_count(0),
m_data(_data) {
// nothing to do.
}
// copy constructor:
RefCount(const RefCount&) = delete;
// copy operator:
RefCount& operator=(RefCount) = delete;
RefCount& operator=(const RefCount& _obj) = delete;
// Move constructor
RefCount(RefCount&& _obj) = delete;
// Move operator
RefCount& operator=(RefCount&& _obj) = delete;
public:
~RefCount() {
m_data = nullptr;
}
public:
void lock() {
m_lock.lock();
}
void unlock() {
m_lock.unlock();
}
void inc() {
lock();
m_count++;
unlock();
}
int64_t dec() {
int64_t val;
lock();
m_count--;
val = m_count;
unlock();
return val;
}
int64_t getCount() const {
return m_count;
}
void remove() {
lock();
m_data = nullptr;
unlock();
}
TYPE* get() {
return m_data;
}
};
template<class TYPE>
class lockSharedPtrRef {
public:
RefCount<TYPE>* m_counter;
public:
lockSharedPtrRef(TYPE* _pointer) :
m_counter(nullptr) {
if (_pointer != nullptr) {
m_counter = new RefCount<TYPE>(_pointer);
m_counter->inc();
}
}
// copy constructor:
lockSharedPtrRef(const lockSharedPtrRef& _obj) :
m_counter(_obj.m_counter) {
if (m_counter == nullptr) {
return;
}
m_counter->inc();
}
// copy operator:
lockSharedPtrRef& operator=(lockSharedPtrRef) = delete;
lockSharedPtrRef& operator=(const lockSharedPtrRef& _obj) {
if (&_obj == this) {
return *this;
}
if (m_counter != nullptr) {
m_counter->dec();
m_counter = nullptr;
}
m_counter = _obj.m_counter;
if (m_counter == nullptr) {
return;
}
m_counter->inc();
return *this;
}
// Move constructor
lockSharedPtrRef(lockSharedPtrRef&& _obj) :
m_counter(std::move(_obj.m_counter)) {
}
// Move operator
lockSharedPtrRef& operator=(lockSharedPtrRef&& _obj) {
m_counter = std::move(_obj.m_counter);
}
~lockSharedPtrRef() {
int64_t count = m_counter->dec();
if (count > 0) {
return;
}
delete m_counter;
m_counter = nullptr;
}
void removeData() {
if (m_counter != nullptr) {
m_counter->remove();
}
}
void disconnect(std::size_t _uid) {
if (m_counter == nullptr) {
return;
}
m_counter->lock();
TYPE* val = m_counter->get();
if (val != nullptr) {
val->disconnect(_uid);
}
m_counter->unlock();
}
};
#undef __class__
#define __class__ "Signal<T>"
class SignalInderection;
class SignalBase {
protected:
lockSharedPtrRef<SignalBase> m_shared;
public:
SignalBase() {
SignalBase() :
m_shared(this) {
}
virtual ~SignalBase() {
// copy constructor:
SignalBase(const SignalBase&) = delete;
// copy operator:
//SignalBase& operator=(SignalBase) = delete;
//SignalBase& operator=(const SignalBase& _obj) = delete;
// Move constructor
SignalBase(SignalBase&& _obj) = delete;
// Move operator
//SignalBase& operator=(SignalBase&& _obj) = delete;
virtual ~SignalBase() {
m_shared.removeData();
}
virtual void disconnect(std::size_t _uid) = 0;
};
class Connection {
public:
Connection(SignalBase* _sig, std::size_t _id):
m_signal(_sig), m_uid(_id) {
Connection(lockSharedPtrRef<SignalBase> _ref, std::size_t _id):
m_signalRefUnique(_ref), m_uid(_id) {
}
Connection(const Connection&) = delete; // not copyable
@ -43,23 +188,15 @@ namespace esignal {
Connection& operator=(Connection&&) = default; // movable op
~Connection() {
if (m_signal == nullptr) {
return;
}
m_signal->disconnect(m_uid);
m_signal = nullptr;
m_signalRefUnique.disconnect(m_uid);
m_uid = 0;
}
void disconnect() {
if (m_signal == nullptr) {
return;
}
m_signal->disconnect(m_uid);
m_signal = nullptr;
m_signalRefUnique.disconnect(m_uid);
m_uid = 0;
}
private:
SignalBase* m_signal;
lockSharedPtrRef<SignalBase> m_signalRefUnique;
std::size_t m_uid;
};
@ -67,8 +204,6 @@ namespace esignal {
class Signal : public SignalBase {
public:
using Observer = std::function<void(const Args&...)>;
public:
private:
class Executor {
@ -132,7 +267,7 @@ namespace esignal {
std::unique_ptr<Executor> executer(new Executor(std::forward<ObserverType>(observer)));
std::size_t uid = executer->m_uid;
m_executors.push_back(std::move(executer));
return Connection(this, uid);
return Connection(SignalBase::m_shared, uid);
}
template<class pointer, class Func, class... Arg>
Connection connect(pointer* _class, Func _f, Arg... _arg) {
@ -141,7 +276,7 @@ namespace esignal {
}));
std::size_t uid = executer->m_uid;
m_executors.push_back(std::move(executer));
return Connection(this, uid);
return Connection(SignalBase::m_shared, uid);
}
template<class pointer, class Func, class... Arg>
void connect(const std::shared_ptr<pointer>& _class, Func _f, Arg... _arg) {
@ -183,6 +318,15 @@ namespace esignal {
m_callInProgress(0) {
}
// copy constructor:
Signal(const Signal&) = delete;
// copy operator:
Signal& operator=(Signal) = delete;
Signal& operator=(const Signal& _obj) = delete;
// Move constructor
Signal(Signal&& _obj) = delete;
// Move operator
Signal& operator=(Signal&& _obj) = delete;
size_t size() const {
return m_executors.size();
}
@ -198,256 +342,8 @@ namespace esignal {
std::vector<std::unique_ptr<Executor>> m_executors;
int32_t m_callInProgress;
};
/*
class SignalInderection : public std::enable_shared_ptr<SignalInderection> {
public:
}
*/
#if 0
template<typename... T> class Signal {
public:
using Observer std::function<void(const T&...)>;
private:
std::vector<std::pair<std::weak_ptr<void>, Observer>> m_callerList; // current list of binded element
std::vector<std::pair<std::weak_ptr<void>, Observer>> m_callerListInCallback; // temporaty list (when add one in call process)
std::vector<Observer> m_callerListDirect; // current list of binded element
std::vector<Observer> m_callerListDirectInCallback; // temporaty list (when add one in call process)
int32_t m_callInProgress;
bool m_someOneRemoveInCall;
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() :
m_callInProgress(0),
m_someOneRemoveInCall(false) {
}
/**
* @brief Destructor.
*/
~Signal() = default;
/**
* @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<class TYPE_CLASS, class TYPE, typename... TArgs>
void bind(std::shared_ptr<TYPE_CLASS> _obj, void (TYPE::*_func)(const T&..., TArgs...), TArgs... _args2) {
std::shared_ptr<TYPE> obj2 = std::dynamic_pointer_cast<TYPE>(_obj);
if (obj2 == nullptr) {
ESIGNAL_ERROR("Can not bind signal ...");
return;
}
if (m_callInProgress == 0) {
if (COUNT_ELEM == 1) {
m_callerList.push_back(std::make_pair(std::weak_ptr<void>(_obj), [](TArgs...){obj2.get()->, std::placeholders::_1, std::forward<TArgs>(_args2)...)));
}
} else {
// m_callerListInCallback.push_back(std::make_pair(std::weak_ptr<void>(_obj), std::bind(_func, obj2.get(), std::placeholders::_1, std::forward<TArgs>(_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<void> _obj, std::function<void(const T&...)> _function ) {
if (m_callInProgress == 0) {
m_callerList.push_back(std::make_pair(std::weak_ptr<void>(_obj), _function));
} else {
m_callerListInCallback.push_back(std::make_pair(std::weak_ptr<void>(_obj), _function));
}
}
//! @previous
void connect(std::function<void(const T&...)> _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<void> _obj) {
if (_obj == nullptr) {
return false;
}
for (auto &it : m_callerList) {
std::shared_ptr<void> obj = it.first.lock();
if (obj == _obj) {
return true;
}
}
for (auto &it : m_callerListInCallback) {
std::shared_ptr<void> 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<void> _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<void> 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();
}
};
#endif
#undef __class__
#define __class__ nullptr
}
//#define SDFGSDFGSDFGSDFGSDFGSDFG
#ifdef SDFGSDFGSDFGSDFGSDFGSDFG
#include <functional>
#include <type_traits>
#include <utility>
template<int I> struct placeholder{};
namespace std{
template<int I>
struct is_placeholder< ::placeholder<I>> : std::integral_constant<int, I>{
};
} // std::
namespace detail{
template<std::size_t... Is, class F, class... Args>
auto easy_bind(indices<Is...>, F const& f, Args&&... args) -> decltype(std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...)) {
return std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...);
}
} // detail::
template<class R, class... FArgs, class... Args>
auto easy_bind(std::function<R(FArgs...)> const& f, Args&&... args) -> decltype(detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...)) {
return detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...);
}
#endif

View File

@ -13,7 +13,7 @@
#include <gtest/gtest.h>
#undef __class__
#define __class__ "etktest"
#define __class__ "etk-test"
int main(int _argc, const char *_argv[]) {
::testing::InitGoogleTest(&_argc, const_cast<char **>(_argv));
@ -22,12 +22,11 @@ int main(int _argc, const char *_argv[]) {
std::string data = _argv[iii];
if ( data == "-h"
|| data == "--help") {
TEST_PRINT("esvg-test - help : ");
TEST_PRINT("esignal-test - help : ");
TEST_PRINT(" " << _argv[0] << " [options]");
TEST_PRINT(" No optiions ...");
return -1;
}
}
//etk::initDefaultFolder("esvg-test");
return RUN_ALL_TESTS();
}

View File

@ -98,6 +98,7 @@ TEST(test_signal_arg, checkType) {
baseClass.emit();
EXPECT_EQ(connectedClass->m_valueInt32, 22);
*/
#if 0
esignal::Signal<int, float, char> signal;
// ----------------------------------------------------
auto display_values_1 = []( int a, float b, char c){
@ -129,6 +130,14 @@ TEST(test_signal_arg, checkType) {
signal.connect(connectedClassShared, &TestConnectShared::display_values_7, "coucou");
signal.emit( 5, 2.99, 'k');
h1.disconnect();
h2.disconnect();
h3.disconnect();
h4.disconnect();
h5.disconnect();
//signal.connect( complete_class(&connectedClass, &TestConnect::display_values_3) );
//signal.connect( TestConnect::display_values_3(&connectedClass) );
@ -144,5 +153,25 @@ TEST(test_signal_arg, checkType) {
// ----------------------------------------------------
signal.emit( 5, 2.99, 'k');
#endif
std::cout << "========> Nes Signal " << std::endl;
std::shared_ptr<esignal::Signal<std::string>> signalShared = std::make_shared<esignal::Signal<std::string>>();
std::cout << "========> Connect callback " << std::endl;
auto display_val = [&]( const std::string& a){
std::cout << " 1010 " << a<< std::endl;
std::cout << " ----------------------------------" << std::endl;
};
esignal::Connection h7 = signalShared->connect(display_val);
std::cout << "========> Emit" << std::endl;
signalShared->emit("coucou");
std::cout << "========> Remove Signal " << std::endl;
signalShared.reset();
std::cout << "========> disconnect " << std::endl;
h7.disconnect();
std::cout << "========================================= " << std::endl;
}