[cfe-dev] [libc++] Should IO manipulators be noncopyable?

Marshall Clow mclow.lists at gmail.com
Tue Jul 8 11:31:10 PDT 2014


On Jul 7, 2014, at 11:23 AM, David Blaikie <dblaikie at gmail.com> wrote:

> +mclow
> 
> do these even need to be movable?

They need to be moveable, because they get returned from std::quoted.

As to Jim’s question; I don’t have an answer off the top of my head.
Though I will point out that string_view has the exact same lifetime problem, 
since it doesn’t own its’ storage, but rather holds a reference to other storage.

— Marshall

> 
> On Sat, Jul 5, 2014 at 3:05 PM, Jim Porter <jvp4846 at g.rit.edu> wrote:
>> Recently, I was trying out the new std::quoted IO manipulator and came
>> across an interesting issue: since libc++'s std::quoted is copyable (as are
>> all the other IO manipulators I looked at), passing a temporary as the first
>> argument introduces a lifetime bug in the following code:
>> 
>>  auto ensure_printable(const std::wstring &s) {
>>    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> conv;
>>    return std::quoted(conv.to_bytes(s));
>>  }
>> 
>>  // ...
>> 
>>  std::cout << ensure_printable(std::wstring(L"hi"));

Great example, btw.


>> 
>> In C++98, it wasn't possible to do something like this without making
>> obviously non-standard code (i.e. explicitly writing out the
>> implementation-defined named for the return type of std::quoted()). In
>> C++11, and even moreso in C++14, this is no longer necessary thanks to the
>> `auto` keyword. Hence, (mostly) innocent-looking code like above can
>> introduce subtle bugs.
>> 
>> Obviously, since the return type of std::quoted() is unspecified, the above
>> code relies on undefined behavior: there's no guarantee in the standard that
>> it's copyable in the first place! After some discussion, this got me to
>> thinking about why any of the IO manipulators are implemented as copyable
>> types; presumably, this is mostly because it didn't matter back in C++98
>> when most of them were introduced. However, with deduced return types, this
>> matters quite a bit more.
>> 
>> If the IO manipulators were written to return a const version of their proxy
>> type, and said type were move-only, I think this would resolve the issue,
>> and only allow the IO manipulators to be used as temporaries (much like they
>> were in C++98). Something like so:
>> 
>>  struct my_manipulator_proxy {
>>    my_manipulator_proxy(/* ... */) {}
>>    my_manipulator_proxy(const my_manipulator_proxy &) = delete;
>>    my_manipulator_proxy(my_manipulator_proxy &&) = default;
>>    /* ... */
>>  };
>> 
>>  std::ostream & operator << (std::ostream &s,
>>                              const my_manipulator_proxy &m) {
>>    /* ... */
>>  }
>> 
>>  const my_manipulator_proxy my_manipulator(/* ... */) {
>>    return my_manipulator_proxy(/* ... */);
>>  }
>> 
>> Having looked at the implementation of std::quoted, this seems like an
>> uncomplicated change (I imagine it's so for the other manipulators as well),
>> and helps prevent undefined behavior. Does this sound like a reasonable way
>> to go? I'd be happy to write a patch if others agree that this is sensible.
>> 
>> - Jim
>> 
>> _______________________________________________
>> cfe-dev mailing list
>> cfe-dev at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev





More information about the cfe-dev mailing list