Does std::vector satisfy the container requirements for Boost.Interprocess allocators?
Asked Answered
I

3

8

In boost::interprocess documentation it is said as requirement for containers to be stored in shared memory:

  1. STL containers may not assume that memory allocated with an allocator can be deallocated with other allocators of the same type. All allocators objects must compare equal only if memory allocated with one object can be deallocated with the other one, and this can only tested with operator==() at run-time.
  2. Containers' internal pointers should be of the type allocator::pointer and containers may not assume allocator::pointer is a raw pointer.
  3. All objects must be constructed-destroyed via allocator::construct and allocator::destroy functions.

I am using gcc 4.7.1 with -std=c++11 (and boost 1.53). Is it safe to use the below defined ShmVector type?

typedef boost::interprocess::allocator<int,
    boost::interprocess::managed_shared_memory::segment_manager>  ShmemAllocator;
typedef std::vector<int, ShmemAllocator> ShmVector;

I tried a dummy process which uses this type, and it looks it is working, but I am still not sure that the vector in gcc4.7.1 does satisfy all the requirements. I am especially not sure about the first requirement.

#include <iostream>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <vector>
#include <cstdlib> //std::system

typedef boost::interprocess::allocator<int,
        boost::interprocess::managed_shared_memory::segment_manager>  ShmemAllocator;
typedef std::vector<int, ShmemAllocator> ShmVector;

int main(int argc, char *argv[])
{
    if(argc == 1){ //Parent process

        struct shm_remove
        {
            shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
            ~shm_remove(){ boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
        } remover;

        //Create a new segment with given name and size
        boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only,
                "MySharedMemory", 65536);

        //Initialize shared memory STL-compatible allocator
        const ShmemAllocator allocator(segment.get_segment_manager());

        ShmVector* v = segment.construct<ShmVector>("ShmVector")(allocator);
        v->push_back(1); v->push_back(2); v->push_back(3);

        //Launch child process
        std::string s(argv[0]); s += " child ";
        if(0 != std::system(s.c_str()))
            return 1;

    } else { // Child process

        //Open the managed segment
        boost::interprocess::managed_shared_memory segment(
                boost::interprocess::open_only, "MySharedMemory");

        //Find the vector using the c-string name
        ShmVector *v = segment.find<ShmVector>("ShmVector").first;

        for (const auto& i : *v) {
            std::cout << i << " ";
        }
        std::cout << std::endl;

    }
}
Impassable answered 8/3, 2013 at 15:54 Comment(4)
According to standard, it should be fine.Zosima
@Zosima I'm not so sure. The standard says that STL implementations are free to assume an allocator of the same type can deallocate memory; most implementations don't rely on this though, but you should check you std library's documentation. However, given the prevalence of g++ and libstdc++, I'd be surprised if Boost didn't explicitly warn you of incompatibility.Pursuance
@Zosima I've found in the standard, that bullet 3 shall be true for all std::containers: 23.2.1.3. But couldn't find anything for bullet 1 and 2.Impassable
The ultmate test is if you can resize the vector. I found that is the function that actually puts the most requirement over the associated types.Haunting
P
0

In C++ 11 allocator rules have slightly changed, but I don't think it affects your question.

You probably want to know first what standard says about it. But you'd actually want to check whether your specific STL implementation conforms to the standard and doesn't contain bugs.

For the second part I'd strongly recommend going to sources of and just checking that, it's not that hard actually.

Also, you could write your tests to see if it's actually working properly:

  • Create custom allocator:
    • use some custom type as pointer, const pointer;
    • In construct(), destruct() count number of calls;
  • Create YourCustomType to be used with the allocator which also counts number of constructions/destructions.
  • Now, create std::vetor<YourCustomType, YourCustomAllocator<YourCustomType>> instance, insert some elements, clear the vector, destroy it and see if:
    • number of construct() destruct() calls is equal to number of constructions destructions of YourCustomType.
    • typeid(YourCustomAllocator::pointer) == typeid(std::vetor<YourCustomType, YourCustomAllocator<YourCustomType>>::pointer)

That's how you can be sure that all restrictions apply.

As for the first part of the question, here's an old C++ standard (not C++ 11).

1 There's no way (properly implemented) vector will take an allocator out of nowhere. It'll use whatever allocator you provide, and will use it for everything. As for operator==, it is implemented in boost's allocator and thus it's boost's problem to make operator== work as they require. Although I wasn't able to find confirmation in the standard.

2 Unless there's a bug, std::vector<T, YourAllocator>::pointer should be allocator's pointer. cppreference.com says that, and the standard says that, (look for "Template class vector"):

    typedef typename Allocator::pointer               pointer;
    typedef typename Allocator::const_pointer         const_pointer;

Although the same standard says this about allocators: Implementations of containers described in this International Standard are permitted to assume that their Allocator template parameter meets the following two additional requirements beyond those in Table 6.

--All instances of a given allocator type are required to be inter- changeable and always compare equal to each other.

--The typedef members pointer, const_pointer, size_type, and differ- ence_type are required to be T*, T const*, size_t, and ptrdiff_t, respectively.

So, actually standard doesn't allow using any pointer types, but my guess is that actual STL implementations will work.

3 Just check the std::vector<T>::clear() method implementation to see if allocator::destroy is called. Check std::vector<T>::resize() method's implementation to see if allocator::construct is used. I was not able to find requirement of calling destroy and construct in the standard.

Platy answered 14/3, 2013 at 18:52 Comment(0)
H
0

I think the answer is no. Because in practice (in C++98) and in theory (C++11 standard), std::vector pointer cannot be something else than T*.

That is why boost::interprocess::vector<T> uses boost::container::vector<T, boost::interprocess::allocator<T>> (instead of std::vector<T, boost::interprocess::allocator<T>>).

Haunting answered 7/7, 2017 at 1:8 Comment(0)
D
0

I have no reputation to comment, so I have to answer.. If two allocators compare equal, they are interchangeable. Allocators of same type that compare unequal may, for example, be initialized with different (shared) memory. See Allocator:

a1 == a2 => returns true only if the storage allocated by the allocator a1 can be deallocated through a2. Establishes reflexive, symmetric, and transitive relationship. Does not throw exceptions.

So, if your ShmVector instances are created with ShmemAllocators which compare equal, you must be safe.

Decent answered 27/5, 2019 at 19:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.