Аллокатор для std::[unordered_][multi]map
От: Alexander G Украина  
Дата: 08.12.15 11:14
Оценка:
А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора?
Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.
Русский военный корабль идёт ко дну!
Re: Аллокатор для std::[unordered_][multi]map
От: watchmaker  
Дата: 08.12.15 11:49
Оценка: 8 (1)
Здравствуйте, Alexander G, Вы писали:

AG>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора?

AG>Ну, т.е. написать какой-нибуь char вместо типа?

Формально это запрещено. И есть примеры, когда из-за этого возникают проблемы. Следовательно, так делать не надо. Смотри дальше, например, обсуждение.
Re: Аллокатор для std::[unordered_][multi]map
От: Mr.Delphist  
Дата: 08.12.15 16:00
Оценка: -2
Здравствуйте, Alexander G, Вы писали:

AG>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора?

AG>Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.

Ну, если рассуждать в подобном ключе далее, то вся система типов — "не нужна"TM, ибо вполне можно жить на "void*". Но зачем стрелять себе ногой в верёвку?
Re: Аллокатор для std::[unordered_][multi]map
От: Кодт Россия  
Дата: 08.12.15 18:23
Оценка: 8 (1)
Здравствуйте, Alexander G, Вы писали:

AG>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора?

AG>Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.

Можно упростить себе жизнь с помощью шаблонных подстановок
http://ideone.com/fVP458

// выковыриваем тип элемента из мапа
template<class K, class V>
using map_item = typename std::map<K,V>::value_type;
// либо из стандарта
               = std::pair<K const, V>;
 
// пишем шаблон мапа, явно параметризуемый шаблоном аллокатора
// (поэтому он идёт первым параметром, а не четвёртым дефолтным после компаратора)
template<template<class> class A, class K, class V, class C = std::less<K>>
using customized_map = std::map<K, V, C, A<map_item<K,V>>>;

// произвольных аллокаторов под рукой не оказалось, взял готовый стандартный :) 
template<class E> using my_allocator = std::allocator<E>;

// делаем на его основе шаблон "нашего" мапа
template<class K, class V, class C = std::less<K>>
using my_map = customized_map<my_allocator, K, V, C>;

// хотя можно всё и в одну строчку было записать
template<class K, class V, class C = std::less<K>>
using my_map = std::map<K, V, C, my_allocator<std::pair<K const, V>>>;

// и пользуемся на здоровье
using the_map = my_map<int,char>;
using eth_map = my_map<int,char,std::greater<int>>;
Перекуём баги на фичи!
Re[2]: Аллокатор для std::[unordered_][multi]map
От: watchmaker  
Дата: 08.12.15 18:29
Оценка: 1 (1) +1
Здравствуйте, Mr.Delphist, Вы писали:

MD>Здравствуйте, Alexander G, Вы писали:


AG>>А можно там не выписывать этот тип pair<const Key, Value> как шаблонный параметр аллокатора?

AG>>Ну, т.е. написать какой-нибуь char вместо типа? Он же всё равно внутри ребиндится.

MD>Ну, если рассуждать в подобном ключе далее, то вся система типов — "не нужна"TM, ибо вполне можно жить на "void*".


Не надо передёргивать.
Изначальный интерфейс аллокаторов в С++ очень плох. И устарел.
В нём все аллокаторы одного типа должны были быть эквивалентны друг-другу, то есть гарантированно работать со стандартными контейнерами могли лишь stateless аллокаторы. Хочешь завести в программе пару пулов строчек? Перебьёшься! То есть даже нельзя надёжно и эффективно написать с независимыми аренами
vector<string, my_arena_allocator<string> > v1(my_arena_allocator<string>(arena1));
vector<string, my_arena_allocator<string> > v2(my_arena_allocator<string>(arena2));
Ибо по стандарту строку, которую выделил аллокатор my_arena_allocator<string>(arena1), должен уметь разрушать любой другой экземпляр, например my_arena_allocator<string>(arena2) или даже my_arena_allocator<string>(NULL), что в общем-то никак не способствует эффективности. В результате чего нужно либо отказываться от std::vector (и прочих контейнеров STL), либо связывать все экземпляры аллокаторов между собой, чтобы один аллокатор мог всегда найти другой экземпляр, который изначально создал объект, и передать ему его для уничтожения (подобно тому, как это сделано в boost::fast_pool_allocator с синхронизацией через mutex), — и это ужасно.

Но зато интерфейс аллокаторов позволяет поддерживать одновременно near и far pointers. Ну очень востребованная возможность в современном программировании. Особенно с учётом того, что как оказалось, этой возможностью всё равно не получилось полноценно пользоваться: сам Степанов в интервью приводит пример почему этот механизм не работает.
Вот что важнее, быстрый и эффективный memory_pool или обязательная поддержка устаревших/редких моделей памяти даже для тех, кто ими не пользуется?
К счастью в C++11 аллокаторы всё же были переработаны. Пусть и и не все многочисленные предложения удалось воплотить. Но хотя бы stateful-аллокаторы теперь разрешены в стандартных контейнерах!

И изначальный вопрос в целом правильный. Точно также можно построить реализацию контейнеров, в которой узаконить обязательный rebind аллокатора, и тогда указывать типы вручную не понадобится — и код станет короче (может быть даже выразительнее, если синтаксический сахар в виде std::allocator<auto> до кучи добавить). Наверное, в некоторых хитрых местах, завязанных на не-плоскую память, может что-то поломаться и понадобится что-то дописать — там код станет длиннее. Кажется, что в среднем было бы выгоднее иметь обязательный rebind и более лаконичный код. И даже если кому-то понадобится писать полный и точный тип, то эта возможность у них останется. Сейчас же типы выписывать вынуждены все.

Так что хоть формальный запрет и есть, но у него нет никакого сакрального смысла — это просто ещё одно из неудачных решений ранних версий языка, которое, к сожалению, уже нельзя легко изменить.
Отредактировано 08.12.2015 22:21 watchmaker . Предыдущая версия . Еще …
Отредактировано 08.12.2015 22:20 watchmaker . Предыдущая версия .
Re[2]: Аллокатор для std::[unordered_][multi]map
От: Кодт Россия  
Дата: 08.12.15 18:35
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Ну, если рассуждать в подобном ключе далее, то вся система типов — "не нужна"TM, ибо вполне можно жить на "void*". Но зачем стрелять себе ногой в верёвку?


Ну это же давно известный баг дизайна, вызванный нищетой древних компиляторов. (Хотя по-настоящему от этой нищеты избавились только в С++14).
Просто в стандарте он гвоздями приколочен, и не дай бог, кто-то где-то будет полагаться на него.

A propos. Ребинд — это вообще прекрасно: это же теоркатовский функтор. Интересно, Степанов курил теоркат специально, или ему муза нашептала, или оно само получилось?
Перекуём баги на фичи!
Re[2]: Аллокатор для std::[unordered_][multi]map
От: sokel Россия  
Дата: 08.12.15 21:13
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Можно упростить себе жизнь с помощью шаблонных подстановок

Можно ещё и template template parameters запользовать, благо type alias теперь есть (чьё отсутсвие, наверное, и сыграло в пользу существующего интерфейса аллокаторов).
#include <iostream>
#include <map>
#include <functional>

template<class K, class V> using map_item = typename std::map<K, V>::value_type;
template<class K, class V, template<class> class C = std::less, template<class> class A = std::allocator>
using my_map = std::map<K, V, C<K>, A<map_item<K,V>>>;

template<class E> using my_allocator = std::allocator<E>;

using the_map = my_map<int, char>;
using eth_map = my_map<int, char, std::greater, my_allocator>;

int main() {
    eth_map m;
    m[1] = 2;

    return 0;
}
Re[2]: Аллокатор для std::[unordered_][multi]map
От: Alexander G Украина  
Дата: 08.12.15 21:30
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Можно упростить себе жизнь с помощью шаблонных подстановок

К>http://ideone.com/fVP458

Годится.
Ещё бы multimap, unordered_map и list (на нём не ощущалась проблема, но тоже используется), и тогда логично всех в один traits-подобный класс:

#include <iostream>
#include <map>
#include <list>

template<template<class> class A>
struct customized_allocator
{
   template<class K, class V, class C = std::less<K>>
   using map = std::map<K, V, C, A<std::pair<K const, V>>>;

   template<class K, class V, class C = std::less<K>>
   using multimap = std::multimap<K, V, C, A<std::pair<K const, V>>>;

   template<class K, class V>
   using list = std::list<V, A<V>>;
};

template<class E> using my_allocator = std::allocator<E>;

using use_my_allocator = customized_allocator<my_allocator>;

using the_map = use_my_allocator::map<int,char>;
using eth_map = use_my_allocator::map<int,char,std::greater<int>>;
Русский военный корабль идёт ко дну!
Re[3]: Аллокатор для std::[unordered_][multi]map
От: Кодт Россия  
Дата: 09.12.15 11:56
Оценка: +1
Здравствуйте, Alexander G, Вы писали:

AG>Ещё бы multimap, unordered_map и list (на нём не ощущалась проблема, но тоже используется), и тогда логично всех в один traits-подобный класс:


Осталось только нормальное имя для фабрики типов дать, чтобы не путать фабрику с аллокатором. use_customized_allocator, with_customized_allocator, и т.п...
Перекуём баги на фичи!
Re: Оказывается, это N3916 и есть в бусте 1.60
От: Alexander G Украина  
Дата: 18.12.15 18:12
Оценка:
Оказывается, то, что мне нужно — N3916, и оно появилось в бусте 1.60
Автор: flаt
Дата: 18.12.15
.

Называется Polymorphic Memory Resources.

Адаптер polymorphic_allocator позволяет вообще не типизировать аллокатор и назначать его в рантайме.

После чего в неймспейсе boost::pmr (std::pmr для N3916) есть тайпдефы с заданным аллокатором

Конкретно для мап:

http://www.boost.org/doc/libs/1_60_0/doc/html/boost_container_header_reference.html#header.boost.container.pmr.map_hpp

Header <boost/container/pmr/map.hpp>

namespace boost {
  namespace container {
    namespace pmr {
      template<typename Key, typename T, typename Compare = std::less<Key>, 
               typename Options = tree_assoc_defaults> 
        struct map_of;
      template<typename Key, typename T, typename Compare = std::less<Key>, 
               typename Options = tree_assoc_defaults> 
        struct multimap_of;


      typedef boost::container::map< Key, T, Compare, polymorphic_allocator< std::pair< const Key, T > >, Options > map;
      typedef boost::container::multimap< Key, T, Compare, polymorphic_allocator< std::pair< const Key, T > >, Options > multimap;
    }
  }
}

(структуры map_of и multimap_of содержат typedef type, ну то есть это "using для бедных")
Русский военный корабль идёт ко дну!
Отредактировано 18.12.2015 18:13 Alexander G . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.