У меня есть структура, похожая на указатель, которая идет вместо указателя. Отличие указателя в том, что он содержит дополнительную информацию, которую (также специальный) распределитель может использовать для освобождения памяти.
Эта подобная указателю структура хорошо подходит для всех основных применений. Я могу выделять и освобождать память, разыменовывать, увеличивать, ->
и т. д.
Теперь я хочу использовать эти указатели для управления STL-подобным контейнером. С самого начала я понял, что вектор STL в принципе не может обрабатывать несырые указатели. T*
слишком жестко закодирован, и стандарт в основном исключает все, что не является указателем.
Вдохновленный Boost.Interprocess offset_ptr<T>
, я решил использовать Boost.Container vector
, который легко настраивается и, в принципе, может управлять чем угодно, распределитель, переданный boost::container::vector
, может обрабатывать все, что похоже на указатель.
Теперь класс boost::container::vector<T, myallocator_with_special_pointer<T>>
может делать что угодно... кроме resize()
!!
Глядя на код в boost/container/vector.hpp
, кажется, что процесс изменения размера (который в основном представляет собой и выделение, за которым следует копирование (или перемещение) и освобождение) включает необработанные указатели.
Оскорбительная строка:
[line 2729:] T * const new_buf = container_detail::to_raw_pointer
(allocator_traits_type::allocate(this->m_holder.alloc(), new_cap, this->m_holder.m_start));
За которым позже следует
[line 3022:] this->m_holder.start(new_start); // new_start is the same as new_buf above.
// member ::start(pointer&) will need to convert a raw pointer to the pointer typedef.
Обе строки абсолютно исключают возможность использования чего-либо, кроме raw_pointer
. Даже если у меня есть оператор преобразования в необработанный указатель, другая информация о специальном указателе будет потеряна.
Кажется довольно глупым, что эта маленькая деталь запрещает использование несырых указателей. Учитывая все усилия, чтобы контейнер был общим (например, определение типа pointer
), почему эта часть кода использует T*
только для изменения размера?
Другими словами, почему Boost Container не использует эту строку вместо
[alternative] pointer const new_buf =
allocator_traits_type::allocate(this->m_holder.alloc(), new_cap, this->m_holder.m_start);
Есть ли обходной путь или альтернативный способ использования вектора Boost Container для обработки не необработанных указателей?
Boost.Container говорит на своей странице руководства http://www.boost.org/doc/libs/1_64_0/doc/html/container/history_and_reasons.html#container.history_and_reasons.Why_boost_container
Boost.Container — это продукт длительной разработки, которая началась в 2004 году с экспериментальной библиотеки Shmem, впервые использовавшей стандартные контейнеры в разделяемой памяти. Shmem включил модифицированный код контейнера SGI STL, настроенный для поддержки не необработанных типов
allocator::pointer
и распределителей с отслеживанием состояния. После рассмотрения Shmem был принят как Boost.Interprocess, и эта библиотека продолжала уточнять и улучшать эти контейнеры.
Текущая реализация (в контексте изменения размера) идет вразрез с этой целью дизайна.
Я задал здесь менее конкретный вопрос о других характеристиках распределителей: type/41581304#41581304">Есть ли возможность настроить тип ссылки вектора STL?
Для справки, распределитель, который указывает специальный указатель (который распространяется на контейнер), выглядит примерно так:
template<class T>
struct allocator{
using value_type = T;
using pointer = array_ptr<T>; // simulates T*
using const_pointer = array_ptr<T const>; // simulates T const*
using void_pointer = array_ptr<void>; // simulates void*
using const_void_pointer = array_ptr<void const>; // simulates void const*
some_managed_shared_memory& msm_;
allocator(some_managed_shared_memory& msm) : msm_(msm){}
array_ptr<T> allocate(mpi3::size_t n){
auto ret = msm_.allocate(n*sizeof(T));
return static_cast<array_ptr<T>>(ret);
}
void deallocate(array_ptr<T> ptr, mpi3::size_t = 0){
msm_.deallocate(ptr);
}
};
Полный рабочий код http://coliru.stacked-crooked.com/a/f43b6096f9464cbf
#include<iostream>
#include <boost/container/vector.hpp>
template<typename T>
struct array_ptr;
template<>
struct array_ptr<void> {
using T = void;
T* p;
int i; //some additional information
// T& operator*() const { return *p; }
T* operator->() const { return p; }
// operator T*() const { return p; }
template<class TT>
operator array_ptr<TT>() const{return array_ptr<TT>((TT*)p, i);}
operator bool() const{return p;}
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr){}
array_ptr(T* ptr, int _i) : p(ptr), i(_i){}
template<class Other>
array_ptr(array_ptr<Other> other) : p(other.p), i(other.i){}
};
template<>
struct array_ptr<void const> {
using T = void const;
T* p;
int i; //some additional information
// T& operator*() const { return *p; }
T* operator->() const { return p; }
operator T*() const { return p; }
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr){}
array_ptr(T* ptr, int _i) : p(ptr), i(_i){}
template<class Other>
array_ptr(array_ptr<Other> other) : p(other.p), i(other.i){}
};
template<typename T>
struct array_ptr {
T* p;
int i; //some additional information
T& operator*() const { return *p; }
T* operator->() const { return p; }
T& operator[](std::size_t n) const{
assert(i == 99);
return *(p + n);
}
bool operator==(array_ptr const& other) const{return p == other.p and i == other.i;}
bool operator!=(array_ptr const& other) const{return not((*this)==other);}
// operator T*() const { return p; }
array_ptr& operator++(){++p; return *this;}
array_ptr& operator+=(std::ptrdiff_t n){p+=n; return *this;}
array_ptr& operator-=(std::ptrdiff_t n){p-=n; return *this;}
array_ptr operator+(std::size_t n) const{array_ptr ret(*this); ret+=n; return ret;}
std::ptrdiff_t operator-(array_ptr const& other) const{return p - other.p;}
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr), i(0){}
operator bool() const{return p;}
array_ptr(T* ptr, int _i) : p(ptr), i(_i){}
array_ptr(T* ptr) : p(ptr), i(0){}
array_ptr(int) : p(nullptr), i(0){}
array_ptr(array_ptr<void> const& other) : p(static_cast<T*>(other.p)), i(other.i){}
};
struct some_managed_shared_memory {
array_ptr<void> allocate(size_t n) { return array_ptr<void>(::malloc(n), 99); }
void deallocate(array_ptr<void> ptr) { if (ptr) ::free(ptr.p); }
};
template<typename T>
struct allocator{
using value_type = T;
using pointer = array_ptr<T>; // simulates T*
using const_pointer = array_ptr<T const>; // simulates T const*
using void_pointer = array_ptr<void>; // simulates void*
using const_void_pointer = array_ptr<void const>; // simulates void const*
some_managed_shared_memory& msm_;
allocator(some_managed_shared_memory& msm) : msm_(msm){}
array_ptr<T> allocate(size_t n){
auto ret = msm_.allocate(n*sizeof(T));
return static_cast<array_ptr<T>>(ret);
}
void deallocate(array_ptr<T> ptr, std::size_t = 0){
msm_.deallocate(ptr);
}
};
int main() {
some_managed_shared_memory realm;
boost::container::vector<int, allocator<int> > v(10, realm);
assert( v[4] == 0 );
v[4] = 1;
assert( v[4] == 1 );
for(std::size_t i = 0; i != v.size(); ++i) std::cout << v[i] << std::endl;
for(auto it = v.begin(); it != v.end(); ++it) std::cout << *it << std::endl;
// none of these compile:
v.push_back(8);
assert(v.size() == 11);
v.resize(100);
std::cout << v[89] << std::endl; // will fail an assert because the allocator information is lost
//v.assign({1,2,3,4,5});
}
boost::interprocess::vector
(который, я думаю, также использует Boost Container) решает эту проблему. (сопутствующий код очень трудно читать). Возможно, это проще обойти дляoffset_ptr
. Также это не похоже на простую ошибку, потому чтоto_raw_pointer
вызывается явно. Это может быть незавершенный наполовину испеченный дизайн. - person alfC   schedule 07.07.2017priv_forward_range_insert_no_capacity
, для которой есть три перегрузки, отправленные третьим аргументом с типамиversion_0
(который безоговорочно выдает в теле функции),version_1
(который, кажется, просто выделяет и освобождает, с помощью указателяT*
) иversion_2
(который, кажется, делает более причудливый ход, также используя указателиT*
). Интересно, нужно ли мне определять класс распределителя или его свойства таким образом, чтобы эти функции не создавались. Смотрите мою правку. - person alfC   schedule 07.07.2017push_back
илиassign
внутренне устанавливает кодresize
, поэтому я не удивлен, что они тоже не работали. Я добавил полный рабочий код (на основе вашего MWE), как видите, мне нужно определить специализацию дляarray_ptr<void>
, которая не может быть разыменована, и мне нужно включить некоторые неявные преобразования (аналогично преобразованию дляint*
вvoid*
). Спасибо за помощь. - person alfC   schedule 07.07.2017.begin()
,.end()
иoperator[]
. Еще раз препятствие.resize()
. В этой упрощенной версии более очевидно, что в строке 714vector.hpp
есть значение по умолчанию, которое распространяется какT*
, а не какpointer
. - person alfC   schedule 07.07.2017resize
иpush_back
. Код компилируется и работает, но семантически некорректен, так как лишняя информация, добавляемая аллокатором, теряется в процессе изменения размера (в примере аллокатор добавляет информацию99
, но из-за конвертации эта информация в итоге теряется). - person alfC   schedule 07.07.2017