Reputation: 42934
According to the MSDN documentation for CString's GetBufferSetLength()
, a call to that method should be followed by a matching call to ReleaseBuffer()
.
However, in the sample code in the same page, a comment states that calling ReleaseBuffer()
is unnecessary:
CSimpleString str(pMgr); LPTSTR pstr = str.GetBufferSetLength(3); pstr[0] = _T('C'); pstr[1] = _T('u'); pstr[2] = _T('p'); // No need for trailing zero or call to ReleaseBuffer() // because GetBufferSetLength() set it for us. str += _T(" soccer is best!"); ASSERT(_tcscmp(str, _T("Cup soccer is best!")) == 0);
So, should correct code call ReleaseBuffer()
after GetBufferSetLength()
, or is that call unnecessary?
Upvotes: 5
Views: 1805
Reputation: 51394
The documentation for CSimpleStringT::GetBufferSetLength
is unambiguously clear:
If you use the pointer returned by
CSimpleStringT::GetBufferSetLength
to change the string contents, callReleaseBuffer
to update the internal state ofCSimpleStringT
before you use any otherCSimpleStringT
methods.
Sample code is not contractual. In the event, where sample code contradicts the formal specification, go with the formal specification. The sample code in the documentation is written against a particular implementation detail that's not part of the contract. The formal specification is contractual.
Since the documentation is a bit shallow on explaining why those class members exist at all, here is some rationale to understand the particular problem they solve, and learn why the rules are the way they are:
Classes typically have a set of properties that always hold. Those are invariants. Class member implementations are allowed to temporarily violate those invariants, but when they return, all invariants must have been restored. Client code as well as class implementations rely on those invariants.
For sake of illustration, let's assume a simplified CString
implementation that stores two pieces of information: A length value and a pointer to a character array. Its invariant is that the pointer will always point to a character array of length
characters followed by a NUL terminator. It is established once a class instance has been constructed, and maintained for each call to its public interface. (The actual CStringT
class template stores a lot more state that needs to remain consistent.)
Occasionally, particularly when interfacing with C code, it becomes necessary to put an object into a state where invariants aren't maintained. This allows a C API to write directly into a buffer that's managed by a C++ class, reducing the overhead of allocating a temporary buffer, possibly resizing the internal buffer, and then copying everything over. That's the main use case for GetBuffer
and GetBufferSetLength
. Once a value has been returned, the class no longer promises to maintain any invariants. A consequence of this is that it is no longer allowed to call any members of the public interface, until the invariants have been restored. That's the purpose of ReleaseBuffer
.
Mind you, there's a lot of state that needs to remain consistent. For example, CStringT
maintains a capacity alongside the string length in an attempt to reduce allocations. It implements short-string optimization, and supports shared ownership with copy-on-write semantics. If you decide to be clever and skip over the mandatory call to ReleaseBuffer
(like the author of the sample), all those invariants instantly become your responsibility.
A final note on whether it is or isn't allowed to call ReleaseBufferSetLength
in place of ReleaseBuffer
: I believe they are functionally equivalent for all values of nNewLength
other than -1
. The documentation is somewhat fuzzy here, calling them "functionally similar", though I think this is more of an issue with the wording rather than a hint towards an actual difference.
Note: It is not required for an implementation to fail, when used without following it's documented protocol. As a consequence, you cannot test, whether all steps of that protocol are required.
Upvotes: 3
Reputation: 308158
The purpose of ReleaseBuffer
is to sync the state of the C-style string that the buffer contains with the state of the CString
internal variables. Presumably this is just getting the final string length and storing it internally, and perhaps reallocating the buffer if there's a large discrepancy.
In the case of the example, the string length was stated to be exactly 3 characters. Since the size of the string didn't change via manipulation of the buffer, there's no need to update the length after that manipulation.
Upvotes: 4