A better CCLog solution!

Hello everyone.
Today I’m bringing you a function that I think can be integrated inside cocos2d-x because it’s a very important one.

If you are an objective c programmer, it’s almost impossible that you have never printed an object with NSLog. Well, after that cool experience with “@" and the “description” method it’s hard to debug in c++, because every time you have to print a CCArray or a CCDictionary you have to iterate. Even if you have written code snippets to accelerate that process, that’s never gonna be equal or better than the cool “`” flag in objective c. I miss NSLog so much!!!

That’s why I think that our CCLog method must be changed. I wrote a method that replaces any "%`” inside the log with the string returned from a “description” method that every subclass of CCObject must have. So basically to resume what has to be changed inside cocos2d-x is:

  1. Modify CCLog method to call my method instead of vsnprintf.
  2. Create a virtual description method inside CCObject that must return a string.
  3. Inside all important classes like CCArray and CCDictionary write a better implementation of the description method.

There are several classes inside cocos2d-x (ex. CCNode) that already have a description method, I’m wondering if actually we have to call them ourselves or they get called somewhere that I don’t know?

Anyway, I hope that somebody of the cocos2d-x team will take this method in consideration, you can change it or create another better one, but please do something like this because it’s a very useful technique to log objects!

This is the public method that CCObject must contain:

virtual const char * description();

This is my method:

#include "CCObject.h"

void ccprintf(char * buffer, const char * format, va_list &args)
{
    char szBuf[kMaxLogLen+1] = {0};
    int len = strlen(format)-1;
    int counter = 0;
    int j=0, i=0;
    for (; i0) va_arg(args,int);

                j=0;

                //get object (argument)
                CCObject * ptr = va_arg(args,CCObject*);

                //get description of object and add it to buffer
                const char * desc = ptr->description();
                strcpy(&buffer[j],desc);
                j+=strlen(desc);

                i++;
                continue;
            }
            else if (format[i+1]!='%') { //skip argument
                counter++;
            }
            else { //next symbol is %, skip it
                i++;
            }
        }

        buffer[j++] = format[i];
    }
    buffer[j++] = format[i];
    buffer[j] = '\0';
    vsnprintf(szBuf, kMaxLogLen, buffer, args);
    printf("%s",szBuf);
}

This is the modified CCLog:

void CCLog(const char * pszFormat, ...)
{
    printf("Cocos2d: ");
    char szBuf[kMaxLogLen];

    va_list ap;
    va_start(ap, pszFormat);
    //vsnprintf(szBuf, kMaxLogLen, pszFormat, ap);
    ccprintf(szBuf, pszFormat, ap);
    va_end(ap);
    //printf("%s", szBuf);
    printf("\n");
}

And this is how you use it:

CCSprite * sprite = CCSprite::create("hello.png");
CCLog("%@",sprite);

Cool right? :wink:

I havent’s tested so much, so if you find bugs, please let me know!

Regards,
Artavazd

Duplicated to https://github.com/cocos2d/cocos2d-x/pull/2406.
Bo be frank, it’s good idea but both implementations are painful.
I’m discussing cocos2d 3.0 with Ricardo Quesada, in 3.0, we would like to
(1) Rename CCObject to CCReference, perhaps. CCObject is only for calculating reference counts. I found people love to add everything into CCObject & CCNode.
(2) Also we’re discussing a component-based design to avoid CCObject & CCNode to be too fat. https://github.com/cocos2d/cocos2d-js/issues/64
(3) Marshaling & unmarshaling functions will be added into each class, to implement the data-driven feature. I think your description() or sergey-shambir’s acceptVisitor() can be a part of marshaling function, the only difference between logging and marshaling is that, one dump to log and another dump to file.

It’s good that you have planned all that changes for cocos2d 3, I really don’t know how marshaling is gonna work, but dumping objects to the log it’s something very important to have.
Anyway thanks for the support.

Artavazd Barseghyan wrote:

It’s good that you have planned all that changes for cocos2d 3, I really don’t know how marshaling is gonna work, but dumping objects to the log it’s something very important to have.
Anyway thanks for the support.
You already can tweak your local cocos2d-x version using CCPrettyPrinter class. It’s temporary compromise for cocos2d v.2.

Artavazd Barseghyan wrote:

It’s good that you have planned all that changes for cocos2d 3, I really don’t know how marshaling is gonna work, but dumping objects to the log it’s something very important to have.
Anyway thanks for the support.

+1 for having a good way to dump objects on a string.

I noticed that `CCPrettyPrinter` was added, but was not integrated into the rest of cocos2d-x. eg:
https://github.com/cocos2d/cocos2d-x/blob/master/cocos2dx/cocoa/CCDictionary.cpp#L190

`valueForKey` is using dynamic_cast to test if the value is a string. Instead of doing that it should try to return the string representation of the value.
There are 2 ways of doing it:

  • visitor pattern (added in cocos2d-x), but not really integrated in the rest of the cocos2d-x code
  • having a `toString` in all the CCObjects.