2013-07-07 7 views
8

Ich versuche, einen einfachen Speicherpoolzuordner mit std::unordered_map zu verwenden. Ich habe diesen gleichen Zuordner scheinbar erfolgreich mit std::string und std::vector verwendet. Ich möchte, dass die in der unordered_map (und dem Vektor) enthaltenen Elemente diesen Zuordner auch verwenden, sodass ich meinen Zuordner in std::scoped_allocator_adaptor eingepackt habe.Verwenden eines benutzerdefinierten Zuordners in einem std :: scoped_allocator_adaptor mit Std :: unordered_map

vereinfachte Definition set:

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>>>; 

Initialised als solche:

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

lake_alloc ist unten mit dem Rest des Iterators Code dargestellt. Der Fehler, den ich in Clang 3.3 bekomme, ist, dass es nicht allocator_type (in diesem Fall der mm_alloc des Paares von String zu Vektor) zu seinem eigenen __pointer_allocator. Dies ist ein interner Typ, der für die Hash-Map-Implementierung verwendet wird. Partielle Fehlerausgabe unter:

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 gibt mir einen ähnlichen Fehler in seiner Hash-Karte internen Strukturen so klar es mache ich falsch, aber das ist mein erster Ausflug in Verteilern in der STL und ich bin ratlos.

Der benutzerdefinierte Zuordner folgt, Es ist eine einfache Implementierung mit einigen Löchern, aber diese Version funktioniert gut in einem enthaltenen Testfall mit ein paar Mega Daten in Vektoren und Strings.

#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

hast du std :: hash und std :: equal_to auf deine benutzerdefinierten Typen spezialisiert? – sehe

+1

Nur eine Frage, warum verwenden Sie einen 'scoped_allocator_adaptor', wenn Sie die verschachtelten Zuordner manuell definieren? Ist es nicht der springende Punkt, dass Sie die Allokatoren auf einmal definieren? –

+0

Konrad, die Beispiele, die ich bisher gefunden habe, spezifizieren den Allokator explizit für jedes Level, aber es gibt eine gute Chance, dass mir etwas fehlt. Siehe z.B. Bjarnes Aufnahme bei: http://www.stroustrup.com/C++11FAQ.html#scoped-allocator – zenmumbler

Antwort

8

Ich glaube, der Grundfehler ist, dass Ihr krystal_alloc eine „Umwandlung Konstruktor“ fehlt:

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

Ich bin mir nicht sicher, ob ich es richtig umgesetzt, das ist nur meine beste Vermutung. Sie werden einen Freund Anweisung brauchen diese Arbeit zu machen:

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

Auch empfehle ich Ihnen „const“ auf die key_type in Ihrem Zuweiser für unordered_map hinzufügen:

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>>>; 

Und ich denken, dass Sie können lake_alloc anstelle von mm_alloc auf Ihren inneren Behältern verwenden. Ihr Beispiel kompiliert für mich beide Wege. Ich habe es nicht auf Laufzeitverhalten getestet.

+0

Danke Howard, das war in der Tat das Problem.Ich experimentiere mit dem Entfernen des Bereichsallokators, da dies in diesem Fall nicht notwendig erscheint. Für andere hätte ich hier nachgesehen: http://www.cplusplus.com/reference/memory/allocator_traits/ und genau das gefunden, was Howard gesagt hat. – zenmumbler

Verwandte Themen