Home c++ How to write your own allocator?

How to write your own allocator?

Author

Date

Category

They often write that code can work faster with a non-standard allocator, but how do you write your own allocator?


Answer 1, authority 100%

Allocator requirements and their default values ​​are given in the “Allocator requirements [allocator.requirements]” chapter . The same chapter provides an example of a minimal allocator:

template & lt; class Tp & gt;
struct SimpleAllocator {
  typedef Tp value_type;
  SimpleAllocator (constructor arguments);
  template & lt; class T & gt; SimpleAllocator (const SimpleAllocator & lt; T & gt; & amp; other);
  Tp * allocate (std :: size_t n);
  void deallocate (Tp * p, std :: size_t n);
};
template & lt; class T, class U & gt;
bool operator == (const SimpleAllocator & lt; T & gt; & amp ;, const SimpleAllocator & lt; U & gt; & amp;);
template & lt; class T, class U & gt;
bool operator! = (const SimpleAllocator & lt; T & gt; & amp ;, const SimpleAllocator & lt; U & gt; & amp;);

Containers are supposed to access the allocator not directly, but through the std :: allocator_traits template, which provides default values ​​such as typedef T * pointer; , etc.

However, existing standard library implementations do not use allocator_traits everywhere.
Therefore, if we try to compile the code for example
std :: basic_string & lt; char, std :: char_traits & lt; char & gt ;, SimpleAllocator & lt; char & gt; & gt; s;
then we get errors like this:

/ usr / local / bin /../ lib / gcc / x86_64-unknown-linux-gnu / 4.9. 2 /../../../../ include / C++ / 4.9.2 / bits / basic_string.h: 114: 41: error: 'rebind' following the 'template' keyword does not refer to a template
 typedef typename _Alloc :: template rebind & lt; _CharT & gt; :: other _CharT_alloc_type;

or

1 & gt; S: \ MSVS2013 \ VC \ include \ xstring (664): error C2903: 'rebind': symbol is neither a class template nor a function template

In both cases, the standard library implementation says _Alloc :: rebind instead of allocator_traits & lt; _Alloc & gt; :: rebind .

Thus, when writing your own allocator, you need to look at compilation errors and, if necessary, add the necessary class members.

Below is an example of an Arena allocator that compiles (VC++ 2013, g ++ – 4.9.2)

# include & lt; cassert & gt;
#include & lt; memory & gt;
#include & lt; vector & gt;
#include & lt; deque & gt;
class Arena {
public:
  Arena () {}
  ~ Arena () {
    assert (m_allocations == 0);
  }
  void * allocate (std :: size_t n) {
    if (n & gt; m_available) {
      m_chunks.emplace_back (100500);
      m_available = m_chunks.back (). size ();
      m_memory = & amp; m_chunks.back (). front ();
    }
    auto mem = m_memory;
    m_available - = n;
    m_memory + = n;
    ++ m_allocations;
    return mem;
  }
  void deallocate (void * p, std :: size_t n) {
    --m_allocations;
    auto mem = (unsigned char *) p;
    if (mem + n == m_memory) {
      m_memory = mem;
      m_available + = n;
    }
  }
private:
  std :: deque & lt; std :: vector & lt; unsigned char & gt; & gt; m_chunks;
  std :: size_t m_available = 0;
  unsigned char * m_memory;
  int m_allocations = 0;
};
template & lt; class T & gt;
struct ArenaAllocator {
  using value_type = T;
  using Traits = std :: allocator_traits & lt; ArenaAllocator & lt; T & gt; & gt ;;
#if! defined _MSC_VER
  // libstdC++ uses the default constructor:
  // __a == _Alloc ()
  ArenaAllocator (): m_arena (nullptr) {}
  // libstdC++ requires the following definitions
  using size_type = typename std :: allocator & lt; T & gt; :: size_type;
  using difference_type = typename std :: allocator & lt; T & gt; :: difference_type;
  using pointer = typename std :: allocator & lt; T & gt; :: pointer;
  using const_pointer = typename std :: allocator & lt; T & gt; :: const_pointer;
  // "reference" is not included in Allocator Requirements, 
// But LIBSTDC++ thinks that it always works with STD :: Allocator.
  Using Reference = TypeName Std :: Allocator & LT; T & GT; :: Reference;
  Using const_reference = typename std :: allocator & lt; t & gt; :: const_reference;
#Endif
  Explicit Arenaallocator (Arena & Amp; Arena): M_ARENA (& AMP; ARENA) {}
  TEMPLATE & LT; Class U & GT; Arenaallocator (Const Arenaallocator & LT; U & GT; & amp; Other): M_ARENA (Other.m_arena) {}
  T * Allocate (STD :: SIZE_T N) {RETURN (T *) M_ARENA- & GT; ALLOCATE (N * SizeOF (T)); }
  Void DEALLOCATE (T * P, STD :: SIZE_T N) {M_ARENA- & GT; DEALLOCATE (P, N * SIZEOF (T)); }
  // Required in VC++ and LIBSTDC++
  TEMPLATE & LT; Class U, Class ... Args & GT; Void Construct (u * p, args & amp; & amp; ... args) {std :: allocator & lt; t & gt; (). Construct (P, STD :: FORWARD & LT; Args & GT; (Args) ...); }
  TEMPLATE & LT; Class U & GT; Void Destroy (U * P) {STD :: ALLOCATOR & LT; T & GT; (). Destroy (P); }
  TEMPLATE & LT; Class U & GT; STRUCT REBIND {USING OTHER = ARENAALLOCATOR & LT; U & GT ;; };
  Arena * M_ARENA;
};
TEMPLATE & LT; Class T, Class U & GT; BOOL Operator == (Const Arenaallocator & LT; T & GT; & amp; LHS, Const Arenaallocator & LT; U & GT; & AMP; RHS) {Return LHS.M_ARENA == RHS.M_ARENA; }
TEMPLATE & LT; Class T, Class U & GT; BOOL OPERATOR! = (Const Arenaallocator & LT; T & GT; & amp; LHS, Const Arenaallocator & lt; u & gt; & amp; RHS) {RETURN! (LHS == RHS); }
#Include & lt; deque & gt;
#Include & lt; Functional & gt;
#Include & lt; List & gt;
#Include & lt; Map & GT;
#Include & lt; Memory & gt;
#Include & lt; set & gt;
#Include & lt; String & GT;
#Include & lt; unordered_set & gt;
#Include & lt; unordered_map & gt;
#Include & lt; vector & gt;
using a_string = std :: basic_string & lt; char, std :: char_traits & lt; char & gt;, Arenaallocator & lt; Char & gt; & gt ;;
TEMPLATE & LT; Class T & GT; using a_vector = std :: vector & lt; t, arenaallocator & lt; t & gt; & gt ;;
TEMPLATE & LT; Class T & GT; using a_deque = std :: deque & lt; t, arenaallocator & lt; t & gt; & gt ;;
TEMPLATE & LT; Class T & GT; Using a_list = std :: list & lt; t, arenaallocator & lt; t & gt; & gt ;;
Template & lt; Class K & GT; Using a_set = std :: set & lt; k, std :: less & lt; k & gt;, Arenaallocator & lt; k & gt; & gt ;;
TEMPLATE & LT; Class K, Class V & GT; Using a_map = STD :: MAP & LT; K, V, STD :: LESS & LT; K & GT;, Arenaallocator & LT; STD :: PAIR & LT; Const K, V & GT; & GT; & GT ;;
Template & lt; Class K & GT; Using a_unordered_set = STD :: unordered_set & lt; k, std :: hash & lt; k & gt;, std :: equal_to & lt; k & gt;, Arenaallocator & lt; k & gt; & gt ;;
TEMPLATE & LT; Class K, Class V & GT; Using a_unordered_map = STD :: unordered_map & lt; k, std :: hash & lt; k & gt;, std :: equal_to & lt; k & gt;, Arenaallocator & lt; STD :: PAIR & LT; const k, V & gt; & gt; & gt ;;
STRUCT X {};
INT MAIN () {
  Arena Arena;
  Arenaallocator & lt; Char & GT; Arena_allocator (Arena);
  A_STRING S_EMPTY (Arena_Allocator);
  A_String S_123 ("123", Arena_allocator);
  A_Vector & LT; Int & GT; v_int ({1, 2, 3}, arena_allocator);
  A_Vector & LT; X & GT; V_X (42, X {}, Arena_allocator);
  A_Vector & LT; A_String & GT; v_str ({s_empty, s_123}, anena_allocator);
  A_Vector & LT; A_String & GT; V_STR_COPY (V_STR, ARENA_ALLOCATOR);
  a_deque & lt; int & gt; d_int ({1, 2, 3}, arena_allocator);
  a_list & lt; int & gt; L_INT ({1, 2, 3}, Arena_Allocator);
  a_set & lt; int & gt; s_int ({1, 2, 3}, STD :: LESS & LT; INT & GT; {}, Arena_Allocator);
  A_MAP & LT; A_STRING, INT & GT; M_STR_INT (ARENA_ALLOCATOR);
  A_UNORDERED_SET & LT; INT & GT; US_INT (Arena_allocator);
  AUTO P = STD :: ALLOCATE_SHARED & LT; INT & GT; (Arena_allocator, 123);
#if 0 // This code does not work in VC++ and G ++
  A_UNORDERED_MAP & LT; A_STRING, INT & GT; UM_STR_INT (Arena_Allocator);
  STD :: FUNCTION & LT; Void () & gt; f (std :: allocator_arg_t {}, anena_allocator, [] {});
#Endif
}

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions