patterncppMinor
Callback system for events in a window
Viewed 0 times
eventssystemcallbackforwindow
Problem
I have written a simple window event capture wrapper that gets events that happen to the window, and can be checked anywhere in the program through a global class. I wanted to be able to create a callback anywhere in the program with any event that I have asked to be checked. To hold the data that is returned from the events, I have used a pointer that is allocated whenever an event occurs, so I can grab the data returned later on in the program.
I don't want to use templates to hold the data since that would require a lot of objects to be created, or a lot of vectors for each specific event that I want. I find this to be too clunky, and I wanted a clean, simple solution.
Data that is captured
Window Event Manager
Grabbing and setting the pointer
```
void windowEventManager::pollEvent(sf::Event event)
{
for (auto &eventCheck : _subscribedEvents)
{
if (eventCheck.event == event.type)
{
switch (event.type)
{
case sf::Event::KeyPressed:
case sf::Event::KeyReleased:
eventCheck.data = new sf::Event::KeyEvent(event.key);
break;
case sf::Event::MouseButtonPressed:
case sf::Event::MouseButtonReleased:
I don't want to use templates to hold the data since that would require a lot of objects to be created, or a lot of vectors for each specific event that I want. I find this to be too clunky, and I wanted a clean, simple solution.
Data that is captured
struct windowEventData
{
void *data = nullptr;
std::string id;
sf::Event::EventType event;
bool polled = false;
};Window Event Manager
class windowEventManager
{
private:
std::vector _subscribedEvents;
public:
void subscribe(const std::string &id, sf::Event::EventType event);
void pollEvent(sf::Event event);
void clearEvents();
template
T* hasEventPolled(const std::string &id);
~windowEventManager();
};Grabbing and setting the pointer
```
void windowEventManager::pollEvent(sf::Event event)
{
for (auto &eventCheck : _subscribedEvents)
{
if (eventCheck.event == event.type)
{
switch (event.type)
{
case sf::Event::KeyPressed:
case sf::Event::KeyReleased:
eventCheck.data = new sf::Event::KeyEvent(event.key);
break;
case sf::Event::MouseButtonPressed:
case sf::Event::MouseButtonReleased:
Solution
delete eventCheck.data;This line invokes undefined behavior, because you aren't allowed to call
delete on an incomplete type (and certainly not on void). Both GCC and Clang warn on this line:(clang++)
prog.cc:4:5: warning: cannot delete expression with pointer-to-'void' type 'void *' [-Wdelete-incomplete]
delete p;
^ ~
(g++)
prog.cc:4:12: warning: deleting 'void*' is undefined
delete p;
^Anyway, it won't do what you want (whatever that might be).
For an open set of types, the type-safe way to do what you're trying to do is to use type erasure, as seen in library containers such as
std::function. (Shameless plug for my CppCon 2014 talk — type erasure is covered starting at 23m00s.) This kind of type-erasure is easy to implement yourself from scratch in about 20 lines of code.For a closed set of types, you might see whether your compiler of choice supports C++17
std::variant and/or Boost boost::variant; you can use the visit function to perform an operation on a variant in a type-safe way, and of course you wouldn't even need delete at that point because you wouldn't be using pointers, just plain old value semantics. If you can't use Boost, then I don't recommend trying to implement variant yourself.Code Snippets
delete eventCheck.data;(clang++)
prog.cc:4:5: warning: cannot delete expression with pointer-to-'void' type 'void *' [-Wdelete-incomplete]
delete p;
^ ~
(g++)
prog.cc:4:12: warning: deleting 'void*' is undefined
delete p;
^Context
StackExchange Code Review Q#144515, answer score: 3
Revisions (0)
No revisions yet.