Reputation: 3171
I'm using Boost.Interprocess shared memory, and I'm using interprocess Allocator with some STL compatible containers, when it comes to placement new
, the code won't compile since placement new
expect void *
while boost::interprocess::allocator::pointer
can't not be converted to void *
.
below is a code to reproduce this
#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;
}
the compile error I got:
/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
I'm sure there must a way to do this, since boost own containers are all compatible with boost::interprocess::allocator::pointer
, but I'm not sure how they did it......I guess I should look for an alternative for placement new?
Upvotes: 1
Views: 473
Reputation: 393653
You're right, the standard containers do not afford fancy pointers. See e.g.
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)
My usual bet is to use Boost Container collections, because they do support these. And also, they seem to behave a lot better with scoped allocator adaptors than most standard library implementations in my experience.
Just to goof off a bit:
#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");
}
}
Note I used a smaller size and managed_mapped_file
because of limits on Coliru
Upvotes: 1
Reputation: 16690
When you are using an allocator in a container, the correct way to construct an object is to call
std::allocator_traits<AllocatorType>::construct(alloc, pointer, ...constructor parameters...)
See CppReference for more.
If you require a C++03 solution (no allocator_traits
in C++03), then you can call the allocators construct
method directly - but that's not the recommended solution.
Upvotes: 1