jeudi 24 décembre 2020

idiomatic way to propagate scoped_allocator_adaptor within container containing multiple types

With containers containing heterogeneous types, e.g. struct S1 below, I'm propagating the allocator to the member elements via rebinding the allocator from the upstream obtained as part of the constructor [same would apply to extended copy and move constructors that take an allocator as an argument]. The question is if this is the idiomatic way to propagate the allocator or if there are other ways than through explicit invocation of allocator_traits?

#include <iostream>
#include <cassert>
#include <vector>
#include <scoped_allocator>

// Move allocator and container aliases into namepsace 
namespace custom
{
    template <typename T>
    struct MyAlloc
    {
        using value_type = T;
        MyAlloc(const std::string &scope) noexcept : _scope(scope)  {} 

        // Rebinding allocatos to different type 
        template <class U> 
        MyAlloc(const MyAlloc<U> & other) noexcept : _scope(other._scope)  {}

        // Allow for move operations to be noexcept
        //using is_always_equal = std::true_type;

        value_type*  allocate(std::size_t n) noexcept
        {
            std::cout << "Allocating " << n << " objects within " << _scope << " from " << __PRETTY_FUNCTION__ << std::endl;
            return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
        }
        void deallocate(value_type* p, std::size_t n) noexcept
        {
            std::cout << "Deallocating " << n << " objects within " << _scope << " from " << __PRETTY_FUNCTION__ << std::endl;
            ::operator delete(p);
        }
        std::string _scope;
    };

    // Allocators compare equal to enable one allocator to de-allocate memory
    // from another
    template <typename T>
    bool operator==(const MyAlloc<T> &x1, const MyAlloc<T> &x2) noexcept
    {
        return true;
    }

    template <typename T>
    bool operator!=(const MyAlloc<T> &x1, const MyAlloc<T> &x2) noexcept
    {
        return !(x1 == x2);
    }

    template <typename T>
    using allocator = std::scoped_allocator_adaptor<MyAlloc<T>>;

    template <typename T> //  adaptor to propagate
    using vector = std::vector<T, allocator<T>>;

    template <typename T> 
    using bstr = std::basic_string<T, std::char_traits<T>, allocator<T>>;
    using string = bstr<char>;
}

struct S1
{
   using allocator_type = custom::allocator<std::byte>;
   S1(const allocator_type &alloc) : str("This is a very long string indeed..", std::allocator_traits<allocator_type>::rebind_alloc<char>(alloc)), 
                              vec(std::allocator_traits<allocator_type>::rebind_alloc<float>(alloc)) 
   {
      vec.emplace_back();
      std::cout << __PRETTY_FUNCTION__ << std::endl;
   }
   S1(const S1 &other, const allocator_type &alloc)  : str(other.str, std::allocator_traits<allocator_type>::rebind_alloc<char>(alloc)),
                              vec(std::allocator_traits<allocator_type>::rebind_alloc<float>(alloc)) 
   {
      std::cout << __PRETTY_FUNCTION__ << std::endl;
   }
   custom::string str;
   custom::vector<float> vec;
};



int main()
{
   custom::allocator<std::byte> sc{"scope1"};
   custom::vector<S1> cv(sc);
   cv.emplace_back();
}

Aucun commentaire:

Enregistrer un commentaire