C++ Lambda Callback - How to capture variables? - c++

I have this class
typedef void(*callback)();
class Button : public Control
{
private:
callback onClickCallback;
public:
void bind(callback func)
{
onClickCallback = func;
}
void onClick()
{
onClickCallback();
}
};
Button inherits Control which is mapped as a component in the Gui class.
The Gui class has a method which will call the onClick method of every component on the list. The onClick method callback is lambda expression
Gui gui;
gui.addControl<Button>(x, y, width, height).bind([] {
MessageBox(NULL, "OK", "OK", 0);
//How to capture gui
//Eg add another button on click:
//gui.addControl<Button>(x, y, width, height);
});
Here is the gui class
class Gui
{
private:
int index;
std::unordered_multimap<int, Control*> controls;
public:
template <typename T, typename ...Args>
T& addControl(Args... args)
{
auto control = new T{ std::forward<Args>(args)... };
std::pair<int, Control*> pair(index, control);
controls.insert(pair);
index++;
return *control;
}
void processMessages()
{
for (auto it = controls.begin(); it != controls.end(); ++it)
{
it->second->onClick();
}
}
};

I actually did it, i don't know if this is the right solution but it seems to work.
Updated the Button class.
class Button : public Control
{
private:
std::function<void()> onClickCallback;
public:
//Override
void bind(std::function<void()> func)
{
onClickCallback = func;
}
//Override
void onClick()
{
onClickCallback();
}
};
It works now !
Also added support for different events eg mouse_click, on_focus_gained etc...
gui.addControl<Button>(hWnd, "Button", 3, 26, 70, 23).bind("mouse_click", [&] {
gui.getControl<Button>(0).setText("Clicked");
});

Related

How to pass pointer to a member within class to outer class?

I am trying to pass a pointer to memeber of class(Dialog::handler) from its method(in scope of Dialog::render) to some outer method(Button::OnClick).
Here is a small example:
class Button
{
public:
void OnClick(void (*handler)())
{
handler();
}
};
class Dialog
{
public:
void handler()
{
//do stuff
}
void render()
{
auto button = new Button;
//Source of problem
button->OnClick(this->*handler);
}
};
But compiler shows error:
non-standard syntax; use '&' to create a pointer to member
Also I triend other combinations, like:
this->handler.
&this.handler.
this.*handler.
etc
But obviously they failed.
You could use std::function and pass it a lambda in which you've caught this of the object you'd like to call back:
#include <functional>
#include <iostream>
class Button {
public:
void OnClick(std::function<void()> handler) {
handler();
}
};
class Dialog {
public:
void handler() {
std::cout << "Dialog::handler\n";
}
void render() {
auto button = new Button;
// a lambda catching "this" Dialog.
button->OnClick([this] { this->handler(); });
delete button; // you didn't delete your button
}
};
int main() {
Dialog d;
d.render();
}
But it looks like you should probably inherit from a common base class that has a virtual void handler() so you can pass object pointers/references around instead. A rough idea:
#include <iostream>
class VisualBase {
public:
virtual void handler() = 0;
virtual ~VisualBase() = 0;
};
VisualBase::~VisualBase() {}
class Button : public VisualBase {
public:
void handler() override {}
void OnClick(VisualBase* caller) {
caller->handler();
}
};
class Dialog : public VisualBase {
public:
void handler() override {
std::cout << "Dialog::handler\n";
}
void render() {
Button button;
button.OnClick(this);
}
};
int main() {
Dialog d;
d.render();
}

Central Event Dispatcher with Templated Event types in C++

I've been learning C++ and trying to implement various idioms from other languages I'm familiar with.
Recently I've tried to implement a central EventDispatcher which multiple objects can register events that can take N number of arguments, and a single call to EventDispatcher::dispatch(event_name) would call all registered event callbacks.
First I created a template class for Event;
template <typename ..._args>
class Event
{
public:
//Alias for template callback
using _callback = std::function<void(_args...)>;
//Ctor & Dtor
explicit Event(std::string const& name, _callback const& cb) : _name(name), _cbFunc(cb) {}
~Event() {}
//Accessors
std::string const& getName() const { return this->_name; }
//Methods
void trigger(_args... a) { this->_cbFunc(a...); }
private:
//Event identifier
std::string _name;
//Event callback - can't be changed inside event.
_callback const _cbFunc;
};
With this structure I can define events like in the following example:
void testCallback(int a) {
std::cout << a << std::endl;
}
Event<int> _event("TestEvent", std::bind(&testCallback, _1));
_event.trigger(20); //This will actually output 20 to the console.
What I want to do next is, have an EventDispatcher class which should be defined like:
class EventDispatcher
{
public:
EventDispatcher();
~EventDispatcher();
void registerEvent(**Event of any type**);
void dispatchEvent(std::string const& eventName, ....args);
private:
std::map<std::string, std::vector<**Event of any type**>> _eventList;
};
so that I can make calls like this;
EventDispatcher _dispatcher;
_dispatcher.registerEvent(_event); //event defined in the previous example.
_dispatcher.dispatchEvent("TestEvent", 20); //this should call event's trigger() method with arguments passed to it actually.
However I couldn't figure out how to implement a central dispatcher and have it be able to register templatized event instances, and then pass variable number of arguments to dispatchEvent(...) and make it trigger all events in a vector of templatized events.
How can I achieve such functionality? Is my thought proccess correct or far from c++ way of implementing such a system? Any tips would be welcome.
I suggest a solution with an abstract base class (IEvent) for the event class (Event)
and a template dispatcher method with variadic arguments and a dynamic_cast.
Apstrac base class with the abstract method getName:
class IEvent
{
public:
virtual const std::string & getName() const = 0;
};
Your event class derived from IEvent:
template <typename ..._args>
class Event : public IEvent
{
public:
//Alias for template callback
using _callback = std::function<void(_args...)>;
//Ctor & Dtor
//template< typename T_CB >
explicit Event( const std::string & name, const _callback & cb ) : _name(name), _cbFunc(cb) {}
~Event() {}
//Accessors
virtual const std::string & getName() const override { return this->_name; }
//Methods
void trigger(_args... a) { this->_cbFunc(a...); }
private:
//Event identifier
std::string _name;
//Event callback - can't be changed inside event.
_callback const _cbFunc;
};
The dispatcher with the template method:
class EventDispatcher
{
public:
EventDispatcher() {}
~EventDispatcher()
{
for ( auto el : _eventList )
{
for ( auto e : el.second )
delete e;
}
}
void registerEvent( IEvent *event )
{
if ( event )
_eventList[event->getName()].push_back( event );
}
template <typename ..._args>
void dispatchEvent( const std::string & eventName, _args...a )
{
auto it_eventList = _eventList.find( eventName );
if ( it_eventList == _eventList.end() )
return;
for ( auto ie : it_eventList->second )
{
if ( Event<_args...> * event = dynamic_cast<Event<_args...>*>( ie ) )
event->trigger( a... );
}
}
private:
std::map<std::string, std::vector<IEvent*>> _eventList;
};
And finally the application:
void testCallback(int a) {
std::cout << a << std::endl;
}
int main()
{
EventDispatcher eventDisp;
eventDisp.registerEvent( new Event<int>( "TestEvent", &testCallback ) );
eventDisp.dispatchEvent( "TestEvent", 20 );
return 0;
}

Setting function pointer inside class

Im trying to create a simple windows api.
I need a little help in setting a function pointer in side class
Im getting errors trying to set the function to a function that is inside a class, Is there anyway this can be done without using static in the class?
class Window
{
public:
WIN_API Window();
struct WIN_API
{
bool(*OnMouseClick)(Vector2D position, MouseButton button);
bool(*OnCreate)();
}Event;
};
class MainWindow : public Window
{
public:
MainWindow() : Window()
{
//error here
this->Event.OnCreate = OnCreate;
this->Event.OnMouseClick = OnMouseClick;
}
bool OnCreate()
{
return true;
}
bool OnMouseClick(Vector2D position, MouseButton button)
{
return true;
}
};
You have to bind the member function of a particular instance. This is best done using a lambda and std::function objects.
class Window
{
public:
Window();
struct WIN_API
{
std::function<bool(Vector2D, MouseButton)> OnMouseClick;
std::function<bool()> OnCreate;
} Event;
};
class MainWindow : public Window
{
public:
MainWindow()
{
this->Event.OnCreate =
[this] () { return this->OnCreate(); };
this->Event.OnMouseClick =
[this] (Vector2D position, MouseButton button)
{ return this->OnMouseClick(position, button); };
}
bool OnCreate()
{
return true;
}
bool OnMouseClick(Vector2D position, MouseButton button)
{
return true;
}
};

A list with the same type as the class it's contained within

I'm trying to create a simple events system, which will have many different events. So, I've tried to create an event class which allows you to register functions, taking the correct type of event, and returning a boolean.
What I want is that the method post in any subclass of Event will take that subclass rather than Event, and the functions in the list listeners in each subclass should take the correct subclass type. Here's the code I already have, which forces the function to cast to the correct event type:
events.h:
namespace events {
class Event {
public:
static const std::List<bool (*)(Event)> listeners;
void post(Event event);
}
class ExampleEvent : Event {
int eventData;
}
}
events.cpp:
namespace events {
void Event::post(Event event) {
for(int i = 0; i < listeners.size(); i++) {
if(listeners[i](event)) return;
}
}
}
Is there some way I can get this to work with subclassed events without having to do the following?
bool handleExample(Event event) {
ExampleEvent exampleEvent = (ExampleEvent)event;
std::cout << exampleEvent.eventData << std::endl;
return false;
}
// Somewhere else in the code
ExampleEvent::listeners.push_back(&handleExample);
I apologise for any incorrect code, I don't quite have the rules of the language perfect yet.
The common way is to use CRTP:
namespace events {
template<typename Derived>
class Event {
public:
static const std::list<bool (*)(Derived)> listeners;
void post(Derived event)
{
static_cast<Derived&>(*this).post(event);
}
};
class ExampleEvent : Event<ExampleEvent> {
int eventData;
void post(ExampleEvent event)
{
//implement post
}
};
}
Just use virtual functions:
namespace events {
class EventHandler {
public:
static const std::list<Event*> listeners;
void post() {
for (Event * listener : listeners) {
if (listener->post()) break;
}
}
};
class BaseEvent {
public:
virtual bool post() = 0;
virtual ~BaseEvent() {}
};
class ExampleEvent : public BaseEvent { // use public inheritance
int eventData;
public:
virtual bool post() override {
if (eventData == 0) return true;
return false;
}
};
}

Saving a function pointer in member of a class

I am making a menu bar that takes in menu items. I want the window that holds the menu bar to decide what the behaviour of a menu item will be. So I want the window to pass a function to the menu item.
My best attempt is getting me this error:
error: no matching function for call to 'MenuItem::setBehaviour(void (MyWindow::*)())'
Here is MenuItem.h:
class MenuItem{
public:
typedef void (sf::RenderWindow::*function_type)();
MenuItem(sf::RenderWindow* win); // window that holds the menu bar
void setBehaviour(function_type f); // I want to be able to change the behaviour
// to reuse the menu item
void action(); // use the function
private:
sf::RenderWindow* m_window;
function_type m_function;
};
MenuItem.cpp
MenuItem::MenuItem(sf::RenderWindow* win) : m_window(win)
{
//ctor
}
void MenuItem::setBehaviour(function_type f)
{
m_function = f;
}
void MenuItem::action()
{
(m_window->*m_function)();
}
The window that adds the menu bar:
class MyWindow : public sf::RenderWindow
{
//...
void close();
};
And the window's cpp file:
MyWindow::MyWindow() : sf::RenderWindow(...)
{
//...
MenuItem item(this);
item.setBehaviour(&MyWindow::close); // error!
//...
}
//...
void MyWindow::close()
{
this->close();
}
What you could try to solve that is to make MenuItem a templated class:
template<typename RenderWindow>
class MenuItem{
public:
typedef void (RenderWindow::*function_type)();
MenuItem::MenuItem(RenderWindow* win)
: m_window(win) {
std::static_assert(
std::is_base_of<sf::RenderWindow,RenderWindow>()
,"RenderWindow must be derived from sf::RenderWindow.");
}
void setBehaviour(function_type f) {
m_function = f;
}
void action() {
m_window->*m_function)();
}
private:
RenderWindow* m_window;
function_type m_function;
};
Omit the MenuItem.cpp file completely.
You can instantiate and access a MenuItem like
MyWindow myWindow;
MenuItem<MyWindow> closeWindow(myWindow);
menuItem.setBehavior(MyWindow::close);
// ...
menuItem.action();
then.
You could use std::function for that:
// MenuItem.h
#include <functional>
class MenuItem
{
public:
using function_type = std::function<void()>;
auto setBehaviour(function_type f) -> void;
auto action() -> void;
private:
function_type m_function;
};
// MenuItem.cpp
auto MenuItem::setBehaviour(function_type f) -> void
{
m_function = f;
}
auto MenuItem::action() -> void
{
m_function();
}
// MyWindow.cpp
#include <functional>
MyWindow::MyWindow() : sf::RenderWindow(...)
{
MenuItem item(this);
item.setBehaviour(std::bind(&MyWindow::close, this));
}
auto MyWindow::close() -> void
{
// do something
}
The two answers given were over my head a little. I spent some time looking at Jan's std::function and learned a little more in the process. Here's what I did wrong:
The functions for MenuItem will come from MyWindow, not sf::RenderWindow, so I need to forward declare MyWindow and change all the sf::RenderWindows to MyWindow.
class MyWindow; // fix: forward declaration
class MenuItem{
public:
typedef void (MyWindow::*function_type)(); // fix
MenuItem(MyWindow* win); // fix
void setBehaviour(function_type f);
void action();
private:
MyWindow* m_window; // fix
function_type m_function;
};
MenuItem.cpp
MenuItem::MenuItem(MyWindow* win) : m_window(win) // fix
{
//ctor
}