placement new auf stack



  • "dass du 2 mal den gleichen Speicher anforderst"
    Imho forder ich ihn nur einmal an... wo soll denn das zweite mal sein? (das das erste mal die deklaration des arrays ist - da solltest du mir ja recht geben!?^^)

    "Aber das[konstruiertes objekt ablegen] könntest du imo auch billiger haben"
    hmm... imho kostet placement new so gut wie nix und wird in meinem anwendungsfall vrmtl so und so wegoptimiert, weil ich es nur für pods nutze...

    der allocator an sich würde so aussehen:

    #ifndef H_MY_ALLOCATOR_ALLOCATE_AT_STACK_INCLUDED_20091202221055
    #define H_MY_ALLOCATOR_ALLOCATE_AT_STACK_INCLUDED_20091202221055
    
    #include <cstddef>
    #include <new>
    #include <stdexcept>
    
    #ifdef _MSC_VER
    #	pragma warning(push) //http://msdn.microsoft.com/en-us/library/26kb9fy0.aspx
    #	pragma warning(disable: 4100) //C4100 can also be issued when code calls a destructor on a otherwise unreferenced parameter of primitive type. This is a limitation of the Visual C++ compiler.
    #endif
    
    namespace my
    {
    	template <typename T, std::size_t count>
    	struct stack_allocator;
    
    	template <std::size_t count>
    	struct stack_allocator <void, count>
    	{
    	public:
    		typedef void* pointer;
    		typedef const void* const_pointer;
    
    		typedef void value_type;
    
    		template <typename U>
    		struct rebind
    		{
    			typedef stack_allocator<U, count> other;
    		};
    	};
    
    	template <typename T, std::size_t count>
    	struct stack_allocator
    	{
    	public:
    		enum
    		{
    			allocating_size = count,
    			allocating_byte = allocating_size*sizeof(T)
    		};
    
    		typedef std::size_t size_type;
    		typedef std::ptrdiff_t difference_type;
    
    		typedef T value_type;
    		typedef value_type* pointer;
    		typedef const value_type* const_pointer;
    		typedef value_type& reference;
    		typedef const value_type& const_reference;
    
    		template <typename U>
    		struct rebind
    		{
    			typedef stack_allocator<U, count> other;
    		};
    
    		stack_allocator() throw()
    		{}
    
    		stack_allocator(const stack_allocator&) throw()
    		{}
    
    		template <typename U, std::size_t Ucount>
    		stack_allocator(const stack_allocator<U, Ucount>&) throw()
    		{}
    
    		~stack_allocator() throw()
    		{}
    
    		pointer address(reference x) const
    		{
    			return &x;
    		}
    
    		const_pointer address(const_reference x) const
    		{
    			return &x;
    		}
    
    		pointer allocate(size_type n, stack_allocator<void, 0>::const_pointer /*hint*/ = 0)
    		{
    			if(n > allocating_size)
    				throw std::bad_alloc();
    
    			if(!empty)
    				throw std::bad_alloc();
    
    			empty = false;
    			return reinterpret_cast<pointer>( data );
    		}
    
    		void deallocate(pointer /*p*/, size_type /*n*/)
    		{
    			/* nothing to do */
    		}
    
    		size_type max_size() const throw()
    		{
    			return allocating_size;
    		}
    
    		void construct(pointer p, const_reference val)
    		{
    			new ( static_cast<void*>(p) ) T(val);
    		}
    
    		void destroy(pointer p)
    		{
    			p->~T();
    		}
    
    	private:
    		bool empty;
    		char data[allocating_byte];
    	};
    
    	template <typename T, std::size_t Tc, typename U, std::size_t Uc>
    	bool operator== (const stack_allocator<T, Tc>& /*lhs*/, const stack_allocator<U, Uc>& /*rhs*/) throw()
    	{
    		return true;
    	}
    
    	template <typename T, std::size_t Tc, typename U, std::size_t Uc>
    	bool operator!= (const stack_allocator<T, Tc>& /*lhs*/, const stack_allocator<U, Uc>& /*rhs*/) throw()
    	{
    		return false;
    	}
    }
    
    #ifdef _MSC_VER
    #	pragma warning(pop)
    #endif
    
    #endif //#ifndef H_MY_ALLOCATOR_ALLOCATE_AT_STACK_INCLUDED_20091202221055
    

    sieht eigtl nicht so verkehrt aus...

    und hier mal noch nen minimalbsp.:

    #include "allocate_at_stack.h"
    
    #include <vector>
    #include <iostream>
    
    int main()
    {
    	typedef std::vector< int, my::stack_allocator<int, 2048> > my_vector;
    	my_vector the_vector;
    
    	the_vector.reserve(2048);
    	for(int tmp; std::cin >> tmp; the_vector.push_back(tmp))
    		;
    
    	for(my_vector::const_iterator i(the_vector.begin()), e(the_vector.end()); i != e; ++i)
    		std::cout << *i << std::endl;
    
    	system("PAUSE");
    }
    

    bb

    edit:
    empty = true beim reserve ist natürlich doof xD
    fällt euch ne idee ein, wie man das erste reserve auf die max.-größe automatisieren kann?



  • camper schrieb:

    Für richtige Ausrichtung ist noch zu sorgen:

    #include <type_traits>
    
    union
    {
        char data[sizeof(T)];
        typename std::tr1::aligned_storage<sizeof(T),std::tr1::alignment_of<T>::value>::type align;
    };
    

    sicher, dass das notwendig ist?
    imho gibt sizeof ja eh schon die größe + alignment aus!? oder ist das zufall oder ist das nur nicht relevant?^^

    bb



  • Dein char-Array könnte sich aber an einer Position befinden, die das nötige Alignment für den Typ T nicht ermöglicht.



  • hmm... stimmt, der anfang nat. nicht^^

    würde das hier:

    char data[allocating_size*sizeof(T) + std::tr1::alignment_of<T>];
    

    nicht schon reichen?
    dann müsste ich(muss ich überhaupt?) nur noch mit bissl mit modulo rumspielen, um ne andere startadresse rauszugeben, richtig!?

    falls das nicht stimmt, würde mir auch nen link reichen - hab gerade keine wirklich gute lektüre zu diesem thema finden können 😕

    bb


  • Mod

    unskilled schrieb:

    hmm... stimmt, der anfang nat. nicht^^

    würde das hier:

    char data[allocating_size*sizeof(T) + std::tr1::alignment_of<T>];
    

    nicht schon reichen?
    dann müsste ich(muss ich überhaupt?) nur noch mit bissl mit modulo rumspielen, um ne andere startadresse rauszugeben, richtig!?

    -1 reicht schon. Allerdings gibt es keinen standardkonformen Weg die Ausrichtung eines Zeigers (data) zu ermitteln. Damit hast du keine Rechengrundlage mehr.



  • camper schrieb:

    -1 reicht schon.

    stimmt^^

    camper schrieb:

    Allerdings gibt es keinen standardkonformen Weg die Ausrichtung eines Zeigers (data) zu ermitteln. Damit hast du keine Rechengrundlage mehr.

    Jopp, ist mir bekannt - macht mir aber nicht so viel aus - gehen tut es in der praxis definitiv - und mir fällt außer "der standard gibt keine garantien darüber" kein grund ein, wieso ichs nciht tun sollte 😉

    bb

    edit:
    hmm...
    ich hätt es jz so:

    template <typename T, std::size_t count>
    struct stack_allocator
    {
    	enum
    	{
    		allocating_size = count,
    		allocating_byte = allocating_size*sizeof(T) + std::tr1::alignment_of<T>-1
    	};
    
    /*...*/
    
    	pointer allocate(size_type n, stack_allocator<void, 0>::const_pointer /*hint*/ = 0)
    	{
    		if(n > allocating_size)
    			throw std::bad_alloc();
    
    		if(!empty)
    			throw std::bad_alloc();
    
    		empty = false;
    		size_type begin = reinterpret_cast<size_type>( data );
    		size_type offset = begin%std::tr1::alignment_of<T>::value;
    		pointer first = reinterpret_cast<pointer>( begin+offset );
    		return first;
    	}
    };
    

    allerdings muss ich erst mal nen tr1 update suchen xD
    iwann hatte ich das doch mal installiert... *grml*

    bb



  • unskilled schrieb:

    camper schrieb:

    -1 reicht schon.

    stimmt^^

    Was!? 😕 Steh ich auf dem Schlauch? Wieso stimmt das?



  • hmmm - ist wie bei restklassen... nen tollerer vergleich fällt mir gerad nicht ein...

    bsp.:
    alignment = 4

    start-adr.: 1

    1
    2
    3
    4 -> OK
    

    => +3

    start-adr.: 0

    0 -> OK
    

    => +0

    start-adr.: 2

    2
    3
    4 -> OK
    

    => +2

    start-adr.: 3

    3
    4 -> OK
    

    => +1

    max {0, 1, 2, 3} = 3 = 4-1 ;o)

    ---

    bsp.:
    alignment = 1
    man braucht keine verschiebung, weil jedes element "überall" sein kann ;o)
    -> = 0

    bb



  • Hallo,

    Ich bin mir nicht sicher, meine aber in einem der "effective C++" Bücher von Scott Meyers gelesen zu habe, dass placement new mit einem Stack-Objekt "böse" ist. (Wenn ich dran denke und dazu komme, schaue ich heute abend nach.)

    Im übrigen frage ich mich ob nicht boost::array oder std::tr1::array deine Anforderungen nicht auch erfüllen?

    Gruß



  • camper schrieb:

    Für richtige Ausrichtung ist noch zu sorgen:

    #include <type_traits>
    
    union
    {
        char data[sizeof(T)];
        typename std::tr1::aligned_storage<sizeof(T),std::tr1::alignment_of<T>::value>::type align;
    };
    

    Hier scheinen einige Leser das

    union
    

    übersehen zu haben und überlegen nun, wie man das ohne union schafft.



  • Ich stelle jetzt mal die Gretchenfrage: Welchen Sinn hat placement new mit Stackspeicher?



  • knivil schrieb:

    Ich stelle jetzt mal die Gretchenfrage: Welchen Sinn hat placement new mit Stackspeicher?

    Speed natürlich.



  • Ja, gegenueber new vielleicht, aber was ist denn mit std::array oder aber das Objekt gleich als lokale Variable anlegen?



  • knivil schrieb:

    Ja, gegenueber new vielleicht, aber was ist denn mit std::array oder aber das Objekt gleich als lokale Variable anlegen?

    Das ist natürlich besser. Deswegen kann ich meiner Vector-Klasse ja auch mitgeben, ob sie Freispeicher benutzt oder ein Member-Array.
    std::array ist keine Alternative, da es die supi vector-Funktionalität nicht hat. also push_back.



  • volkard schrieb:

    camper schrieb:

    Für richtige Ausrichtung ist noch zu sorgen:

    #include <type_traits>
    
    union
    {
        char data[sizeof(T)];
        typename std::tr1::aligned_storage<sizeof(T),std::tr1::alignment_of<T>::value>::type align;
    };
    

    Hier scheinen einige Leser das

    union
    

    übersehen zu haben und überlegen nun, wie man das ohne union schafft.

    soll so wie heißen, wie ich solls auch mit dem union machen?
    in meinen augen ist das bei nem einzelnen element zwar möglich, aber bei mehreren nur platzverschwendung... kann aber auch sein, dass ichs falsch verstanden hab - wie gesagt: hab dazu nix gefunden - allg. ist das tr1 auch 6jahre nach erscheinen noch so schlecht/wenig dokumentiert... :<

    volkard schrieb:

    std::array ist keine Alternative, da es die supi vector-Funktionalität nicht hat. also push_back.

    Japp - hatte ich auch weiter oben schon mal geschrieben

    Drehleiter schrieb:

    Ich bin mir nicht sicher, meine aber in einem der "effective C++" Bücher von Scott Meyers gelesen zu habe, dass placement new mit einem Stack-Objekt "böse" ist. (Wenn ich dran denke und dazu komme, schaue ich heute abend nach.)

    das wäre cool : >

    bb



  • unskilled schrieb:

    in meinen augen ist das bei nem einzelnen element zwar möglich, aber bei mehreren nur platzverschwendung...

    Richte doch das ganze Array aus und nicht einzeln jedes Element.



  • unskilled schrieb:

    volkard schrieb:

    std::array ist keine Alternative, da es die supi vector-Funktionalität nicht hat. also push_back.

    Japp - hatte ich auch weiter oben schon mal geschrieben

    Yup. Aber knivil hat's ignoriert und zur Gretchenfrage gemacht. Deswegen hab ich's ihm nochmal gesagt.



  • volkard schrieb:

    unskilled schrieb:

    in meinen augen ist das bei nem einzelnen element zwar möglich, aber bei mehreren nur platzverschwendung...

    Richte doch das ganze Array aus und nicht einzeln jedes Element.

    Tu ich doch!?

    template <typename T, std::size_t count> 
    struct stack_allocator 
    { 
        enum 
        { 
            allocating_size = count, 
            allocating_byte = allocating_size*sizeof(T) + std::tr1::alignment_of<T>-1 
        }; 
    
    /*...*/ 
    
        pointer allocate(size_type n, stack_allocator<void, 0>::const_pointer /*hint*/ = 0) 
        { 
            if(n > allocating_size) 
                throw std::bad_alloc(); 
    
            if(!empty) 
                throw std::bad_alloc(); 
    
            empty = false; 
            size_type begin = reinterpret_cast<size_type>( data ); 
            size_type offset = begin%std::tr1::alignment_of<T>::value; 
            pointer first = reinterpret_cast<pointer>( begin+offset ); 
            return first; 
        } 
    };
    

    oder gibts hier ein problem?
    mit offset berechne ich ja, welchen zeiger ich rausgebe und somit, wo das erste element gespeichert wird...

    bb



  • Dieser Thread macht mich traurig.



  • volkard schrieb:

    Dieser Thread macht mich traurig.

    wie wärs, wenn du mal sagen würdest, was du für nen fehler hältst!? bzw. was dir hier nicht passt...

    bb


Anmelden zum Antworten