I'll consider interleaving, but whether I do or not doesn't affect the main question of the thread. To avoid needing dynamic/writeable buffers for static data, I need to have all the data in a contiguous memory region (in interleaved case, or one contiguous region per attribute in the non-interleaved case--it's really besides the point).

Here's my attempt at a custom allocator. And it works in gcc regardless of build options, and on MSVC2012 in Release mode. But in MSVC in Debug mode, it doesn't (that is, it runs, but the semantics don't work):

template<class T>
class ContigAlloc
{
public:
typedef T value_type;
typedef T *pointer;
typedef const T *const_pointer;
typedef T &reference;
typedef T const &const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template<class U>
struct rebind
{
typedef ContigAlloc<U> other; // TODO: typedef -> using when VC++2013
};
inline ContigAlloc(MemBuff &buff); // TODO: noexcept when VC++2013
template<class U>
inline ContigAlloc(ContigAlloc<U> const &other); // TODO: noexcept when VC++2013
inline pointer address(reference x) const;
inline const_pointer address(const_reference x) const;
inline pointer allocate(std::size_t n);
inline void deallocate(pointer p, std::size_t n); // TODO: noexcept when VC++2013
inline size_type max_size(void) const;
// TODO: Variadic temlates for construct() when VC++2013
template<class U>
inline void construct(U *p);
template<class U, class A>
inline void construct(U *p, A &&a);
template<class U, class A0, class A1>
inline void construct(U *p, A0 &&a0, A1 &&a1);
template<class U, class A0, class A1, class A2>
inline void construct(U *p, A0 &&a0, A1 &&a1, A2 &&a2);
template<class U, class A0, class A1, class A2, class A3>
inline void construct(U *p, A0 &&a0, A1 &&a1, A2 &&a2, A3 &&a3);
template<class U>
inline void destroy(U *p);
template<class T0, class U>
inline friend bool operator==(ContigAlloc<T0> const &x, ContigAlloc<U> const&y); // TODO: noexcept when VC++2013
template<class T0, class U>
inline friend bool operator!=(ContigAlloc<T0> const &x, ContigAlloc<U> const&y); // TODO: noexcept when VC++2013
private:
ContigAlloc(void); // TODO: = delete instead of private when VC++2013
ContigAlloc &operator=(ContigAlloc const &);
template<class U>
friend class ContigAlloc;
MemBuff &_buff;
};
template<class T>
inline ContigAlloc<T>::ContigAlloc(MemBuff &buff) : _buff(buff)
{
}
template<class T>
template<class U>
inline ContigAlloc<T>::ContigAlloc(ContigAlloc<U> const &other) : _buff(other._buff) // TODO: noexcept when VC++2013
{
}
template<class T>
inline typename ContigAlloc<T>::pointer ContigAlloc<T>::address(reference x) const
{
return ::std::addressof(x);
}
template<class T>
inline typename ContigAlloc<T>::const_pointer ContigAlloc<T>::address(const_reference x) const
{
return ::std::addressof(x);
}
template<class T>
inline typename ContigAlloc<T>::pointer ContigAlloc<T>::allocate(std::size_t n)
{
return reinterpret_cast<T *>(_buff.alloc(sizeof(T) * n);
}
template<class T>
inline void ContigAlloc<T>::deallocate(T *p, std::size_t n) // TODO: noexcept when VC++2013
{
_buff.deall(p, sizeof(T) * n);
}
template<class T>
inline typename ContigAlloc<T>::size_type ContigAlloc<T>::max_size(void) const
{
return _buff.remain();
}
template<class T>
template<class U>
inline void ContigAlloc<T>::construct(U *p)
{
::new(reinterpret_cast<void *>(p)) U;
}
template<class T>
template<class U, class A>
inline void ContigAlloc<T>::construct(U *p, A &&a)
{
::new(reinterpret_cast<void *>(p)) U(std::forward<A>(a));
}
template<class T>
template<class U, class A0, class A1>
inline void ContigAlloc<T>::construct(U *p, A0 &&a0, A1 &&a1)
{
::new(reinterpret_cast<void *>(p)) U(std::forward<A0>(a0), std::forward<A1>(a1));
}
template<class T>
template<class U, class A0, class A1, class A2>
inline void ContigAlloc<T>::construct(U *p, A0 &&a0, A1 &&a1, A2 &&a2)
{
::new(reinterpret_cast<void *>(p)) U(std::forward<A0>(a0), std::forward<A1>(a1), std::forward<A2>(a2));
}
template<class T>
template<class U, class A0, class A1, class A2, class A3>
inline void ContigAlloc<T>::construct(U *p, A0 &&a0, A1 &&a1, A2 &&a2, A3 &&a3)
{
::new(reinterpret_cast<void *>(p)) U(std::forward<A0>(a0), std::forward<A1>(a1), std::forward<A2>(a2), std::forward<A3>(a3));
}
template<class T>
template<class U>
inline void ContigAlloc<T>::destroy(U *p)
{
p->~U();
}
template<class T0, class U>
inline bool operator==(ContigAlloc<T0> const &x, ContigAlloc<U> const&y) // TODO: noexcept when VC++2013
{
return x._buff == y._buff;
}
template<class T0, class U>
inline bool operator!=(ContigAlloc<T0> const &x, ContigAlloc<U> const&y) // TODO: noexcept when VC++2013
{
return x._buff != y._buff;
}

I then feed this allocator to the std::vector container(s) I use in my mesh class. If, in allocate I check the types with std::is_same(), I find that *only* in Debug builds in MSVC does the allocator get rebinded to another type. The thing is, I don't know how to handle that. What I would optimally want to do is delegate to the default std::allocator in any case where the a rebind to another type has been used by the container, as that means the container is using the allocator to store other things such as members of the class or debugging crap rather than element storage. I tried changing the rebind declaration to the following, but it doesn't work in MSVC Debug:

struct rebind
{
typedef typename std::conditional<std::is_same<T, U>::value, ContigAlloc<U>, std::allocator<U>>::type other;
}

**Edited by Prune, 03 June 2014 - 07:26 PM.**