Mordachai Mordachai - 3 months ago 15
C++ Question

Temporary modifyable adapters

I find it convenient to (and have a lot of code which) wrappers some storage object in an allocation adapter, and then this allocation adapter is used commonly to scope the guaranteed backstore in a managing object for its lifetime, which is normally the lifetime of the function call.

This seems to be using a nonstandard VisualStudio extension, however, and I'm curious as to what is a better paradigm and why..

e.g. a lot of our code still uses

CString
. One of the features of a
CString
is the ability to lock its contents so that you can take a non-const pointer into the underlying buffer and manipulate it directly - a'la C library functions.

This makes it very easy to use a
CString
for its automatic resource management, but still interface to legacy libraries / code which needed a writable character buffer. However, locking the buffer is not itself an RAII operation, so I made an RAII class to perform that function (like taking on a lock):

// replaces each occurrence of any of the given list of characters with a specified replacement character in-place
inline void ReplaceAll(CStringW & str, const wchar_t * chsOld, const wchar_t chNew)
{
ReplaceAll(make_autobuffer(str), chsOld, chNew);
}


The code behind make_autobuffer is rather long-winded, but the idea boils down to returning an object that holds a lock on the underlying string's buffer, and will ask the str to release that buffer lock on its destruction, thus releasing the buffer back to control by the CString.

This is a trivial idea, and the implementation is fairly trivial as well (lots of boiler plate to make it robust wrt wide/narrow strings, and variations that take a fixed buffer size or not, and some debugging helpers, etc.).

But more generally speaking, I have often found it very useful to have a class which takes a non-const reference to an instance of something, and wrappers that instance in some sort of mutable adapter layer, and then releases the underlying entity upon termination.

The question is whether there is a better way to accomplish this than having to create a named variable to do the adaptation:

// replaces each occurrence of any of the given list of characters with a specified replacement character in-place
inline void ReplaceAll(CStringW & str, const wchar_t * chsOld, const wchar_t chNew)
{
auto adapter = make_autobuffer(str);
ReplaceAll(adapter, chsOld, chNew);
}


This works, and doesn't violate the standard. I'm no longer trying to pass a non-const object by ref from a temp object - since I've forced the temp object into being less temporary by naming it.

But... that seems silly. At the end of the day, doing the above doesn't change the meaning as far as I can tell. And if the VisualStudio allowance was non-standard, then is there a better standard way that isn't so silly?

Answer

Ultimately what I chose to do to solve this was to change from using Wrapper & to Wrapper (by copy / move).

Passing a non-const wrapper by ref is a clear violation of C++ argument rules. Having to declare it explicitly was annoying. Passing it by value meant that I could take advantage of Cx11's move rules and maintain no copies for a CStringAutoBuffer (and other similar types of temporary wrapper objects), but still create one on the fly without having to explicitly name one locally.

This preserves const correctness as well.

Overall, a happy resolution to this genre of issue. :)