Is there way to use rapidjson with std::string efficiently?

ckorzhik picture ckorzhik · May 11, 2015 · Viewed 12.1k times · Source

I'm trying to work with rapidjson.

I want to generate string and add it to some rapidjson::Value which is object.

I was using std::string when worked with qjson, but in case of rapidjson it seems inappropriate. I don't want to generate string and then copy it, string object lifetime ends before object (rapidjson::Value) lifetime (therefore generated_string.c_str() is not a case). There may be \0 in json, so, char* with null-terminated string also not a solution.

So, I must write my own string type? Or use something like

auto create_string_object() -> rapidjson::GenericStringRef<char>
{
   size_t len;
   char* result;
    // generate char* result with length len
   const char* return_value = result;
   return rapidjson::StringRef(return_value,len);
}
auto create_object_object(rapidjson::Document::AllocatorType &allocator) -> rapidjson::Value
{
   // ...
   rapidjson::Value result(rapidjson::kObjectType);
   rapidjson::Value tmp;  // tmp = create_string_object() will not compile
   tmp = create_string_object();
   result.AddMember("key", tmp, allocator); 
   // ...
}

Or there are some other ways to work with strings? It seems hard to me. We can't move string to rapidjson::Value because of different structures inside that Value, we can't set pointer inside Value to c_str() because string will be destroyed before Value. Even with GenericStringRef<char> I must rewrite almost all work with strings.

By the way, why RAPIDJSON_HAS_STDSTRING is 0 by default? Some problems with work? I see that i can copy string into rapidjson::Value and copy pointer if I know that string lifetime will end before value lifetime.

UPD: Now I see that rapidjson frees only strings that were copied:

  //! Destructor.
  /*! Need to destruct elements of array, members of object, or copy-string.
  */
  ~GenericValue() {
      if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
          switch(flags_) {
          case kArrayFlag:
              for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
                  v->~GenericValue();
              Allocator::Free(data_.a.elements);
              break;

          case kObjectFlag:
              for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
                m->~Member();
              Allocator::Free(data_.o.members);
              break;

          case kCopyStringFlag:
              Allocator::Free(const_cast<Ch*>(data_.s.str));
              break;

          default:
              break;  // Do nothing for other types.
          }
      }
  }

So, as it was said in answer, using GenericStringRef in a way such as in my code here is a bad idea, because in such case I must manage memory by myself.

Answer

Milo Yip picture Milo Yip · May 11, 2015

I do not completely understand the question. But I try to clarify a few things here.

  1. GenericStringRef is used for preventing string copy. It should only be used if the string's lifetime is known to be sufficient. For string dynamically created, normally you should not use GenericStringRef.
  2. Setting RAPIDJSON_HAS_STDSTRING=1 is fine. It is not turned on by default because its support is added after early release. And I don't want RapidJSON header to include <string> if the user don't need to. Since you use std::string, you can turn it on. It should make you easier to deal with std::string.