2013-07-07 21 views
8

Tôi đang cố gắng sử dụng một bộ cấp phát pool bộ nhớ đơn giản với std::unordered_map. Tôi đã sử dụng cùng một trình phân bổ này dường như thành công với cả hai số std::stringstd::vector. Tôi muốn các mục chứa trong unordered_map (và vectơ) cũng sử dụng phân bổ này vì vậy tôi đã bọc phần cấp phát của tôi trong std::scoped_allocator_adaptor.Sử dụng một trình phân bổ tùy chỉnh bên trong một std :: scoped_allocator_adaptor với std :: unordered_map

Giản đặt định nghĩa:

template <typename T> 
using mm_alloc = std::scoped_allocator_adaptor<lake_alloc<T>>; 

using mm_string = std::basic_string<char, std::char_traits<char>, mm_alloc<char>>; 
using mm_vector = std::vector<mm_string, mm_alloc<mm_string>>; 
using mm_map = std::unordered_map<mm_string, mm_vector, std::hash<mm_string>, std::equal_to<mm_string>, mm_alloc<std::pair<mm_string, mm_vector>>>; 

khởi như vậy:

lake pool; 
mm_map map { mm_alloc<std::pair<mm_string, mm_vector>>{pool} }; 

lake_alloc được hiển thị bên dưới với phần còn lại của mã iterator. Lỗi tôi nhận được trong Clang 3.3 là nó không thể allocator_type (trong trường hợp này là mm_alloc của cặp chuỗi thành vectơ) thành __pointer_allocator của riêng nó. Đó là loại nội bộ được sử dụng để triển khai bản đồ băm. đầu ra lỗi phần dưới đây:

lib/c++/v1/__hash_table:848:53: error: no matching conversion for functional-style 
     cast from 'const allocator_type' (aka 'const std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::pair<std::__1::basic_string<char, 
     std::__1::char_traits<char>, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, std::__1::vector<std::__1::basic_string<char, 
     std::__1::char_traits<char>, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, 
     std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::basic_string<char, std::__1::char_traits<char>, 
     std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, krystal::lake> > > >, krystal::lake> >') to '__pointer_allocator' (aka 
     'std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::__hash_node<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, 
     std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, 
     std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<std::__1::basic_string<char, 
     std::__1::char_traits<char>, std::__1::scoped_allocator_adaptor<krystal::krystal_alloc<char, krystal::lake> > >, krystal::lake> > > >, void *> *, krystal::lake> >') 
    : __bucket_list_(nullptr, __bucket_list_deleter(__pointer_allocator(__a), 0)), 
                ^~~~~~~~~~~~~~~~~~~~~~~ 

GCC 4.7.1 mang lại cho tôi một lỗi tương tự trong bản đồ băm cấu trúc nội bộ của mình như vậy rõ ràng tôi đang làm nó sai, nhưng đây là bước đột phá đầu tiên của tôi trong allocators trong STL và tôi thua lỗ.

Trình phân bổ tùy chỉnh theo sau, Đây là một triển khai đơn giản với một số lỗ trong đó, nhưng phiên bản này hoạt động tốt trong một trường hợp thử nghiệm chứa một vài megs dữ liệu trong vectơ và chuỗi.

#include <cstddef> 
#include <memory> 
#include <scoped_allocator> 

class lake { 
    const size_t block_size_; 
    mutable std::vector<std::unique_ptr<uint8_t[]>> blocks_; 
    mutable uint8_t *arena_, *pos_; 

    static constexpr const size_t DefaultBlockSize = 48 * 1024; 

    void add_block(size_t of_size) const { 
     blocks_.emplace_back(new uint8_t[of_size]); 
     pos_ = arena_ = blocks_.back().get(); 
    } 

    inline void add_block() const { add_block(block_size_); } 

public: 
    lake(const size_t block_size) 
    : block_size_ {block_size} 
    { 
     add_block(); 
    } 
    lake() : lake(DefaultBlockSize) {} 

    void* allocate(size_t n) const { 
     if (pos_ + n - arena_ > block_size_) { 
      if (n > block_size_) 
       add_block(n); // single-use large block 
      else 
       add_block(); 
     } 

     auto result = pos_; 
     pos_ += n; 
     return result; 
    } 

    void deallocate(void* p, size_t n) const { 
    } 
}; 


template <typename T, typename Alloc> 
class krystal_alloc { 
    const Alloc* allocator_; 

public: 
    using value_type = T; 
    using size_type = size_t; 
    using difference_type = ptrdiff_t; 
    using pointer = T*; 
    using const_pointer = const T*; 
    using reference = T&; 
    using const_reference = const T&; 

    template <typename U> 
    struct rebind { typedef krystal_alloc<U, Alloc> other; }; 

    krystal_alloc() : allocator_{ new Alloc() } {} // not used 
    krystal_alloc(const Alloc& alloc) : allocator_{ &alloc } {} 

    pointer address(reference v) { 
     return 0; 
    } 

    const_pointer address(const_reference v) { 
     return 0; 
    } 

    size_type max_size() const { 
     return static_cast<size_type>(-1)/sizeof(value_type); 
    } 

    pointer allocate(size_type n) { 
     return static_cast<pointer>(allocator_->allocate(sizeof(T) * n)); 
    } 

    void deallocate(pointer p, size_type n) { 
     allocator_->deallocate(p, n); 
    } 
}; 

template <typename T, typename Alloc, typename U> 
inline bool operator==(const krystal_alloc<T, Alloc>&, const krystal_alloc<U, Alloc>) { return true; } 

template <typename T, typename Alloc, typename U> 
inline bool operator!=(const krystal_alloc<T, Alloc>&, const krystal_alloc<U, Alloc>) { return false; } 


// -- standard usage 
template <typename T> 
using lake_alloc = krystal_alloc<T, lake>; 
+0

bạn có chuyên về std :: hash và std :: equal_to cho các loại tùy chỉnh của bạn không? – sehe

+1

Chỉ một câu hỏi, tại sao bạn lại sử dụng 'scoped_allocator_adaptor' nếu bạn định nghĩa các trình phân bổ lồng nhau theo cách thủ công? Không phải là toàn bộ vấn đề mà bạn định nghĩa các nhà phân phối trong một lần? –

+0

Konrad, các ví dụ tôi đã tìm thấy cho đến nay tất cả chỉ định cấp phát một cách rõ ràng cho mỗi cấp, nhưng có một cơ hội tốt tôi thiếu một cái gì đó. Xem ví dụ Bjarne lấy tại: http://www.stroustrup.com/C++11FAQ.html#scoped-allocator – zenmumbler

Trả lời

8

tôi tin rằng các lỗi cơ bản là krystal_alloc của bạn đang thiếu một "constructor chuyển đổi":

template <class U> 
    krystal_alloc(const krystal_alloc<U, Alloc>& u) 
     : allocator_(u.allocator_) {} 

Tôi không chắc tôi thực hiện nó một cách chính xác, đó chỉ là phỏng đoán tốt nhất của tôi. Bạn sẽ cần một tuyên bố người bạn để làm cho công việc này:

template <class U, class A> friend class krystal_alloc; 

Ngoài ra tôi khuyên bạn nên thêm "const" vào key_type trong allocators của bạn cho unordered_map:

using mm_map = std::unordered_map<mm_string, mm_vector, std::hash<mm_string>, 
           std::equal_to<mm_string>, 
           mm_alloc<std::pair<const mm_string, mm_vector>>>; 

Và tôi nghĩ rằng bạn có thể sử dụng lake_alloc thay vì mm_alloc trên các thùng chứa bên trong của bạn. Ví dụ của bạn biên dịch cho tôi cả hai cách. Tôi đã không kiểm tra nó cho hành vi thời gian chạy.

+0

Cảm ơn Howard, đó thực sự là vấn đề.Tôi đang thử nghiệm với việc loại bỏ các phân bổ scoped như trong trường hợp này nó không có vẻ cần thiết. Đối với những người khác, tôi nên xem ở đây: http://www.cplusplus.com/reference/memory/allocator_traits/ và tìm thấy chính xác những gì Howard nói. – zenmumbler