custom Allocator compilation difficulties 2 - c++

I am defining a custom allocator but I need to keep pointers of the allocator as offsets. As I change the pointer definition to a "size_t" type the code doesn't compile again with no apparent clues !
The need is to create a custom allocator where the memory is addressed in a "segment:offset" style and the address of the segment ( object ) is relocatable.
#include <bits/c++config.h>
#define _GLIBCXX_FULLY_DYNAMIC_STRING 0
#include <stdint.h>
#include <stddef.h>
#include <memory>
#include <string>
#include <limits>
typedef int32_t Token;
typedef unsigned char byte;
using namespace std;
template <typename T>
struct Allocator {
public:
// http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement
typedef T value_type;
typedef std::size_t pointer;
typedef const std::size_t const_pointer;
typedef value_type& reference;typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U> struct rebind {typedef Allocator<U> other;};
static const size_t heapSize=0x1000;
static pointer freePos;
static Token freeT;
static byte m[heapSize];
inline explicit Allocator() {freeT=0;freePos=0;}
inline ~Allocator() {}
inline Allocator(Allocator const&) {} // with explicit it doesn't compile
inline pointer address(reference r) {return &r;}
inline const_pointer address(const_reference r) {return &r;}
//static inline pointer allocate(size_type n, typename std::allocator<void>::const_pointer hint = 0){
// pointer t=freePos;freePos+=n*sizeof(T);return t;
static inline pointer allocate(size_type n){pointer t=freePos;freePos+=n*sizeof(T);return t;}
static void deallocate(pointer p, size_type n){
*(pointer*)((byte*)m+p)=(pointer)(n*sizeof(T));
}
static inline void deallocate(T* p,size_type n){deallocate((pointer)((byte*)p-m),n);}
static inline void deallocate(const T* p,size_type n){deallocate((pointer)((byte*)p-m),n);}
inline size_type max_size() const{
return std::numeric_limits<size_type>::max() / sizeof(T);
}
inline void construct(pointer p, const T& t) {}
inline void destroy(pointer p) {}
};
template <typename T>
bool operator==(Allocator<T> const &, Allocator<T> const &) { return true; }
template <typename T>
bool operator!=(Allocator<T> const &, Allocator<T> const &) { return false; }
using namespace std;
typedef std::basic_string< char,std::char_traits<char>,Allocator<char> > String;
int main(){
String s("nice");
String t("very nice");
String u("good");
return 0;
}

Keep the definitions of pointer and const_pointer the way the standard library expects it to be:
typedef T* pointer;
typedef const T* const_pointer;
Add a new typedef:
typedef std::size_t offset_type;
Use offset_type instead of pointer in suitable places.
Here's a version that compiles and builds but produces Segmentation Fault at run time. I'll leave it to you to figure out that problem.
#include <bits/c++config.h>
#define _GLIBCXX_FULLY_DYNAMIC_STRING 0
#include <stdint.h>
#include <stddef.h>
#include <memory>
#include <string>
#include <limits>
typedef int32_t Token;
typedef unsigned char byte;
using namespace std;
template <typename T>
struct Allocator {
public:
// http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::size_t offset_type;
typedef value_type& reference;typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U> struct rebind {typedef Allocator<U> other;};
static const size_t heapSize=0x1000;
static offset_type freePos;
static Token freeT;
static byte m[heapSize];
inline explicit Allocator() {freeT=0;freePos=0;}
inline ~Allocator() {}
inline Allocator(Allocator const&) {} // with explicit it doesn't compile
inline offset_type address(reference r) {return &r;}
inline const_pointer address(const_reference r) {return &r;}
//static inline offset_type allocate(size_type n, typename std::allocator<void>::const_pointer hint = 0)
// offset_type t=freePos;freePos+=n*sizeof(T);return t;
static inline pointer allocate(size_type n){offset_type t=freePos;freePos+=n*sizeof(T);return (pointer)t;}
static void deallocate(offset_type p, size_type n){
*(offset_type*)((byte*)m+p)=(offset_type)(n*sizeof(T));
}
static inline void deallocate(pointer p,size_type n){deallocate((offset_type)((byte*)p-m),n);}
static inline void deallocate(const_pointer* p,size_type n){deallocate((offset_type)((byte*)p-m),n);}
inline size_type max_size() const{
return std::numeric_limits<size_type>::max() / sizeof(T);
}
inline void construct(offset_type p, const T& t) {}
inline void destroy(offset_type p) {}
};
template <typename T>
typename Allocator<T>::offset_type Allocator<T>::freePos;
template <typename T>
Token Allocator<T>::freeT;
template <typename T>
byte Allocator<T>::m[Allocator::heapSize];
template <typename T>
bool operator==(Allocator<T> const &, Allocator<T> const &) { return true; }
template <typename T>
bool operator!=(Allocator<T> const &, Allocator<T> const &) { return false; }
using namespace std;
typedef std::basic_string< char,std::char_traits<char>,Allocator<char> > String;
int main(){
String s("nice");
String t("very nice");
String u("good");
return 0;
}

Related

How to align memory allocated for std::vector? [duplicate]

Is it possible to make std::vector of custom structs allocate aligned memory for further processing with SIMD instructions? If it is possible to do with Allocator, does anyone happen to have such an allocator he could share?
Edit: I removed the inheritance of std::allocator as suggested by GManNickG and made the alignment parameter a compile time thing.
I recently wrote this piece of code. It's not tested as much as I would like it so go on and report errors. :-)
enum class Alignment : size_t
{
Normal = sizeof(void*),
SSE = 16,
AVX = 32,
};
namespace detail {
void* allocate_aligned_memory(size_t align, size_t size);
void deallocate_aligned_memory(void* ptr) noexcept;
}
template <typename T, Alignment Align = Alignment::AVX>
class AlignedAllocator;
template <Alignment Align>
class AlignedAllocator<void, Align>
{
public:
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class U> struct rebind { typedef AlignedAllocator<U, Align> other; };
};
template <typename T, Alignment Align>
class AlignedAllocator
{
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef std::true_type propagate_on_container_move_assignment;
template <class U>
struct rebind { typedef AlignedAllocator<U, Align> other; };
public:
AlignedAllocator() noexcept
{}
template <class U>
AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept
{}
size_type
max_size() const noexcept
{ return (size_type(~0) - size_type(Align)) / sizeof(T); }
pointer
address(reference x) const noexcept
{ return std::addressof(x); }
const_pointer
address(const_reference x) const noexcept
{ return std::addressof(x); }
pointer
allocate(size_type n, typename AlignedAllocator<void, Align>::const_pointer = 0)
{
const size_type alignment = static_cast<size_type>( Align );
void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));
if (ptr == nullptr) {
throw std::bad_alloc();
}
return reinterpret_cast<pointer>(ptr);
}
void
deallocate(pointer p, size_type) noexcept
{ return detail::deallocate_aligned_memory(p); }
template <class U, class ...Args>
void
construct(U* p, Args&&... args)
{ ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }
void
destroy(pointer p)
{ p->~T(); }
};
template <typename T, Alignment Align>
class AlignedAllocator<const T, Align>
{
public:
typedef T value_type;
typedef const T* pointer;
typedef const T* const_pointer;
typedef const T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef std::true_type propagate_on_container_move_assignment;
template <class U>
struct rebind { typedef AlignedAllocator<U, Align> other; };
public:
AlignedAllocator() noexcept
{}
template <class U>
AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept
{}
size_type
max_size() const noexcept
{ return (size_type(~0) - size_type(Align)) / sizeof(T); }
const_pointer
address(const_reference x) const noexcept
{ return std::addressof(x); }
pointer
allocate(size_type n, typename AlignedAllocator<void, Align>::const_pointer = 0)
{
const size_type alignment = static_cast<size_type>( Align );
void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));
if (ptr == nullptr) {
throw std::bad_alloc();
}
return reinterpret_cast<pointer>(ptr);
}
void
deallocate(pointer p, size_type) noexcept
{ return detail::deallocate_aligned_memory(p); }
template <class U, class ...Args>
void
construct(U* p, Args&&... args)
{ ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }
void
destroy(pointer p)
{ p->~T(); }
};
template <typename T, Alignment TAlign, typename U, Alignment UAlign>
inline
bool
operator== (const AlignedAllocator<T,TAlign>&, const AlignedAllocator<U, UAlign>&) noexcept
{ return TAlign == UAlign; }
template <typename T, Alignment TAlign, typename U, Alignment UAlign>
inline
bool
operator!= (const AlignedAllocator<T,TAlign>&, const AlignedAllocator<U, UAlign>&) noexcept
{ return TAlign != UAlign; }
The implementation for the actual allocate calls is posix only but you can extent that easily.
void*
detail::allocate_aligned_memory(size_t align, size_t size)
{
assert(align >= sizeof(void*));
assert(nail::is_power_of_two(align));
if (size == 0) {
return nullptr;
}
void* ptr = nullptr;
int rc = posix_memalign(&ptr, align, size);
if (rc != 0) {
return nullptr;
}
return ptr;
}
void
detail::deallocate_aligned_memory(void *ptr) noexcept
{
return free(ptr);
}
Needs C++11, btw.
In the upcoming version 1.56, the Boost library will include Boost.Align. Among other memory alignment helpers it provides boost::alignment::aligned_allocator, which can be used a drop-in replacement for std::allocator and allows you to specify an alignment. See the documentation on https://boostorg.github.io/align/
Starting in C++17, just use std::vector<__m256i> or with any other aligned type. There's aligned version of operator new, it is used by std::allocator for aligned types (as well as by plain new-expression, so new __m256i[N] is also safe starting in C++17).
There's a comment by #MarcGlisse saying this, making this an answer to make it more visible.
Yes, it should be possible. If you put this question on google then you will get lots of sample code, below is some promising results:
https://bitbucket.org/marten/alignedallocator/wiki/Home
http://code.google.com/p/mastermind-strategy/source/browse/trunk/src/util/aligned_allocator.hpp?r=167
https://gist.github.com/1471329

Crash when try to write a custom allocate_shared allocator and make it thread_local

My program has several type of small objects to be created and destroyed very frequently in each thread using make_shared, and the shared_ptr will not be passed to another thread, in which case, I decide to write a custom allocate_shared allocator with a boost::pool as its member to allocate fixed size of memory according to the type.
My code is as follows:
ObjectAllocator.h:
#include <boost/pool/pool.hpp>
template<typename T>
class ObjectAllocator
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
auto static constexpr block_size=64+sizeof(value_type);
public:
ObjectAllocator() noexcept:pool_(block_size){}
ObjectAllocator(const ObjectAllocator &other) noexcept :pool_(block_size){}
~ObjectAllocator()=default;
template<typename U>
ObjectAllocator(const ObjectAllocator<U> &other) noexcept :pool_(block_size){}
template<typename U>
ObjectAllocator& operator= (const ObjectAllocator<U> &other){
return *this;
}
ObjectAllocator<T>& operator = (const ObjectAllocator &other){
return *this;
}
template<typename U>
struct rebind{ typedef ObjectAllocator<U> other; };
T *allocate(size_type n, const void *hint=nullptr){
#ifdef _DEBUG
assert(n==1);
#endif
return static_cast<T*>(pool_.malloc());
}
void deallocate(T *ptr, size_type n){
#ifdef _DEBUG
assert(n==1);
#endif
pool_.free(ptr);
}
private:
boost::pool<> ObjectAllocator<T>::pool_(block_size);
}
template<typename T, typename U>
inline bool operator == (const ObjectAllocator<T>&, const ObjectAllocator<U>&){
return true;
}
template<typename T, typename U>
inline bool operator != (const ObjectAllocator<T>& a, const ObjectAllocator<U> &b){
return !(a==b);
}
namespace Allocator {
template <typename T>
thread_local ObjectAllocator<T> allocator;
}
main.cpp:
class ObjectA{
public:
int s=0;
void func(){
std::cout<<s<<std::endl;
}
ObjectA() {//std::cout<<"()"<<std::endl;}
~ObjectA() {//std::cout<<"~"<<std::endl;}
};
std::vector<std::shared_ptr<ObjectA>> vec;
void test(){
static uint32_t loop_count=1000*1000;
for(uint32_t i=0;i<loop_count;i++){
shared_ptr<ObjectA> packet = allocate_shared<ObjectA, ObjectAllocator<ObjectA>>(Allocator::allocator<ObjectA>);
vec.push_back(packet);
}
vec.clear();
}
std::vector<std::shared_ptr<ObjectA>> vec2;
void test2(){
static uint32_t loop_count=1000*1000;
for(uint32_t i=0;i<loop_count;i++){
shared_ptr<ObjectA> packet = allocate_shared<ObjectA, ObjectAllocator<ObjectA>>(Allocator::allocator<ObjectA>);
vec2.push_back(packet);
}
vec2.clear();
}
int main() {
std::thread thread1(test);
test2();
return 0;
}
When I try to test it, it crashs and I have no idea why.
Could anyone helps to make it correct? Thanks in advance.
The debugger says seg fault in shared_ptr_base.h
void* _M_get_deleter(const std::type_info& __ti) const noexcept { return _M_pi ? _M_pi->_M_get_deleter(__ti) : nullptr; }
When I try to make boost::pool static, it works fine in single thread and crashes in multi-thread
The debugger says seg fault in shared_ptr_base.h
: _M_use_count(1), _M_weak_count(1) { }
update:
I make boost::pool to be static thread_local and it works properly now
template<typename T>
class ObjectAllocator
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
auto static constexpr block_size=64+sizeof(value_type);
public:
ObjectAllocator() noexcept{}
ObjectAllocator(const ObjectAllocator &other) noexcept {}
~ObjectAllocator()=default;
template<typename U>
ObjectAllocator(const ObjectAllocator<U> &other) noexcept {}
template<typename U>
ObjectAllocator& operator= (const ObjectAllocator<U> &other){
return *this;
}
ObjectAllocator<T>& operator = (const ObjectAllocator &other){
return *this;
}
template<typename U>
struct rebind{ typedef ObjectAllocator<U> other; };
T *allocate(size_type n, const void *hint=nullptr){
#ifdef _DEBUG
assert(n==1);
#endif
return static_cast<T*>(pool_.malloc());
}
void deallocate(T *ptr, size_type n){
#ifdef _DEBUG
assert(n==1);
#endif
pool_.free(ptr);
}
private:
thread_local static boost::pool<> pool_;
};
template<typename T>
thread_local boost::pool<> ObjectAllocator<T>::pool_(block_size);
template<typename T, typename U>
inline bool operator == (const ObjectAllocator<T>&, const ObjectAllocator<U>&){
return true;
}
template<typename T, typename U>
inline bool operator != (const ObjectAllocator<T>& a, const ObjectAllocator<U> &b){
return !(a==b);
}
namespace Allocator {
template <typename T>
thread_local static ObjectAllocator<T> allocator;
}
template <typename T, typename ...Args>
inline auto custom_make_shared(Args... args){
return std::allocate_shared<T,ObjectAllocator<T>>(Allocator::allocator<T>,std::forward<Args>(args)...);
}
Both your copy constructors for ObjectAllocator create a new instance of boost::pool each time they're called.
As std::allocate_shared copies the allocator (cppreference), the instance of ObjectAllocator used to allocate std::shared_ptr gets destructed with it's pool before the shared_ptr is destroyed.
Related question: C++ stateful allocator de-allocate issues
Probably unrelated to you problem, but there are also few other issues:
you don't join thread1 in main. This will call std::terminate and crash you program.
boost::pool<> ObjectAllocator<T>::pool_(block_size); - the ObjectAllocator<T>:: part is superfluous and nonstandard. (afaik accepted only in MSVC)

custom Allocator compilation difficulties 3

Having managed to define a custom allocator, it compiles when Char=char while it doesn΄t when Char=char16_t or char32_t . The allocator allocates all Strings sequentially inside the array m ( may be seen debugging and looking at memory ).
#include <string>
#include <limits>
typedef char char8_t; // just for symmetry.
#define charSz 8 // may by 16 or 32. change by hand !
//#define CONCAT_(a,b,c) a ## b ## c
//#define CONCAT(a,b,c) CONCAT_(a,b,c)
//typedef CONCAT(char,charSz,_t) Char; // may be char8_t or char16_t or char32_t
#if charSz == 8
#define STR(s) #s
typedef char8_t Char;
#elif charSz == 16
#define STR(s) u ## #s
typedef char16_t Char;
#elif charSz == 32
#define STR(s) U ## #s
typedef char32_t Char;
#endif
typedef int32_t Token;
typedef unsigned char byte;
typedef size_t addr;addr freePos=0;Token freeT=0;
const size_t heapSize=0x400;byte m[heapSize];
addr Allocate(size_t sz){addr t=freePos;freePos+=sz;return t;}
void Deallocate(addr p,size_t sz){/**((size_t*)(m+p))=sz;*/}
using namespace std;
template <typename T>
struct Allocator {
// http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef value_type& reference;typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U> struct rebind {typedef Allocator<U> other;};
inline Allocator() {}
// The initialization of freePos and freeT is done after the class definition.
// Doing it in the class creator ins't correct because another intializer of another Allocator
// class would reinitialize that.
inline ~Allocator() {}
inline Allocator(Allocator const&) {}
inline addr address(reference r) {return (byte*)(&r)-m;}
inline addr address(const_reference r) {return (byte*)(&r)-m;}
//static inline offset_type allocate(size_type n, typename std::allocator<void>::const_pointer hint = 0)
// offset_type t=freePos;freePos+=n*sizeof(T);return t;
static inline pointer allocate(size_type n){return (pointer)(m+Allocate(n*sizeof(T)));}
static void deallocate(pointer p, size_type n){Deallocate((byte*)p-m,sizeof(T)*n);}
inline size_type max_size() const{
return std::numeric_limits<size_type>::max() / sizeof(T);
}
inline void construct(pointer p, const T& t) {}
inline void destroy(pointer p) {}
};
template <typename T>
bool operator==(Allocator<T> const &, Allocator<T> const &) { return true; }
template <typename T>
bool operator!=(Allocator<T> const &, Allocator<T> const &) { return false; }
typedef std::basic_string< Char,std::char_traits<Char>,Allocator<Char> > String;
int main(){
for (size_t i=0;i<sizeof(m);i++) m[i]=0xDD; // fill memory to be able to see changes - especially 0s
String s=STR(nice) ;
String t=STR(very nice) ;
String u=STR(good) ;
return 0;
}
Adding
template<typename U> Allocator(U){}
works !

custom Allocator compilation difficulties

I am trying to create a custom allocator, but there are compilation problems.
By changing the value of the define from
#define _GLIBCXX_FULLY_DYNAMIC_STRING 0
to
#define _GLIBCXX_FULLY_DYNAMIC_STRING 1
I managed to switch from being unable to compile to being able to compile, but should it be like that? Shouldn't it be a little bit simpler ?
Does any body has experienc on that and already knows how these compilation problems may be solved.
The code the minimal required:
#include <bits/c++config.h>
#define _GLIBCXX_FULLY_DYNAMIC_STRING 0
#include <stdint.h>
#include <stddef.h>
#include <memory>
#include <string>
#include <limits>
typedef int32_t Token;
typedef unsigned char byte;
using namespace std;
template <typename T>
struct Allocator {
public:
static const size_t heapSize=0x1000;
static size_t freePos;
static Token freeT;
static byte m[heapSize];
// http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement
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;
template<typename U> struct rebind {typedef Allocator<U> other;};
inline explicit Allocator() {freeT=0;freePos=0;}
inline ~Allocator() {}
inline Allocator(Allocator const&) {} // with explicit it doesn't compile
//template<typename U>
//inline explicit Allocator(Allocator<U> const&) {}
inline pointer address(reference r) {return &r;}
inline const_pointer address(const_reference r) {return &r;}
static inline pointer allocate(size_type cnt, typename std::allocator<void>::const_pointer hint = 0){
return reinterpret_cast<pointer>(::operator new(cnt * sizeof (T)));
/* pointer allocate(size_type n, const_pointer hint = 0 ){
size_t t=freePos;freePos+=sizeof(T)*n;return t;
}
*/
}
static inline void deallocate(pointer p, size_type){
::operator delete(p);
/* pointer deallocate(pointer p,size_type n){
size_t sz=sizeof(T)*n;
*(size_t*)(m+p)=sz;
}
*/
}
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); }
/*
void construct(pointer p, const T& val)
{ new ((T*) p) T(val); }
*/
inline void destroy(pointer p) { p->~T(); }
// void destroy(pointer p) { ((T*)m[p])->~T(); }
inline bool operator==(Allocator const&) {return true;}
inline bool operator!=(Allocator const& a) {return false;}
};
#endif
using namespace std;
typedef std::basic_string< char,std::char_traits<char>,Allocator<char> > String;
int main(){
String s("Nice-the-data-goes-in-my-memory");
return 0;
}
It works fine if you make the comparison operators free functions. In general, relational operators should be free functions so they can convert implicitly on both sides:
template <typename T>
bool operator==(Allocator<T> const &, Allocator<T> const &) { return true; }
template <typename T>
bool operator!=(Allocator<T> const &, Allocator<T> const &) { return false; }

Making std::vector allocate aligned memory

Is it possible to make std::vector of custom structs allocate aligned memory for further processing with SIMD instructions? If it is possible to do with Allocator, does anyone happen to have such an allocator he could share?
Edit: I removed the inheritance of std::allocator as suggested by GManNickG and made the alignment parameter a compile time thing.
I recently wrote this piece of code. It's not tested as much as I would like it so go on and report errors. :-)
enum class Alignment : size_t
{
Normal = sizeof(void*),
SSE = 16,
AVX = 32,
};
namespace detail {
void* allocate_aligned_memory(size_t align, size_t size);
void deallocate_aligned_memory(void* ptr) noexcept;
}
template <typename T, Alignment Align = Alignment::AVX>
class AlignedAllocator;
template <Alignment Align>
class AlignedAllocator<void, Align>
{
public:
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class U> struct rebind { typedef AlignedAllocator<U, Align> other; };
};
template <typename T, Alignment Align>
class AlignedAllocator
{
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef std::true_type propagate_on_container_move_assignment;
template <class U>
struct rebind { typedef AlignedAllocator<U, Align> other; };
public:
AlignedAllocator() noexcept
{}
template <class U>
AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept
{}
size_type
max_size() const noexcept
{ return (size_type(~0) - size_type(Align)) / sizeof(T); }
pointer
address(reference x) const noexcept
{ return std::addressof(x); }
const_pointer
address(const_reference x) const noexcept
{ return std::addressof(x); }
pointer
allocate(size_type n, typename AlignedAllocator<void, Align>::const_pointer = 0)
{
const size_type alignment = static_cast<size_type>( Align );
void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));
if (ptr == nullptr) {
throw std::bad_alloc();
}
return reinterpret_cast<pointer>(ptr);
}
void
deallocate(pointer p, size_type) noexcept
{ return detail::deallocate_aligned_memory(p); }
template <class U, class ...Args>
void
construct(U* p, Args&&... args)
{ ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }
void
destroy(pointer p)
{ p->~T(); }
};
template <typename T, Alignment Align>
class AlignedAllocator<const T, Align>
{
public:
typedef T value_type;
typedef const T* pointer;
typedef const T* const_pointer;
typedef const T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef std::true_type propagate_on_container_move_assignment;
template <class U>
struct rebind { typedef AlignedAllocator<U, Align> other; };
public:
AlignedAllocator() noexcept
{}
template <class U>
AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept
{}
size_type
max_size() const noexcept
{ return (size_type(~0) - size_type(Align)) / sizeof(T); }
const_pointer
address(const_reference x) const noexcept
{ return std::addressof(x); }
pointer
allocate(size_type n, typename AlignedAllocator<void, Align>::const_pointer = 0)
{
const size_type alignment = static_cast<size_type>( Align );
void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));
if (ptr == nullptr) {
throw std::bad_alloc();
}
return reinterpret_cast<pointer>(ptr);
}
void
deallocate(pointer p, size_type) noexcept
{ return detail::deallocate_aligned_memory(p); }
template <class U, class ...Args>
void
construct(U* p, Args&&... args)
{ ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }
void
destroy(pointer p)
{ p->~T(); }
};
template <typename T, Alignment TAlign, typename U, Alignment UAlign>
inline
bool
operator== (const AlignedAllocator<T,TAlign>&, const AlignedAllocator<U, UAlign>&) noexcept
{ return TAlign == UAlign; }
template <typename T, Alignment TAlign, typename U, Alignment UAlign>
inline
bool
operator!= (const AlignedAllocator<T,TAlign>&, const AlignedAllocator<U, UAlign>&) noexcept
{ return TAlign != UAlign; }
The implementation for the actual allocate calls is posix only but you can extent that easily.
void*
detail::allocate_aligned_memory(size_t align, size_t size)
{
assert(align >= sizeof(void*));
assert(nail::is_power_of_two(align));
if (size == 0) {
return nullptr;
}
void* ptr = nullptr;
int rc = posix_memalign(&ptr, align, size);
if (rc != 0) {
return nullptr;
}
return ptr;
}
void
detail::deallocate_aligned_memory(void *ptr) noexcept
{
return free(ptr);
}
Needs C++11, btw.
In the upcoming version 1.56, the Boost library will include Boost.Align. Among other memory alignment helpers it provides boost::alignment::aligned_allocator, which can be used a drop-in replacement for std::allocator and allows you to specify an alignment. See the documentation on https://boostorg.github.io/align/
Starting in C++17, just use std::vector<__m256i> or with any other aligned type. There's aligned version of operator new, it is used by std::allocator for aligned types (as well as by plain new-expression, so new __m256i[N] is also safe starting in C++17).
There's a comment by #MarcGlisse saying this, making this an answer to make it more visible.
Yes, it should be possible. If you put this question on google then you will get lots of sample code, below is some promising results:
https://bitbucket.org/marten/alignedallocator/wiki/Home
http://code.google.com/p/mastermind-strategy/source/browse/trunk/src/util/aligned_allocator.hpp?r=167
https://gist.github.com/1471329