Хранение BSONObj в контейнере: невозможно получить поля

Я использую драйвер mongodb c ++ для получения некоторых записей из базы данных mydbколлекция mycollection,
Когда я получаю запись с mongo::BSONObj current_obj=cursor->next(); а затем попытаться получить какое-то поле field1 с помощью getField или же getFieldDotted (поле присутствует в записи), оно всегда успешно (и печатает "got the field"— см. код ниже). Я также проверил содержимое BSONElement вернулся: они в порядке.

Затем я возвращаю все найденные записи как BSONObjв векторе (QVector или же boost::container::vector) и вернуть вектор из запроса.

Позже, в другой функции, я перебираю BSONObjэто из этого вектора. призвание getField или же getFieldDotted здесь выбрасывает исключение с некоторой вероятностью: например, в 40% запусков программы исключение не возникает (выводится "got the field from vector" 10 раз), 60% прогонов программы я получаю Caught BSONElement: bad type 88 (последний int значение может быть различным в зависимости от тока BSONObj: может быть -120, 110, 126или какой-то другой. Впрочем, это сохраняется на каждой записи).

Почему это происходит?

Код:

boost::container::vector<mongo::BSONObj> makeQuery(QString query)
{
std::auto_ptr <mongo::DBClientCursor> cursor = my_connection->query("mydb.mycollection", mongo::fromjson(query.toStdString()));
//...(checking getLastError())

//QVector<mongo::BSONObj> bsonobjects;
boost::container::vector<mongo::BSONObj> bsonobjects;

while (cursor->more())
{
mongo::BSONObj current_obj=cursor->next();
for (int ii=0; ii<10; ii++)
{
current_obj.getField("field1");
std::cout<<"---got the field---\n";
}
bsonobjects.push_back(current_obj);
}
return bsonobjects;
}

void processBSONObjVector()
{
//....

//(below we call makeQuery)
try
{
//QVector<mongo::BSONObj> objects=makeQuery(query_string);
boost::container::vector<mongo::BSONObj> objects=makeQuery(query_string);
for (int i=0; i<objects.size; i++)
for (int ii=0; ii<10; ii++)
{
objects[i].getField("field1");
std::cout<<"---got the field from vector---\n";
}
}
catch (const mongo::DBException &e)
{
std::cout << "Caught " << e.what() << std::endl;
}
}

Кажется, что происходит heisenbug, что-то с памятью: если (счастливо) BSONObj не переписывается другими данными, значение field1 может быть принято
BSONObj В векторе может быть поврежден так же, как и вне контекста, и не осталось никаких умных ptrs.

Является BSONObj не копируется с BSONObj(const BSONObj & other)?

Документация предлагает пройти BSONObj по значению — это тот случай, когда я использую push_back(объект), не так ли?


Кроме того, возможно, не имеет значения, есть сомнительный код, когда я открываю соединение (не уверен, что это безопасно для памяти, чтобы выполнить dynamic_cast<mongo::DBClientConnection*> из mongo::DBClientBase*, DBClientBase абстрактно):

my_connection — это установленное соединение с mongodb:

//... (make a connection)
mongo::client::initialize();
//...
my_connection = dynamic_cast<mongo::DBClientConnection*> (connection_string.connect(errmsg)); //connection_string is valid mongo::ConnectionString
....

0

Решение

Эта ссылка оказал влияние на ответ.

Для с ++ — 0x (#if __cplusplus >= 201103L), BSONObj иметь конструктор перемещения и явно созданный конструктор копирования:

BSONObj(const BSONObj&) = default;

Для версии c ++ ниже, чем c ++ 0x, также будет создана копия по умолчанию ctor.

Но объект BSONObj содержит указатели (!!!) и внутренний объект (буфер):
const char* _objdata;
SharedBuffer _ownedBuffer;
SharedBuffer также содержит указатель boost::intrusive_ptr _holder без конструктора копирования (кроме по умолчанию).

Так что копируется не содержимое, а указатели.

legacy-1.0.5 BSONObj.h:

     BSONObj& operator=(BSONObj otherCopy) {
this->swap(otherCopy);
return *this;
}

/** Swap this BSONObj with 'other' */
void swap(BSONObj& other) {
using std::swap;
swap(_objdata, other._objdata);
swap(_ownedBuffer, other._ownedBuffer);
}

Снова, указатели поменялись местами.

Итак, мы не можем скопировать BSONObj с копией ctor или operator=,

Это должно быть должным образом отражено в документации, как для BSONObjBuilder, если это сделано намеренно.

Два решения:

1) Создать умный указатель на BSONObj в makeQuery() и вернуть его (лучше).

2) Работа с BSONObj в makeQuery() контекст (хуже: его можно использовать только для краткой и не повторной работы над BSONObj, в противном случае приводит к нечитаемости и невозможности повторного использования кода).

EDIT1: Создание умного указателя и его возврат приводит к той же ошибке.
Ответ внутри комментарии к коду:

СОБСТВЕННЫЙ СЛУЧАЙ

Если BSONObj владеет буфером, буфер может быть общим
среди нескольких BSONObj (по назначению). В этом случае буфер
в основном реализовано как shared_ptr.

НЕИЗВЕСТНЫЙ ДЕЛО

BSONObj может также указывать на данные BSON в некоторой другой структуре данных, которую он не «владеет» или освобождает позже. Например, в отображенном в память файле. В этом случае важно, чтобы исходные данные оставались в области действия до тех пор, пока BSONObj используется. Если вы считаете, что исходные данные могут выходить за рамки, вызовите BSONObj :: getOwned (), чтобы ваш BSONObj получил свою собственную копию. При назначении BSONObj, если источник неизвестен, и источник, и назначение будут иметь неизвестные указатели на исходный буфер после назначения. Если вы не уверены в праве собственности, но хотите, чтобы буфер работал до тех пор, пока BSONObj, вызовите getOwned ().

copy() метод работает так же, как и getOwned (), предоставляя собственную копию BSONObj,
Возврат копии принадлежит без ошибок.

mongo::BSONObj* curr_elem=new mongo::BSONObj();
(*curr_elem)=cursor->next().getOwned(); //or: next().copy();
0

Другие решения