Accessing Rapidjson Value from Singleton

I’m trying to get a rapidjson::Value from a singleton.

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.

I’m not an expert at RapidJSON, but looks like copying a Value to a variable will set the original copy to null. I’m not sure why, but there’s probably a good reason for it. Using pointers works for me, although I’m not sure if it is the proper way to do it. E.g.

const rapidjson::Value* t1;
t1 = &AppData::getInstance()->locations[2];

Thank you for the answer @grimfate, the problem is that when I, for example, replace the scene, so then that assignment is called another time it fails returning IsObject error.
It seems like the data in Singleton remain, but it can be accessed only the first time, after a replaceScene it fails, but only if I recall the same AppData::getInstance()->locations[2], if I use another number for other datas, like 3, 4 etc it’s ok.

Imagine the scenario, datas are loaded for every location, if I navigate in location in consecutive order, 1, 2, 3 etc it’s ok, the variable t1 will be filled correctly, but if I return on a previous location it fails, like I can reference another time that pointer.
It’s the same if I use const or not.
I have also tried to make a method in the singleton for getting location, but nothing, always the same story, the first time it’s ok, but if I recall a second time it fails.

Maybe I have to make a different approach and this is wrong.

I’m reading now this:

Maybe the approach taken by @stevetranby is the right way, from the singleton I can transform the rapidjson::Value in “another” value and return this to the main scene, maybe I can resolve the consecutive accessing data problem…

As @grimfate mentioned, if you copy construct Value it will set the original to null. I.e. only one GenericValue can hold the data. I don’t think this problem has anything to do with your singleton.

In my projects I use rapidjson only as a parser to construct data objects which I then use in my app. Allowing me to change the source of the data without affecting the rest of the app.

If you want to reference Values objects use pointers as @grimfate suggested:

const auto t1 = &AppData::getInstance()->locations[2];

This will not perform a copy construct and so the data will stay within ‘locations’

Ok I think I will also use another data objects to use in the App and leave rapidjson function only as parser.

Thank you.

You should find what works best for you. We are using both JSON and PLIST config files and so we decided to just use the CCValue.h and its associated vector/map. This way we only have one class format for configuration/properties/etc. We do also convert a few to/from JSON directly into a struct’s fields. It’s not necessarily ideal.

We just decided that rapidjson is not a great format/library for storing and using values, but instead is good at just getting JSON into another data structure(s).

YMMV as usual, and storing and using the rapidjson values is perfectly valid if it works for you.

Thank you @stevetranby for you answer.

I think I have solved my problem, partially is in rapidjson that don’t permit copy constructor and partially in my code, because I loaded my json in AppDelegate and then when I replace the scene I got error like access violation.
Now I tried to load the json after the first runscene, and it seems to work, no more access violation to data.
In the meantime I have also rewritten the parse json with getter and setter methods so it seems more polite now, but the loading in appdelegate was the main problem I think.