C + + Allocator Customization Guide

Source: Internet
Author: User
Tags shallow copy

Gossip

Yesterday, in the group, Bacon complained that the custom allocator encountered strange problems and then chose the PMR, which I understood. Allocator this thing, born accompanied with the design error and useless abstraction, c++03-14 paste so long, even added a new feature to compatible with old Xiang and paste new Xiang, the result c++17 finally or another gate sent a PMR.

Simply put, although allocator concept said a lot of things, there are some surrounding concept such as allocator aware container and language facilities such as allocator_traits support, The customization of allocator still receives great restrictions.

My conclusion to this is that although the c++11 start standard claims to support stateful allocator, it derives from the implicit qualification of various historical and standard library implementations, and the allocator customization can only be stateless . From the realization, the allocator can only put a pointer.

The construction of the container enforces the requirement that allocator support copy and transform

Allocator as a container's construction parameters, is copied into the container, and for a stateful allocator, its copy is not necessarily reasonable. Second, the container might use rebind to get another allocator type, and then use the incoming allocator to transform the construct, which is actually a copy, and the copy is not necessarily reasonable. Again, the major standard library will be in the debug version of the container to save some meta-information, and these meta-information occupied by the memory, is also used allocator allocation, so often in debug mode, the container implementation needs two allocator, one is allocator< Value_type> and the other is allocator<metadata> the container will be converted to allocator<value_type> on-the-spot by the place where the meta information needs to be allocated allocator< Metadata> and use (such as MSVC), so you may see the following code in the container

//片段1
void some_container::check_some_metadata() {#ifdef _DEBUG    allocator<metadata> _metaal(this->_allocator); //当场构造    _metaal.allocate(...); //使用    _metaal.deallocate(...); //使用#endif}

For stateful allocator, the copy is not necessarily reasonable, take stack_allocator as an example, he may have two implementations, one is the interior of a fixed-length array

struct stack_allocator {    byte _stack[MAX_SIZE];    byte* _stack_ptr;};

The other is the interior with a pointer to the external fixed space

struct stack_allocator {    byte* _stack_bottom;    byte* _stack_top;};

The first implementation cannot be copied at all, as it can only be defined by the user in a determined location, and the user is assured that his life cycle is greater than or equal to the allocated memory life cycle, and if its copy is allowed, there will be a problem in the code in fragment 1. Because the function returns after the space is no longer present.

The second implementation and the first one is not feasible, first, shallow copy is unreasonable, if you use copy allocator allocated space, the copy of the Stack_top pointer moved to a new position, but the body of the pointer does not change, the next time to use the ontology to allocate space, there will also be problems.

To this end, the C + + standard specifically stipulates that after the allocator copy (or conversion), two allocator allocated space must be able to release each other, further confirming the fact that allocator cannot have a state.

Even if you give up debugging convenience, using a macro and other conditional compilation options to disable the debug information in the container, ensure that the container only use a allocator, still does not solve the problem. The problem stems from what we have mentioned, the allocator is copied into the container, and the rebind exists. You may want to avoid copying by not providing the allocator parameter, allowing the container to construct the allocator in a way that is constructed by default, but this is still not possible, taking MSVC's container as an example, when allocator is not provided, the container's outermost constructor constructs the container by default and then copy to the internal implementation, just like the pseudo code below

template<class T, class Al>class vector_base { //内部实现    vector(const Al& a)        : _allocator(a) //拷贝    {}    //...};template<class T, class Al = /*...*/>class vector : vector_base<T, Al> //内部实现{    vector() :        vector_base(Al())    {}    //...}

The implementation will also accept (or default construct) a allocator<value_type> from the constructor for a situation where the value_type and allocator are actually assigned. And then pass him to the rebind to get the allocator<node> to carry on the transformation constructs, also not.

It seems that in this case, the stateful allocator can only be shared by reference counting, which is what I said above, and the allocator has a maximum of one pointer.

Ironically, the copy construction and copy assignment of a container does not require a copy of the allocator, and the implementation will determine whether allocator can be copied by allocator_traits (propagate_on_container_copy_ Construction), for allocator that cannot be copied, the move construct (Assignment) does not directly exchange the internal pointer, but, as with the copy construct (Assignment), reserves enough space in the target container and then moves the element one by one move past. But this design has p use, you can not write out the allocator that cannot copy.

Master-Slave allocator cannot be achieved

For this kind of can't copy situation, I have conceived a kind of hack, is through the special realization allocator rebind, lets allocator::rebind<u> return a ref_allocator, through it to refer to the main allocator , so that the rebind allocator and the main allocator are not shared by reference counting (as in the code below), which seems to solve the problem in fragment 1. However, there are still other problems not solved, that is, for map, set such a container, the container will only save rebind after the Allocator_ref, and will not store the main allocator, This way you can only manually control the life cycle of the allocator outside ringing with the container, this is not good. Also, this implementation disables the container's default construction, because the default constructed allocator<value_type> is used to construct allocator_ref and ends the life cycle, at which point Allocator_ Ref internally stores a hanging reference for the main allocator.

//主从allocator设计示意template<class U, class T>struct allocator_ref {    allocator<T>* _ref;        template<class U2>    struct rebind {        using other = allocator_ref<U2, T>;    };};template<class T>struct allocator {    template<class U>    struct rebind {        using other = allocator_ref<U, T>;    };};
Fancy pointer is not so fancy.

Allocator::p Ointer can be a custom fancy pointer, and the implementation of the container also assumes allocator fancy may be used, For example, in MSVC string, the union writes an empty constructor to support the case where the pointer member is the object. However fancy pointer still cannot be stateful, the standard requires that fancy pointer must be able and the bare pointer of the object it points to the painless conversion, so shared_ptr is not fancy_pointer,unique_ptr barely counted. The hope that you want to encapsulate some complex abstraction through fancy pointer is shattered.

Allocator::construct can't do anything but placement new.

The construct function takes a variable-length parameter and constructs an object on a given pointer, which you think has a lot of room for expansion? In fact, he can hardly do too many articles on object construction, I would like to use it to implement the custom construction on the fancy pointer, but he began to accept the bare pointer from c++11, even if you forcibly do not write bare pointers as parameters, Allocator_ Traits is a bare pointer to you when forwarding a costruct call, and there is no way to implement a custom construct on fancy pointer.

Second,allocator<t> 's construct cannot be used only to construct T, which makes it impossible to attempt to compress the space allocated to memory based on the type of T. Why not just construct T, or rebind, take msvc map for example, rebind get the allocator<node> assigned to the node type, then implement will use Allocator<node> The construct in node type of VALUE_TYPE member construct that pair, finish the ball, this also play fur.

What more can you expect from construct? Also use cout to hit a log, save a few debugging objects can not do, because his allocator can not have a state.

How does the standard library work? Pmr

What did the PMR do? You open a memory_resource somewhere else, and then pmr::allocator it with a pointer to reference it. OK, stateless, support copy and conversion, support custom allocation, implementation is simple, container does not because the allocator type is not the same can not copy, compatible with the old standard code, compatible with Scoped_allocator_adapter, simply perfect.

Is that there is a virtual function very uncomfortable.

C + + Allocator Customization Guide

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.