Boost.Interprocess 兼容 STL 的分配器提供了一个兼容 STL 的分配器接口,并且如果它们将其内部 pointer typedef 定义为相对指针,则可以用于将 STL 容器放置在共享内存、内存映射文件或用户定义的内存段中。
最初,在 C++98 和 C++03 中,这个 pointer typedef 是无用的。正如 Scott Meyers 在他的《Effective STL》一书的第 10 项中提到的,“注意分配器约定和限制”
显然,如果任何 STL 实现忽略 pointer typedef,则不能使用智能指针作为 allocator::pointer。如果 STL 实现假设所有相同类型的分配器对象都比较相等,它将假设两个分配器(每个分配器从不同的内存池分配)是相等的,这将是一场彻底的灾难。
我们想要使用 Boost.Interprocess 放置在共享内存或内存映射文件中的 STL 容器不能做出任何这些假设,因此
理论上,这在 C++11 中得到了修复,当时引入了 std::pointer_traits
实用程序,并且标准库容器添加了对它们的支持。然而,由于 ABI 问题和用户需求不高,一些实现仍然不完全支持像 boost::interprocess:offset_ptr
这样的花式指针类型。这些实现仍然在某些容器中使用原始指针用于内部数据或迭代器实现。
这种不可移植的情况在 “P0773R0:迈向有意义的花式指针” 中进行了描述
我们可以使用偏移花式指针类型来安全地共享内存区域吗?
是的;但是对供应商的要求尚不明确,并且程序员可能会遇到陷阱。
情景 (A),偏移指针,只有一个站得住脚的解决方案,“继续部分支持。”
![]() |
重要提示 |
---|---|
由于 Boost.Container 是在 2011 年从 Boost.Interprocess 创建的,因此该库为了向后兼容性,维护了几个 <boost/interprocess/containers/.hpp> 头文件。这些头文件现在已被弃用,并将在未来的 Boost 版本中删除。用户应直接使用 Boost.Container 头文件 |
由于标准库实现对花式指针的部分支持,Boost.Interprocess 强烈建议使用 Boost.Container 容器,这些容器保证可以与 Boost.Interprocess 一起使用
以下 Boost.Container 容器与 Boost.Interprocess 兼容
deque
devector
flat_map/multimap
flat_set/multiset
list
map/multimap
set/multiset
slist
small_vector
stable_vector
static_vector
string
vector
要将这些容器中的任何一个放置在托管内存段中,我们必须使用 Boost.Interprocess 分配器定义分配器模板参数,以便容器在托管内存段中分配值。要将容器本身放置在共享内存中,我们只需像使用 Boost.Interprocess 创建任何其他对象一样在托管内存段中构造它
#include <boost/container/vector.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/managed_shared_memory.hpp> int main () { using namespace boost::interprocess; //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; //A managed shared memory where we can construct objects //associated with a c-string managed_shared_memory segment(create_only,"MyName", 65536); //Alias an STL-like allocator of ints that allocates ints from the segment typedef allocator<int, managed_shared_memory::segment_manager> ShmemAllocator; //Alias a vector that uses the previous STL-like allocator typedef boost::container::vector<int, ShmemAllocator> MyVector; int initVal[] = {0, 1, 2, 3, 4, 5, 6 }; const int *begVal = initVal; const int *endVal = initVal + sizeof(initVal)/sizeof(initVal[0]); //Initialize the STL-like allocator const ShmemAllocator alloc_inst (segment.get_segment_manager()); //Construct the vector in the shared memory segment with the STL-like allocator //from a range of iterators MyVector *myvector = segment.construct<MyVector> ("MyVector")/*object name*/ (begVal /*first ctor parameter*/ ,endVal /*second ctor parameter*/ ,alloc_inst /*third ctor parameter*/); //Use vector as your want std::sort(myvector->rbegin(), myvector->rend()); // . . . //When done, destroy and delete vector from the segment segment.destroy<MyVector>("MyVector"); return 0; }
这些容器还展示了创建/修改现有容器以使其可以放置在共享内存中是多么容易。
Boost.Interprocess 容器通过两种机制同时放置在共享内存/内存映射文件等中
construct<>
, find_or_construct<>
... 函数。这些函数将 C++ 对象放置在共享内存/内存映射文件中。但这仅放置对象本身,而不放置该对象可能动态分配的内存。这意味着要将任何 Boost.Interprocess 容器(包括 Boost.Interprocess 字符串)放置在共享内存或内存映射文件中,容器必须
construct<>
/find_or_construct<>
... 函数将容器放置在托管内存中。如果您执行了前两点,但没有使用 construct<>
/find_or_construct<>
,那么您创建的容器仅放置在您的进程中,但会从共享内存/内存映射文件为包含的类型分配内存。
让我们看一个例子
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/container/vector.hpp> #include <boost/container/string.hpp> #include <boost/interprocess/allocators/allocator.hpp> int main () { using namespace boost::interprocess; //Typedefs typedef allocator<char, managed_shared_memory::segment_manager> CharAllocator; typedef boost::container::basic_string<char, std::char_traits<char>, CharAllocator> MyShmString; typedef allocator<MyShmString, managed_shared_memory::segment_manager> StringAllocator; typedef boost::container::vector<MyShmString, StringAllocator> MyShmStringVector; //Open shared memory //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; managed_shared_memory shm(create_only, "MyName", 10000); //Create allocators CharAllocator charallocator (shm.get_segment_manager()); StringAllocator stringallocator(shm.get_segment_manager()); //This string is in only in this process (the pointer pointing to the //buffer that will hold the text is not in shared memory). //But the buffer that will hold "this is my text" is allocated from //shared memory MyShmString mystring(charallocator); mystring = "this is my text"; //This vector is only in this process (the pointer pointing to the //buffer that will hold the MyShmString-s is not in shared memory). //But the buffer that will hold 10 MyShmString-s is allocated from //shared memory using StringAllocator. Since strings use a shared //memory allocator (CharAllocator) the 10 buffers that hold //"this is my text" text are also in shared memory. MyShmStringVector myvector(stringallocator); myvector.insert(myvector.begin(), 10, mystring); //This vector is fully constructed in shared memory. All pointers //buffers are constructed in the same shared memory segment //This vector can be safely accessed from other processes. MyShmStringVector *myshmvector = shm.construct<MyShmStringVector>("myshmvector")(stringallocator); myshmvector->insert(myshmvector->begin(), 10, mystring); //Destroy vector. This will free all strings that the vector contains shm.destroy_ptr(myshmvector); return 0; }
在创建容器的容器时,每个容器都需要一个分配器。为了避免使用具有复杂类型定义的多个分配器,我们可以利用 void 分配器提供的类型擦除以及将 void 分配器隐式转换为分配其他类型的分配器的能力。
这里我们有一个在共享内存中构建 map 的示例。键是一个字符串,映射的类型是一个存储多个容器的类
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/container/map.hpp> #include <boost/container/vector.hpp> #include <boost/container/string.hpp> using namespace boost::interprocess; //Typedefs of allocators and containers typedef managed_shared_memory::segment_manager segment_manager_t; typedef allocator<void, segment_manager_t> void_allocator; typedef allocator<int, segment_manager_t> int_allocator; typedef boost::container::vector<int, int_allocator> int_vector; typedef allocator<int_vector, segment_manager_t> int_vector_allocator; typedef boost::container::vector<int_vector, int_vector_allocator> int_vector_vector; typedef allocator<char, segment_manager_t> char_allocator; typedef boost::container::basic_string<char, std::char_traits<char>, char_allocator> char_string; class complex_data { int id_; char_string char_string_; int_vector_vector int_vector_vector_; public: //Since void_allocator is convertible to any other allocator<T>, we can simplify //the initialization taking just one allocator for all inner containers. complex_data(int id, const char *name, const void_allocator &void_alloc) : id_(id), char_string_(name, void_alloc), int_vector_vector_(void_alloc) {} //Other members... }; //Definition of the map holding a string as key and complex_data as mapped type typedef std::pair<const char_string, complex_data> map_value_type; typedef std::pair<char_string, complex_data> movable_to_map_value_type; typedef allocator<map_value_type, segment_manager_t> map_value_type_allocator; typedef boost::container::map< char_string, complex_data , std::less<char_string>, map_value_type_allocator> complex_map_type; int main () { //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; //Create shared memory managed_shared_memory segment(create_only,"MyName", 65536); //An allocator convertible to any allocator<T, segment_manager_t> type void_allocator alloc_inst (segment.get_segment_manager()); //Construct the shared memory map and fill it complex_map_type *mymap = segment.construct<complex_map_type> //(object name), (first ctor parameter, second ctor parameter) ("MyMap")(std::less<char_string>(), alloc_inst); for(int i = 0; i < 100; ++i){ //Both key(string) and value(complex_data) need an allocator in their constructors char_string key_object(alloc_inst); complex_data mapped_object(i, "default_name", alloc_inst); map_value_type value(key_object, mapped_object); //Modify values and insert them in the map mymap->insert(value); } return 0; }