Make ASSERT safe in release mode

In C++ codes of cocos2dx, there has may ASSERT statement to help user to check parameter error, These ASSERT is useful in debug mode, but in release version, we hope it won’t stop the program running, just return immediately. What should we do?

ASSERT only takes effect in debug mode, isn’t it?

CCASSERT is not implemented if COCOS2D_DEBUG <= 0

ccMacros.h:39

#if COCOS2D_DEBUG > 0
    #if CC_ENABLE_SCRIPT_BINDING
    extern bool CC_DLL cc_assert_script_compatible(const char *msg);
    #define CCASSERT(cond, msg) do {                              \
          if (!(cond)) {                                          \
            if (!cc_assert_script_compatible(msg) && strlen(msg)) \
              cocos2d::log("Assert failed: %s", msg);             \
            CC_ASSERT(cond);                                      \
          } \
        } while (0)
    #else
    #define CCASSERT(cond, msg) CC_ASSERT(cond)
    #endif
#else
    #define CCASSERT(cond, msg)
#endif

Make sure you either define the preprocessor macro as COCOS2D_DEBUG = 0 or not at all in release mode.

*************************************
Edit: However it should be noted that CC_ASSERT (not CCASSERT) is implemented differently per platform (No idea why).

CCPlatformDefine-iOS.h

#define CC_ASSERT(cond) assert(cond)

*************
CCPlatformDefine-mac.h

#if CC_DISABLE_ASSERT > 0
#define CC_ASSERT(cond)
#else
#define CC_ASSERT(cond) assert(cond)
#endif

*************
CCPlatformDefine-android.h

#define CC_NO_MESSAGE_PSEUDOASSERT(cond)                        \
    if (!(cond)) {                                              \
        __android_log_print(ANDROID_LOG_ERROR,                  \
                            "cocos2d-x assert",                 \
                            "%s function:%s line:%d",           \
                            __FILE__, __FUNCTION__, __LINE__);  \
    }

#define CC_ASSERT(cond) CC_NO_MESSAGE_PSEUDOASSERT(cond)

*************
CCPlatformDefine-linux.h

#define CC_ASSERT(cond)    assert(cond)

*************
CCPlatformDefine-win32.h

#if CC_DISABLE_ASSERT > 0
#define CC_ASSERT(cond)
#else
#define CC_ASSERT(cond)    assert(cond)
#endif

Yes, set cocos2d_debug = 0 can stop crash, but we want it return here in release version, if it still running to follow codes, may cause display in wrong status.

+1 for having a mode where cocos2d-x doesn’t crash.

We shouldn’t remove asserts from all possible places… only from places where input from the user makes it crash.

For example

  • creating a sprite with a non-existing texture file, should use a default texture with the string “texture not found” (I think we are using a empty texture right now, which is good).
  • creating a shader with a non-existing shader file, should use a default shader that does basic stuff
  • etc, etc, etc.

ASSERTS are good in some cases… and we shouldn’t remove all of them.

So, if Cocos Studio team is finding places where cocos2d-x is crashing, probably those are good places to insert a default behavior.
Returning nullptr as suggested is a good practice as well, as long as the function returns a pointer.

Please, Cocos Studio team, post here all the functions that are crashing when user input is passed. Thanks.

For Cocos Studio, following 3 ASSERT is big problem. They are put here for check if the value is validate, if only remove crash in release version, wrong value will be set into ActionFrame. And Cocos Studio support LUA extensions, user may change these value in LUA code, in this case, Cocos Studio can not verify whether these value a valid. If these value set to ActionFrame, the animation will in wrong status. So we need not only stop crash in release version, but also return immediately if the value is bad.

void InnerActionFrame::setStartFrameIndex(int frameIndex)
{
    CCASSERT(!_enterWithName, " cannot setStartFrameIndex when enterWithName is set");
    _startFrameIndex = frameIndex;
}


void InnerActionFrame::setEndFrameIndex(int frameIndex)
{
    CCASSERT(!_enterWithName, " cannot setEndFrameIndex when enterWithName is set");
    _endFrameIndex = frameIndex;
}

void InnerActionFrame::setAnimationName(const std::string& animationName)
{
    CCASSERT(_enterWithName, " cannot set aniamtioname when enter frame with index. setEnterWithName true firstly!");
    _animationName = animationName;
   
}

And following assert can be remove by set COCOS2D_DEBUG = 0, but Cocos Studio has it’s own default sprite texture, return false when ASSERT is trigger in release version can help to improve Cocos Studio performance.

bool Sprite::initWithFile(const std::string& filename)
{
    CCASSERT(filename.size()>0, "Invalid filename for sprite");

    Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
    if (texture)
    {
        Rect rect = Rect::ZERO;
        rect.size = texture->getContentSize();
        return initWithTexture(texture, rect);
    }

    // don't release here.
    // when load texture failed, it's better to get a "transparent" sprite then a crashed program
    // this->release();
    return false;
}

Also the following ASSERT may break Cocos Studio, but set COCOS2D_DEBUG=0 to remove this ASSERT is enough.

bool Bundle3D::loadJson(const std::string& path)
{
    clear();

    Data data = FileUtils::getInstance()->getDataFromFile(path);
    ssize_t size = data.getSize();

    // json need null-terminated string.
    _jsonBuffer = new char[size + 1];
    memcpy(_jsonBuffer, data.getBytes(), size);
    _jsonBuffer[size] = '\0';
    if (_jsonReader.ParseInsitu<0>(_jsonBuffer).HasParseError())
    {
        clear();
        CCASSERT(false, "Parse json failed");
        return false;
    }

    const rapidjson::Value& mash_data_array = _jsonReader[VERSION];
    if (mash_data_array.IsArray()) // Compatible with the old version
        _version = "1.2";
    else
        _version = mash_data_array.GetString();
    
    return true;
}

These is all ASSERT may break Cocos Studio.

Now code editing feature of Cocos Studio is in development, in simulator project, there’s some codeIDE support function need set COCOS2D_DEBUG > 0, We can’t sure when code editing add to Cocos Studio, does editor need this setting either. So if there’s a way make ASSERT won’t stop running and can keep COCOS2D_DEBUG > 0, is much helpful.

yeah… it should not crash.

No idea what InnerActionFrame is, it seems that it is a Cocos Studio thing… so your call there.

For the Sprite::initWithFile I think it is safe to remove the CCASSERT.

In Bundle3D, I think it is safe to replace the CCASSERT with CCLOG@super626 , do you agree?