C ++ 17 - извлечение / повторная вставка узла с помощью специального распределителя - работает с clang ++ / libc ++, но не с libstdc ++ - PullRequest
0 голосов
/ 24 октября 2018

В моем текущем проекте я храню некоторые данные в unordered_map в общей памяти, используя библиотеку Boost Interprocess.Вот минимальный пример:

#include <unordered_map>
#include <atomic>
#include <iostream>
#include <iterator>
#include <utility>
#include <string>
#include <scoped_allocator>

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>

namespace ipc = boost::interprocess;

struct Foo {
  Foo(size_t _a){
    a = _a;
  }
  size_t a;
  std::atomic<bool> deleted {false};
};

/**
 * allocator type needed to construct maps in shared memory
 */
template <typename OBJ_T>
using OBJ_ALLOC = ipc::allocator<std::pair<const size_t, OBJ_T>,
                 ipc::managed_shared_memory::segment_manager>;

/**
 * map type to construct maps in shared memory. ideally, only construct the content in-place
 * to avoid excessive copying ... 
 */
template <typename OBJ_T>
using OBJ_MAP = std::unordered_map<size_t,
                   OBJ_T,
                   std::hash<size_t>,
                   std::equal_to<size_t>,
                   std::scoped_allocator_adaptor<OBJ_ALLOC<OBJ_T>>>;

int main(){

  OBJ_MAP<Foo> *map;
  ipc::managed_shared_memory data_segment(ipc::open_or_create,
                      std::string("data_segment").c_str(),
                      4096);

  OBJ_ALLOC<Foo> alloc(data_segment.get_segment_manager());

  map = data_segment.find_or_construct<OBJ_MAP<Foo>>
    (ipc::unique_instance) 
    (alloc);

  map->emplace(std::piecewise_construct,
          std::forward_as_tuple(1),
          std::forward_as_tuple(2321));
  // before  
  for(auto it = map->begin(); it != map->end(); it++) {
    std::cout << "Key: " << it->first << " Value: " << it->second.a << std::endl;
  }

  auto i = map->extract(1);
  i.key() = 2;
  map->insert(std::move(i));

  // after
  for(auto it = map->begin(); it != map->end(); it++) {
    std::cout << "Key: " << it->first << " Value: " << it->second.a << std::endl;
  }

  data_segment.destroy<OBJ_MAP<Foo>>(ipc::unique_instance);
  ipc::shared_memory_object::remove(std::string("data_segment").c_str());

  return 0;  
}

Первоначально я разработал проект для MacOS, поэтому я скомпилировал для clang ++ / libc ++ 7.0.0, который прекрасно работает.

clang++ test_map.cpp -std=c++17 -stdlib=libc++ -lpthread -lrt

Теперь яm переносит все это на Linux и получает ошибки с g ++ и libstdc ++ (g ++ 8.2.1).

g++ test_map.cpp -std=c++17 -lpthread -lrt

Извлечение узла не компилируется из-за неявно удаленного конструктора копии, и-insertion не удается из-за некоторого нежизнеспособного преобразования, но сообщения об ошибках очень загадочные.

Он отлично работает с clang ++ / libc ++ в linux, но это означает, что все зависимые библиотеки, такие как Boost, должны быть скомпилированыс тем же набором инструментов, что довольно хлопотно.

Как объяснить эти различия и есть ли способ их обойти?

РЕДАКТИРОВАТЬ : вот код: https://gcc.godbolt.org/z/L40Jsl

РЕДАКТИРОВАТЬ : и фактическое сообщение об ошибке ...

user@computer ~/repos/playground % g++ -std=c++17 test_map.cpp
In file included from /usr/include/c++/8.2.1/unordered_map:46,
                 from test_map.cpp:1:
/usr/include/c++/8.2.1/bits/hashtable.h: In instantiation of ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::insert_return_type 
std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_M_reinsert_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, 
_H2, _Hash, _RehashPolicy, _Traits>::node_type&&) [with _Key = long unsigned int; _Value = std::pair<const long unsigned int, Foo>; _Alloc = std::scoped_allocator_adaptor<boost::inte
rprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost
::interprocess::iset_index> > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<long unsigned int>; _H1 = std::hash<long unsigned int>; _H2 = std::__detail::_Mod_ran
ge_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::_Ha
shtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::insert_return_type = std::_Node_insert_return<std::__detail::_Node_iterator<std::pair<con
st long unsigned int, Foo>, false, false>, std::_Node_handle<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<
std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_
family>, boost::interprocess::iset_index> > > > >; typename std::__allocator_traits_base::__rebind<_Alloc, std::__detail::_Hash_node<_Value, typename _Traits::__hash_cached::value>, 
void>::type = std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_man
ager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >; typename std::__detail::_Hashtable_base<_Key, _Value, _Extra
ctKey, _Equal, _H1, _H2, _Hash, _Traits>::iterator = std::__detail::_Node_iterator<std::pair<const long unsigned int, Foo>, false, false>; std::_Hashtable<_Key, _Value, _Alloc, _Extr
actKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::node_type = std::_Node_handle<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boos
t::interprocess::allocator<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<
boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > >]’:
/usr/include/c++/8.2.1/bits/unordered_map.h:438:53:   required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::insert_return_type std::unordered_map<_Key, _Tp, _Hash, _Pre
d, _Alloc>::insert(std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::node_type&&) [with _Key = long unsigned int; _Tp = Foo; _Hash = std::hash<long unsigned int>; _Pred = std::equ
al_to<long unsigned int>; _Alloc = std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, bo
ost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::insert_return_type =
 std::_Node_insert_return<std::__detail::_Node_iterator<std::pair<const long unsigned int, Foo>, false, false>, std::_Node_handle<long unsigned int, std::pair<const long unsigned int
, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<c
har, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::node_type
 = std::_Node_handle<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Hash_node<std::pair<cons
t long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index
> > > >]’
test_map.cpp:63:27:   required from here
/usr/include/c++/8.2.1/bits/hashtable.h:809:5: error: no matching function for call to ‘std::_Hashtable<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_alloca
tor_adaptor<boost::interprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interproces
s::mutex_family>, boost::interprocess::iset_index> > >, std::__detail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, 
std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(std::_Hashtable<long unsigned 
int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long unsigned int, Foo>, boost::interprocess::segment_manag
er<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >, std::__detail::_Select1st, std::equal_to<long unsigned int>, s
td::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, fals
e, true> >::size_type&, std::_Hashtable<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long 
unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >, std::__d
etail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehas
h_policy, std::__detail::_Hashtable_traits<false, false, true> >::__hash_code&, std::allocator_traits<std::scoped_allocator_adaptor<boost::interprocess::allocator<std::__detail::_Has
h_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::int
erprocess::iset_index> > > >::pointer&)’
     = _M_insert_unique_node(__bkt, __code, __nh._M_ptr);
/usr/include/c++/8.2.1/bits/hashtable.h:1717:5: note: candidate: ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::iterator std::_
Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_M_insert_unique_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _
H2, _Hash, _RehashPolicy, _Traits>::size_type, std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hash_code, std::_Hashtable<_Key,
 _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_type*, std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolic
y, _Traits>::size_type) [with _Key = long unsigned int; _Value = std::pair<const long unsigned int, Foo>; _Alloc = std::scoped_allocator_adaptor<boost::interprocess::allocator<std::p
air<const long unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index
> > >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<long unsigned int>; _H1 = std::hash<long unsigned int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::_
_detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::_Hashtable<_Key, _Value, _All
oc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::iterator = std::__detail::_Node_iterator<std::pair<const long unsigned int, Foo>, false, false>; std::_Hashtable<_K
ey, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = long unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2,
 _Hash, _RehashPolicy, _Traits>::__hash_code = long unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__node_type = s
td::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>; typename _Traits::__hash_cached = std::integral_constant<bool, false>]’
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         _H1, _H2, _Hash, _RehashPolicy, _Traits>::
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/8.2.1/bits/hashtable.h:1717:5: note:   no known conversion for argument 3 from ‘std::allocator_traits<std::scoped_allocator_adaptor<boost::interprocess::allocator<st
d::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_fa
mily>, boost::interprocess::iset_index> > > >::pointer’ {aka ‘boost::interprocess::offset_ptr<std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>, long int, lon
g unsigned int, 0>’} to ‘std::_Hashtable<long unsigned int, std::pair<const long unsigned int, Foo>, std::scoped_allocator_adaptor<boost::interprocess::allocator<std::pair<const long
 unsigned int, Foo>, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >, std::__
detail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_reha
sh_policy, std::__detail::_Hashtable_traits<false, false, true> >::__node_type*’ {aka ‘std::__detail::_Hash_node<std::pair<const long unsigned int, Foo>, false>*’}

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

Больше обходного пути, чем исправление, но замена std::unordered_map на boost::unordered_map компилирует и работает с обоими наборами инструментов.

Единственные изменения, которые необходимо было сделать:

// use boost instead ... 
#include <boost/unordered_map.hpp>

template <typename OBJ_T>
using OBJ_MAP = boost::unordered_map<size_t,
                                     OBJ_T,
                                     std::hash<size_t>,
                                     std::equal_to<size_t>,
                                     std::scoped_allocator_adaptor<OBJ_ALLOC<OBJ_T>>>;
0 голосов
/ 24 октября 2018

Жалуется, что вызов _M_insert_unique_node передает причудливый указатель, но _M_insert_unique_node принимает только необработанный указатель.Выглядит как ошибка библиотеки.

...