同时处理非成员函数指针和成员函数指针
Handle non-member function pointer and member function pointer at the same time
我想做一个每 X 秒调用一次的成员函数。做了一个可以处理非成员函数的小原型,不知道自己做的好不好,不能同时处理成员函数和非成员函数
我有一个 Event
对象,它处理函数和延迟,带有一个基本计时器,以检测我们何时需要 运行 函数:
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;
}
}
};
然后,我有另一个对象将每个帧的每个函数调用到 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
}
};
如您所见,我有一个函数 Schedule
可以注册新的 Event
。但是现在,它只处理非成员函数。有没有一种方法可以处理非成员函数和成员函数,不仅来自 Handler
,而且还来自 所有其他对象 ?
如果没有办法,我该如何实现?
更新 1
在下面添加了 "call any object's members"。
简介
我建议使用 std::function
和 std::bind
。但提醒一下,由于内部机制,std::function有一些开销!
std::function 非常强大,因为你可以在里面存储很多东西。
重要:
仅使用 function-pointer 方法是可能的,但如果您必须保留简单的统一界面,则会导致一些代码和复杂性。
例子
#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;
}
}
};
如您所见,包括 <functional>
header 将为您提供模板 std::function<R(Args...)>
,其中 R 是 return 类型和 Args... 一个逗号完全限定参数类型的分隔列表。
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 */ }
};
这样你现在几乎可以绑定任何你想要的东西。 :D
更新:
不能为具有匹配 public 方法的任何其他类型调用 Schedule-function,例如:
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);
}
资源
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
工作示例
使用 std::function
是正确的方法。任何可以调用的东西都可以 transformed/wrapped 变成 std::function
.
在您的情况下,您可以像这样编写事件构造函数:
Event(std::function<void(float)>, double delay);
您可以使用独立函数、函子或 lambda 调用它。
一些例子:
// declaration
auto myDummyFunction (float) -> void;
// Calling the constructor
auto event = Event(myDummyFunction,1.0);
如果我们想传递一个成员函数,只需要使用一个lambda:
// declaration of the class with the member function
class SomeOtherClass
{
public:
auto someMethod(float) -> void;
};
// Calling the constructor
auto someOtherClass = SomeOtherClass{};
auto event = Event([&someOtherClass](float f){someOtherClass.someMethod(v)},1.0);
总的来说,我发现 lambda 比 std::bind 方法更具可读性和灵活性。据我所知,建议(是 Herb 还是 Scott?)不要再使用 std::bind,而是改用 lambda。
我想做一个每 X 秒调用一次的成员函数。做了一个可以处理非成员函数的小原型,不知道自己做的好不好,不能同时处理成员函数和非成员函数
我有一个 Event
对象,它处理函数和延迟,带有一个基本计时器,以检测我们何时需要 运行 函数:
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;
}
}
};
然后,我有另一个对象将每个帧的每个函数调用到 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
}
};
如您所见,我有一个函数 Schedule
可以注册新的 Event
。但是现在,它只处理非成员函数。有没有一种方法可以处理非成员函数和成员函数,不仅来自 Handler
,而且还来自 所有其他对象 ?
如果没有办法,我该如何实现?
更新 1 在下面添加了 "call any object's members"。
简介
我建议使用 std::function
和 std::bind
。但提醒一下,由于内部机制,std::function有一些开销!
std::function 非常强大,因为你可以在里面存储很多东西。
重要: 仅使用 function-pointer 方法是可能的,但如果您必须保留简单的统一界面,则会导致一些代码和复杂性。
例子
#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;
}
}
};
如您所见,包括 <functional>
header 将为您提供模板 std::function<R(Args...)>
,其中 R 是 return 类型和 Args... 一个逗号完全限定参数类型的分隔列表。
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 */ }
};
这样你现在几乎可以绑定任何你想要的东西。 :D
更新: 不能为具有匹配 public 方法的任何其他类型调用 Schedule-function,例如:
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);
}
资源
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
工作示例
使用 std::function
是正确的方法。任何可以调用的东西都可以 transformed/wrapped 变成 std::function
.
在您的情况下,您可以像这样编写事件构造函数:
Event(std::function<void(float)>, double delay);
您可以使用独立函数、函子或 lambda 调用它。 一些例子:
// declaration
auto myDummyFunction (float) -> void;
// Calling the constructor
auto event = Event(myDummyFunction,1.0);
如果我们想传递一个成员函数,只需要使用一个lambda:
// declaration of the class with the member function
class SomeOtherClass
{
public:
auto someMethod(float) -> void;
};
// Calling the constructor
auto someOtherClass = SomeOtherClass{};
auto event = Event([&someOtherClass](float f){someOtherClass.someMethod(v)},1.0);
总的来说,我发现 lambda 比 std::bind 方法更具可读性和灵活性。据我所知,建议(是 Herb 还是 Scott?)不要再使用 std::bind,而是改用 lambda。