Дорогие форумчане!
Честно говоря, не думал, что пообщаюсь в живую с мастодонтами своего дела.
А именно такими мне казались дорогие форумчане, когда что-то искал по теме.
Но, похоже, время пришло.
На cyberforum мне не ответили. Теперь надежда на Вас.
Собственный аллокатор, который нормально сработал с std::vector.
Прокачать аллокатор для использования с std::map(и в будущем для собственного list'a), с имитацией поведения метода reserve(), который есть у 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ти элементов.
Хочется такого же поведения и в связке "аллокатор + 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 вызовов аллокатора.
Вот что я пробовал, но у меня не получилось.
Исправление в аллокаторе. Добавление нового параметра
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;
}
}
Но... ругается. Не пойму, что ему надо.
Помогите, пожалуйста, разобраться.