Boost 分配器的向量大小不正确

Incorrect vector size with Boost allocators

以下程序在内存映射文件的 space 中为 c 类型的对象 C 分配内存。将单个字符添加到包含在 c 中的矢量会将矢量的报告大小从 0 更改为 18446744073709551520。

#include <iostream>
#include <new>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>

using namespace boost::interprocess;

class C;

typedef managed_mapped_file::segment_manager SegmentManager;
typedef allocator<void, SegmentManager> VoidAllocator;
typedef allocator<char, SegmentManager> CharAllocator;
typedef allocator<C, SegmentManager> CAllocator;
typedef offset_ptr<C> CPtr;

class C {
  public:
    std::vector<char, CharAllocator> data;

    C(const VoidAllocator &voidAlloc) : data(voidAlloc) {}

    void add_char() {
      std::cout << data.size() << std::endl;
      data.push_back('x');
      std::cout << data.size() << std::endl;
    }
};

int main(int argc, char *argv[]) {
  boost::filesystem::remove_all("file");
  managed_mapped_file segment(create_only, "file", 100000);

  VoidAllocator allocator_instance(segment.get_segment_manager());
  CAllocator alloc_c(allocator_instance);
  CPtr c = alloc_c.allocate_one();
  *c = C(allocator_instance);
  c->add_char();

  return 0;
}

c 分配在堆栈上而不是动态分配时,不会出现此问题。

C c(allocator_instance);
c.add_char();

我使用以下命令使用 Boost 1.62 和 g++ 6.3.0-18 在 Debian GNU/Linux stretch 上编译代码。

g++ -Wall -pthread  -lboost_system -lboost_filesystem t.cpp -o t

似乎将构造的对象从默认内存池移动到段 1 以某种方式混淆了向量的实现。使用construct方法直接在segment内存池内构造对象即可解决

CPtr c = segment.construct<C>(anonymous_instance) (allocator_instance);

分配器returns原始的、未初始化的内存。

通过它间接指向类型 C 的对象是 Undefined Behaviour.

当然,您可以使用 placement-new 实际完成繁重的工作:

CPtr c = alloc_c.allocate_one();
new (&*c) C(allocator_instance);

请注意,同样,对于非 POD(或者实际上,不可平凡破坏的类型),您还必须记住在适当的时间调用析构函数:

CPtr c = alloc_c.allocate_one();

new (&*c) C(allocator_instance);
*c = C(allocator_instance);

c->add_char();

c->~C();
alloc_c.deallocate_one(c);

但是正如您已经指出的那样,高级方法避免了 new/delete 并使用了段管理器:

CPtr c = segment.construct<C>("Name") (allocator_instance); 

演示

正在使用

  • find_or_construct 这样可以按名称检索共享对象(如果您不想要,请使用 anonymousunique 实例;另请注意,您可以通过相同的名称实例化多个实例)

  • 使用 allocator_type 有助于使用作用域分配器适配器(思考:vector<C>

  • 参数化C使得它可以很容易地与标准分配器和共享内存分配器一起使用

  • 使用段管理器指针和分配器实例的隐式转换

  • 隐藏实现细节offset_ptr(90% 的时间你不需要知道)

Live On Coliru

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <iostream>

namespace bip = boost::interprocess;

namespace Shared {
    using Segment         = bip::managed_mapped_file;
    using Manager         = Segment::segment_manager;
    template <typename T = void>
        using Alloc       = bip::allocator<T, Manager>;
    template <typename T>
        using Vector      = bip::vector<T, Alloc<T> >;

    template <typename Alloc = std::allocator<void> >
    struct C {
        using allocator_type = Alloc;
        bip::vector<char, typename Alloc::template rebind<char>::other> data;

        C(Alloc alloc = {}) : data(alloc) {}

        void add_char() {
            std::cout << data.size() << std::endl;
            data.push_back('x');
            std::cout << data.size() << std::endl;
        }
    };
}

int main() {
    std::remove("file");
    Shared::Segment mmf(bip::create_only, "file", 1000000);

    using Alloc = Shared::Alloc<>;
    using C = Shared::C<Alloc>;

    auto* c = mmf.find_or_construct<C>("byname")(mmf.get_segment_manager());

    c->add_char();

    //mmf.destroy_ptr(c);
}

版画

0
1