Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Can't propagate polymorphic_allocator with scoped_allocator_adaptor

I have a vector<vector<int>> and want the entire memory (i.e., of both the outer and the inner vector) to be taken from a memory_resource. Here is a stripped down example, first the boring part:

#include <boost/container/pmr/memory_resource.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/pmr/polymorphic_allocator.hpp>
#include <iostream>
#include <string>
#include <vector>

// Sample memory resource that prints debug information
class MemoryResource : public boost::container::pmr::memory_resource {
  void* do_allocate(std::size_t bytes, std::size_t alignment) {
    std::cout << "Allocate " << bytes << " bytes" << std::endl;
    return malloc(bytes);
  }
  void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) { free(p); }
  bool do_is_equal(const memory_resource& other) const noexcept { return true; }
};

This is the part that I am interested in:

template <typename T>
using Alloc = boost::container::pmr::polymorphic_allocator<T>;
// using Alloc = std::allocator<T>;

template <typename T>
using PmrVector = std::vector<T, boost::container::scoped_allocator_adaptor<Alloc<T>>>;

using Inner = PmrVector<int>;

int main() {
  MemoryResource resource{};

  PmrVector<Inner> v(1000, Alloc<Inner>{&resource});
  // PmrVector<Inner> v(1337, Alloc<Inner>{});
  v[0].resize(100);
}

This gives me a lengthy compiler warning, essentially saying that it can't find a constructor for the inner vector.

If, instead of the polymorphic allocator, I use a regular allocator (e.g., std::allocator - see the lines that are commented out), everything seems to work.

The gcc error message is a bit better than that of clang:

/usr/local/include/boost/container/allocator_traits.hpp:415:10:
error: no matching function for call to '
  std::vector<int, polymorphic_allocator<int> >::vector(
    scoped_allocator_adaptor<...>&, polymorphic_allocator<...>&
  )
'

Why would boost try to construct a vector by passing the allocator twice?

Also, here is a version that uses STL (experimental) instead of boost. That one gives an actual error message "construction with an allocator must be possible if uses_allocator is true", but that doesn't help me either.

Maybe I am understanding something conceptually wrong. Is this the way to do it or is there a better way to solve the original problem?

like image 782
mrks Avatar asked Feb 12 '19 16:02

mrks


1 Answers

Argh. The explanation is hidden in std::experimental::pmr::polymorphic_allocator::construct:

This function is called (through std::allocator_traits) by any allocator-aware object, such as std::vector, that was given a std::polymorphic_allocator as the allocator to use. Since memory_resource* implicitly converts to polymorphic_allocator, the memory resource pointer will propagate to any allocator-aware subobjects using polymorphic allocators.

So it turns out that polymorphic allocators automatically propagate. That also explains why the allocator is passed twice in the gcc error message.

Here is a working version:

template <typename T>
using Alloc = std::experimental::pmr::polymorphic_allocator<T>;

template <typename T>
using PmrVector = std::vector<T, Alloc<T>>;

using Inner = PmrVector<int>;

int main() {
  MemoryResource resource{};

  PmrVector<Inner> v(1000, Alloc<Inner>{&resource});
  v[0].resize(100);
}

And here is the information that I would have need a couple of hours ago:

How do I use polymorphic_allocator and scoped_allocator_adaptor together?

You don't. Make sure that all inner containers also use polymorphic allocators, then the memory resource will be handed down automatically.

like image 67
mrks Avatar answered Oct 12 '22 22:10

mrks