BSONObj 在容器中的存储:无法获取字段
Storage of BSONObj in container: can't get the fields
我使用 mongodb c++ 驱动程序从数据库 mydb
、集合 mycollection
获取一些记录。
当我使用 mongo::BSONObj current_obj=cursor->next();
获取记录,然后尝试使用 getField
或 getFieldDotted
获取某些字段 field1
(字段存在于记录中)时,它总是成功(并打印"got the field"
- 请参阅下面的代码)。我还检查了 BSONElement
returned 的内容:它们很好。
然后我将所有找到的记录作为 BSONObj
推回一个向量(QVector
或 boost::container::vector
),然后 return 来自查询的向量。
稍后,在其他函数中,我从该向量迭代 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
向量中的内容可能已损坏,因为它脱离了上下文并且没有留下任何智能指针。
BSONObj
不能用 BSONObj(const BSONObj & other)
复制吗?
文档建议按值传递 BSONObj
- 当我使用 push_back
(object) 时就是这种情况,不是吗?
此外,也许无关紧要,当我打开连接时有一个有问题的代码(不确定执行 mongo::DBClientBase*
的 dynamic_cast<mongo::DBClientConnection*>
是否是内存安全的,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
....
That link对答案有影响。
对于 c++-0x (#if __cplusplus >= 201103L
),BSONObj
具有移动构造函数和显式创建的复制构造函数:
BSONObj(const BSONObj&) = default;
对于低于c++0x的c++版本,也会创建默认的copy 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);
}
同样,指针被交换。
所以,我们不能用copy ctor或operator=
复制BSONObj
。
它应该在文档中正确反映,like for BSONObjBuilder
,如果它是有意的。
两种解决方案:
1) 在 makeQuery()
和 return 中的 BSONObj
上创建智能指针(更好)。
2) 在 makeQuery()
上下文中使用 BSONObj
(更糟糕的是:它可能仅用于短期且不重复的工作 BSONObj
,否则会导致代码不可读和非可重用性)。
EDIT1:创建智能指针并return使用它会导致相同的错误。
答案在the code comments:
内
OWNED CASE
If the BSONObj owns the buffer, the buffer can be shared
among several BSONObj's (by assignment). In this case the buffer is
basically implemented as a shared_ptr.
UNOWNED CASE
A BSONObj can also point to BSON data in some other data structure it does not "own" or free later. For example, in a memory mapped file. In this case, it is important the original data stays in scope for as long as the BSONObj is in use. If you think the original data may go out of scope, call BSONObj::getOwned() to promote your BSONObj to having its own copy. On a BSONObj assignment, if the source is unowned, both the source and dest will have unowned pointers to the original buffer after the assignment. If you are not sure about ownership but need the buffer to last as long as the BSONObj, call getOwned().
copy()
方法与 getOwned() 一样有效,提供 BSONObj
的拥有副本。
Return of owned copy 结果没有错误。
mongo::BSONObj* curr_elem=new mongo::BSONObj();
(*curr_elem)=cursor->next().getOwned(); //or: next().copy();
我使用 mongodb c++ 驱动程序从数据库 mydb
、集合 mycollection
获取一些记录。
当我使用 mongo::BSONObj current_obj=cursor->next();
获取记录,然后尝试使用 getField
或 getFieldDotted
获取某些字段 field1
(字段存在于记录中)时,它总是成功(并打印"got the field"
- 请参阅下面的代码)。我还检查了 BSONElement
returned 的内容:它们很好。
然后我将所有找到的记录作为 BSONObj
推回一个向量(QVector
或 boost::container::vector
),然后 return 来自查询的向量。
稍后,在其他函数中,我从该向量迭代 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
向量中的内容可能已损坏,因为它脱离了上下文并且没有留下任何智能指针。
BSONObj
不能用 BSONObj(const BSONObj & other)
复制吗?
文档建议按值传递 BSONObj
- 当我使用 push_back
(object) 时就是这种情况,不是吗?
此外,也许无关紧要,当我打开连接时有一个有问题的代码(不确定执行 mongo::DBClientBase*
的 dynamic_cast<mongo::DBClientConnection*>
是否是内存安全的,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
....
That link对答案有影响。
对于 c++-0x (#if __cplusplus >= 201103L
),BSONObj
具有移动构造函数和显式创建的复制构造函数:
BSONObj(const BSONObj&) = default;
对于低于c++0x的c++版本,也会创建默认的copy 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);
}
同样,指针被交换。
所以,我们不能用copy ctor或operator=
复制BSONObj
。
它应该在文档中正确反映,like for BSONObjBuilder
,如果它是有意的。
两种解决方案:
1) 在 makeQuery()
和 return 中的 BSONObj
上创建智能指针(更好)。
2) 在 makeQuery()
上下文中使用 BSONObj
(更糟糕的是:它可能仅用于短期且不重复的工作 BSONObj
,否则会导致代码不可读和非可重用性)。
EDIT1:创建智能指针并return使用它会导致相同的错误。 答案在the code comments:
内OWNED CASE
If the BSONObj owns the buffer, the buffer can be shared among several BSONObj's (by assignment). In this case the buffer is basically implemented as a shared_ptr.
UNOWNED CASE
A BSONObj can also point to BSON data in some other data structure it does not "own" or free later. For example, in a memory mapped file. In this case, it is important the original data stays in scope for as long as the BSONObj is in use. If you think the original data may go out of scope, call BSONObj::getOwned() to promote your BSONObj to having its own copy. On a BSONObj assignment, if the source is unowned, both the source and dest will have unowned pointers to the original buffer after the assignment. If you are not sure about ownership but need the buffer to last as long as the BSONObj, call getOwned().
copy()
方法与 getOwned() 一样有效,提供 BSONObj
的拥有副本。
Return of owned copy 结果没有错误。
mongo::BSONObj* curr_elem=new mongo::BSONObj();
(*curr_elem)=cursor->next().getOwned(); //or: next().copy();