I want to do a member function that will call every X seconds. I did a little prototype that can handle non member function, but I don't know if I did it well, and I can't handle both member function and non member function.
I have an Event
object, which handle the function and the delay, with a basic timer, to detect when we need to run the function:
typedef void (*ScheduleFunction)(float dt);
class Event
{
private:
ScheduleFunction m_Func;
double m_Timer;
double m_Delay;
public:
Event(ScheduleFunction function, double delay)
{
m_Func = function;
m_Delay = delay;
}
void Call(float dt)
{
m_Timer += dt;
if (m_Timer >= m_Delay)
{
m_Func(dt);
m_Timer = 0.0;
}
}
};
Then, I have another object that call every frames each function into a vector<Event>
:
class Handler
{
private:
void m_MemberFunction(float dt)
{
std::cout << "A member function." << std::endl;
}
std::vector<Event> m_ScheduleItems;
public:
Handler()
{
// This will not compile, because the function expect a non member function
Schedule(&Handler::m_MemberFunction, 1.0);
}
void CallScheduledFunctions(float dt)
{
for (std::vector<Event>::iterator it = m_ScheduleItems.begin(); it != m_ScheduleItems.end(); ++it)
{
it->Call(dt);
}
}
void Schedule(ScheduleFunction func, double delay)
{
Event event(func, delay);
m_ScheduleItems.push_back(event);
}
void Unschedule()
{
// TODO
}
};
As you can see, I have a function Schedule
that register new Event
. But right now, it only handle non member function. Is there a way that I can handle non member function and member function, not only from the Handler
but also on all others objects?
If there is no way, how can I achieve this?
UPDATE 1 Added "call any object's members" below.
BRIEF
I recommend using std::function
and std::bind
. But remind that std::function has some overhead due to the internal mechanisms!
std::function is very powerful as there are many things you can store in it.
Important: Using a function-pointer only approach is possible, but would cause some code and complexity if you must retain the simple unified interface.
EXAMPLE
#include <functional>
using ScheduleFunction_t = std::function<void(float)>;
class Event {
private:
ScheduleFunction_t
m_Func;
double
m_Timer,
m_Delay;
public:
Event(
ScheduleFunction_t const&function,
double delay)
: m_Func(function)
, m_Delay(delay)
{ }
void Call(float dt) {
m_Timer += dt;
if (m_Timer >= m_Delay)
{
// Important, if you do not assert in the constructor, check if the fn is valid...
// The ctr shouldn't throw on runtime assert fail... memory leak and incpomplete construction...
if(m_Func)
m_Func(dt);
m_Timer = 0.0;
}
}
};
As you can see, including the <functional>
header will give you the template std::function<R(Args...)>
, where R is the return type and Args... a comma separated list of fully qualified argument types.
void g_freeFunction(float f) {
std::cout << "Globally floating for " << f << "ms" << std::endl;
}
class Handler {
private:
void m_MemberFunction(float dt) {
std::cout << "Floating around with " << dt << " m/s" << std::endl;
}
std::vector<Event> m_ScheduleItems;
public:
Handler() {
// Bind member function
Schedule<Handler, &Handler::m_MemberFunction>(this);
// Or free
Schedule(&g_freeFunction);
// Or lambda
Schedule([](float f) -> void { std::cout << "Weeeeeeeh...." << std::endl; });
}
void CallScheduledFunctions(float dt)
{
for(Event& e : m_ScheduleItems)
e.Call(dt);
}
template <typename TClass, void(TClass::*TFunc)(float)>
void Schedule(
TClass *const pInstance,
double delay = 0.0)
{
m_ScheduleItems.emplace_back(std::bind(TFunc, pInstance, std::placeholders::_1), delay); // Create in place at the end of vector.
}
void Schedule(
ScheduleFunction_t fn,
double delay = 0.0)
{
m_ScheduleItems.emplace_back(fn, delay); // Create in place at the end of vector.
}
void Unschedule() { /* TODO */ }
};
This way you can now bind almost whatever you want. :D
Update: The Schedule-function can not be called for any other type that has a matching public method, e.g.:
struct Test {
void foo(float f) {
std::cout << "TEST ME!" << std::endl;
}
};
int main()
{
Test t={};
Handler h = Handler();
h.Schedule<Test, &Test::foo>(&t);
for(uint32_t k=0; k < 32; ++k)
h.CallScheduledFunctions(k);
}
RESOURCES
http://en.cppreference.com/w/cpp/utility/functional http://en.cppreference.com/w/cpp/utility/functional/function http://en.cppreference.com/w/cpp/utility/functional/bind
WORKING EXAMPLE