C++17 (20), аллокаторы и контейнеры
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 29.01.20 09:06
Оценка:
В C++17 (может уже и в 14-ом) у аллокаторов часть интерфейса объявлена как deprecated.

В С++20 этот deprecated вырезали окончательно.

В частности речь идет про rebind.

У меня в коде есть "raw_memory" allocator. Который сам по себе возвращает void*.

Я его подсовывал в std::vector, std::list ..., они его ребиндили к нужному типу и все прекрасно работало.

При включении C++17 в VS2019, вылазит ошибка компиляции:

Ошибка C2338 vector<T, Allocator> requires that Allocator's value_type match T (See N4659 26.2.1 [container.requirements.general]/16 allocator_type) Either fix the allocator value_type or define _ENFORCE_MATCHING_ALLOCATORS=0 to suppress this diagnostic.


class vector { // varying size array of values
//.... вот эта проверка
    static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Ty, typename _Alloc::value_type>,
        _MISMATCHED_ALLOCATOR_MESSAGE("vector<T, Allocator>", "T"));


Смысла решать проблему через _ENFORCE_MATCHING_ALLOCATORS я не вижу.

Поэтому решал пересесть на свои костылики:

template<class T,class allocator>
using t_stl_vector
 =std::vector
   <T,
    typename allocator::template rebind<T>::other>;

template<class Key,class T,class Less,class allocator=t_void_allocator>
using t_stl_map
 =std::map
   <Key,
    T,
    Less,
    typename allocator::template rebind<std::pair<const Key,T>>::other>;


Все собралось и работает.

Вопрос — это что, если я хочу юзать свои распределяторы памяти, мне с этими костылями до конца жизни жить?

Раньше было лучше!
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: C++17 (20), аллокаторы и контейнеры
От: reversecode google
Дата: 29.01.20 11:05
Оценка: +1
лучше сначала проверять в гугле
прежде чем создавать избыточность интернета
https://stackoverflow.com/questions/17957366/is-it-wrong-if-the-standard-container-element-type-and-stdallocator-type-are-d
Re: C++17 (20), аллокаторы и контейнеры
От: Vamp Россия  
Дата: 29.01.20 14:33
Оценка:
КД>У меня в коде есть "raw_memory" allocator. Который сам по себе возвращает void*.
Вот здесь и возникает вопрос, зачем ты так сделал? Аллокатор — он типизированный. Почему твой value_type не такой, как тот, для которого попросили аллокатор?

КД>Вопрос — это что, если я хочу юзать свои распределяторы памяти, мне с этими костылями до конца жизни жить?

Покажи код своего аллокатора и мы его пофиксим )))

КД>Раньше было лучше!

Это бесспорно.
Да здравствует мыло душистое и веревка пушистая.
Re[2]: C++17 (20), аллокаторы и контейнеры
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 29.01.20 17:41
Оценка:
Здравствуйте, Vamp, Вы писали:

КД>>У меня в коде есть "raw_memory" allocator. Который сам по себе возвращает void*.

V>Вот здесь и возникает вопрос, зачем ты так сделал? Аллокатор — он типизированный. Почему твой value_type не такой, как тот, для которого попросили аллокатор?

КД>>Вопрос — это что, если я хочу юзать свои распределяторы памяти, мне с этими костылями до конца жизни жить?

V>Покажи код своего аллокатора и мы его пофиксим )))

В контейнеры подсовывается t_void_allocator (стандартная реализация для t_raw_allocator).

Там он ребиндится к нужному типу и возвращает t_allocator_interface.

template<class RawMemManager>
class t_raw_allocator;

template<class Allocator,class T>
class t_allocator_interface;

class t_void_allocator_adapter;

typedef t_raw_allocator<t_void_allocator_adapter> t_void_allocator;

////////////////////////////////////////////////////////////////////////////////
//class t_raw_allocator

// RawMemManager:
// - static void* raw_allocate(size_t n);
// - static void raw_deallocate(void* p);

template<class RawMemManager>
class t_raw_allocator
{
 private:
  typedef t_raw_allocator<RawMemManager>    self_type;

 public: //typedefs
  typedef RawMemManager                     adapter;
                                            
  typedef size_t                            size_type;
  typedef ptrdiff_t                         difference_type;
  typedef void*                             pointer;
  typedef const void*                       const_pointer;
  typedef void                              value_type;
                                            
  template<class U>
  struct rebind
  {
   typedef structure::t_allocator_interface<self_type,U> other;
  };

  template<>
  struct rebind<void>
  {
   typedef self_type                                     other;
  };

 public:
  static self_type instance;

  t_raw_allocator();

  t_raw_allocator(const self_type& x);

  template<class U>
  t_raw_allocator(const structure::t_allocator_interface<self_type,U>&);

  self_type& operator = (const self_type& x);

  bool operator == (const self_type& x)const;

  bool operator != (const self_type& x)const;

  pointer allocate(size_type n,pointer ptr=structure::null_ptr);

  void deallocate(pointer p, size_type size);

  size_type max_size()const;

  size_type max_size(size_type size)const;

  void swap(self_type& x);
};//class t_raw_allocator

////////////////////////////////////////////////////////////////////////////////
//class t_allocator_interface (from BCB3)

template<class T>
class t_allocator_interface__base
{
 public:
  typedef T                                            value_type;
};//class t_allocator_interface__base

//------------------------------------------------------------------------
template<class T>
class t_allocator_interface__base<const T>
{
 public:
  typedef T                                            value_type;
};//class t_allocator_interface__base - const

//------------------------------------------------------------------------
//trap for incorrect use of t_allocator_interface 

template<>
class t_allocator_interface__base<void>;

//------------------------------------------------------------------------
template<class Allocator,class T>
class t_allocator_interface:private t_allocator_interface__base<T>
{
 private:
  typedef t_allocator_interface<Allocator,T>               self_type;
  typedef t_allocator_interface__base<T>                   base_type;

 public: //typedefs ------------------------------------------------------
  typedef typename Allocator::template rebind<void>::other raw_allocator_type;

  typedef typename base_type::value_type                   value_type;

  typedef value_type*                                      pointer;
  typedef const value_type*                                const_pointer;
  typedef value_type&                                      reference;
  typedef const value_type&                                const_reference;
  typedef typename raw_allocator_type::size_type           size_type;
  typedef typename raw_allocator_type::difference_type     difference_type;
  typedef void*                                            void_pointer;
  typedef const void*                                      const_void_pointer;

  template<class U>
  struct rebind
  {
   typedef typename Allocator::template rebind<U>::other   other;
  };

 public:
  static self_type instance;

 public:
  t_allocator_interface();

  //C++ Standart: compiler always use NOT-template copy constructor
  t_allocator_interface(const self_type& x);

  template<class U>
  t_allocator_interface(const t_allocator_interface<Allocator,U>& x);

  t_allocator_interface(const Allocator& a);

  self_type& operator = (const self_type& x);

  bool operator == (const self_type& UNUSED_ARG(x))const
  {
   return true;
  }

  bool operator != (const self_type& UNUSED_ARG(x))const
  {
   return false;
  }

 public:
  typename pointer address(reference x)const;

  typename size_type max_size()const;

  typename pointer allocate(size_type n,pointer p=structure::null_ptr);

  void deallocate(pointer p,size_type n);

  void construct(pointer p,const_reference val);

 #if(COMP_CONF_SUPPORT_RVALUE_REFERENCE!=0)
  void construct(pointer p,value_type&& val);

  template<typename U>
  void construct(pointer p,U&& val);
 #endif //COMP_CONF_SUPPORT_RVALUE_REFERENCE!=0

  void destroy(pointer p);

  void swap(self_type& x);
};//class t_allocator_interface

////////////////////////////////////////////////////////////////////////////////
//class t_void_allocator

class t_void_allocator_adapter
{
 public:
  static void* raw_allocate(size_t n);

  static void raw_deallocate(void* pv);
};//class t_void_allocator_adapter
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: C++17 (20), аллокаторы и контейнеры
От: Alexander G Украина  
Дата: 31.01.20 07:58
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>Вопрос — это что, если я хочу юзать свои распределяторы памяти, мне с этими костылями до конца жизни жить?


Уже можно перейти на std::pmr, и выкинуть все костыли.

Используешь std::pmr::vector<T>, std::pmr::map<K, V> и т.д. , которые синонимы для std::vector<T, std::polymorphic_allocator<T>>, std::vector<T, std::polymorphic_allocator<std::pait<const K, V>>>, и т.д.

Подсовываешь им параметром наследника std::pmr::memory_resource, в котором нужно переопределить три виртуальные функции:

void* allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t));
void deallocate(void* p,
                std::size_t bytes,
                std::size_t alignment = alignof(std::max_align_t));
bool is_equal(const memory_resource& other) const noexcept;

(если последнюю не понял, можно просто вернуть всегда false, это может быть субоптимально, но работать будет)

Плюс ещё в том, что в этом коде

your_memory_resource mr;

std::pmr::vector<std::pmr::vector<std::pmr::list<T>>> v(&mr);


распределение через your_memory_resource попадёт не только в объемлющий контейнер, но и в оба вложенных.
Т.е. если в your_memory_resource сделан какой-то кастомный пул,требующий контекста, то контекст сам пропагейтится.
Русский военный корабль идёт ко дну!
Re[2]: C++17 (20), аллокаторы и контейнеры
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 31.01.20 13:10
Оценка:
Здравствуйте, Alexander G, Вы писали:

КД>>Вопрос — это что, если я хочу юзать свои распределяторы памяти, мне с этими костылями до конца жизни жить?


AG>Уже можно перейти на std::pmr, и выкинуть все костыли.


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