I'm trying to get a rapidjson::Value from a singleton in a code written for Cocos2dx.
This is my extract from singleton: AppData.h
class AppData {
private:
AppData() { }; // Constructor
AppData(AppData const &) = delete;
void operator=(AppData const &) = delete;
public:
static AppData *getInstance() { // Get instance
static AppData instance; // Instantiated on first use
return &instance;
};
void getData(); // Get Data from JSON
rapidjson::Value& getCurrentLocation(); // Get Current Location Branch
rapidjson::Value locations; // JSON locations tree
int selectedLocation = 1; // Cursor to current location
rapidjson::Document doc; // JSON document
};
and AppData.cpp
void AppData::getData() {
auto fileutil = FileUtils::getInstance();
std::string path = fileutil->fullPathForFilename("data/locations.json");
std::string data = fileutil->getStringFromFile(path.c_str());
if (!doc.Parse<0>(data.c_str()).HasParseError()) {
locations = doc["locations"];
}
}
// Get Current Location (Public)
rapidjson::Value& AppData::getCurrentLocation() {
return locations[selectedLocation];
}
and then the extract from the main code cpp:
auto DATA = AppData::getInstance();
rapidjson::Value& loc0 = DATA->getCurrentLocation();
CCLOG("loc0: %s", loc0["title"].GetString());
This works, even if I don't know it's the correct way to implement it.
But if I write this:
rapidjson::Value& test0 = DATA->locations[0];
I got an error saying that private GenericValue::GenericValue(const GenericValue &) is inaccessible
But locations is declared as public.
I got error too also with:
auto test0 = DATA->locations[0];
But it's ok if I write split into 2 lines:
rapidjson::Value t1;
t1 = DATA->locations[1];
This is ok, but I got another problem, if I use another variable with the same value I got an error IsObject on rapidjson value, if I write:
rapidjson::Value t1;
t1 = AppData::getInstance()->locations[2];
rapidjson::Value t2;
t2 = AppData::getInstance()->locations[2];
Using the same value from locations[2] gives IsObject error
Please help me, I don't know if it is a problem with the singleton implementation, because some people use the pointer approach, other with just the reference. But other methods and variable is ok with this type of singleton, just rapidjson gives me problems maybe I have not understood the rapidjson:Value correctly.
Thank you.
[EDIT] I add the part of code that reuses the rapidjson Value:
Location.h
class Location : public Layer {
public:
Location() { }; // Constructor
static Scene *createScene(); // Create a scene with this class inside
CREATE_FUNC(Location); // Implement create with macro
bool init() override; // Initialize after creation
};
Location.cpp (this is the standard scene creation in Cocos2Dx)
Scene *Location::createScene() {
auto scene = Scene::create();
auto layer = Location::create();
scene->addChild(layer);
return scene;
}
// Called by create for initialize
bool Location::init() {
if (!Layer::init()) return false; // Super init
const rapidjson::Value* t1;
t1 = &AppData::getInstance()->locations[1];
CCLOG("t1: %s", (*t1)["title"].GetString());
auto locationPanel = Layout::create();
locationPanel->setLayoutType(Layout::Type::RELATIVE);
// Back Button
auto btnBack = Button::create("gfx/button.png", "gfx/buttonHighlighted.png");
btnBack->setScale9Enabled(true);
btnBack->setContentSize(Size(180, btnBack->getContentSize().height * 1.2f));
btnBack->setTitleText("Back");
btnBack->addTouchEventListener([=](Ref *sender, Widget::TouchEventType type) {
if (type == Widget::TouchEventType::ENDED) {
Director::getInstance()->replaceScene(TransitionFade::create(0.2f, Location::createScene()));
}
});
locationPanel->addChild(btnBack);
addChild(locationPanel);
return true;
}
In AppDelegate before Location.cpp start I read the JSON with AppData::getInstance().getData(); described above
I removed some unnecessary code but the the error occurs when I for example print the t1["title"].GetString(), the first time is ok, the second time return error.
The process line is: AppDelegate -> AppData -> getData -> Location scene creation -> replace the scene with button click -> if the new scene recall the same AppData::getInstance()->locations[1] it gives error, if I access to another branch like 2, 3, 4, it's ok, so if in the game I play only consecutive scenes the json infos are readed in a good way, when I return to a previous location, datas are not accessible anymore with the same variable t1.
Thank you for your help.
Your implementation of singleton should be fine. It is just that you don't know how rapidjson works. If you don't need to modify any value, you can use const auto&
to declare those variables.
private GenericValue::GenericValue(const GenericValue &) is inaccessible
It means that the copy constructor is inaccessible. Changing rapidjson::Value& test0 = DATA->locations[0];
into const rapidjson::Value& test0 = DATA->locations[0];
, or const auto& test0
if you like.