C++ placement new 与 boost 分配器不兼容
C++ placement new not compatible with boost allocator
我正在使用 Boost.Interprocess 共享内存,并且我正在将进程间分配器与一些 STL 兼容容器一起使用,当涉及到 placement new
时,代码无法编译,因为 placement new
期望 void *
而 boost::interprocess::allocator::pointer
无法转换为 void *
.
下面是重现这个的代码
#include <memory>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
namespace ipc = boost::interprocess;
typedef ipc::allocator<int, ipc::managed_shared_memory::segment_manager> ShmemAllocator;
int main() {
struct shm_remove {
shm_remove() { ipc::shared_memory_object::remove("MySharedMemory"); }
~shm_remove() { ipc::shared_memory_object::remove("MySharedMemory"); }
} remover;
ipc::managed_shared_memory segment(ipc::open_or_create, "MySharedMemory", 1024 * 1024 * 10);
ShmemAllocator shm_alloc(segment.get_segment_manager());
ShmemAllocator::pointer shm_ptr = shm_alloc.allocate(1);
std::allocator<int> std_alloc;
std::allocator<int>::pointer std_ptr = std_alloc.allocate(1);
new(std_ptr) int(3);
new(shm_ptr) int(3); // this line doesn't work
return 0;
}
我得到的编译错误:
/home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp: In function ‘int main()’:
/home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp:22:21: error: no matching function for call to ‘operator new(sizetype, boost::interprocess::allocator<int, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >::pointer&)’
new(shm_ptr) int(3); // this line doesn't work
^
In file included from /usr/include/c++/5/ext/new_allocator.h:33:0,
from /usr/include/x86_64-linux-gnu/c++/5/bits/c++allocator.h:33,
from /usr/include/c++/5/bits/allocator.h:46,
from /usr/include/c++/5/memory:63,
from /home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp:1:
/usr/include/c++/5/new:111:7: note: candidate: void* operator new(std::size_t)
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
^
/usr/include/c++/5/new:111:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/5/new:119:7: note: candidate: void* operator new(std::size_t, const std::nothrow_t&)
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
^
/usr/include/c++/5/new:119:7: note: no known conversion for argument 2 from ‘boost::interprocess::allocator<int, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >::pointer {aka boost::interprocess::offset_ptr<int, long int, long unsigned int, 0ul>}’ to ‘const std::nothrow_t&’
/usr/include/c++/5/new:129:14: note: candidate: void* operator new(std::size_t, void*)
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
^
/usr/include/c++/5/new:129:14: note: no known conversion for argument 2 from ‘boost::interprocess::allocator<int, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >::pointer {aka boost::interprocess::offset_ptr<int, long int, long unsigned int, 0ul>}’ to ‘void*’
In file included from /home/ziqi.liu/.tspkg/include/boost/interprocess/mem_algo/detail/mem_algo_common.hpp:35:0,
from /home/ziqi.liu/.tspkg/include/boost/interprocess/allocators/detail/allocator_common.hpp:35,
from /home/ziqi.liu/.tspkg/include/boost/interprocess/allocators/allocator.hpp:30,
from /home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp:2:
/home/ziqi.liu/.tspkg/include/boost/container/detail/placement_new.hpp:24:14: note: candidate: void* operator new(std::size_t, void*, boost_container_new_t)
inline void *operator new(std::size_t, void *p, boost_container_new_t)
^
/home/ziqi.liu/.tspkg/include/boost/container/detail/placement_new.hpp:24:14: note: candidate expects 3 arguments, 2 provided
CMakeFiles/main_a.dir/build.make:62: recipe for target 'CMakeFiles/main_a.dir/src/main_a.cpp.o' failed
make[2]: *** [CMakeFiles/main_a.dir/src/main_a.cpp.o] Error 1
CMakeFiles/Makefile2:162: recipe for target 'CMakeFiles/main_a.dir/all' failed
make[1]: *** [CMakeFiles/main_a.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
我确定一定有办法做到这一点,因为 boost 自己的容器都与 boost::interprocess::allocator::pointer
兼容,但我不确定他们是怎么做到的......我想我应该寻找替代 placement new?
当您在容器中使用分配器时,构造对象的正确方法是调用
std::allocator_traits<AllocatorType>::construct(alloc, pointer, ...constructor parameters...)
有关更多信息,请参阅 CppReference。
如果您需要 C++03 解决方案(C++03 中没有 allocator_traits
),那么您可以直接调用分配器 construct
方法 - 但这不是推荐的解决方案。
你是对的,标准容器不提供花哨的指针。参见示例
https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers:
An example of a fancy pointer is the mapping address-independent
pointer boost::interprocess::offset_ptr, which makes it possible to
allocate node-based data structures such as std::set in shared memory
and memory mapped files mapped in different addresses in every
process. Fancy pointers can be used independently of the allocator
that provided them, through the class template std::pointer_traits
(since C++11). The function std::to_address can be used to obtain a
raw pointer from a fancy pointer. (since C++20)
-
我通常选择使用 Boost Container 集合,因为它们 支持这些。而且,根据我的经验,它们在作用域分配器适配器上的表现似乎比大多数标准库实现要好得多。
偷懒一下:
#include <boost/container/container_fwd.hpp>
#include <boost/container/scoped_allocator_fwd.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/container/vector.hpp>
#include <boost/container/map.hpp>
#include <boost/container/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <functional>
#include <memory>
namespace ipc = boost::interprocess;
namespace bc = boost::container;
template <typename T>
using Alloc = ipc::allocator<T, ipc::managed_mapped_file::segment_manager>;
template <typename T>
using ScopedAlloc = bc::scoped_allocator_adaptor<Alloc<T> >;
template <typename T>
using SharedVector = bc::vector<T, Alloc<T> >;
template <typename K, typename V, typename Pair = std::pair<K const, V> >
using SharedMap = bc::map<K, V, std::less<>, ScopedAlloc<Pair> >;
int main() {
ipc::managed_mapped_file segment(ipc::open_or_create, "MySharedMemory", 1024 * 100);
auto* sm = segment.get_segment_manager();
{
auto &v = *segment.find_or_construct<SharedVector<int>>("vector")(sm);
v.push_back(3);
v.insert(v.begin(), 5);
v.emplace_back(16);
}
{
using String = bc::basic_string<char, std::char_traits<char>, ScopedAlloc<char> >;
auto &m = *segment.find_or_construct<SharedMap<String, String> >("map")(sm);
m.emplace("whoa", "not bad");
m.emplace("at", "all");
}
}
请注意,由于 Coliru 的限制,我使用了较小的尺寸 managed_mapped_file
我正在使用 Boost.Interprocess 共享内存,并且我正在将进程间分配器与一些 STL 兼容容器一起使用,当涉及到 placement new
时,代码无法编译,因为 placement new
期望 void *
而 boost::interprocess::allocator::pointer
无法转换为 void *
.
下面是重现这个的代码
#include <memory>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
namespace ipc = boost::interprocess;
typedef ipc::allocator<int, ipc::managed_shared_memory::segment_manager> ShmemAllocator;
int main() {
struct shm_remove {
shm_remove() { ipc::shared_memory_object::remove("MySharedMemory"); }
~shm_remove() { ipc::shared_memory_object::remove("MySharedMemory"); }
} remover;
ipc::managed_shared_memory segment(ipc::open_or_create, "MySharedMemory", 1024 * 1024 * 10);
ShmemAllocator shm_alloc(segment.get_segment_manager());
ShmemAllocator::pointer shm_ptr = shm_alloc.allocate(1);
std::allocator<int> std_alloc;
std::allocator<int>::pointer std_ptr = std_alloc.allocate(1);
new(std_ptr) int(3);
new(shm_ptr) int(3); // this line doesn't work
return 0;
}
我得到的编译错误:
/home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp: In function ‘int main()’:
/home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp:22:21: error: no matching function for call to ‘operator new(sizetype, boost::interprocess::allocator<int, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >::pointer&)’
new(shm_ptr) int(3); // this line doesn't work
^
In file included from /usr/include/c++/5/ext/new_allocator.h:33:0,
from /usr/include/x86_64-linux-gnu/c++/5/bits/c++allocator.h:33,
from /usr/include/c++/5/bits/allocator.h:46,
from /usr/include/c++/5/memory:63,
from /home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp:1:
/usr/include/c++/5/new:111:7: note: candidate: void* operator new(std::size_t)
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
^
/usr/include/c++/5/new:111:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/5/new:119:7: note: candidate: void* operator new(std::size_t, const std::nothrow_t&)
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
^
/usr/include/c++/5/new:119:7: note: no known conversion for argument 2 from ‘boost::interprocess::allocator<int, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >::pointer {aka boost::interprocess::offset_ptr<int, long int, long unsigned int, 0ul>}’ to ‘const std::nothrow_t&’
/usr/include/c++/5/new:129:14: note: candidate: void* operator new(std::size_t, void*)
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
^
/usr/include/c++/5/new:129:14: note: no known conversion for argument 2 from ‘boost::interprocess::allocator<int, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >::pointer {aka boost::interprocess::offset_ptr<int, long int, long unsigned int, 0ul>}’ to ‘void*’
In file included from /home/ziqi.liu/.tspkg/include/boost/interprocess/mem_algo/detail/mem_algo_common.hpp:35:0,
from /home/ziqi.liu/.tspkg/include/boost/interprocess/allocators/detail/allocator_common.hpp:35,
from /home/ziqi.liu/.tspkg/include/boost/interprocess/allocators/allocator.hpp:30,
from /home/ziqi.liu/ClionProjects/shared_memory_experiment/src/main_a.cpp:2:
/home/ziqi.liu/.tspkg/include/boost/container/detail/placement_new.hpp:24:14: note: candidate: void* operator new(std::size_t, void*, boost_container_new_t)
inline void *operator new(std::size_t, void *p, boost_container_new_t)
^
/home/ziqi.liu/.tspkg/include/boost/container/detail/placement_new.hpp:24:14: note: candidate expects 3 arguments, 2 provided
CMakeFiles/main_a.dir/build.make:62: recipe for target 'CMakeFiles/main_a.dir/src/main_a.cpp.o' failed
make[2]: *** [CMakeFiles/main_a.dir/src/main_a.cpp.o] Error 1
CMakeFiles/Makefile2:162: recipe for target 'CMakeFiles/main_a.dir/all' failed
make[1]: *** [CMakeFiles/main_a.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
我确定一定有办法做到这一点,因为 boost 自己的容器都与 boost::interprocess::allocator::pointer
兼容,但我不确定他们是怎么做到的......我想我应该寻找替代 placement new?
当您在容器中使用分配器时,构造对象的正确方法是调用
std::allocator_traits<AllocatorType>::construct(alloc, pointer, ...constructor parameters...)
有关更多信息,请参阅 CppReference。
如果您需要 C++03 解决方案(C++03 中没有 allocator_traits
),那么您可以直接调用分配器 construct
方法 - 但这不是推荐的解决方案。
你是对的,标准容器不提供花哨的指针。参见示例
https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers:
An example of a fancy pointer is the mapping address-independent pointer boost::interprocess::offset_ptr, which makes it possible to allocate node-based data structures such as std::set in shared memory and memory mapped files mapped in different addresses in every process. Fancy pointers can be used independently of the allocator that provided them, through the class template std::pointer_traits (since C++11). The function std::to_address can be used to obtain a raw pointer from a fancy pointer. (since C++20)
我通常选择使用 Boost Container 集合,因为它们 支持这些。而且,根据我的经验,它们在作用域分配器适配器上的表现似乎比大多数标准库实现要好得多。
偷懒一下:
#include <boost/container/container_fwd.hpp>
#include <boost/container/scoped_allocator_fwd.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/container/vector.hpp>
#include <boost/container/map.hpp>
#include <boost/container/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <functional>
#include <memory>
namespace ipc = boost::interprocess;
namespace bc = boost::container;
template <typename T>
using Alloc = ipc::allocator<T, ipc::managed_mapped_file::segment_manager>;
template <typename T>
using ScopedAlloc = bc::scoped_allocator_adaptor<Alloc<T> >;
template <typename T>
using SharedVector = bc::vector<T, Alloc<T> >;
template <typename K, typename V, typename Pair = std::pair<K const, V> >
using SharedMap = bc::map<K, V, std::less<>, ScopedAlloc<Pair> >;
int main() {
ipc::managed_mapped_file segment(ipc::open_or_create, "MySharedMemory", 1024 * 100);
auto* sm = segment.get_segment_manager();
{
auto &v = *segment.find_or_construct<SharedVector<int>>("vector")(sm);
v.push_back(3);
v.insert(v.begin(), 5);
v.emplace_back(16);
}
{
using String = bc::basic_string<char, std::char_traits<char>, ScopedAlloc<char> >;
auto &m = *segment.find_or_construct<SharedMap<String, String> >("map")(sm);
m.emplace("whoa", "not bad");
m.emplace("at", "all");
}
}
请注意,由于 Coliru 的限制,我使用了较小的尺寸 managed_mapped_file