Собственный аллокатор с задаваемым выделением памяти
От: avovana Россия  
Дата: 26.03.18 20:18
Оценка:
Дорогие форумчане!

Честно говоря, не думал, что пообщаюсь в живую с мастодонтами своего дела.
А именно такими мне казались дорогие форумчане, когда что-то искал по теме.
Но, похоже, время пришло.

На cyberforum мне не ответили. Теперь надежда на Вас.

Что было сделано:

Собственный аллокатор, который нормально сработал с std::vector.

Что хочется сделать:

Прокачать аллокатор для использования с std::map(и в будущем для собственного list'a), с имитацией поведения метода reserve(), который есть у std::vector.


I. Пример использования std::vector со своим аллокатором.


Реализация аллокатора:
#ifndef ALLOCATOR_HEADER
#define ALLOCATOR_HEADER
 
#include <cstddef> // size_t
#include <iostream>
#include <memory>
 
template<typename T>
struct AllocatorLogger {
  using size_type = size_t;
  using difference_type = ptrdiff_t;
 
  using value_type = T;
  using pointer = T*;
  using const_pointer = const T*;
  using reference = T&;
  using const_reference = const T&;
/*
  template<typename U>
  struct rebind {
    using other = AllocatorLogger<U>;
  };
*/
  pointer allocate(size_type n) {
    std::cout << __PRETTY_FUNCTION__
      << "[n = " << n << ']' << std::endl;
    void *ptr = malloc(sizeof(value_type) * n);
    if (ptr == nullptr)
      throw std::bad_alloc();
    return reinterpret_cast<pointer>(ptr);
  }
 
  template<typename U, typename... Args>
  void construct(U* p, Args&&... args) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    new (p) U(std::forward<Args>(args)...);
  }
 
  void destroy(pointer p) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    p->~T();
  }
 
  void deallocate(pointer p, size_type) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    free(p);
  }
};
 
#endif // ALLOCATOR_HEADER


Использования аллокатора:
#include <iostream>
#include <cstdlib>
#include <vector>
#include <map>
#include "allocator.h"
 
int main()
{
    auto v = std::vector<int, AllocatorLogger<int>>{};
    
    v.reserve(5);
    
    for(size_t i = 0; i < 5; ++i)
    {
        v.emplace_back(i);
    }
}

Запустить код на wandbox.

Вывод:

T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = int; AllocatorLogger<T>::pointer = int*; AllocatorLogger<T>::size_type = long unsigned int][n = 5]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = int; Args = {long unsigned int&}; T = int]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::destroy(AllocatorLogger<T>::pointer) [with T = int; AllocatorLogger<T>::pointer = int*]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = int; AllocatorLogger<T>::pointer = int*; AllocatorLogger<T>::size_type = long unsigned int]

Здесь получаем, что хотим — 1 вызов аллокатора с выделением места для последующего конструирования 5ти элементов.

II. Пример использования std::map со своим аллокатором.


Хочется такого же поведения и в связке "аллокатор + std::map":

int main()
{
    auto m = std::map<int, int, std::less<int>, AllocatorLogger<std::pair<const int, int>>>{};
    
    for(size_t i = 0; i < 5; ++i)
    {
        m[i] = i;
    }
}


Но здесь получаем вывод:
 AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
T* AllocatorLogger<T>::allocate(AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int][n = 1]
void AllocatorLogger<T>::construct(U*, Args&& ...) [with U = std::pair<const int, int>; Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; T = std::_Rb_tree_node<std::pair<const int, int> >]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]
void AllocatorLogger<T>::deallocate(AllocatorLogger<T>::pointer, AllocatorLogger<T>::size_type) [with T = std::_Rb_tree_node<std::pair<const int, int> >; AllocatorLogger<T>::pointer = std::_Rb_tree_node<std::pair<const int, int> >*; AllocatorLogger<T>::size_type = long unsigned int]

Т.е. 5 вызовов аллокатора.

III. Попытка задания аллокации.

Вот что я пробовал, но у меня не получилось.

Исправление в аллокаторе. Добавление нового параметра int Size:
template<typename T, int Size>
struct AllocatorLogger {
 
  pointer allocate(size_type n) {
    std::cout << __PRETTY_FUNCTION__
      << "[n = " << n << ']' << std::endl;
    void *ptr = malloc(sizeof(value_type) * n * Size);
    if (ptr == nullptr)
      throw std::bad_alloc();
    return reinterpret_cast<pointer>(ptr);
  }
 
  //...
}


Вызов c заданием параметра — 5:
int main()
{
    auto m = std::map<int, int, std::less<int>, AllocatorLogger<std::pair<const int, int>, 5>>{};
    
    for(size_t i = 0; i < 5; ++i)
    {
        m[i] = i;
    }
}

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