如何修复构建嵌套 Documents 时 RapidJSON 的段错误

问题:

你想构建一个 RapidJSON 应用程序,从零开始构建 JSON 并使用嵌套在其他文档中的 Documents,但当你尝试运行它时,你看到类似这样的错误消息

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

解决方案

段错误(即非法内存访问)可能有很多原因,但最常见的是你使用了局部分配器

为了修复此问题,为你的整个应用程序使用一个分配器

allocator_example.cpp
MemoryPoolAllocator<> jsonAlloc; // 我建议静态声明此变量

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

注意 MemoryPoolAllocator 从不释放其内存池中的任何内存。

解释:

许多 RapidJSON 示例告诉你使用类似这样的调用

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

但始终使用 doc.GetAllocator() 的方法只适用于非常简单的示例。

段错误的原因似乎是一旦 Document 超出作用域,其分配器实例就会被释放,因此内存将被重用。但是,到目前为止我没有进一步追踪此问题。

复现问题的最小示例:

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

Document generateInnerDoc() {
    // 生成文档:{"text": "Hello JSON!"}
    Document doc;
    doc.SetObject(); // 使 doc 成为对象!
    // 通过 SetString() 强制分配
    doc.AddMember("text", Value().SetString("Hello JSON!"), doc.GetAllocator());
    return doc;
}

Document generateOuterDoc() {
    // 生成文档:{"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);
}

修复后的示例:

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

MemoryPoolAllocator<> jsonAlloc;

Document generateInnerDoc() {
    // 生成文档:{"text": "Hello JSON!"}
    Document doc;
    doc.SetObject(); // 使 doc 成为对象!
    // 通过 SetString() 强制分配
    doc.AddMember("text", Value().SetString("Hello JSON!"), jsonAlloc);
    return doc;
}

Document generateOuterDoc() {
    // 生成文档:{"text": "Hello JSON!"}
    Document doc;
    doc.SetObject(); // 使 doc 成为对象!
    // 通过 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);
}

Check out similar posts by category: C/C++