Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/135.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用boost';共享内存容器的s作用域\u分配器\u适配器_C++_Boost_Shared Memory_Boost Interprocess - Fatal编程技术网

C++ 使用boost';共享内存容器的s作用域\u分配器\u适配器

C++ 使用boost';共享内存容器的s作用域\u分配器\u适配器,c++,boost,shared-memory,boost-interprocess,C++,Boost,Shared Memory,Boost Interprocess,我正在编写一个C++17应用程序,我需要管理STL或 共享内存中的等效数据结构 我不确定最简单的语法(它避免了到处传递分配器) 创建和更新共享数据结构的位置) 我已经搜索了一段时间,但不是一个简单的字符串->字符串 然而,专注于自定义数据结构或POD结构的示例很难找到 通过(我怀疑与POD结构相关联的分配器会 很容易,因为这些可以从连续内存中分配,因此可以使用 简单的字符分配器(相当于下面的Shared::Alloc) 据我所知,在 共享内存围绕着正确选择 以及使该分配器与其嵌套子级共享的能力

我正在编写一个C++17应用程序,我需要管理STL或 共享内存中的等效数据结构

我不确定最简单的语法(它避免了到处传递分配器) 创建和更新共享数据结构的位置)

我已经搜索了一段时间,但不是一个简单的字符串->字符串 然而,专注于自定义数据结构或POD结构的示例很难找到 通过(我怀疑与POD结构相关联的分配器会 很容易,因为这些可以从连续内存中分配,因此可以使用 简单的字符分配器(相当于下面的
Shared::Alloc

据我所知,在 共享内存围绕着正确选择 以及使该分配器与其嵌套子级共享的能力

例如,假设我有一张
map
在共享内存中,
scoped\u分配器\u适配器
的魔力不知何故可以发挥作用

除了上面的
map
这个简单的例子之外,我想 非常想管理
映射
,其中
UserStruct
可以 可以是POD结构,也可以是包含
字符串
列表
字符串的结构

我从以下内容开始,作为另一个答案的有用起点 我发现:

namespace bip = boost::interprocess;

namespace Shared {
    using Segment = bip::managed_shared_memory;

    template <typename T>
        using Alloc   = bip::allocator<T, Segment::segment_manager>;
    using Scoped  = boost::container::scoped_allocator_adaptor<Alloc<char>>;

    using String  = boost::container::basic_string<char, std::char_traits<char>, Scoped>;
    using KeyType = String;
}
名称空间bip=boost::进程间;
命名空间共享{
使用段=bip::托管共享内存;
模板
使用Alloc=bip::分配器;
使用Scoped=boost::container::Scoped\u分配器\u适配器;
使用String=boost::container::basic\u字符串;
使用KeyType=String;
}
看起来
Shared:Scoped
分配器适配器是传播 从顶级容器到其子容器的分配器。我不确定这是不是真的 应用于增压容器与标准容器时不同

一个示例,以及如何以 允许我将
作用域分配程序适配器
传播到我的POD或自定义
struct就是我要找的。

为星星而射击,我们是:)无痛的分配器传播是圣杯

看起来,共享:作用域分配器适配器是将分配器从顶级容器传播到其子容器的关键

确实如此

我不确定这在应用于boost容器和标准容器时是否有所不同

在我的理解中,现代C++标准库应该支持同样的方法,但在实践中我的经验表明它经常与Boost容器容器一起使用。(YMMV和标准库实现可能/将迎头赶上)

怎么办 我想您应该了解
使用分配程序
协议:

我想这确实回答了你所有的问题。如果可以的话,我会试着拿出一个快速的样品

演示 到目前为止,我采用了以下两种方法:

struct MyStruct {
    String data;

    using allocator_type = Alloc<char>;

    MyStruct(MyStruct const& rhs, allocator_type = {}) : data(rhs.data) {}
    template <typename I, typename = std::enable_if_t<not std::is_same_v<MyStruct, I>, void> >
    MyStruct(I&& init, allocator_type a)
     : data(std::forward<I>(init), a)
    { }
};
调用它三次:

db has 3 elements: one two three
db has 6 elements: one two three one two three
db has 9 elements: one two three one two three one two three
更新:更复杂 作为对评论的回应,让我们从两个方面将其变得更加复杂:

  • 结构构造函数将采用各种参数初始化各种成员,其中一些将使用分配器
  • 我们希望将其存储在一个Map中,一些涉及Map的使用模式由于作用域分配器支持而令人讨厌(安放,
    Map[k]=v
    updateassignment和默认构造要求)
  • std::initalizer\u列表
    不会在通用转发包装中推导:(
定义结构:

struct MyPodStruct {
    using allocator_type = ScopedAlloc<char>;

    int a = 0; // simplify default constructor using NSMI
    int b = 0;
    Vec<uint8_t> data;

    explicit MyPodStruct(allocator_type alloc) : data(alloc) {}
    //MyPodStruct(MyPodStruct const&) = default;
    //MyPodStruct(MyPodStruct&&) = default;
    //MyPodStruct& operator=(MyPodStruct const&) = default;
    //MyPodStruct& operator=(MyPodStruct&&) = default;

    MyPodStruct(std::allocator_arg_t, allocator_type, MyPodStruct&& rhs) : MyPodStruct(std::move(rhs)) {}
    MyPodStruct(std::allocator_arg_t, allocator_type, MyPodStruct const& rhs) : MyPodStruct(rhs) {}

    template <typename I, typename A = Alloc<char>>
        MyPodStruct(std::allocator_arg_t, A alloc, int a, int b, I&& init)
         : MyPodStruct(a, b, Vec<uint8_t>(std::forward<I>(init), alloc)) { }

  private:
    explicit MyPodStruct(int a, int b, Vec<uint8_t> data) : a(a), b(b), data(std::move(data)) {}
};    
它打印

#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <iostream>

namespace bip = boost::interprocess;

namespace Shared {
    using Segment = bip::managed_mapped_file;
    using SMgr = Segment::segment_manager;

    template <typename T> using Alloc = boost::container::scoped_allocator_adaptor<
            bip::allocator<T, SMgr>
        >;

    template <typename T> using Vec = boost::container::vector<T, Alloc<T> >;

    using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;

    struct MyStruct {
        String data;

        using allocator_type = Alloc<char>;

#if 1 // one approach
        MyStruct(std::allocator_arg_t, allocator_type, MyStruct const& rhs) : data(rhs.data) {}

        template <
            typename I,
            typename A = Alloc<char>,
            typename = std::enable_if_t<not std::is_same_v<MyStruct, I>, void> >
        MyStruct(std::allocator_arg_t, A alloc, I&& init)
         : data(std::forward<I>(init), alloc.get_segment_manager())
        { }
#else // the simpler(?) approach
        MyStruct(MyStruct const& rhs, allocator_type = {}) : data(rhs.data) {}
        template <typename I, typename = std::enable_if_t<not std::is_same_v<MyStruct, I>, void> >
        MyStruct(I&& init, allocator_type a)
         : data(std::forward<I>(init), a)
        { }
#endif
    };

    using Database = Vec<MyStruct>;
}

namespace std {
    // this appears optional for the current use case
    template <typename T> struct uses_allocator<Shared::MyStruct, T> : std::true_type {};
}

int main() {
    Shared::Segment mf(bip::open_or_create, "test.bin", 10<<20);

    auto& db = *mf.find_or_construct<Shared::Database>("db")(mf.get_segment_manager());

    db.emplace_back("one");
    db.emplace_back("two");
    db.emplace_back("three");

    std::cout << "db has " << db.size() << " elements:";

    for (auto& el : db) {
        std::cout << " " << el.data;
    }

    std::cout << std::endl;
}
=== Before updates
db has 3 elements: {one: 1,2, [1,2,]} {three: 3,4, [5,8,]} {two: 2,3, [4,]}

=== After updates
db has 4 elements: {nine: 9,100, [5,6,42,]} {one: 1,20, [7,8,9,]} {three: 3,4, [5,8,]} {two: 2,30, []}
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <iostream>

namespace bip = boost::interprocess;

namespace Shared {
    using Segment = bip::managed_mapped_file;
    using SMgr = Segment::segment_manager;

    template <typename T> using Alloc = bip::allocator<T, SMgr>;
    template <typename T> using ScopedAlloc = boost::container::scoped_allocator_adaptor<Alloc<T> >;

    using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;

    using boost::interprocess::map;

    template <typename T> using Vec = 
        boost::container::vector<T, ScopedAlloc<T>>;

    template <typename K, typename T> using Map = 
        map<K, T, std::less<K>, ScopedAlloc<typename map<K, T>::value_type>>;

    struct MyPodStruct {
        using allocator_type = ScopedAlloc<char>;

        int a = 0; // simplify default constructor using NSMI
        int b = 0;
        Vec<uint8_t> data;

        explicit MyPodStruct(allocator_type alloc) : data(alloc) {}
        //MyPodStruct(MyPodStruct const&) = default;
        //MyPodStruct(MyPodStruct&&) = default;
        //MyPodStruct& operator=(MyPodStruct const&) = default;
        //MyPodStruct& operator=(MyPodStruct&&) = default;

        MyPodStruct(std::allocator_arg_t, allocator_type, MyPodStruct&& rhs) : MyPodStruct(std::move(rhs)) {}
        MyPodStruct(std::allocator_arg_t, allocator_type, MyPodStruct const& rhs) : MyPodStruct(rhs) {}

        template <typename I, typename A = Alloc<char>>
            MyPodStruct(std::allocator_arg_t, A alloc, int a, int b, I&& init)
             : MyPodStruct(a, b, Vec<uint8_t>(std::forward<I>(init), alloc)) { }

      private:
        explicit MyPodStruct(int a, int b, Vec<uint8_t> data) : a(a), b(b), data(std::move(data)) {}
    };    

    using Database = Map<String, MyPodStruct>;

    static inline std::ostream& operator<<(std::ostream& os, Database const& db) {
        os << "db has " << db.size() << " elements:";

        for (auto& [k,v] : db) {
            os << " {" << k << ": " << v.a << "," << v.b << ", [";
            for (unsigned i : v.data)
                os << i << ",";
            os << "]}";
        }

        return os;
    }
}

int main() {
    using Shared::MyPodStruct;
    Shared::Segment mf(bip::open_or_create, "test.bin", 10<<10); // smaller for Coliru
    auto mgr = mf.get_segment_manager();

    auto& db = *mf.find_or_construct<Shared::Database>("complex")(mgr);

    // Issues with brace-enclosed initializer list
    using Bytes = std::initializer_list<uint8_t>;

    // More magic: piecewise construction protocol :)
    static constexpr std::piecewise_construct_t pw{};
    using std::forward_as_tuple;
    db.emplace(pw, forward_as_tuple("one"), forward_as_tuple(1,2, Bytes {1,2}));
    db.emplace(pw, forward_as_tuple("two"), forward_as_tuple(2,3, Bytes {4}));
    db.emplace(pw, forward_as_tuple("three"), forward_as_tuple(3,4, Bytes {5,8}));

    std::cout << "\n=== Before updates\n" << db << std::endl;

    // Clumsy:
    db[Shared::String("one", mgr)] = MyPodStruct{std::allocator_arg, mgr, 1,20, Bytes {7,8,9}};

    // As efficient or better, and less clumsy:
    auto insert_or_update = [&db](auto&& key, auto&&... initializers) -> MyPodStruct& {
        // Be careful not to move twice: https://en.cppreference.com/w/cpp/container/map/emplace
        // > The element may be constructed even if there already is an element
        // > with the key in the container, in which case the newly constructed
        // > element will be destroyed immediately.
        if (auto insertion = db.emplace(pw, forward_as_tuple(key), std::tie(initializers...)); insertion.second) {
            return insertion.first->second;
        } else {
            return insertion.first->second = MyPodStruct(
                std::allocator_arg, 
                db.get_allocator(),
                std::forward<decltype(initializers)>(initializers)...); // forwarding ok here
        }
    };

    insert_or_update("two", 2,30, Bytes{});
    insert_or_update("nine", 9,100, Bytes{5,6});

    // partial updates:
    db.at(Shared::String("nine", mgr)).data.push_back(42);

    // For more efficient key lookups in the case of unlikely insertion, use
    // heterogeneous comparer, see https://stackoverflow.com/a/27330042/85371

    std::cout << "\n=== After updates\n" << db << std::endl;
}
完整列表 对于保护:

#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <iostream>

namespace bip = boost::interprocess;

namespace Shared {
    using Segment = bip::managed_mapped_file;
    using SMgr = Segment::segment_manager;

    template <typename T> using Alloc = boost::container::scoped_allocator_adaptor<
            bip::allocator<T, SMgr>
        >;

    template <typename T> using Vec = boost::container::vector<T, Alloc<T> >;

    using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;

    struct MyStruct {
        String data;

        using allocator_type = Alloc<char>;

#if 1 // one approach
        MyStruct(std::allocator_arg_t, allocator_type, MyStruct const& rhs) : data(rhs.data) {}

        template <
            typename I,
            typename A = Alloc<char>,
            typename = std::enable_if_t<not std::is_same_v<MyStruct, I>, void> >
        MyStruct(std::allocator_arg_t, A alloc, I&& init)
         : data(std::forward<I>(init), alloc.get_segment_manager())
        { }
#else // the simpler(?) approach
        MyStruct(MyStruct const& rhs, allocator_type = {}) : data(rhs.data) {}
        template <typename I, typename = std::enable_if_t<not std::is_same_v<MyStruct, I>, void> >
        MyStruct(I&& init, allocator_type a)
         : data(std::forward<I>(init), a)
        { }
#endif
    };

    using Database = Vec<MyStruct>;
}

namespace std {
    // this appears optional for the current use case
    template <typename T> struct uses_allocator<Shared::MyStruct, T> : std::true_type {};
}

int main() {
    Shared::Segment mf(bip::open_or_create, "test.bin", 10<<20);

    auto& db = *mf.find_or_construct<Shared::Database>("db")(mf.get_segment_manager());

    db.emplace_back("one");
    db.emplace_back("two");
    db.emplace_back("three");

    std::cout << "db has " << db.size() << " elements:";

    for (auto& el : db) {
        std::cout << " " << el.data;
    }

    std::cout << std::endl;
}
=== Before updates
db has 3 elements: {one: 1,2, [1,2,]} {three: 3,4, [5,8,]} {two: 2,3, [4,]}

=== After updates
db has 4 elements: {nine: 9,100, [5,6,42,]} {one: 1,20, [7,8,9,]} {three: 3,4, [5,8,]} {two: 2,30, []}
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <iostream>

namespace bip = boost::interprocess;

namespace Shared {
    using Segment = bip::managed_mapped_file;
    using SMgr = Segment::segment_manager;

    template <typename T> using Alloc = bip::allocator<T, SMgr>;
    template <typename T> using ScopedAlloc = boost::container::scoped_allocator_adaptor<Alloc<T> >;

    using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;

    using boost::interprocess::map;

    template <typename T> using Vec = 
        boost::container::vector<T, ScopedAlloc<T>>;

    template <typename K, typename T> using Map = 
        map<K, T, std::less<K>, ScopedAlloc<typename map<K, T>::value_type>>;

    struct MyPodStruct {
        using allocator_type = ScopedAlloc<char>;

        int a = 0; // simplify default constructor using NSMI
        int b = 0;
        Vec<uint8_t> data;

        explicit MyPodStruct(allocator_type alloc) : data(alloc) {}
        //MyPodStruct(MyPodStruct const&) = default;
        //MyPodStruct(MyPodStruct&&) = default;
        //MyPodStruct& operator=(MyPodStruct const&) = default;
        //MyPodStruct& operator=(MyPodStruct&&) = default;

        MyPodStruct(std::allocator_arg_t, allocator_type, MyPodStruct&& rhs) : MyPodStruct(std::move(rhs)) {}
        MyPodStruct(std::allocator_arg_t, allocator_type, MyPodStruct const& rhs) : MyPodStruct(rhs) {}

        template <typename I, typename A = Alloc<char>>
            MyPodStruct(std::allocator_arg_t, A alloc, int a, int b, I&& init)
             : MyPodStruct(a, b, Vec<uint8_t>(std::forward<I>(init), alloc)) { }

      private:
        explicit MyPodStruct(int a, int b, Vec<uint8_t> data) : a(a), b(b), data(std::move(data)) {}
    };    

    using Database = Map<String, MyPodStruct>;

    static inline std::ostream& operator<<(std::ostream& os, Database const& db) {
        os << "db has " << db.size() << " elements:";

        for (auto& [k,v] : db) {
            os << " {" << k << ": " << v.a << "," << v.b << ", [";
            for (unsigned i : v.data)
                os << i << ",";
            os << "]}";
        }

        return os;
    }
}

int main() {
    using Shared::MyPodStruct;
    Shared::Segment mf(bip::open_or_create, "test.bin", 10<<10); // smaller for Coliru
    auto mgr = mf.get_segment_manager();

    auto& db = *mf.find_or_construct<Shared::Database>("complex")(mgr);

    // Issues with brace-enclosed initializer list
    using Bytes = std::initializer_list<uint8_t>;

    // More magic: piecewise construction protocol :)
    static constexpr std::piecewise_construct_t pw{};
    using std::forward_as_tuple;
    db.emplace(pw, forward_as_tuple("one"), forward_as_tuple(1,2, Bytes {1,2}));
    db.emplace(pw, forward_as_tuple("two"), forward_as_tuple(2,3, Bytes {4}));
    db.emplace(pw, forward_as_tuple("three"), forward_as_tuple(3,4, Bytes {5,8}));

    std::cout << "\n=== Before updates\n" << db << std::endl;

    // Clumsy:
    db[Shared::String("one", mgr)] = MyPodStruct{std::allocator_arg, mgr, 1,20, Bytes {7,8,9}};

    // As efficient or better, and less clumsy:
    auto insert_or_update = [&db](auto&& key, auto&&... initializers) -> MyPodStruct& {
        // Be careful not to move twice: https://en.cppreference.com/w/cpp/container/map/emplace
        // > The element may be constructed even if there already is an element
        // > with the key in the container, in which case the newly constructed
        // > element will be destroyed immediately.
        if (auto insertion = db.emplace(pw, forward_as_tuple(key), std::tie(initializers...)); insertion.second) {
            return insertion.first->second;
        } else {
            return insertion.first->second = MyPodStruct(
                std::allocator_arg, 
                db.get_allocator(),
                std::forward<decltype(initializers)>(initializers)...); // forwarding ok here
        }
    };

    insert_or_update("two", 2,30, Bytes{});
    insert_or_update("nine", 9,100, Bytes{5,6});

    // partial updates:
    db.at(Shared::String("nine", mgr)).data.push_back(42);

    // For more efficient key lookups in the case of unlikely insertion, use
    // heterogeneous comparer, see https://stackoverflow.com/a/27330042/85371

    std::cout << "\n=== After updates\n" << db << std::endl;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
名称空间bip=boost::进程间;
命名空间共享{
使用Segment=bip::managed_-mapped_文件;
使用SMgr=Segment::Segment\u管理器;
使用Alloc=bip::分配器的模板;
使用ScopedAlloc=boost::container::scoped_分配器_适配器的模板;
使用String=bip::basic_String;
使用boost::interprocess::map;
使用Vec的模板=
boost::container::vector;
使用映射的模板=
地图;
结构MyPodStruct{
使用分配器_type=ScopedAlloc;
int a=0;//使用NSMI简化默认构造函数
int b=0;
向量数据;
显式MyPodStruct(分配器类型alloc):数据(alloc){
//MyPodStruct(MyPodStruct const&)=默认值;
//MyPodStruct(MyPodStruct&&)=默认值;
//MyPodStruct&运算符=(MyPodStruct常量&)=默认值;
//MyPodStruct&operator=(MyPodStruct&&)=默认值;
MyPodStruct(std::allocator_arg_t,allocator_type,MyPodStruct&&rhs):MyPodStruct(std::move(rhs)){}
MyPodStruct(std::分配器参数,分配器类型,MyPodStruct常量和rhs):MyPodStruct(rhs){}
模板
MyPodStruct(std::分配器参数、alloc、int A、int b、I&&init)
:MyPodStruct(a,b,Vec(std::forward(init),alloc)){
私人:
显式MyPodStruct(inta,intb,Vec数据):a(a),b(b),data(std::move(data)){
};    
使用数据库=地图;

静态内联std::ostream&operatories一个例子会令人惊讶,谢谢。像我这样的非元程序员很难理解uses\u分配器协议。到目前为止,您的例子似乎是唯一好的例子,您可能认识到共享名称空间:)。事实上,我确实认识到了这一点;这么多优秀的程序员构建库来拥有这些令人惊叹的功能,但似乎没有人知道/懒得展示如何使用它们。我认为这解决了问题(具有讽刺意味的是,它不再适合这个问题,因为结构不使用任何分配器:))注意下面的评论,我喜欢你在评论中的观察