Last year I had joined BSC to work on my master’s thesis, my goal was to replace the standard cpp allocator with a custom allocator which would allocate to heterogenous memory systems automatically.

It was the first time for me to write a custom allocator in cpp.Here is how I did it.

#include <iostream>

using namespace std;

template<class T>
struct TestAllocator {
    typedef T value_type;

    TestAllocator() noexcept;
    
    template<class U>
    TestAllocator(const TestAllocator <U> &) noexcept {}

    template<class U>
    bool operator==(const TestAllocator<U> &) const noexcept {
        return true;
    }

    template<class U>
    bool operator!=(const TestAllocator<U> &) const noexcept {
        return false;
    }

    T *allocate(const size_t n) const;

    void deallocate(T *const p, size_t) const noexcept;
};

template<class T>
T *TestAllocator<T>::allocate(const size_t n) const {
    if (n == 0) {
        return nullptr;
    }
    if (n > static_cast<size_t>(-1) / sizeof(T)) {
        throw std::bad_array_new_length();
    }

    void *baseptr = malloc(n);
    if (!baseptr){
        throw std::bad_alloc();
    }

    return static_cast<T *>(baseptr);
}

template<class T>
void TestAllocator<T>::deallocate(T *const p, size_t) const noexcept {
    free(p);
}

template<class T>
TestAllocator<T>::TestAllocator() noexcept {
}

One of the issues I faced was not declaring typedef T value_type for the class template.The error message that would be returned by the compiler was not easy to comprehend

/usr/include/c++/11/bits/stl_vector.h:1582:75: error: ‘_M_get_Tp_allocator’ was not declared in this scope; did you mean ‘get_allocator’?
 1582 |             = this->_M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator()));
      |                                                        ~~~~~~~~~~~~~~~~~~~^~
      |                                                        get_allocator
/usr/include/c++/11/bits/stl_vector.h:1583:25: error: ‘struct std::_Vector_base<int, TestAllocator<int> >::_Vector_impl’ has no member named ‘_M_end_of_storage’
 1583 |           this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
      |           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
/usr/include/c++/11/bits/stl_vector.h:1583:59: error: ‘struct std::_Vector_base<int, TestAllocator<int> >::_Vector_impl’ has no member named ‘_M_start’
 1583 |           this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
      |                                             ~~~~~~~~~~~~~~^~~~~~~~
/usr/include/c++/11/bits/stl_vector.h:1584:25: error: ‘struct std::_Vector_base<int, TestAllocator<int> >::_Vector_impl’ has no member named ‘_M_finish’
 1584 |           this->_M_impl._M_finish =
      |           ~~~~~~~~~~~~~~^~~~~~~~~
/usr/include/c++/11/bits/stl_vector.h:1586:55: error: ‘struct std::_Vector_base<int, TestAllocator<int> >::_Vector_impl’ has no member named ‘_M_start’
 1586 |                                         this->_M_impl._M_start,

Looking at the stl_vector.h file we come across the following which gave me the hint that we are expecting the missing type information.

/usr/include/c++/11/bits/stl_vector.h

   128       struct _Vector_impl
   129           : public _Tp_alloc_type, public _Vector_impl_data
   ...
   ...
   246       private:
   247         _Vector_impl& _M_impl;
   ...
   279       const _Tp_alloc_type&
   280       _M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT
   281       { return this->_M_impl; }
   ...
   293       _Vector_base(const allocator_type& __a) _GLIBCXX_NOEXCEPT
   294       : _M_impl(__a) { }
   ...
   397       __glibcxx_class_requires2(_Tp, _Alloc_value_type, _SameTypeConcept)

Now we can use it to replace any standard library class.

int main() {
    vector<int, TestAllocator<int>> test { 10, 10, 10,10,10,10,10 };
    for(int i: test){
        cout << i << endl;
    }
    return 0;
}