С++ повысить межпроцессный обмен (копировать) неразделяемый и совместно используемый вектор строки

Я хочу добиться того, что было достигнуто в ответе на это вопрос, но для вектора строк, т.е. строковых объектов. Я хочу иметь возможность писать общий код, который можно использовать как с памятью кучи, так и с разделяемой памятью. Я эмулировал код и написал класс для реализации вектора строк, который будет работать как с памятью кучи, так и с разделяемой памятью. У меня это работает по большей части. Однако, когда я пытаюсь инициализировать/создать общий вектор памяти строк из вектора строк, выделенного кучей, я получаю ошибки компиляции. В упомянутом выше вопросе вектор общей памяти был инициализирован из вектора общей памяти (цитируя «это работает из-за ... MAGIC!»). Не уверен, что это потому, что этот вопрос касался вектора целых чисел, которые легко копируются и перемещаются. Я предоставил конструктор копирования для класса строк с общей памятью, но он все еще не работает. Я предоставил свой код ниже (закомментировал код, который дает мне ошибки компиляции). Любая помощь приветствуется. Спасибо.

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <iostream>
#include <string>
#include <sstream>

namespace bip = boost::interprocess;

namespace generic
{

template <typename T, typename Alloc/* = std::allocator<T>*/ >
using vector = bip::vector<T, typename Alloc::template rebind<T>::other >;

template <typename Alloc/* = std::allocator<T>*/ >
using GenericString = bip::basic_string<char, std::char_traits<char>
                           , typename Alloc::template rebind<char>::other>;

template <typename Alloc>
class String
{
public:
  typedef Alloc allocator_type; // ties in with uses_allocator/scoped_allocator

  String(Alloc alloc = Alloc())
  : mString(alloc)
  {
  }

  String(const std::string str, Alloc alloc = Alloc())
  : mString(str.c_str(), alloc)
  {
  }

  String(const char* str, Alloc alloc = Alloc())
  : mString(str, alloc)
  {
  }

  template <typename OtherAlloc>
  String(String<OtherAlloc> const& other, Alloc alloc = Alloc())
  : mString(other.mString.begin(), other.mString.end(), alloc)
  {
  }

  std::string ToString() { return std::string(mString.begin(), mString.end()); }

  friend std::ostream& operator<<(std::ostream& os, const String& str)
  {
    os << str.mString;
    return os;
  }

private:
  template<typename OtherAlloc> friend struct String;
  GenericString<Alloc> mString;
};

template <typename Alloc>
class StringVector
{
public:
  typedef Alloc allocator_type; // ties in with uses_allocator/scoped_allocator
  typedef vector<String<Alloc>, Alloc> GenericStringVector;
  using Iterator = typename GenericStringVector::iterator;
  using ConstIterator = typename GenericStringVector::const_iterator;

  StringVector(Alloc alloc = Alloc())
  : mStrings(alloc)
  {
  }

  template <typename OtherAlloc>
  StringVector(StringVector<OtherAlloc> const& other, Alloc alloc = Alloc())
  : mStrings(other.mStrings.begin(), other.mStrings.end(), alloc)
  {
  }

  void Add(const String<Alloc> str) { mStrings.emplace_back(str); }

  void Remove(Iterator iter) { mStrings.erase(iter); }

  void Clear() { mStrings.clear(); }

  void Print() const
  {
    std::cout << "[";
    for (ConstIterator it = mStrings.begin(); it != mStrings.end(); it++)
      std::cout << (it == mStrings.begin() ? "" : ", ") << *it;
    std::cout << "]\n";
  }

private:
  template<typename OtherAlloc> friend struct StringVector;
  GenericStringVector mStrings;
};

}

namespace heap
{
  using VAlloc = std::allocator<void>;
  using String = generic::String<VAlloc>;
  using StringVector = generic::StringVector<VAlloc>;
}

namespace shared
{
  using VAlloc =boost::container::scoped_allocator_adaptor<
          bip::allocator<void, bip::managed_shared_memory::segment_manager> >;

  using String = generic::String<VAlloc>;
  using StringVector = generic::StringVector<VAlloc>;
}

int main(void)
{
  struct shm_remove {
      shm_remove()  { bip::shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove() { bip::shared_memory_object::remove("MySharedMemory"); }
  } remover;

  std::cout << "Heap based storage: \n";
  heap::StringVector svec;

  svec.Add(heap::String("test1"));
  svec.Add(heap::String("test2"));
  svec.Add(heap::String("test3"));
  svec.Add(heap::String("test4"));
  svec.Print();

  std::cout << "Shared memory storage: \n";
  bip::managed_shared_memory seg(bip::create_only, "MySharedMemory", 65536);
  shared::VAlloc shalloc(seg.get_segment_manager());

  shared::StringVector sh_svec(shalloc);
  sh_svec.Add(shared::String("test1", shalloc));
  sh_svec.Add(shared::String("test2", shalloc));
  sh_svec.Add(shared::String("test3", shalloc));
  sh_svec.Add(shared::String("test4", shalloc));
  sh_svec.Print();


  shared::StringVector sh_svec2(sh_svec, shalloc);
  sh_svec2.Print();

  //shared::StringVector sh_svec3(svec, shalloc);   // gives compile error :(
  //sh_svec3.Print();
}

В том числе ошибки компиляции:

In file included from src/test_string_vector.cc:5:0:
/usr/include/boost/container/scoped_allocator.hpp: In instantiation of ‘boost::container::container_detail::scoped_allocator_adaptor_base<OuterAlloc>::scoped_allocator_adaptor_base() [with OuterAlloc = boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >]’:
/usr/include/boost/container/scoped_allocator.hpp:963:7:   required from ‘boost::container::scoped_allocator_adaptor<OuterAlloc, InnerAllocs>::scoped_allocator_adaptor() [with OuterAlloc = boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >; InnerAllocs = {}]’
/usr/include/boost/container/vector.hpp:1174:15:   required from ‘void boost::container::vector<T, Allocator>::assign(FwdIt, FwdIt, typename boost::container::container_detail::enable_if_c<((! boost::container::container_detail::is_convertible<InIt, typename boost::container::allocator_traits<Allocator>::size_type>::value) && ((! boost::container::container_detail::is_input_iterator<InIt>::value) && (! boost::move_detail::is_same<typename boost::container::container_detail::version<Allocator>::type, boost::container::container_detail::integral_constant<unsigned int, 0u> >::value)))>::type*) [with FwdIt = boost::container::container_detail::vec_iterator<generic::String<std::allocator<void> >*, true>; T = generic::String<boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > >; Allocator = boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<generic::String<boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > >, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >; typename boost::container::container_detail::enable_if_c<((! boost::container::container_detail::is_convertible<InIt, typename boost::container::allocator_traits<Allocator>::size_type>::value) && ((! boost::container::container_detail::is_input_iterator<InIt>::value) && (! boost::move_detail::is_same<typename boost::container::container_detail::version<Allocator>::type, boost::container::container_detail::integral_constant<unsigned int, 0u> >::value)))>::type = void]’
/usr/include/boost/container/vector.hpp:864:7:   required from ‘boost::container::vector<T, Allocator>::vector(InIt, InIt, const allocator_type&) [with InIt = boost::container::container_detail::vec_iterator<generic::String<std::allocator<void> >*, true>; T = generic::String<boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > >; Allocator = boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<generic::String<boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > >, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >; boost::container::vector<T, Allocator>::allocator_type = boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<generic::String<boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > > >, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >]’
src/test_string_vector.cc:78:65:   required from ‘generic::StringVector<Alloc>::StringVector(const generic::StringVector<OtherAlloc>&, Alloc) [with OtherAlloc = std::allocator<void>; Alloc = boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> > >]’
src/test_string_vector.cc:151:46:   required from here
/usr/include/boost/container/scoped_allocator.hpp:762:7: error: no matching function for call to ‘boost::interprocess::allocator<void, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index> >::allocator()’
       {}
       ^
In file included from /usr/include/boost/interprocess/segment_manager.hpp:38:0,
                 from /usr/include/boost/interprocess/detail/managed_memory_impl.hpp:30,
                 from /usr/include/boost/interprocess/managed_shared_memory.hpp:25,
                 from src/test_string_vector.cc:1:
/usr/include/boost/interprocess/allocators/allocator.hpp:142:4: note: candidate: template<class T2> boost::interprocess::allocator<T, SegmentManager>::allocator(const boost::interprocess::allocator<T2, SegmentManager>&)
    allocator(const allocator<T2, SegmentManager> &other)
    ^
/usr/include/boost/interprocess/allocators/allocator.hpp:142:4: note:   template argument deduction/substitution failed:
In file included from src/test_string_vector.cc:5:0:
/usr/include/boost/container/scoped_allocator.hpp:762:7: note:   candidate expects 1 argument, 0 provided
       {}
       ^
In file included from /usr/include/boost/interprocess/segment_manager.hpp:38:0,
                 from /usr/include/boost/interprocess/detail/managed_memory_impl.hpp:30,
                 from /usr/include/boost/interprocess/managed_shared_memory.hpp:25,
                 from src/test_string_vector.cc:1:
/usr/include/boost/interprocess/allocators/allocator.hpp:136:4: note: candidate: boost::interprocess::allocator<T, SegmentManager>::allocator(const boost::interprocess::allocator<T, SegmentManager>&) [with T = void; SegmentManager = boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index>]
    allocator(const allocator &other)
    ^
/usr/include/boost/interprocess/allocators/allocator.hpp:136:4: note:   candidate expects 1 argument, 0 provided
/usr/include/boost/interprocess/allocators/allocator.hpp:131:4: note: candidate: boost::interprocess::allocator<T, SegmentManager>::allocator(boost::interprocess::allocator<T, SegmentManager>::segment_manager*) [with T = void; SegmentManager = boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index>; boost::interprocess::allocator<T, SegmentManager>::segment_manager = boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>, boost::interprocess::iset_index>]
    allocator(segment_manager *segment_mngr)
    ^
/usr/include/boost/interprocess/allocators/allocator.hpp:131:4: note:   candidate expects 1 argument, 0 provided
Makefile:16: recipe for target 'obj/test_string_vector.o' failed
make: *** [obj/test_string_vector.o] Error 1

person Dnj Abc    schedule 04.06.2017    source источник


Ответы (2)


Думаю, теперь я понимаю ваше намерение. вам нужен этот фрагмент кода, где OtherAlloc = std::allocator , Alloc = bi:allocator

template <typename OtherAlloc>
  StringVector(StringVector<OtherAlloc> const& other, Alloc alloc = Alloc())
  : mStrings(other.mStrings.begin(), other.mStrings.end(), alloc)
  {
  }

для вызова конструктора копирования, который вы предоставили для преобразования из кучи в общий тип

 template <typename OtherAlloc>
  String(String<OtherAlloc> const& other, Alloc alloc = Alloc())
  : mString(other.mString.begin(), other.mString.end(), alloc)
  {
  }

Но boost::interprocess::vector::assign(first,last) не передает экземпляр вашего распределителя, и похоже, что он использует Alloc alloc = Alloc(), где Alloc = boost::interprocess::allocator, и у него нет конструктора по умолчанию. Это коррелирует со всеми моими сообщениями :-) Завтра я попробую написать свой код, очень сложная головоломка.

Сегодня я изменил ваш код, пожалуйста, посмотрите ниже и посмотрите, нравится ли он вам.

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>

namespace bip = boost::interprocess;

namespace generic
{

template <typename T, typename Alloc/* = std::allocator<T>*/ >
using vector = bip::vector<T, typename Alloc::template rebind<T>::other >;

template <typename Alloc/* = std::allocator<T>*/ >
using GenericString = bip::basic_string<char, std::char_traits<char>
                           , typename Alloc::template rebind<char>::other>;

template <typename Alloc>
class String
{
public:
  typedef Alloc allocator_type; // ties in with uses_allocator/scoped_allocator

  template<typename ...T>
  String(const std::string &str, T && ...t)
  : alloc_(std::forward<T>(t)...) , mString(str.c_str(), alloc_)
  {}

  template<typename ...T>
  String(const char* str, T && ...t)
  : alloc_(std::forward<T>(t)...) , mString(str, alloc_)
  {}

  template <typename OtherAlloc, typename ...T>
  String(String<OtherAlloc> const& other, T && ...t)
  : alloc_(std::forward<T>(t)...) , mString(other.mString.begin(), other.mString.end(), alloc_)
  {}

  std::string ToString() { return std::string(mString.begin(), mString.end()); }

  friend std::ostream& operator<<(std::ostream& os, const String& str)
  {
    os << str.mString;
    return os;
  }

private:
  template<typename OtherAlloc> friend struct String;
  Alloc alloc_;
  GenericString<Alloc> mString;
};

template <typename Alloc>
class StringVector
{
public:
  typedef Alloc allocator_type; // ties in with uses_allocator/scoped_allocator
  typedef vector<String<Alloc>, Alloc> GenericStringVector;
  using Iterator = typename GenericStringVector::iterator;
  using ConstIterator = typename GenericStringVector::const_iterator;

  StringVector()
  : alloc_(), mStrings(alloc_)
  {}

  template<typename T>
  StringVector(T && t)
  : alloc_(std::forward<T>(t)), mStrings(alloc_)
  {}

  template <typename OtherAlloc, typename ...T>
  StringVector(StringVector<OtherAlloc> const& other, T && ...t)
  : alloc_(std::forward<T>(t)...), mStrings(alloc_)
  {
      std::transform ( other.mStrings.begin(), other.mStrings.end(), std::back_inserter(mStrings), [&t...](auto &s) {
          return typename GenericStringVector::value_type(s, std::forward<T>(t)...);
      });
  }

  void Add(const String<Alloc> str) { mStrings.emplace_back(str); }

  void Remove(Iterator iter) { mStrings.erase(iter); }

  void Clear() { mStrings.clear(); }

  void Print() const
  {
    std::cout << "[";
    for (ConstIterator it = mStrings.begin(); it != mStrings.end(); it++)
      std::cout << (it == mStrings.begin() ? "" : ", ") << *it;
    std::cout << "]\n";
  }

private:
  template<typename OtherAlloc> friend struct StringVector;
  Alloc alloc_;
  GenericStringVector mStrings;
};

}

namespace heap
{
  using VAlloc = std::allocator<void>;
  using String = generic::String<VAlloc>;
  using StringVector = generic::StringVector<VAlloc>;
}

namespace shared
{
  using VAlloc =boost::container::scoped_allocator_adaptor<
          bip::allocator<char, bip::managed_shared_memory::segment_manager> >;

  using String = generic::String<VAlloc>;
  using StringVector = generic::StringVector<VAlloc>;
}

int main(void)
{
  struct shm_remove {
      shm_remove()  { bip::shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove() { bip::shared_memory_object::remove("MySharedMemory"); }
  } remover;

  std::cout << "Heap based storage: \n";
  heap::StringVector svec;

  svec.Add(heap::String("test1"));
  svec.Add(heap::String("test2"));
  svec.Add(heap::String("test3"));
  svec.Add(heap::String("test4"));
  svec.Print();

  std::cout << "Shared memory storage: \n";
  bip::managed_shared_memory seg(bip::create_only, "MySharedMemory", 65536);
  auto smp = seg.get_segment_manager();

  shared::StringVector sh_svec(smp);
  sh_svec.Add(shared::String("test1", smp));
  sh_svec.Add(shared::String("test2", smp));
  sh_svec.Add(shared::String("test3", smp));
  sh_svec.Add(shared::String("test4", smp));
  sh_svec.Print();


  shared::StringVector sh_svec2(sh_svec, smp);
  sh_svec2.Print();

  shared::StringVector sh_svec3(svec, smp);   // does not give compile error 
  sh_svec3.Print();
}
person Marcus Brody    schedule 05.06.2017
comment
Да. Мне нужно, чтобы мой код работал как в куче памяти, так и в системах памяти с общей памятью. Поэтому я пытаюсь придумать базовые структуры данных, которые позволят мне это сделать. Спасибо, что нашли время. - person Dnj Abc; 05.06.2017
comment
Не беспокойтесь, я немного изменил ваш общий код, он компилирует его. - person Marcus Brody; 06.06.2017
comment
Это круто. Спасибо. Последняя услуга. Не могли бы вы объяснить, что вы сделали в конструкторе копирования StringVector. То, что ты сделал, кажется мне черной магией. :) - person Dnj Abc; 06.06.2017
comment
Итак, поскольку конструктору std::allocator() не требуется параметр, но для boost::interprocess::allocator(segment_manager *) требуется по крайней мере один указатель параметра на segment_manager для достижения одного и того же конструктора для обоих, я использовал вариативный шаблон для конструктора копирования StringVector. - person Marcus Brody; 06.06.2017
comment
T && ...t преобразуется в параметр 0...N в нашем случае 0 или 1 . Я не передаю allocator , но передаю параметры в allocator. Для Heap ничего не передается, для Shared проходит segment_manager *. Пробовал более длинный комментарий, не получилось :-( - person Marcus Brody; 06.06.2017
comment
Спасибо. Использование лямбда-выражений и переменных было разумным. Надеюсь, у вас не возникнет проблем, если я позже опубликую это в своей личной учетной записи github. Надеюсь, это будет полезно для других. Спасибо еще раз. - person Dnj Abc; 07.06.2017

Вы не предоставили свои ошибки компиляции, однако, просто взглянув на код и некоторые рабочие знания boost::interprocess , я думаю, что ваша проблема заключается в std::allocator, используемом для boost::interprocess , но я не уверен на 100%, поэтому, пожалуйста, предоставьте ошибки компиляции . А пока, если вам нравится
, вы можете ознакомиться с нашим стековым решением с открытым исходным кодом для Shared, Mmap, Heap memory_types.hpp Я думаю, вы можете использовать boost::interprocess::allocator с boost::interprocess::managed_heap_memory::segment_manager или что-то в этом роде. Пожалуйста, также укажите, используете ли вы компилятор С++ 03 или С++ 11, поскольку я думаю, что в С++ 03 это было typedef T* pointer_type, и это не будет работать с увеличением общей памяти, поскольку для этого требуется offset_ptr , в основном смещение от начала сегмента памяти и не абсолютный адрес.

Я изменил ваш код, который вы использовали shared::StringVector для хранения структуры SHM. Теперь он компилируется:

heap::StringVector sh_svec3(svec);   // does not gives compile error :-)
sh_svec3.Print();
person Community    schedule 05.06.2017
comment
Добавлены ошибки компиляции. Не хотел задавать вопрос слишком долго... :( - person Dnj Abc; 05.06.2017
comment
Он компилируется и связывается для меня, я использую gcc 5.4 и boost 1.61. - person ; 05.06.2017
comment
Я предполагаю, что вы используете C++11, потому что в противном случае template‹T›, использующий, не будет компилироваться, поэтому единственный вопрос — ваша версия Boost ‹ 1.58 - person ; 05.06.2017
comment
вы раскомментировали последние две строки в конце... //shared::StringVector sh_svec3(svec,halloc); // выдает ошибку компиляции :( //sh_svec3.Print(); Я прокомментировал их, чтобы проверить другие части кода. Извините за это. FWIW, я скомпилировал его с Boost 1.58. - person Dnj Abc; 05.06.2017
comment
понятно . boost::interprocess::allocator не имеет конструктора по умолчанию, как в вашем общем коде String(Alloc alloc = Alloc()) : mString(alloc) { }, поэтому единственный способ решить это, как я сказал в своем ответе, посмотреть примеры в нашем проекте и использовать boost::interprocess::managed_heap_memory: :segment_manager - person ; 05.06.2017
comment
не уследил :( - person Dnj Abc; 05.06.2017
comment
Alloc() - не существует в boost::interprocess::allocator , boost::interprocess::allocator(segment_manager *) принимает 1 аргумент, поэтому вам придется немного изменить свой код при использовании Alloc alloc = Alloc() - person ; 05.06.2017
comment
Распределитель по умолчанию предназначен для распределителя кучи. Я пытаюсь обобщить код в своем общем пространстве имен, чтобы я мог использовать его как с памятью кучи, так и с общей памятью, передавая разные распределители (и по умолчанию для кучи). При создании объектов общей памяти я всегда передаю распределитель общей памяти. Все еще немного в замешательстве... :( - person Dnj Abc; 05.06.2017
comment
Я думаю, что нашел вашу проблему, посмотрите мой обновленный ответ, последние 2 строки неверны, должно быть: heap::StringVector sh_svec3(svec); // does not gives compile error :-) sh_svec3.Print(); - person ; 05.06.2017
comment
Я думаю, вы неправильно поняли мое намерение. Я хочу создать вектор общей памяти из std::vector (то есть вектор строки на основе кучи). Поскольку у меня есть конструктор копирования, который принимает StringVector, я надеялся, что этого будет достаточно, чтобы заставить его работать. Если вы посмотрите на примерную ссылку, на которую я ссылался в начале своего поста, этот пример делает именно это. Однако этот пример имеет дело с векторными целыми числами, и я пытаюсь добиться аналогичного результата с векторными строками. - person Dnj Abc; 05.06.2017