Porting hierarchy of CCArray/CCDictionary from V2 to V3

Hello,

In cocos2d-x V2, I used to abuse CCArray / CCDictionary, especially the fact that you can create an untyped hierarchy, which allows to easily load/save from/to a plist with a bit of introspection.

How would you port that to V3?

My problems so far: cocos::Vector and cocos::Map are strongly typed, and moreover they do not inherit from Ref.

The only ugly solution I see is to use std::vector<void*> and std::map<void*, std::string>, use introspection like I used to. This would add a heavy code burden, as the memory of what’s inside the vector/map would need to be manually handled.

Does anyone have a better solution?

We discussed a similar issue migrating to 3.x where my points were only meant to say I preferred the old CCArray/etc, but that I wanted all the rest of the improvements so I just worked around things and restructured code. I basically have serialize methods now for save/load and am currently in process of moving to FlatBuffer for this aspect of my game(s).

It would be smart for us to either come up with a good examples (past games if you can share) or set of patterns to use the cocos2d:: (or std::slight_smile: container classes as they exist, or propose a new class which would support Ref*, for example. I’ll think about it some more and reply if I think of any more elegant solution.

Question: your comment on easy save/load did you mean that you still parse (or probably “trim” out) any non-PLIST compatible types? So if you’re storing a full Character* instance in a dict then you just skip over that in saving, and its created by normal create() during load that is then filled in with the valid plist data?

Indeed, using save/load methods to map to strongly typed C++ objets would be the ideal. The problem is that it is very time consuming to rewrite code, whereas previously I could just use loosely typed CCArray/CCDictionary.

Unfortunately, I can’t share my games code. I thought about proposing a new class which would inherit from Ref*, but I think there is probably a good reason why cocos2d team choosed not to.

I am not sure I understood your question. I am only loading/saving in plist format, because it support everything I need. You can see my load/save implementation here: https://github.com/FenneX/FenneX/blob/master/Classes/FenneX/Core/Utility/PListPersist.cpp It relies on pugixml to handle xml parsing.

This implementation ignores types it doesn’t know about. On the other hand, it supports almost every type of plist specification (I left out a few I do not use).

ValueMap supports all of the PLIST types you are parsing though, right? And if I understand it correctly the only reason that ValueMap doesn’t work is that you want to store non-Value types in your arr/dict containers while the game is running? If you only dealt with Save/Load you could just use ValueMap since it maps PLIST well?

So my question was trying to figure out what you’re storing in the untyped arr/dict objects that ValueMap doesn’t support? My guess is at runtime outside of the save/load use case you are wanting to pass around ValueMaps that also include some non-PLIST types? Or is it a desire to not deal with getType() and isNull() and asString() verbose excess methods due to static typing?

I’m also looking into FlatBuffer or others as well to see if the static type mapping can be generated for me, while also providing for easy schema checking for all our configuration files (currently we only check for JSON validity).

I realize I’m probably questioning based on trying to conform to the current engine capability, so I stand by my other posts and realize that adding back in Ref* or untyped container support back in may be beneficial.

Oh I didn’t realize ValueMap and ValueVector could be stored in a Value, which means it can be recursive!
If I understand correctly, I can migrate this structure

CCArray (as CCObject)
  CCDictionary (as CCObject)
    std::string => CCstring (as CCObject)
  CCArray (as CCObject)
    CCInteger (as CCObject)
    CCFloat (as CCObject)

to this in V3:

ValueVector (inside Value)
  ValueMap (inside Value)
    std::string => std::string (inside Value)
  ValueVector (inside Value)
    int (inside Value)
    float (inside Value)

Which 100% fits my use case, as I only use strict PLIST types.

Thanks a lot @stevetranby

1 Like

I would have to say no, or there is something I don’t understand. Because we can store a Node and any subclass of it into a CCArray, but it cannot go into a ValueVector etc because that would not retain it and your nodes would get deallocated despite being in the ValueVector. Anyone has a fix for that?

@umai there is no fix or provided solution to the original problem. The solution from the discussion is in effect the fact that one usually doesn’t need to mix the two types together in a vector/map and if they currently are mixing them there’s a chance they could use tags to get rid of Node* or else one could still use CCString if you really want to in 3.x, so that’s the accurate solution, but it has the issue of going away in 4.x.

As mentioned somewhere Qt’s QVariant and other engine’s similar variant would be a type of class setup that would support this question, but we don’t have one in cocos2d-x 3.x. I’m guessing this won’t change in 4.x unless many are vocal about it.

If mixing means having the basic plist types and node types together in the same container, then I don’t need that either. I however do miss or haven’t found a way, other than CCarray, to have node objects nicely retained in a container before use, I.e. before adding them to a scene. I used that pattern with NSArray while using C2-iPhone and didn’t think much about it but haven’t found a real replacement so far.

You can definitely add any Node* derived objects to a CCArray and have them be auto retained/released. Just no longer “supports” CCString, or other value types (I don’t remember if it was CCInteger or CCNumber, etc).

Technically Ref* derived objects should be allowed in CCArray, but no retain/release in the ValueVector[Map].