Problem mit operator new



  • CStoll schrieb:

    Ja, man kann es wirklich bis zum Extrem treiben - aber für dich dürfte die erste Version ausreuchen. Du mußt nur die Methoden allocate() und deallocate() anpassen, so daß sie mit malloc() bzw. free() arbeiten.

    Danke!

    Hab mir jetzt mal schnell einen simplen Allocator geschrieben. Das hier sind wohl die wichtigsten Methode:

    inline pointer allocate(size_type cnt,  typename std::allocator<void>::const_pointer = 0) { 
        return reinterpret_cast<pointer>(malloc(cnt * sizeof(T))); 
    }
    
    inline void deallocate(pointer p, size_type) { 
        free(p);
    }
    
    inline size_type max_size() const { 
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }
    
    inline void construct(pointer p, const T& t) { new(p) T(t); }
    inline void destroy(pointer p) { p->~T(); }
    

    Sieht das ok aus? Meine einzige Frage dazu: In construct() benutze ich new(p). Aber das ruft ja NICHT meinen globalen operator new(size_t s) auf, sondern das ist ein placement new, das operator new(size_t s, void* p) aufruft, oder?

    Noch was: Wenn ich in meinem MemoryManager jetzt die Map mit meinem Custom Allocator erzeugen will:

    typedef MemManagerMapAllocator< std::pair<void*, AllocInfo> > MapAllocator;
    std::map<void*, AllocInfo, std::less<void*>, MapAllocator>	mAllocMap;
    

    kriege ich folgende kryptische Fehlermeldung:

    D:\Microsoft Visual Studio 10.0\VC\include\xtree(698): error C2664: 'std::_Tree_val<_Traits>::_Tree_val(const std::less<_Ty> &,ple::MemManagerMapAllocator<T>)' : cannot convert parameter 2 from 'const ple::MemManagerMapAllocator<T>' to 'ple::MemManagerMapAllocator<T>'
    1> with
    1> [
    1> _Traits=std::_Tmap_traits<void *,ple::MemManager::AllocInfo,std::less<void *>,ple::MemManager::MapAllocator,false>,
    1> _Ty=void *,
    1> T=std::pair<void *const ,ple::MemManager::AllocInfo>
    1> ]
    1> and
    1> [
    1> T=std::pair<void *const ,ple::MemManager::AllocInfo>
    1> ]
    1> and
    1> [
    1> T=std::pair<void *const ,ple::MemManager::AllocInfo>
    1> ]
    1> Constructor for class 'ple::MemManagerMapAllocator<T>' is declared 'explicit'
    1> with
    1> [
    1> T=std::pair<void *const ,ple::MemManager::AllocInfo>
    1> ]
    1> D:\Microsoft Visual Studio 10.0\VC\include\xtree(695) : while compiling class template member function 'std::_Tree<_Traits>::_Tree(const std::less<_Ty> &,const ple::MemManagerMapAllocator<T> &)'
    1> with
    1> [
    1> _Traits=std::_Tmap_traits<void *,ple::MemManager::AllocInfo,std::less<void *>,ple::MemManager::MapAllocator,false>,
    1> _Ty=void *,
    1> T=std::pair<void *const ,ple::MemManager::AllocInfo>
    1> ]
    1> D:\Microsoft Visual Studio 10.0\VC\include\map(81) : see reference to class template instantiation 'std::_Tree<_Traits>' being compiled
    1> with
    1> [
    1> _Traits=std::_Tmap_traits<void *,ple::MemManager::AllocInfo,std::less<void *>,ple::MemManager::MapAllocator,false>
    1> ]
    1> E:\Programmieren\C++\MemManager.h(174) : see reference to class template instantiation 'std::map<_Kty,_Ty,_Pr,_Alloc>' being compiled
    1> with
    1> [
    1> _Kty=void *,
    1> _Ty=ple::MemManager::AllocInfo,
    1> _Pr=std::less<void *>,
    1> _Alloc=ple::MemManager::MapAllocator
    1> ]

    Was ist der Fehler?



  • Karya22 schrieb:

    Sieht das ok aus? Meine einzige Frage dazu: In construct() benutze ich new(p). Aber das ruft ja NICHT meinen globalen operator new(size_t s) auf, sondern das ist ein placement new, das operator new(size_t s, void* p) aufruft, oder?

    Ja, diese Methoden sehen auf den ersten Blick OK aus, also liegt der Fehler vermutlich woanders. Und ja, construct*( verwendet nicht deinen operator new, sondern das placement new.

    Noch was: Wenn ich in meinem MemoryManager jetzt die Map mit meinem Custom Allocator erzeugen will:

    typedef MemManagerMapAllocator< std::pair<void*, AllocInfo> > MapAllocator;
    std::map<void*, AllocInfo, std::less<void*>, MapAllocator>	mAllocMap;
    

    kriege ich folgende kryptische Fehlermeldung:

    Das sieht so aus, als hättest du irgendwo ein const zu viel oder zu wenig.



  • CStoll schrieb:

    Noch was: Wenn ich in meinem MemoryManager jetzt die Map mit meinem Custom Allocator erzeugen will:

    typedef MemManagerMapAllocator< std::pair<void*, AllocInfo> > MapAllocator;
    std::map<void*, AllocInfo, std::less<void*>, MapAllocator>	mAllocMap;
    

    kriege ich folgende kryptische Fehlermeldung:

    Das sieht so aus, als hättest du irgendwo ein const zu viel oder zu wenig.

    Hm ja. Ich versteh nur nicht wo. Er bringt auch nicht irgend eine Zeile, in der der Fehler ist. Wenn ich auf die Fehlermeldung doppelt klicke, springt VS in diesen Code (Datei xtree):

    explicit _Tree(const key_compare& _Parg,
    		const allocator_type& _Al)
    		: _Mybase(_Parg, _Al)
    		{	// construct empty tree
    		}
    

    Zur Vollständigkeit hier mein Allocator:

    template<typename T>
    class MemManagerMapAllocator {
    public : 
        typedef T value_type;
        typedef value_type* pointer;
        typedef const value_type* const_pointer;
        typedef value_type& reference;
        typedef const value_type& const_reference;
        typedef std::size_t size_type;
        typedef std::ptrdiff_t difference_type;
    
    public : 
        template<typename U>
        struct rebind {
            typedef MemManagerMapAllocator<U> other;
        };
    
    public : 
        inline explicit MemManagerMapAllocator() {}
        inline ~MemManagerMapAllocator() {}
        inline explicit MemManagerMapAllocator(MemManagerMapAllocator const&) {}
        template<typename U>
        inline explicit MemManagerMapAllocator(MemManagerMapAllocator<U> const&) {}
    
        inline pointer address(reference r) { return &r; }
        inline const_pointer address(const_reference r) { return &r; }
    
        inline pointer allocate(size_type cnt,  typename std::allocator<void>::const_pointer = 0) { 
         return reinterpret_cast<pointer>(malloc(cnt * sizeof(T))); 
    }
    
        inline void deallocate(pointer p, size_type) { 
            free(p); 
        }
    
        inline size_type max_size() const { 
            return std::numeric_limits<size_type>::max() / sizeof(T);
     }
    
        inline void construct(pointer p, const T& t) { new(p) T(t); }
        inline void destroy(pointer p) { p->~T(); }
    
        inline bool operator==(MemManagerMapAllocator const&) { return true; }
        inline bool operator!=(MemManagerMapAllocator const& a) { return !operator==(a); }
    };
    

    Jemand eine Idee, wo da mein Fehler ist? 😕


  • Mod

    Lass das explizit weg. Wie kommt man eigentlich auf die Idee, das routinemäßig zu machen, insbes. mit Kopierkonstruktoren?
    allocate sollte static_cast benutzen:

    inline pointer allocate(size_type cnt, const_pointer = 0) { 
         return static_cast<pointer>(malloc(cnt * sizeof(T)));
    }
    

    construct sollte einen cast enthalten:

    inline void construct(pointer p, const T& t) { new(static_cast<void*>(p)) T(t); }
    


  • Vielen Dank! Die Fehler sind weg!

    Äh noch was. Hab mir 3 Beispielcodes für Custom Allocators ergoogelt und irgendwie machen alle 3 recht unterschiedliche Sachen. Offenbar ist das noch immer so ein bißchen ein Mysterium. 😃
    Einer hat z.B. von public std::allocator<T> abgeleitet. Sollte man das machen? Bringt das irgendwas?



  • Tja, da sieht man mal wieder, daß viele Wege nach Rom führen können 😉

    Karya22 schrieb:

    Einer hat z.B. von public std::allocator<T> abgeleitet. Sollte man das machen? Bringt das irgendwas?

    Normalerweise hast du im Allokator ziemlich viele Sachen, die sich nicht groß bei verschiedenen Implementationen unterscheiden (construct(), destroy(), die ganzen typedef's). In std::allokator<> sind die alle schon enthalten, also würdest du dir damit ein wenig der Arbeit einsparen.


  • Mod

    Entscheidend ist nicht die Definition des Standardallokators sondern die allgemeinen Anforderungen an Allokatoren (20.1.5)



  • camper schrieb:

    construct(pointer p, const T& t) { new(static_cast<void*>(p)) T(t); }
    

    Nach void* kann implizit gecastet werden, daher ist dieser Cast nicht nötig.


  • Mod

    314159265358979 schrieb:

    camper schrieb:

    construct(pointer p, const T& t) { new(static_cast<void*>(p)) T(t); }
    

    Nach void* kann implizit gecastet werden, daher ist dieser Cast nicht nötig.

    "impliziter cast" ist schon mal ein Widerspruch in sich. Ein Cast ist immer explizit, schließlich handelt es sich um eine bestimmte grammatikalische Form.
    Ein Konvertierung kann implizit sein, und natürlich können Objektzeiger implizit in void* konvertiert werden.
    Der Cast hier dient dazu, garantiert die Standardversion des placement new (die nicht ersetzt werden darf) aufzurufen. Schließlich wäre es denkbar, dass für ein T eine placement-new-Funktion etwa der Signatur

    operator new(size_t,T*)
    

    existiert.



  • @314159265358979:
    Tip: wenn camper was schreibt, macht es tendenziell eher Sinn zu fragen was der Sinn ist, als zu behaupten dass es unnötig wäre 😉



  • hustbaer schrieb:

    Tip: wenn camper was schreibt, macht es tendenziell eher Sinn zu fragen was der Sinn ist, als zu behaupten dass es unnötig wäre 😉

    An manchen Stellen, wo ich nicht sicher bin was der Standard vorschreibt/verbietet, würd ich im Code einfach gerne folgendes schreiben:

    using std::camper;
    

    😉


Anmelden zum Antworten