boostorg / container

STL-like containers from Boost
http://www.boost.org/libs/container/
Boost Software License 1.0
96 stars 116 forks source link

Feature request: Make `static_vector` "trivially relocatable" when possible #258

Open Quuxplusone opened 7 months ago

Quuxplusone commented 7 months ago

It was recently discussed on the cpplang Slack that Boost implements a lot of class types that are "Platonically trivially relocatable" without being known to the compiler to be trivially relocatable. In P1144R9 my go-to example was boost::movelib::unique_ptr<int>; but in the above discussion it was pointed out that boost::container::static_vector<int,10> would be a less archaic example, so P1144R10 might switch to that.

Executive summary: Wouldn't it be cool if we could write—

static_assert(__is_trivially_relocatable(boost::container::static_vector<int,10>));
static_assert(!__is_trivially_relocatable(boost::container::static_vector<std::list<int>,10>));

The __is_trivially_relocatable is available only on Clang so far, and the necessary std::is_trivially_relocatable_v/[[trivially_relocatable]] trait/attribute are available only in my fork of Clang/libc++; but both levels of support are detectable via the preprocessor (see Godbolt).

I propose to add an #ifdef so that static_vector and perhaps other containers will be recognized as trivially relocatable whenever the compiler/STL support that notion.

psiha commented 6 months ago

upvote

psiha commented 6 months ago

Actually aren't all Boost.Containers ('platonically') trivially relocatable (simply by the fact of not being self-referential)?

static_vector should in addition be fully trivial for all trivial Ts - yet it fails for example the std::is_trivially_move_constructible check for a trivial T.

Quuxplusone commented 6 months ago

Actually aren't all Boost.Containers ('platonically') trivially relocatable (simply by the fact of not being self-referential)?

Yes, if their components are. For example,

  template<class T>
  using ShmAlloc = boost::interprocess::allocator<T, boost::interprocess::managed_shared_memory::segment_manager>;
  static_assert(!is_trivially_relocatable_v<boost::container::vector<int, ShmAlloc<int>>>);

because such a vector's move-construct-and-destroy needs more than memcpy — it needs to update the object representation of its interprocess::offset_ptr data member. So it's generally tricky to figure that out without a lot more machinery (one piece of which, ideally, would be STL support for std::is_trivially_relocatable — that's P1144).

Also, I'm taking your word for it that "all Boost.Containers" are "not self-referential." E.g. I know Microsoft's std::list<int> is trivially relocatable because it uses a "sentinel node" on the heap; libstdc++'s and libc++'s std::list<int> are non-trivially relocatable because they use a "dummy node" inside the list object itself; but I have no idea what boost::container::list does. Basically static_vector is the only one where I'm confident I understand (and can correctly implement) all of the icky innards involved with its relocatability.

I also agree that is_trivially_move_constructible_v<T> ought to imply is_trivially_move_constructible_v<static_vector<T,10>>, and is_trivially_copyable_v<T> ought to imply is_trivially_copyable_v<static_vector<T,10>>. But the latter (changing trivial copyability of a type) is an ABI break, and I don't know Boost's policies on ABI breaks. Adding the [[trivially_relocatable]] attribute definitely isn't an ABI break.