Overloading issues (cocos2d-2.0-rc2-x-2.0.1)

The recent changes in cocos2d-2.0-rc2-x-2.0.1 to change all the static node creation methods, like CCRotateTo::actionWithDuration(a,b) to CCRotateTo::create(a,b) has had some bad consequences for lua binding. Now that there are many methods called create(), create(x), create(x,y, …) etc. overloaded there are issues with the tolua binding.

tolua overloading is not smart, it only does overloading by attempting to call the last registered function for a class, if that function has an error then it tries the next one. The issue is that in the LuaCocos2d.cpp it attempts to see if your function signatures are correct by calling tolua_isusertype to determine whether the next overloaded function should be tried.

The easiest way to see this problem is with CCSpawn. In lua if you do something like the following it will crash.

local testSpawn = CCSpawn:create(CCRotateTo:create(1,1), CCScaleTo:create(2,2))

This is because the return types in CCAction.pkg for CCRotateTo and CCScaleTo look like this:

static CCRotateTo* create(float duration, float fDeltaAngle);

and

static CCScaleTo* create(float duration, float s);

Then for CCSpawn:create in the tolua bindings in LuaCocos2d.cpp when it checks tolua_isusertype, it checks for “CCFiniteTimeAction” because CCSpawn inside of CCAction.pkg looks like this:

static create(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2);

That check fails and then the binding attempt to call the wrong CCSpawn:create function causing a crash.

Hi, do you think this issue only happens in rc2-2.0.1? Did the cocos2d-x 1.0.1 also exist this bug? The parameters are the same between v1.0.1 and v2.0.

In 1.0.1, we have :

static CCFiniteTimeAction* actions(CCFiniteTimeAction *pAction1, ...);
static CCFiniteTimeAction* actionWithArray(CCArray *arrayOfActions);
static CCSpawn* actionOneTwo(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2);

2.0.1:

static CCFiniteTimeAction* create(CCFiniteTimeAction *pAction1, ...);
static CCFiniteTimeAction* create(CCArray *arrayOfActions);
static CCSpawn* create(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2);

btw, do you have a good solution about this ?

Hi James,

The issue is because now there is 2 create methods now, and tolua++ doesn’t do a very nice job of figuring out the function overloading. In 1.0.1 there are 2 different function names so there is no function overloading and each function can be called properly when it is supposed to be.

From the toLua*+ documentation:

Overloaded functions are accepted. Remember that the distinction between two functions with the same name is made based on the parameter types that are mapped to Lua. So, although
void func ;
void func ;
>
represent two different functions in C*+, they are the same function for tolua, because both int and double are mapped to the same Lua type: number.

Are we running in to the same situation here, where we have overloaded methods but different cocos2d-x data types mapped to the same Lua data type?

James Chen wrote:

Hi, do you think this issue only happens in rc2-2.0.1? Did the cocos2d-x 1.0.1 also exist this bug? The parameters are the same between v1.0.1 and v2.0.
>
In 1.0.1, we have :
[…]
>
2.0.1:
[…]
>
btw, do you have a good solution about this ?

One solution would be to keep the old naming style methods as a proxy between lua and the new naming style methods. That would keep the native C*+ developers and the toLua*+ tool happy at the cost of another level of indirection and some more code to maintain. Lua bindings are critical to me, it was the major reason I choose cocos2d-x over cocos2d and right now I am stuck in 1.0.x because of this problem. I am willing to give it a shot if time permits and if the people involved agree with my suggestion.

fabrice armisen wrote:

James Chen wrote:
That will be great. If you finish it, just let me know. Thanks. :slight_smile:

> Hi, do you think this issue only happens in rc2-2.0.1? Did the cocos2d-x 1.0.1 also exist this bug? The parameters are the same between v1.0.1 and v2.0.
>
> In 1.0.1, we have :
> […]
>
> 2.0.1:
> […]
>
> btw, do you have a good solution about this ?
>
One solution would be to keep the old naming style methods as a proxy between lua and the new naming style methods. That would keep the native C*+ developers and the toLua*+ tool happy at the cost of another level of indirection and some more code to maintain. Lua bindings are critical to me, it was the major reason I choose cocos2d-x over cocos2d and right now I am stuck in 1.0.x because of this problem. I am willing to give it a shot if time permits and if the people involved agree with my suggestion.

I find the real reason is that, the overloaded function will check type and cause crash.
The codes cause crash is:

/* method: create of class  CCSpawn */
#ifndef TOLUA_DISABLE_tolua_Cocos2d_CCSpawn_create01
static int tolua_Cocos2d_CCSpawn_create01(lua_State* tolua_S)
{
 tolua_Error tolua_err;
 if (
     !tolua_isusertable(tolua_S,1,"CCSpawn",0,&tolua_err) ||
     !tolua_isusertype(tolua_S,2,"CCFiniteTimeAction",0,&tolua_err) ||
     !tolua_isusertype(tolua_S,3,"CCFiniteTimeAction",0,&tolua_err) ||
     !tolua_isnoobj(tolua_S,4,&tolua_err)
 )
  goto tolua_lerror;
 else
 {
  CCFiniteTimeAction* pAction1 = ((CCFiniteTimeAction*)  tolua_tousertype(tolua_S,2,0));
  CCFiniteTimeAction* pAction2 = ((CCFiniteTimeAction*)  tolua_tousertype(tolua_S,3,0));
  {
   CCSpawn* tolua_ret = (CCSpawn*)  CCSpawn::create(pAction1,pAction2);
    int nID = (tolua_ret) ? tolua_ret->m_uID : -1;
    int* pLuaID = (tolua_ret) ? &tolua_ret->m_nLuaID : NULL;
    tolua_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)tolua_ret,"CCSpawn");
  }
 }
 return 1;
tolua_lerror:
 return tolua_Cocos2d_CCSpawn_create00(tolua_S);
}
#endif //#ifndef TOLUA_DISABLE

And if we use CCSpawn::actionOneTwo(), generated codes is

/* method: actionOneTwo of class  CCSpawn */
#ifndef TOLUA_DISABLE_tolua_Cocos2d_CCSpawn_actionOneTwo00
static int tolua_Cocos2d_CCSpawn_actionOneTwo00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
 tolua_Error tolua_err;
 if (
     !tolua_isusertable(tolua_S,1,"CCSpawn",0,&tolua_err) ||
     !tolua_isusertype(tolua_S,2,"CCFiniteTimeAction",0,&tolua_err) ||
     !tolua_isusertype(tolua_S,3,"CCFiniteTimeAction",0,&tolua_err) ||
     !tolua_isnoobj(tolua_S,4,&tolua_err)
 )
  goto tolua_lerror;
 else
#endif
 {
  CCFiniteTimeAction* pAction1 = ((CCFiniteTimeAction*)  tolua_tousertype(tolua_S,2,0));
  CCFiniteTimeAction* pAction2 = ((CCFiniteTimeAction*)  tolua_tousertype(tolua_S,3,0));
  {
   CCSpawn* tolua_ret = (CCSpawn*)  CCSpawn::actionOneTwo(pAction1,pAction2);
    int nID = (tolua_ret) ? tolua_ret->m_uID : -1;
    int* pLuaID = (tolua_ret) ? &tolua_ret->m_nLuaID : NULL;
    tolua_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)tolua_ret,"CCSpawn");
  }
 }
 return 1;
#ifndef TOLUA_RELEASE
 tolua_lerror:
 tolua_error(tolua_S,"#ferror in function 'actionOneTwo'.",&tolua_err);
 return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE

The difference is that, the create function go to checkout the type, and actionOneTwo doesn’t.
And i don’t know why create go to check the type information.
May be it is the bug of tolua++ when binding overload functions.