Я использую драйвер 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
....
Эта ссылка оказал влияние на ответ.
Для с ++ — 0x (#if __cplusplus >= 201103L
), BSONObj
иметь конструктор перемещения и явно созданный конструктор копирования:
BSONObj(const BSONObj&) = default;
Для версии c ++ ниже, чем c ++ 0x, также будет создана копия по умолчанию ctor.
Но объект BSONObj содержит указатели (!!!) и внутренний объект (буфер):
const char* _objdata;
SharedBuffer _ownedBuffer;
SharedBuffer также содержит указатель boost::intrusive_ptr _holder
без конструктора копирования (кроме по умолчанию).
Так что копируется не содержимое, а указатели.
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();