patterncppMinor
C++ EventHandler Class
Viewed 0 times
eventhandlerclassstackoverflow
Problem
I need to have some thread-safe event handler (like C#'s
```
#include
#include
#include
#include
#include
////////////////////////////////////////////////////////////////////////////
// Event Handler class
template
class EventHandler
{
public:
EventHandler()
{
m_flag.clear();
}
EventHandler(const EventHandler& other)
{
m_flag.clear();
m_ptr = other.m_ptr;
}
EventHandler& operator= (const EventHandler& other)
{
m_flag.clear();
m_ptr = other.m_ptr;
return *this;
}
virtual ~EventHandler()
{
while (m_flag.test_and_set(std::memory_order_acquire));
}
template
EventHandler(void (_Type::memFn)(_EventArg), _Type obj)
{
m_flag.clear();
m_ptr.reset(new std::function(std::bind(memFn, obj, std::placeholders::_1)));
}
void operator() (_EventArg e)
{
while (m_flag.test_and_set(std::memory_order_acquire));
if (m_ptr.get()) { (*m_ptr)(e); }
m_flag.clear(std::memory_order_release);
}
bool operator== (const EventHandler& other)
{
while (m_flag.test_and_set(std::memory_order_acquire));
bool equal = (m_ptr == other.m_ptr);
m_flag.clear(std::memory_order_release);
return equal;
}
private:
std::atomic_flag m_flag;
std::shared_ptr> m_ptr;
};
template
class IEvent
{
public:
IEvent()
{
m_flag.clear();
}
virtual ~IEvent()
{
while (m_flag.test_and_set(std::memory_order_acquire));
}
void operator+= (const EventHandler& handler)
{
while (m_flag.test_and_set(std::memory_order_acquire));
m_list.push_back(handler);
m_flag.clear(std::memory_order_release);
}
void operator-= (const EventHandler& handler)
{
EventHandler), so I made one. Here is my event handler class and sample code (compiled with VS2013). Is this code OK to be operated in multithreaded environment?```
#include
#include
#include
#include
#include
////////////////////////////////////////////////////////////////////////////
// Event Handler class
template
class EventHandler
{
public:
EventHandler()
{
m_flag.clear();
}
EventHandler(const EventHandler& other)
{
m_flag.clear();
m_ptr = other.m_ptr;
}
EventHandler& operator= (const EventHandler& other)
{
m_flag.clear();
m_ptr = other.m_ptr;
return *this;
}
virtual ~EventHandler()
{
while (m_flag.test_and_set(std::memory_order_acquire));
}
template
EventHandler(void (_Type::memFn)(_EventArg), _Type obj)
{
m_flag.clear();
m_ptr.reset(new std::function(std::bind(memFn, obj, std::placeholders::_1)));
}
void operator() (_EventArg e)
{
while (m_flag.test_and_set(std::memory_order_acquire));
if (m_ptr.get()) { (*m_ptr)(e); }
m_flag.clear(std::memory_order_release);
}
bool operator== (const EventHandler& other)
{
while (m_flag.test_and_set(std::memory_order_acquire));
bool equal = (m_ptr == other.m_ptr);
m_flag.clear(std::memory_order_release);
return equal;
}
private:
std::atomic_flag m_flag;
std::shared_ptr> m_ptr;
};
template
class IEvent
{
public:
IEvent()
{
m_flag.clear();
}
virtual ~IEvent()
{
while (m_flag.test_and_set(std::memory_order_acquire));
}
void operator+= (const EventHandler& handler)
{
while (m_flag.test_and_set(std::memory_order_acquire));
m_list.push_back(handler);
m_flag.clear(std::memory_order_release);
}
void operator-= (const EventHandler& handler)
{
Solution
RAII
Whenever you need to acquire a resource, hold it for a while, and then release it deterministically - think resource acquisition is initialization. RAII! When I look at your equality comparison:
We're really doing two things. We're acquiring and releasing a lock on
And then our equality operator becomes:
Keep that in mind throughout. Furthermore, if you do it this way, then
Generalize Your Class
Right now, your event handler is constructible with:
But why limit to just member functions? Your class has a
Additionally, prefer
The user can pass in a bind expression if they want, or a lambda, or any number of other things. Just lets you do more things.
Also
Whenever you need to acquire a resource, hold it for a while, and then release it deterministically - think resource acquisition is initialization. RAII! When I look at your equality comparison:
bool operator== (const EventHandler& other)
{
while (m_flag.test_and_set(std::memory_order_acquire));
bool equal = (m_ptr == other.m_ptr);
m_flag.clear(std::memory_order_release);
return equal;
}We're really doing two things. We're acquiring and releasing a lock on
m_flag, and then we're doing the equality comparison. So let's make that a lot more explicit. std::lock_guard is an RAII lock, templated on a BasicLockable - which is just a class that supports a lock() and unlock(). So instead of holding a std::atomic_flag member, we can hold a member of type:struct FlagMutex {
void lock() {
while (m_flag.test_and_set(std::memory_order_acquire));
}
void unlock() {
m_flag.clear(std::memory_order_release);
}
/*
* note that you were not initializing your flag correctly!
*/
std::atomic_flag m_flag = ATOMIC_INIT_FLAG;
};And then our equality operator becomes:
bool operator== (const EventHandler& other) {
std::lock_guard lk(m_mutex);
return m_ptr == other.m_ptr;
}Keep that in mind throughout. Furthermore, if you do it this way, then
FlagMutex can become a template argument for EventHandler so you can actually test the assertion that a std::atomic_flag is faster than a std::mutex.Generalize Your Class
Right now, your event handler is constructible with:
template
EventHandler(void (_Type::*memFn)(_EventArg), _Type *obj)But why limit to just member functions? Your class has a
std::function member. That can support free functions. Just generalize it.Additionally, prefer
make_shared to just new:using FuncType = std::function;
template ::value
>>
EventHandler(F&& func)
{
m_ptr = std::make_shared(std::forward(func));
}The user can pass in a bind expression if they want, or a lambda, or any number of other things. Just lets you do more things.
Also
_EventArg is a reserved name.Code Snippets
bool operator== (const EventHandler& other)
{
while (m_flag.test_and_set(std::memory_order_acquire));
bool equal = (m_ptr == other.m_ptr);
m_flag.clear(std::memory_order_release);
return equal;
}struct FlagMutex {
void lock() {
while (m_flag.test_and_set(std::memory_order_acquire));
}
void unlock() {
m_flag.clear(std::memory_order_release);
}
/*
* note that you were not initializing your flag correctly!
*/
std::atomic_flag m_flag = ATOMIC_INIT_FLAG;
};bool operator== (const EventHandler& other) {
std::lock_guard<FlagMutex> lk(m_mutex);
return m_ptr == other.m_ptr;
}template <typename _Type>
EventHandler(void (_Type::*memFn)(_EventArg), _Type *obj)using FuncType = std::function<void(_EventArg)>;
template <typename F,
typename = std::enable_if_t<
std::is_constructible<FuncType(F&&)>::value
>>
EventHandler(F&& func)
{
m_ptr = std::make_shared<FuncType>(std::forward<F>(func));
}Context
StackExchange Code Review Q#108366, answer score: 3
Revisions (0)
No revisions yet.