jsonc++builder

Who owns the JSONData Object?


I'm trying to get JSON working in C++Builder. I have been looking at code for Delphi on making a JSON file, and they all seem to create the Doc and Data parts of the JSON file/string, but only free the Doc.

For example:

void __fastcall TMainForm::Panel1Click(TObject* Sender)
{
    std::unique_ptr<TJSONObject> jsonDoc { new TJSONObject() };
    TJSONObject* jsonData = new TJSONObject();

    jsonDoc->AddPair("cmd", rdsNowPlaying);
    jsonDoc->AddPair("station", "WBCB_FM");
    jsonDoc->AddPair("data", jsonData);
    jsonData->AddPair("testNull", new TJSONNull() );
    jsonData->AddPair("testTrue", new TJSONBool(true) );
    jsonData->AddPair("testFalse", new TJSONBool(false) );

    Memo1->Lines->Text = jsonDoc->Format(3);
}

Does jsonDoc->AddPair("data", jsonData); give "ownership" of jsonData to jsonDoc? So when jsonDoc is freed, jsonData is freed too?

Does jsonData->AddPair("testTrue", new TJSONBool(true) ); assign ownership of the TJSONBool to jsonData and it is freed when jsonData is freed?

Triggering the OnClick event displays correctly, but it's another "How do I free (delete) the memory" question? Or does std::unique_ptr and the TJSONObject take care of itself?

p.s. rdsNowPlaying is an enum declared in the header.


Solution

  • By default, the top-level JSON value owns all of the child objects in its hierarchy. So yes, freeing jsonDoc will free everything that is inside of it.

    You can manually set a JSON value's Owned property to false if you want to take ownership of something yourself.


    UPDATE:

    That being said, in your particular code example, AddPair() has the potential to throw an exception, and if that happens while you are calling new on one of its parameters, you will leak that new'ed object before jsonDoc can take ownership of it. If you are going to new an object directly, make sure you delete it if you are not able to transfer ownership of it. unique_ptr can help you with that, eg:

    void __fastcall TMainForm::Panel1Click(TObject* Sender)
    {
        std::unique_ptr<TJSONObject> jsonDoc = std::make_unique<TJSONObject>();
    
        std::unique_ptr<TJSONObject> jsonData = std::make_unique<TJSONObject>();
        ...
        jsonDoc->AddPair(_D("data"), jsonData.get());
        jsonData.release();
    
        std::unique_ptr<TJSONNull> jsonNull = std::make_unique<TJSONNull>();
        jsonData->AddPair(_D("testNull"), jsonNull.get());
        jsonNull.release();
    
        std::unique_ptr<TJSONTrue> jsonTrue = std::make_unique<TJSONTrue>();
        jsonData->AddPair(_D("testTrue"), jsonTrue.get());
        jsonTrue.release();
    
        std::unique_ptr<TJSONFalse> jsonFalse = std::make_unique<TJSONFalse>();
        jsonData->AddPair(_D("testFalse"), jsonFalse.get());
        jsonFalse.release();
    
        Memo1->Lines->Text = jsonDoc->Format(3);
    }
    

    In the case of your booleans (and integers and strings), you can use the AddPair() overloads that create JSON objects for you (unfortunately, there is no method that will create a TJSONNull for you), eg:

    void __fastcall TMainForm::Panel1Click(TObject* Sender)
    {
        std::unique_ptr<TJSONObject> jsonDoc = std::make_unique<TJSONObject>();
    
        std::unique_ptr<TJSONObject> jsonData = std::make_unique<TJSONObject>();
        ...
        jsonDoc->AddPair(_D("data"), jsonData.get());
        jsonData.release();
    
        std::unique_ptr<TJSONNull> jsonNull = std::make_unique<TJSONNull>();
        jsonData->AddPair(_D("testNull"), jsonNull.get());
        jsonNull.release();
    
        jsonData->AddPair(_D("testTrue"), true);
        jsonData->AddPair(_D("testFalse"), false);
    
        Memo1->Lines->Text = jsonDoc->Format(3);
    }