Reputation: 507095
Say we have a function store
void store(const QByteArray& data);
This function's job is to take data
and store it away. Unfortunately, it is not safe to do that if the argument was created with QByteArray::fromRawData(ptr, size)
, because it and all its copies require that ptr
remains valid.
Therefore store
has no option to forbid its callers to pass in such an array, to treat data
as if it was a const char*
on stereoids or to force a deep-copy with detach
. All of this is not satisfying and especially the latter hurts performance because if data
was COW-copied before being passed to store
, we will be doing an unnecessary deep-copy.
QByteArray
has a private
function nulTerminated
whose implementation seems to do just what I want: If it doesn't own the memory, it deep copies. And if it owns the memory, it does nothing but return *this
.
Two questions really
Is there a work-around using public
facilities?
The Qt docs mention that ptr
must only be alive for the lifetime of the return value and any copies of it. If you say .right(.size())
, it would seem this is not a copy, so Qt would need to make a deep copy according to the docs. But does it really do so?
Upvotes: 5
Views: 3385
Reputation: 153
this sounds more like a conceptual problem, rather than technical.
store()
tries to take absolute ownership, but "taking ownership" on a QByteArray does not make much sense conceptually. A copy might share its memory with another, or it might not, or it might be created from raw data, so there's a hidden ownership juggle going on under the hood and the whole class is designed to hide this from you. The whole implicit sharing thing doesn't mesh well with modern explicit ownership transfer paradigm in general, and QByteArray complicates matters further by providing the raw data facility.
If possible, accepting a non-implicitly-shared container that holds absolute ownership over their content (such as std::string, which is easily generated with QByteArray::toStdString()) by rvalue, forcing callers to copy, would be the best option.
Upvotes: 0
Reputation: 62838
Looking at the source (for example here), ba.right(ba.size());
is actually plain shallow copy, almost a no-op, so that's not a solution for you. And in any case, relying on any behaviour not backed by documentation is a bit unsafe, it could be changed without notification in future Qt versions.
That being said, QByteArray::detach()
is undocumented but public. It will perform deep copy on arrays created with fromRawData()
, but not on already unshared data, and I think this is unlikely to change. Demonstration:
QByteArray ba1 = QByteArray::fromRawData("foo", 4);
QByteArray ba2("foo");
qDebug() << (void*)ba1.constData() << (void*)ba2.constData();
ba1.detach(); ba2.detach();
qDebug() << (void*)ba1.constData() << (void*)ba2.constData();
Output of above for example:
0x804b960 0x93ebfd8
0x93d2170 0x93ebfd8
Looking at the source, IS_RAW_DATA
macro is in the qbytearray.cpp file, and I did not spot any way that you could take advantage of it using public interface. So, to do what you want seems to not be possible, and detach()
is the closest you can get, even QByteArray::squeeze()
doesn't detach raw data.
Upvotes: 2