Reputation: 494
I am trying to implement the following pattern and I need some help filling in the blanks:
// In a class, declare (and initialize?) a custom event
class Foo
{
public:
TEvent* Bar = nullptr;
Foo::Foo()
{
Bar = new TEvent(false);
}
private:
// This event trigger method could be called during the lifetime of a Foo object
void Foo::TriggerMethod()
{
// ??? Raise Bar (pun intended :) here
}
};
// In a separate Unit (FMXMainForm):
private:
Foo* myFoobject = nullptr;
// An event handler method
void FMXMainForm::BarTender(TObject* Sender)
{
// Do stuff with event data via Sender
ShowMessage("Bar Mitzvah!");
}
public:
// And when constructing FMXMainForm:
__fastcall FMXMainForm::FMXMainForm(TComponent* Owner)
{
// Create Foo object
myFoobject = new Foo();
// Assign an event handler to myFoobject's Bar event
// ??? myFoobject->Bar = &(this->BarTender); // This doesn't work
}
Can this be done in C++ Builder/FMX? And if so, would it be via TEvent
s? What pieces am I missing? I need some help, please.
Upvotes: 0
Views: 854
Reputation: 595792
Yes, it is quite doable in C++Builder, but it is done a little bit differently than what you have shown, ie utilizing Classes::TNotifyEvent
instead of Syncobjs::TEvent
(which deals only with thread synchronization and nothing to do with object events), eg:
#include <fmx.h>
#include <System.Classes.hpp>
...
// In a class, declare (and initialize?) a custom event
class Foo : public TObject
{
public:
TNotifyEvent Bar = nullptr;
private:
// This event trigger method could be called during the lifetime of a Foo object
void TriggerMethod()
{
if (Bar) Bar(this);
}
};
// In a separate Unit (FMXMainForm):
private:
Foo* myFoobject = nullptr;
// An event handler method
void __fastcall BarTender(TObject* Sender)
{
// Do stuff with event data via Sender
ShowMessage("Bar Mitzvah!");
}
public:
// And when constructing FMXMainForm:
__fastcall FMXMainForm(TComponent* Owner)
{
// Create Foo object
myFoobject = new Foo();
// Assign an event handler to myFoobject's Bar event
myFoobject->Bar = &BarTender;
}
Later on, if you want to add additional parameters to the event, simply replace TNotifyEvent
with a custom type that includes the desired parameters, eg:
#include <fmx.h>
typedef void __fastcall (__closure *TMyEventType)(... param types here ...);
// or:
// using TMyEventType = void __fastcall (__closure *)(... param types here ...);
...
// In a class, declare (and initialize?) a custom event
class Foo : public TObject
{
public:
TMyEventType Bar = nullptr;
private:
// This event trigger method could be called during the lifetime of a Foo object
void TriggerMethod()
{
if (Bar) Bar(... param values here ...);
}
};
// In a separate Unit (FMXMainForm):
private:
Foo* myFoobject = nullptr;
// An event handler method
void __fastcall BarTender(... params here ...)
{
// Do stuff with event data via Sender
ShowMessage("Bar Mitzvah!");
}
public:
// And when constructing FMXMainForm:
__fastcall FMXMainForm(TComponent* Owner)
{
// Create Foo object
myFoobject = new Foo();
// Assign an event handler to myFoobject's Bar event
myFoobject->Bar = &BarTender;
}
Note that the above approach does rely on a number of Embarcadero-specific compiler extensions, namely:
the __fastcall
calling convention.
the __closure
keyword (used in the declaration of TNotifyEvent
), which allows a pointer-to-member-function to point at a member function of any class type, unlike a standard C++ pointer-to-member-function, which is class-specific.
a non-standard operator&
that creates a pointer-to-member-function via an object pointer/reference, which is compatible with __closure
.
the Delphi-based TObject
base class.
If you want a pure C++ solution, try std::function
instead, eg:
#include <functional>
// In a class, declare (and initialize?) a custom event
class Foo
{
public:
std::function<void()> Bar;
private:
// This event trigger method could be called during the lifetime of a Foo object
void TriggerMethod()
{
if (Bar) Bar();
}
};
// In a separate Unit (FMXMainForm):
private:
Foo* myFoobject = nullptr;
// An event handler method
void BarTender()
{
// Do stuff with event data via Sender
ShowMessage("Bar Mitzvah!");
}
public:
// And when constructing FMXMainForm:
__fastcall FMXMainForm(TComponent* Owner)
{
// Create Foo object
myFoobject = new Foo();
// Assign an event handler to myFoobject's Bar event
myFoobject->Bar = std::bind(&FMXMainForm::BarTender, this);
or:
myFoobject->Bar = [this]{ BarTender(); };
}
Or, using additional parameters:
#include <functional>
// In a class, declare (and initialize?) a custom event
class Foo
{
public:
std::function<void(... param types here ...)> Bar;
private:
// This event trigger method could be called during the lifetime of a Foo object
void TriggerMethod()
{
if (Bar) Bar(... param values here ...);
}
};
// In a separate Unit (FMXMainForm):
private:
Foo* myFoobject = nullptr;
// An event handler method
void BarTender(... params here ...)
{
// Do stuff with event data via Sender
ShowMessage("Bar Mitzvah!");
}
public:
// And when constructing FMXMainForm:
__fastcall FMXMainForm(TComponent* Owner)
{
// Create Foo object
myFoobject = new Foo();
// Assign an event handler to myFoobject's Bar event
using namespace std::placeholders;
myFoobject->Bar = std::bind(&FMXMainForm::BarTender, this, ... _1, _2, etc ...);
or:
myFoobject->Bar = [this](... params here ...){
BarTender(... params here ...);
};
}
Upvotes: 3