Удовлетворяет ли std :: vector требованиям контейнера для распределителей Boost.Interprocess?

В boost::interprocess документации говорится, что контейнеры должны храниться в общей памяти:

  1. Контейнеры STL могут не предполагать, что память, выделенная с помощью распределителя, может быть освобождена с помощью других распределителей того же типа. Все объекты распределителей должны сравниваться на равных, только если память, выделенная для одного объекта, может быть освобождена другим, и это можно проверить только с operator==() во время выполнения.
  2. Внутренние указатели контейнеров должны быть типа allocator::pointer, и контейнеры не могут предполагать, что allocator::pointer является необработанным указателем.
  3. Все объекты должны быть сконструированы-уничтожены с помощью функций allocator::construct и allocator::destroy.

Я использую gcc 4.7.1 с -std = c ++ 11 (и boost 1.53). Безопасно ли использовать указанный ниже тип ShmVector?

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

Я попробовал фиктивный процесс, использующий этот тип, и, похоже, он работает, но я все еще не уверен, что вектор в gcc4.7.1 удовлетворяет всем требованиям. Я особенно не уверен в первом требовании.

#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;

    }
}

person Gabor Marton    schedule 08.03.2013    source источник
comment
По стандарту должно быть нормально.   -  person Xeo    schedule 08.03.2013
comment
@Xeo Я не уверен. Стандарт говорит, что реализации STL могут предполагать, что распределитель того же типа может освобождать память; большинство реализаций не полагаются на это, но вам следует проверить std документацию библиотеки. Однако, учитывая распространенность g++ и libstdc++, я был бы удивлен, если бы Boost явно не предупредил вас о несовместимости.   -  person Alex Chamberlain    schedule 08.03.2013
comment
@Xeo Я обнаружил в стандарте, что пункт 3 должен быть верным для всех std :: container: 23.2.1.3. Но ничего не нашел для пули 1 и 2.   -  person Gabor Marton    schedule 08.03.2013
comment
Окончательный тест - можете ли вы resize использовать вектор. Я обнаружил, что это функция, которая на самом деле предъявляет наибольшие требования к связанным типам.   -  person alfC    schedule 05.08.2017


Ответы (3)


В C ++ 11 правила распределителя немного изменились, но я не думаю, что это повлияет на ваш вопрос.

Вы, вероятно, сначала захотите узнать, что об этом говорится в стандарте. Но вы действительно хотите проверить, соответствует ли ваша конкретная реализация STL стандарту и не содержит ли ошибок.

Что касается второй части, я настоятельно рекомендую обратиться к источникам и просто проверить это, на самом деле это не так уж и сложно.

Кроме того, вы можете написать свои тесты, чтобы убедиться, что он действительно работает правильно:

  • Create custom allocator:
    • use some custom type as pointer, const pointer;
    • В construct(), destruct() подсчитывают количество вызовов;
  • Создайте YourCustomType для использования с распределителем, который также считает количество построений / разрушений.
  • 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)

Так вы можете быть уверены, что действуют все ограничения.

Что касается первой части вопроса, вот старый Стандарт C ++ (не C ++ 11).

1 Нет способа (правильно реализованный) вектор взять распределитель из ниоткуда. Он будет использовать любой предоставленный вами распределитель и будет использовать его для всего. Что касается operator ==, он реализован в распределителе boost, и поэтому проблема boost - заставить operator == работать так, как им требуется. Хотя мне не удалось найти подтверждения в стандарте .

2 Если нет ошибки, std::vector<T, YourAllocator>::pointer должен быть указателем распределителя. cppreference.com говорит, что, а стандарт говорит, что, (ищите" Вектор класса шаблона "):

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

Хотя в том же стандарте говорится об распределители: реализациям контейнеров, описанных в этом международном стандарте, разрешается предполагать, что их параметр шаблона распределителя удовлетворяет следующим двум дополнительным требованиям помимо требований, указанных в таблице 6.

- Все экземпляры данного типа распределителя должны быть взаимозаменяемыми и всегда сравниваться на равных.

--Указатель элементов typedef, const_pointer, size_type и Different_type должны быть T *, T const *, size_t и ptrdiff_t соответственно.

Итак, на самом деле стандарт не позволяет использовать какие-либо типы указателей, но я предполагаю, что реальные реализации STL будут работать.

3 Просто проверьте реализацию метода std::vector<T>::clear(), чтобы узнать, вызывается ли allocator :: destroy. Проверьте реализацию метода std::vector<T>::resize(), чтобы узнать, используется ли allocator :: construct. Мне не удалось найти требование вызова destroy и construct в стандарт.

person Artem Tokmakov    schedule 14.03.2013

Думаю, ответ отрицательный. Потому что на практике (в C ++ 98) и теоретически (стандарт C ++ 11) указатель std::vector не может быть чем-то другим, кроме T*.

Вот почему boost::interprocess::vector<T> использует boost::container::vector<T, boost::interprocess::allocator<T>> (вместо std::vector<T, boost::interprocess::allocator<T>>).

person alfC    schedule 07.07.2017

У меня нет репутации, чтобы комментировать, поэтому я должен ответить .. Если два распределителя сравниваются одинаково, они взаимозаменяемы. Распределители одного типа, которые сравнивают неравные, могут, например, быть инициализированы другой (общей) памятью. См. Распределитель:

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.

Итак, если ваши ShmVector экземпляры созданы с ShmemAllocator, которые сравниваются равными, вы должны быть в безопасности.

person oknenavin    schedule 27.05.2019