提升进程间:在遍历从结构中引用对象的映射时计算字符串变量

Posted

技术标签:

【中文标题】提升进程间:在遍历从结构中引用对象的映射时计算字符串变量【英文标题】:Boost interprocess: cout a string variable when iterating through a map that references an object from a struct 【发布时间】:2018-02-07 23:24:24 【问题描述】:

我正在使用 boost::interprocess 在进程之间共享对象。 我有两个文件,一个“server.cpp”,它生成一个结构对象并将该对象传递到一个带有 int 索引的映射中;和一个“client.cpp”文件,它检索内存数据并遍历数据,输出到控制台。

结构如下所示:

    struct mydata o 
    string MY_STRING;
    int MY_INT; 
;

还有对象:

mydata o;
o.MY_STRING = "hello";
o.MY_INT = 45;

服务器和客户端都正确编译。 但是由于某种原因,如果我尝试访问客户端中的字符串而不是浮点数或整数,客户端可执行文件会引发分段错误。 例如下面的 second.MY_INT 会输出到控制台,但是 second.MY_STRING 在运行时会抛出这个错误。

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <functional>
#include <utility>
#include <iostream>
#include <string>
#include "objects.cpp" //definitions for objects
using std::string;
using namespace boost::interprocess;

    int main ()


    try
    
            managed_shared_memory segment(open_or_create, "SharedMemoryName",65536);

            //Again the map<Key, MappedType>'s value_type is std::pair<const Key, MappedType>, so the allocator must allocate that pair.
            typedef int    KeyType;
            typedef order  MappedType;
            typedef std::pair<const int, order> ValueType;

            //Assign allocator 
            typedef allocator<ValueType, managed_shared_memory::segment_manager> ShmemAllocator;

            //The map
            typedef map<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> MySHMMap;

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

            //access the map in SHM through the offset ptr                                                         
            MySHMMap :: iterator iter;
            offset_ptr<MySHMMap> m_pmap = segment.find<MySHMMap>("MySHMMapName").first;

            iter=m_pmap->begin();
            for(; iter!=m_pmap->end();iter++)
            
                   //std::cout<<"\n "<<iter->first<<" "<<iter->second;
                   std::cout<<iter->first<<" "<<iter->second.MYINT<<" "<<iter->second.MYSTRING<<"\n";
            
    catch(std::exception &e)            
    
            std::cout<<" error  " << e.what() <<std::endl;
           shared_memory_object::remove("SharedMemoryName");
    
    return 0;

运行时的错误:

Segmentation fault (core dumped)

我很确定服务器将整个对象传递到内存,客户端可以检索它(因为我可以访问一些对象属性),这只是一个格式问题。

【问题讨论】:

【参考方案1】:

就像 Justin 提到的,std::string 本身就是一个动态分配的容器。

仅使用 Interprocess 的 string 是不够的。事实上,这只是boost::container::basic_string&lt;&gt;

重要的是使用分配器。

但是,使用带有分配器的映射,并在需要构造包含的容器时传递分配器(令人作呕)是很烦人的。

输入作用域分配器

这些使您不必知道分配器,任何知道如何使用作用域分配器的容器都会将分配器传递给嵌套容器。

Live On Coliru ¹

#include <boost/interprocess/managed_shared_memory.hpp>

#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>

#include <iostream>
#include <string>

namespace bip = boost::interprocess;

namespace Shared 
    using Segment = bip::managed_shared_memory;
    using Manager = Segment::segment_manager;

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

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

    template <typename K, typename V, typename Cmp = std::less<K> > using Map 
        = bip::map<K, V, Cmp, Alloc<std::pair<K const, V> > >;

    struct Order 
        using allocator_type = Alloc<char>;

        template <typename S, typename Alloc>
        Order(int i, S const& s, Alloc alloc = ) : i(i), s(s, alloc) 

        int    i;
        String s;
    ;


int main() 
    try 
        using namespace Shared;
        Segment segment(bip::open_or_create, "095540a3-ceaa-4431-828d-df21d5e384ae", 65536);

        auto& pmap = *segment.find_or_construct<Map<int, Order>>("MySHMMapName")(segment.get_segment_manager());

        if (pmap.empty()) 
            std::cout << "Inserting data\n";
            auto insert = [&pmap](int i, auto&& s) 
                using namespace std;
                pmap.emplace(piecewise_construct, tie(i), tie(i, s));
            ;

            insert(1, "one");
            insert(2, "two");
            insert(3, "three");
         else 
            std::cout << "Existing data:\n";
            for (auto& [k,v] : pmap) 
                std::cout << k << " " << v.i << " " << v.s << "\n";
            
        
     catch (std::exception &e) 
        std::cout << " error  " << e.what() << std::endl;
        bip::shared_memory_object::remove("095540a3-ceaa-4431-828d-df21d5e384ae");
    

让它更优雅

我注意到映射是Map&lt;int, Order&gt;:键似乎复制了Order 中的整数值。

选项 1:Map&lt;int, String&gt;

您可以使用Map&lt;int, String&gt; 并获得更流畅的体验(因为不需要std::piecewise_construct):

Live On Coliru

auto& pmap = *segment.find_or_construct<Map<int, String>>("MySHMMapName")(segment.get_segment_manager());

if (pmap.empty()) 
    std::cout << "Inserting data\n";

    pmap.emplace(1, "one");
    pmap.emplace(2, "two");
    pmap.emplace(3, "three");

选项 2:多索引

或者,您应该考虑使用 Multi-Index,它能够直接按以下类型的成员索引 Order

Live On Coliru

namespace bmi = boost::multi_index;
using Table = bmi::multi_index_container<Order,
    bmi::indexed_by<
        bmi::ordered_unique< bmi::member<Order, int, &Order::i> >
    >,
    Alloc<Order>
>;

遗憾的是,Multi Index 在使用分配器的嵌套类型中表现不佳,因此您必须再次传递它:

if (pmap.empty()) 
    std::cout << "Inserting data\n";

    pmap.emplace(1, "one", pmap.get_allocator());
    pmap.emplace(2, "two", pmap.get_allocator());
    pmap.emplace(3, "three", pmap.get_allocator());
 else 
    std::cout << "Existing data:\n";
    for (Order const& o : pmap) 
        std::cout << o.i << " " << o.s << "\n";
    

    // demonstrate lookup:
    std::cout << "Finding element 2:" << pmap.find(2)->s << "\n";

打印

Existing data:
1 one
2 two
3 three
Finding element 2:two

¹ 使用 Coliru 上的映射文件代替。重构后的代码使这变成了 1 行更改。

【讨论】:

【参考方案2】:

但由于某种原因,如果我尝试访问客户端中的字符串而不是浮点数或整数,客户端可执行文件会引发分段错误。

看起来您在共享内存映射的结构中使用了std::string。你不能这样做,因为std::string 分配了它自己的内存,这超出了boost::interprocess 的范围。您应该尝试使用boost::interprocess::basic_string 类型代替std::string。有一个如何在进程间映射中使用它的示例in the Boost documentation

#include <boost/interprocess/containers/string.hpp>

【讨论】:

【参考方案3】:

以前的海报中的一些重要信息使我得到了我自己的简单答案。在struct中使用char数组,然后读入字符串如下:

std::cout<<iter->first<<" "<<iter->second.ORDERTYPE<<" "<<string(iter->second.MYID)<<"\n";

这是我所说的结构:

struct order 
    char MYID[100];
    int ORDERTYPE;
    char DATE_UPDATED[64];
;
order o

这是我将结构传递到内存的方式:

m_pmap->insert(std::pair<const int, order>(i, (order)o));

现在我可以将包含各种类型(包括字符串)的结构映射到内存,并从内存中检索它们。

【讨论】:

以上是关于提升进程间:在遍历从结构中引用对象的映射时计算字符串变量的主要内容,如果未能解决你的问题,请参考以下文章

实现自己的HAL-5 Binder,IDLE简介

在没有共享内存的情况下提升进程间字符串

SQL:使用 LIMIT 查询时计算匹配结果的数量

c#进程之间对象传递方法

使用类内的类提升进程间共享内存

开车时计算android中两个位置之间的距离