How to fix RapidJSON segmentation faults when building nested Documents

Problem:

You want to build a RapidJSON application that builds a JSON from scratch and is using Documents nested inside other documents, but when you try to run it, you see an error message like

zsh: segmentation fault (core dumped)  ./rapidjson-example

Solution:

Segmentation faults (i.e. illegal memory accesses) can have many reasons, but the most common one is that you use local allocators.

In order to fix the issue, use one allocator for your entire application.

MemoryPoolAllocator<> jsonAlloc; // I recommend to declare this statically

// ...
doc.AddMember("text", Value().SetString("Hello JSON!"), jsonAlloc);

Note that MemoryPoolAllocator never releases any memory from its memory pool.

Explanation:

Many RapidJSON examples tell you to use calls like

Document d;
doc.AddMember("text", ... , doc.GetAllocator());

But the approach of always using doc.GetAllocator() only works for very simple examples.

The reason for the segfault seems to be that once the Document gets out of scope, its allocator instance is deallocated and hence the memory will be re-used. However, I did not track down the issue any further so far.

Minimal example to reproduce the issue:

#include <iostream>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/ostreamwrapper.h>
using namespace rapidjson;
using namespace std;

Document generateInnerDoc() {
    // Generate document: {"text": "Hello JSON!"}
    Document doc;
    doc.SetObject(); // Make doc an object !
    // Force allocation via SetString()
    doc.AddMember("text", Value().SetString("Hello JSON!"), doc.GetAllocator());
    return doc;
}

Document generateOuterDoc() {
    // Generate document: {"text": "Hello JSON!"}
    Document doc;
    doc.SetObject();
    doc.AddMember("text", generateInnerDoc(), doc.GetAllocator());
    return doc;
}

int main() {
    // Write to stdout
    OStreamWrapper out(cout);
    // Write document...
    Writer<OStreamWrapper> writer(out);
    generateOuterDoc().Accept(writer);
}

Fixed example:

#include <iostream>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/ostreamwrapper.h>
using namespace rapidjson;
using namespace std;

MemoryPoolAllocator<> jsonAlloc;

Document generateInnerDoc() {
    // Generate document: {"text": "Hello JSON!"}
    Document doc;
    doc.SetObject(); // Make doc an object !
    // Force allocation via SetString()
    doc.AddMember("text", Value().SetString("Hello JSON!"), jsonAlloc);
    return doc;
}

Document generateOuterDoc() {
    // Generate document: {"text": "Hello JSON!"}
    Document doc;
    doc.SetObject(); // Make doc an object !
    // Force allocation via SetString()
    doc.AddMember("text", generateInnerDoc(), jsonAlloc);
    return doc;
}

int main() {
    // Write to stdout
    OStreamWrapper out(cout);
    // Write document...
    Writer<OStreamWrapper> writer(out);
    generateOuterDoc().Accept(writer);
}