C++11 grammar sugar for cocos2dx


#1

I’d like to share some c++11 grammar sugar I create in my project.

* cc::create

namespace cc {
    template
    T* create(Args&& ...args) {
        auto pRet = new T();
        if (pRet && pRet->init(std::forward(args)...)) {
            pRet->autorelease();
            return pRet;
        }
        delete pRet;
        return nullptr;
    }
}

cc::create<T> can support init(...) functions with any args, you don’t need the CREATE_FUNC macro in your CustomNode anymore. Just provide the init(...) functions, and when instancing, use:

auto node = cc::create(...)

* cc::unique_ptr

namespace cc {
    template
    using unique_ptr = std::unique_ptr>;
    template
    unique_ptr make_unique(T *ptr) {
        if (ptr) ptr->retain();
        auto deleter = [](void *p) {
            auto ptr = static_cast(p);
            if (ptr) ptr->release();
        };
        return unique_ptr(ptr, deleter);
    }
}

cc::unique_ptr<T> is a smart pointer for CCObject, it can cope with STL containers. We don’t need to manually retain&release anymore. For example, CCObject as a class member can be more easier:

# in xx.h
private:
    cc::unique_ptr _objects;
# in xx.cpp
bool CustomNode::init() {
    _objects = cc::make_unique(CCArray::createWithCapacity(objectCount));
    ...
}

#2

Thanks. With which compilers did you test this? Do you enable c11 only for your project, not cocos2d-x? AFAIK cocos2d-x is unable to compile with c11 support ATM.

BTW here’s more traditional approach to smart pointers: https://github.com/ivzave/cocos2dx-ext


#3

Yes, a traditional handmade smart pointer with the overloaded operator T*() function is easier when work with cocos2dx.

I enable c11 for cocos2dx project. Clang and g both support c11 now.
for XCode project:
<pre>
Build Settings -> C
Language Dialect choose C11
> C*+ StandardLibrary choose libc*+
</pre>
for android project with android-ndk-r8b:
<pre>
add
std=gnu
0x to the APP_CPPFLAGS in jni/Application.mk


#4

Did you try msvc? There was mentioned here on forums some time ago that cocos2d-x can’t be compiled with c++11 support. Here’s issue I’ve found: https://github.com/cocos2d/cocos2d-x/issues/1313. Maybe some work was done since then.


#5

I haven’t try msvc. AFAIK, VC2012 supports c11. See http://channel9.msdn.com/Events/Build/2012/2-005


#6

I know they all support c++11, thanks. Problem was cocos2d-x isn’t written standard-compliant enough to be compiled :slight_smile:


#7

I’m using cocos2dx v2.x with c**11 enabled without problem. :slight_smile: I’m heavily use auto, lambda, range-base for etc.
Igor Zavorotkin wrote:

I know they all support c**11, thanks. Problem was cocos2d-x isn’t written standard-compliant enough to be compiled :slight_smile:


#8

Awesome stuff! I will use this in my project. :slight_smile: I also added make_shared, though I’m not sure it will work as intended, since I’m not good with C++:

template
    using shared_ptr = std::shared_ptr;
    template
    shared_ptr make_shared(T *ptr) {
        if (ptr) ptr->retain();
        auto deleter = [](void *p) {
            auto ptr = static_cast(p);
            if (ptr) ptr->release();
        };
        return shared_ptr(ptr, deleter);
    }

Edit

I also modified your version of make_unique like so:

template
    using unique_ptr = std::unique_ptr>;
    template
    unique_ptr make_unique(Args&& ...args) {
        T *ptr = create(std::forward(args)...);
        if (ptr) ptr->retain();
        auto deleter = [](void *p) {
            auto ptr = static_cast(p);
            if (ptr) ptr->release();
        };
        return unique_ptr(ptr, deleter);
    }

With this, you can write your code like this:

atuo myNode = cc::make_unique(param1, param2, param3);

instead of like this:

atuo myNode = cc::make_unique(cc::create(param1, param2, param3));

#9

Hi Lee Yang,

Could you tell us what version of XCode that you’re using? And how about the IOS deployment target version?

I’m using XCode 4.5.2 and my deployment target is IOS 5.1 but I’m still failed to compile it.

Thanks,

Terry.

EDIT:
Ok, I finally figure out how to solve it. It’s because I’m using cocos2d-x as a separate project so I also have to set cocos2d-x project to use c**11 as you mentioned it.
Le Yang wrote:

Yes, a traditional handmade smart pointer with the overloaded operator T*() function is easier when work with cocos2dx.
>
I enable c**11 for cocos2dx project. Clang and g*+ both support c*+11 now.
>
for XCode project:
[…]
>
for android project with android-ndk-r8b:
[…]


#10

I’ve been doing this kind of stuff too. Haven’t tried working in Visual Studio but cocos2d-x v2.0.2 builds fine in Xcode version 4.5.2 with C11 as the language dialect modulo a bunch of warnings — this is to iOS6, not sure if that matters but I doubt it. Has anyone tried building with C11 to Android?

Le Yang, why even bother calling autorelease(), retain() and release() if you are using std smart pointers? I just use standard smart pointers and don’t use the cocos2dx pool allocator stuff at all for objects in the CCNode hierarchy. The only minor difficulty with working this way is that all of the builtin creation functions call autorelease() on the created object so you have to make your own versions that don’t do this i.e. you have to make a version of createWithSpriteFrameName(), for example, that doesn’t call autorelease().

Beyond being more idiomatic C++ I think handling sprite memory this way is probably more efficient. I say “probably” because I don’t totally understand how the managed object stuff is implemented … but doesn’t it kind of have to be iterating over all the managed objects after each iteration of the game loop to see if anything needs to be reallocated? So including less items in the pool should be faster, right?


#11

@Joe Wezorek, I once built a game on android-ndk-r8b (gcc4.7), you can use some features of c++11, see gcc 4.7 status (http://gcc.gnu.org/projects/cxx0x.html)

Because it’s hard to totally avoid cocos2dx memory manage, I think it’s clear to just use it. cocos2dx uses reference count to keep the variable life to the next frame. It cleanup the refCount==0 variables at each frame.

I post my handmade cc::unique_ptr below, which overloads operator T**function to better cope with cocos2dx.
<pre>
namespace cc {
// unique_ptr
template
class unique_ptr {
public:
unique_ptr : ptr {
}
unique_ptr :ptr {
retain;
}
unique_ptr : unique_ptr {
}
~unique_ptr {
release;
}
unique_ptr& operator= {
if {
retain;
release;
ptr = ptr;
}
return this;
}
unique_ptr& operator= {
return operator=;
}
T
operator-> {
return
ptr;
}
const T
operator~~> const {
return ptr;
}
operator T* const {
return
ptr;
}
private:
void retain { if ptr~~>retain(); }
void release(T ptr) { if ptr->release; }
private:
T
_ptr;
};
}


#12

Le Yang wrote:
>

Because it’s hard to totally avoid cocos2dx memory manage, I think it’s clear to just use it. cocos2dx uses reference count to keep the variable life to the next frame. It cleanup the refCount==0 variables at each frame.
>

Yes, cocos2dx memory management is intertwined with its logic throughout the framework. However, I think that it isn’t too hard to not use for a game’s primary allocation and de-allocation. Basically, it’s not that hard to avoid using cocos2d-x memory management in situations where the following are true:

(1) I am explicitely allocating something in my own game code.
(2) The thing that I am explicitly allocating inherits from CCNode

(2) is arbitrary. I do this because it would be a pain in the ass to not use cocos2dx memory management everywhere. (1) is just common sense.

But basically I let cocos2d-x handle management of temporary objects that won’t be retained across iterations of game loop and try to manage primary game data, i.e. sprites, myself. I do worry that I am introducing leaks doing it this way, but it seems to work.


#13

@Le Yang:

Thanks for sharing :)! This is great!


#14

This is interesting man! I have to learn more about C++11 first before dive into it.
Glad to know it supports in XCode.


#15

If someone is interested here is the additional code for scene function (if you use it):

// scene
namespace cc {
    template
    cocos2d::CCScene* scene(Args&& ...args) {
        cocos2d::CCScene *scene = cocos2d::CCScene::create();
        T *layer = cc::create(std::forward(args)...);
        scene->addChild(layer);
        return scene;
    }
}

#16

I’m having some problems with c++11 on Xcode 5 with cocos2d-x v2.2.0. Linking errors all over the place.

I posted some screenshots here:


#17

Hi Guys,

This is an awesome post. However I got some compiler errors now that the custom deleter needs to be part of the unique_ptr template declare. It is probably the pre/post C++11 differences. The below is my update to this code: -

template <class T>
void unique_ptr_deleter(T *p)
{
    auto ptr = static_cast<Ref *>(p);
    if (ptr)
        ptr->release();
}

template <class T>
using unique_ptr = std::unique_ptr<T, decltype(*unique_ptr_deleter<T>)>;

template <class T>
unique_ptr<T> make_unique(T *ptr)
{
    if (ptr)
        ptr->retain();
    return unique_ptr<T>(ptr, unique_ptr_deleter);
}