C++ Event class
From the desire of having a callback function which includes a void* userdata argument, I've made this generic event class.
It is tested and working, but some desired features are missing
- Allow to have a parameter less event with no userdata
- Allow to specify a TResult template argument, for example to have operator() returns bool instead of void.
Also I do not like the fact that
- TUserData is turned into a pointer for _userdata and into a reference for the callback function argument. It does not seems intuitive enough to use.
The problem with adding a TResult, is that the operator() function will
- Not compile, either because TResult is void and it tries to return a value, or because TResult is not void and it does try to return something.
- Have to return a default value when _func is null.
I've looked into template partial specialization but it does not seem to be a solution. Or does it?
Would using concepts be of any help?
Any generic improvements to be made?
event.hpp
#pragma once
namespace shoujin {
template<typename TUserData, typename... TArguments>
class Event {
using TFunc = void (*)(TArguments..., TUserData&);
TFunc _func;
TUserData* _userdata;
public:
Event() :
_func{}, _userdata{}
{}
Event(TFunc func, TUserData& userdata)
{
_func = func;
_userdata = &userdata;
}
Event& operator=(const Event& rhs)
{
_func = rhs._func;
_userdata = rhs._userdata;
return *this;
}
~Event()
{
_func = nullptr;
_userdata = nullptr;
}
void operator()(TArguments... args) const
{
if(_func)
_func(args..., *_userdata);
}
operator bool() const
{
return _func != nullptr;
}
};
}
event_test.cpp
#include "CppUnitTest.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <tchar.h>
#include <shoujin/event.hpp>
using namespace shoujin;
TEST_CLASS(EventTest) {
static void OnEventTwoParam(int x, int y, int& value)
{
value = x + y;
}
public:
TEST_METHOD(Event_IsCopyConstructible) {
Assert::IsTrue(std::is_copy_constructible_v<Event<void*>>);
}
TEST_METHOD(Event_IsCopyAssignable) {
Assert::IsTrue(std::is_copy_assignable_v<Event<void*>>);
}
TEST_METHOD(Event_IsMoveConstructible) {
Assert::IsTrue(std::is_move_constructible_v<Event<void*>>);
}
TEST_METHOD(Event_IsMoveAssignable) {
Assert::IsTrue(std::is_move_assignable_v<Event<void*>>);
}
TEST_METHOD(Event_WhenTwoParam_EventRaised) {
//Arrange
int value;
Event<int, int, int> event_two_param(OnEventTwoParam, value);
int x{3}, y{2};
//Act
event_two_param(x, y);
//Assert
Assert::AreEqual(x + y, value);
}
TEST_METHOD(Event_CopyAssignment_EventRaised) {
//Arrange
int value;
Event<int, int, int> event_two_param;
int x{3}, y{2};
event_two_param = {OnEventTwoParam, value};
//Act
event_two_param(x, y);
//Assert
Assert::AreEqual(x + y, value);
}
TEST_METHOD(Event_OperatorBool_EventRaised) {
//Arrange
int value;
Event<int, int, int> event_two_param;
int x{3}, y{2};
//Act
bool before = event_two_param;
event_two_param = {OnEventTwoParam, value};
bool after = event_two_param;
//Assert
Assert::IsFalse(before);
Assert::IsTrue(after);
}
};
All criticism is much appreciated.