有没有办法为向量采用内存资源?

is there any way to adopt a memory resource for a vector?

我已经开始在我的项目中使用 pmr::allocators,我已经看到使用它们带来的性能提升和优势。我使用的分配器与我在下面的简单示例中展示的非常相似:

#include <array>
#include <boost/container/flat_map.hpp>
#include <cassert>
#include <iostream>
#include <memory_resource>
#include <string>
#include <vector>

struct MessageBody {
  using map_t = boost::container::flat_map<
      char, char, std::less<char>,
      std::pmr::polymorphic_allocator<std::pair<char, char>>>;

  using vector_t = std::vector<char, std::pmr::polymorphic_allocator<char>>;

  MessageBody(std::pmr::memory_resource& mem_v,
              std::pmr::memory_resource& mem_m)
      : vec_(0, &mem_v), map_(&mem_m) {}

  vector_t vec_;
  map_t map_;
};

int main() {
  std::array<char, 1000> buffer;
  buffer.fill('[=10=]');
  std::pmr::monotonic_buffer_resource vec_mem(buffer.data(), 500,
                                              std::pmr::null_memory_resource());
  std::pmr::monotonic_buffer_resource map_mem(buffer.data() + 500, 500,
                                              std::pmr::null_memory_resource());
  {
    MessageBody message(vec_mem, map_mem);
    message.vec_.push_back('1');
    assert(message.vec_.size() == 1);
  }

  {
    MessageBody message(vec_mem, map_mem);
    assert(message.vec_.size() == 1);   /// I want to adopt the previous class for a new class.
  }
}

我的问题是是否有任何方法可以将 memory_resources 用于另一个 class 而无需在 vectormap 中重新填充整个数据。

我能想到的唯一方法(我知道这是一个糟糕的想法)是实现一个新的 class 继承自 std::vector,即 class有一个 adopt data 方法,在不修改缓冲区的情况下将向量内部的 size 设置为先前使用的向量的大小。

这里有一个 link 的例子。

https://godbolt.org/z/fcox5vTdE

您想要的不是采用内存资源 - 因为您可以而且可以做到 - 而是也将向量本身存储在那里

你可以,例如使用助手创建带有自定义删除器的 unique_ptr:

template <typename T>
inline static auto make_T(Mem& mem) {
    std::pmr::polymorphic_allocator<T> alloc(&mem);
    return std::unique_ptr{
        alloc.template new_object<T>(), // allocator is propagated
        [alloc](T* p) { alloc.delete_object(p); }};
}

It requires a bit more work when the library support for C++20 is not complete, so let me show that in a live demo on Compiler Explorer

但是,您无法使用内存的 C++ 抽象机器模型在同一内存上“复活”非平凡类型。

缓冲区中的活动对象 - 输入 Boost Interprocess

您真正想要的是在那里存储/活动对象/并从其他地方访问它们。

您可以使用共享内存分配器来做到这一点,例如 Boost Interprocess。您可以在

  • 手动分配缓冲区(如单调的内存资源)
  • 托管共享内存段
  • 托管映射文件

在后一种情况下当然有持久化和多进程访问的好处。

If you don't need IPC or persistence, but like the managed segment abilities, use managed_external_buffer

使用托管外部缓冲区的演示

设置几个类型定义:

namespace Shared {
    using Mem = bip::managed_external_buffer;

    template <typename T>
    using Alloc = boost::container::scoped_allocator_adaptor<
        bip::allocator<T, Mem::segment_manager>>;

    template <typename T>
    using Vector = boost::container::vector<T, Alloc<T> >;

    template <typename K, typename V, typename Cmp = std::less<K>>
    using Map =
        boost::container::flat_map<K, V, std::less<K>, Alloc<std::pair<K, V>>>;
} // namespace Shared

请注意我是如何使用 scoped_allocator_adaptor 来尽可能接近 PMR 根据 uses_allocator<>.

将容器分配器传播到元素类型的行为
struct MessageBody
{
    using map_t = Shared::Map<char, char>;
    using vector_t = Shared::Vector<char>;

    template <typename Alloc>
    MessageBody(Alloc alloc) : vec_(alloc), map_(alloc)
    { }

    vector_t vec_;
    map_t    map_;
};

不再需要unique_ptr,你的数据结构基本上就是你的数据结构,但是我们将所有内容都存储在一个内存资源中,所以整个东西可以“复活”为一个:

int main() {
  std::array<char, 1000> buffer;
  buffer.fill('[=13=]');

  {
      Shared::Mem mem(bip::create_only, buffer.data(), buffer.size());

      auto& message = *mem.find_or_construct<MessageBody>("message")(
          mem.get_segment_manager());

      message.vec_.push_back('1');
      assert(message.vec_.size() == 1);
  }

  {
      Shared::Mem mem(bip::open_only, buffer.data(), buffer.size());

      auto& message = *mem.find_or_construct<MessageBody>("message")(
          mem.get_segment_manager());

      assert(message.vec_.size() == 1);
  }
  std::cout << "Bye" << "\n";
}

get_segment_manager() 调用 returns 一个指针作为 Shared::Alloc<> 个实例的初始值设定项。

现在它运行通过断言:Live On Coliru

只打印

Bye