9

コンパイル時にサイズが定義されていない可能性がある場所に配置sizeする必要があるサイズのメモリのブロックを割り当てようとしています。、、などAlignmentのルーチンが存在することは知っていますが、コードの移植性が低下するため、使用したくありません。C++11はルーチンとクラスを提供し、そこから POD 型を取得して、要件に合わせて要素を割り当てることができます。ただし、私の目標は、整列されるサイズのメモリ ブロック (単一の要素だけでなく) を割り当てるアロケータを作成することです。 これは可能ですか?私が尋ねる理由は_aligned_allocposix_memalign_mm_alloc
std::alignstd::aligned_storagesize
std::alignstd::alignポインターを移動すると、そのポインターを使用するクラスはアロケーターに割り当て解除のために移動されたアドレスへのポインターを与えますが、これは無効です。この方法でaligned_allocatorを作成する方法はありますか?

4

2 に答える 2

3

編集: OPからの説明の後、元の回答はトピックから外れているようです。参考までに、この回答の最後に記載されています。

実際には、答えはかなり単純です。単純に、ストレージ ブロックと最初のアイテムの両方へのポインターを保持する必要があります。

std::align実際には、これにはステートフル アロケーターは必要ありません (カスタムルーチンを使用しても、C++03 でも可能です)。秘訣は、ユーザー データを格納するのに正確に十分なメモリをシステムに要求するだけでアロケータが要求されるわけではないということです。独自の簿記目的で、もう少し多くを完全に要求できます。

そこで、整列アロケータを作成します。簡単にするために、割り当て/割り当て解除ルーチンに焦点を当てます。

template <typename T>
class aligned_allocator {
    // Allocates block of memory:
    // - (opt) padding
    // - offset: ptrdiff_t
    // - T * n: T
    // - (opt) padding
public:
    typedef T* pointer;
    typedef size_t size_type;

    pointer allocate(size_type n);
    void deallocate(pointer p, size_type n);

}; // class aligned_allocator

そして今、割り当てルーチン。たくさんのメモリいじ​​り、結局のところ、アロケータの心臓部です!

template <typename T>
auto aligned_allocator<T>::allocate(size_type n) -> pointer {
    size_type const alignment = std::max(alignof(ptrdiff_t), alignof(T));
    size_type const object_size = sizeof(ptrdiff_t) + sizeof(T)*n;
    size_type const buffer_size = object_size + alignment;

    // block is correctly aligned for `ptrdiff_t` because `std::malloc` returns
    // memory correctly aligned for all built-ins types.
    void* const block = std::malloc(buffer_size);

    if (block == nullptr) { throw std::bad_alloc{}; }

    // find the start of the body by suitably aligning memory,
    // note that we reserve sufficient space for the header beforehand
    void* storage = reinterpret_cast<char*>(block) + sizeof(ptrdiff_t);
    size_t shift = buffer_size;

    void* const body = std::align(alignment, object_size, storage, shift);

    // reverse track to find where the offset field starts
    char* const offset = reinterpret_cast<char*>(body) - sizeof(ptrdiff_t);

    // store the value of the offset (ie, the result of body - block)
    *reinterpret_cast<ptrdiff_t*>(offset) = sizeof(ptrdiff_t) + shift;

    // finally return the start of the body
    return reinterpret_cast<ptrdiff_t>(body);
} // aligned_allocator<T>::allocate

幸いなことに、割り当て解除ルーチンははるかに単純で、オフセットを読み取って適用するだけです。

template <typename T>
void aligned_allocator<T>::deallocate(pointer p, size_type) {
    // find the offset field
    char const* header = reinterpret_cast<char*>(p) - sizeof(ptrdiff_t);

    // read its value
    ptrdiff_t const offset = *reinterpret_cast<ptrdiff_t*>(header);

    // apply it to find start of block
    void* const block = reinterpret_cast<char*>(p) - offset;

    // finally deallocate
    std::free(block);
} // aligned_allocator<T>::deallocate

他のルーチンはメモリ レイアウトを意識する必要がないので、それらを書くのは簡単です。


元の答え:

template <typename T>
class Block {
public:
    Block(Block const&) = delete;
    Block& operator=(Block const&) = delete;

    explicit Block(size_t n);
    ~Block();

private:
    void* _storage;
    T* _begin;
    T* _end;
}; // class Block

template <typename T>
Block<T>::Block(size_t n) {
    size_t const object_size = n * sizeof(T);
    size_t const buffer_size = object_size + alignof(T);

    _storage = std::malloc(size);

    void* stock = _storage;
    size_t shift = buffer_size;
    std::align(alignof(T), object_size, stock, shift);

    _begin = _end = reinterpret_cast<T*>(stock);
} // Block<T>::Block

template <typename T>
Block<T>::~Block() {
    for (; _end != _begin; --_end) {
        (_end - 1)->~T();
    }

    std::free(_storage);
} // Block<T>::~Block
于 2013-06-29T13:53:22.517 に答える
1

C++11 ソリューションでなければならない場合は、この回答を無視してください。

そうでない場合...これをすでに知っているかどうかはわかりませんが、ここに1つのオプションがあります。

void * aligned_malloc( size_t size, size_t alignement )
{
    void * p = malloc( size + --alignement );
    void * p1 = (void*)( ( (size_t)p + alignement ) & ~alignement );

    ((char*)p1)[ -1 ] = (char)((char*)p1 - (char*)p);

    return p1;
}

void aligned_free( void * pMem )
{
    char * pDelete = (char*)pMem - ((char*)pMem)[ -1 ];
    free( pDelete );
}

おそらくmallocfreeは 100% 移植可能ではありませんが、そのような場合はプリプロセッサ ディレクティブを使用して簡単に処理できます。

于 2013-06-29T13:40:49.410 に答える