Daniel Drywa

std::aligned_storage template class

A simple gist that handles aligned storage in C++11

I wrote a simple template class that makes use of the std::aligned_storage. An aligned storage is basically a reserved block of memory with a certain size that is aligned within a certain offset to increase system performance. You can read more about basic data structure alignment on Wikipedia.

So let’s say we have a POD type like this one:

struct my_data {
    std::uint32_t id;
    std::uint32_t code;
};

The data of the struct is irrelevant. To reserve a memory block that corresponds to the struct we need the size of the struct and also the best alignment for the data.

typedef typename std::aligned_storage< sizeof( my_data ), alignof( my_data ) >::type my_storage_type;
my_storage_type my_storage;

As you can see we get the size of the struct with the sizeof() operator and the alignment with alignof() operator. With this information we can feed the std::aligned_storage template. The my_storage variable we created has already a fixed size in memory and is aligned in the best way. To fill the storage with data we have to use the placement new operator.

new ( &my_storage ) my_data();

This invokes the constructor of my_data. But instead of creating a new memory block it will just fill the memory at the address of my_storage. Now we have filled our storage, which begs the question: How do we access it?

reinterpret_cast< my_data* >( &my_storage )->id;
reinterpret_cast< my_data* >( &my_storage )->code;

Since the storage itself has no idea of our data structure we have to reinterpret the address of the storage as a pointer of our struct. That way we then can access the members and get or set the values we need. It is really important to understand that the storage itself is just a raw block of memory with no concept of types or values.

Now deleting the memory is quite tricky because there is no such thing as an placement delete. Although it certainly would be handy. So to delete the memory we have to manually invoke the destructor of our type. Of course the destructor does not delete the reserved memory block. The uninitialized memory block gets deleted as soon as our storage variable falls out of scope.

reinterpret_cast< my_data* >( &my_storage )->~my_data();

So with all this information I created a simple template class that allows initialization, destruction and access to the storage in a easy and sensible way. This class has no real error checks but it should be enough for a start. I also posted the complete header on GitHub Gist. So you can easily fork the header and start improving my code, if you really want to.

/*********************************************
The MIT License (MIT)

Copyright (c) 2014 Daniel Drywa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*********************************************/
#pragma once
#ifndef __ALIGNED_STORAGE_TEMPLATE_H
#define __ALIGNED_STORAGE_TEMPLATE_H

#include <type_traits>
#include <utility>

namespace dd {
    template< typename pod_type >
    class astorage final {
    private:
        typename std::aligned_storage< sizeof( pod_type ), alignof( pod_type ) >::type data;

    public:
        typedef pod_type        value_type;
        typedef pod_type*       pointer;
        typedef pod_type&       reference;
        typedef const pod_type*     const_pointer;
        typedef const pod_type* const   const_pointer_const;
        typedef const pod_type&     const_reference;

        template< typename ...Args >
        explicit astorage( Args &&...args ) {
            new ( &data ) value_type( std::forward< Args >( args )... );
        }

        ~astorage() {
            reinterpret_cast< pointer >( &data )->~pod_type();
        }

        astorage( astorage &&other ) noexcept :
            data( std::move( other.data ) ) {
        }

        astorage& operator=( astorage &&other ) noexcept  {
            std::swap( data, other.data );
            return *this;
        }

        astorage( const astorage& ) = delete;
        astorage& operator=( const astorage& ) = delete;

        reference operator*() {
            return *reinterpret_cast< pointer >( &data );
        }

        const_reference operator*() const {
            return *reinterpret_cast< const_pointer >( &data );
        }

        pointer operator->() {
            return reinterpret_cast< pointer >( &data );
        }

        const_pointer operator->() const {
            return reinterpret_cast< const_pointer >( &data );
        }

        pointer get() {
            return reinterpret_cast< pointer >( &data );
        }

        const_pointer get() const {
            return reinterpret_cast< const_pointer >( &data );
        }
    };
}
#endif


03 August 2014