前言
得益于C++11的新特性,cocos 3.x版本在很多地方的代码看起来都优美了许多。这其中就包括一些回调函数的写法,CC_CALLBACK_N
系列宏的作用是对一个成员函数进行适配并返回一个回调函数。本文介绍一下我对CC_CALLBACK_N
系列宏的理解。
使用CC_CALLBACK_N
的例子
下面这段代码来自cocos官方示例中的ActionTest.cpp,这是在创建一个CallFunc的回调。
使用CC_CALLBACK_0
来代替其原来的创建回调的方式:
使用CC_CALLBACK_0
来改写上面三个回调的创建:
@H_
403_24@
auto action1 = Sequence::create(
MoveBy::create(
2,Vec2(
200,
0)),
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback1,
this)),CallFunc::create(
[&](){
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF(
"called:lambda callback",
"fonts/Marker Felt.ttf",
16.0f);
label->setPosition(s.width/
4*
1,s.height/
2-
40);
this->addChild(label);
} ),
nullptr);
auto action2 = Sequence::create(
ScaleBy::create(
2,
2),FadeOut::create(
2),
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback2,
this,
nullptr);
auto action3 = Sequence::create(
RotateBy::create(
3,
360),
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback3,
42)),
nullptr);
void ActionCallFunction::callback1()
{
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF(
"callback 1 called",
16.0f);
label->setPosition(s.width/
4*
1,s.height/
2);
addChild(label);
}
void ActionCallFunction::callback2(Node* sender)
{
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF(
"callback 2 called",
16.0f);
label->setPosition(s.width/
4*
2,s.height/
2);
addChild(label);
CCLOG(
"sender is: %p",sender);
}
void ActionCallFunction::callback3(Node* sender,
long data)
{
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF(
"callback 3 called",
16.0f);
label->setPosition(s.width/
4*
3,s.height/
2);
addChild(label);
CCLOG(
"target is: %p,data is: %ld",sender,data);
}
如何理解CC_CALLBACK_0
,CC_CALLBACK_1
,CC_CALLBACK_2
,CC_CALLBACK_3
这个CC_CALLBACK_0
其实就是std::bind,下面是它和它的小伙伴们:
defined in ccMacro.h
@H_
403_24@
using std::bind;
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
#define CC_CALLBACK_0(__selector__,__target__,...) bind(&__selector__,##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,_1,##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,_2,##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,_3,##__VA_ARGS__)
为了让这几个宏看起来更清晰,在上面我使用了using声明整理了一下代码。
这四个宏的作用都是用来适配函数,把一个原始函数A,包装成函数B。这里面的A需要是一个类的成员函数,其中的:
__selector__
就是这个成员函数,比如MyClass::func;
__target__
是MyClass类型的一个对象(或者是对象的引用和指针,比如最常见的this)
其它的参数,或者是占位符(_1,_3
)或者是具体的参数,具体的细节请参考我的这篇【C++ STL学习与应用总结】22: 函数组合之1:如何使用std::bind.
如何理解这几个参数的命名呢?为什么是0,1,2,3?
是这样的,结尾的数字N,代表者CC_CALLBACK_N
这个宏返回的结果是一个需要N个参数的函数。
CC_CALLBACK_<font color="red">0</font>
意思就是返回一个需要0个参数方可调用的函数,也就是说不需要参数就能调用的函数
CC_CALLBACK_<font color="red">1</font>
意思就是返回一个需要1个参数方可调用的函数
CC_CALLBACK_<font color="red">2</font>
意思就是返回一个需要2个参数方可调用的函数
CC_CALLBACK_<font color="red">3</font>
意思就是返回一个需要3个参数方可调用的函数
这里需要的参数个数其实也就是占位符的个数(_1,_3
),占位符是需要在函数调用的时候用具体的实参来替换的。
这样理解了之后,就很容易知道什么时候该用哪个宏了。
比如,我要创建一个CallFunc,static CallFunc * create(const std::function<void()>& func);
,从其声明可以看出它需要一个不用参数就能调用的函数,那么我就可以用CC_CALLBACK_0
。
@H_
403_24@auto action2
= Sequence
::create(
ScaleBy
::create(
2,FadeOut
::create(
2),原来的方式
CallFunc
::create(CC_CALLBACK_0(ActionCallFunction
::callback2,nullptr);
void ActionCallFunction
::callback2(Node
* sender)
{
auto s
= Director
::getInstance()
->getWinSize();
auto label
= Label
::createWithTTF(
"callback 2 called",
16.0f);
label
->setPosition(s
.width/
4*2,s
.height/
2);
addChild(label);
CCLOG(
"sender is: %p",sender);
}
即使callback2接收N个参数,我也可以使用CC_CALLBACK_0
来把它适配成一个不需要参数就能调用的函数。这是std::bind的工作方式决定的,我只需把callback2需要的参数填入CC_CALLBACK_0
里面就好。
@H_
403_24@auto action2 = Sequence::create(
ScaleBy::create(
2,CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback5,
100,
200,
11,
12,
23)),nullptr);
void ActionCallFunction::callback5(int i1,int i2,int i3,int i4,int i5)
{
//
...
}
CC_CALLBACK_1
在CallFuncN::create中的使用
CallFuncN::create的原型:
static CallFuncN * create(const std::function<void(Node*)>& func);
从create的参数可以看到,它需要一个“需要一个Node*参数的参数方可调用的函数”, 这刚好和前面讲到的CC_CALLBACK_1
的作用一样。
@H_
403_24@
void ActionCallFuncND
::onEnter()
{
auto action
= Sequence
::create(
MoveBy
::create(
2.0f,CallFuncN
::create( CC_CALLBACK_1(ActionCallFuncND
::doRemoveFromParentAndCleanup,
true)),nullptr);
auto action2
= Sequence
::create(
MoveBy
::create(
2.0f,CallFuncN
::create(std
::bind(
&ActionCallFuncND
::doRemoveFromParentAndCleanup,std
::placeholders::_1,nullptr);
_grossini
->runAction(action2);
}
void ActionCallFuncND
::doRemoveFromParentAndCleanup(Node
* sender,bool cleanup)
{
_grossini
->removeFromParentAndCleanup(cleanup);
}
CC_CALLBACK_2
,CC_CALLBACK_3
的使用方式与此类似,不再赘述。
作者水平有限,对相关知识的理解和总结难免有错误,还望给予指正,非常感谢!
在这里也能看到这篇文章:github博客,CSDN博客,欢迎访问