希望有效地克服 Boost.Process 间共享内存中映射中键类型之间的不匹配

want to efficiently overcome mismatch between key types in a map in Boost.Interprocess shared memory

本文关键字:映射 类型 之间 不匹配 内存 共享 有效地 希望有 克服 Boost Process      更新时间:2023-10-16

我正在使用Boost.Interprocess在共享内存中创建映射(在本例中从字符串到字符串(。编译器似乎想强迫我在从地图检索期间在托管段只是(不必要地(包含查询词。

我希望能够通过将映射的键与已在非共享内存中的实例进行匹配,更高效地查找共享映射中的值,而无需执行此额外分配。 但它是如果我尝试使用 std::stringconst char * 作为地图find方法的参数,则拒绝编译。(请参阅底部的编译器错误消息(。

我需要定义某种我的共享内存密钥类型与其非共享等效项之间的比较器方法(在本例中std::string(?如果是这样,应该怎么做这看起来像,我应该如何使地图使用它? 如果没有,我该怎么办?

下面是代码,后跟编译器错误。 问题出在main()的底部.

// shmap2.cpp
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
//Typedefs of allocators and containers
namespace Shared
{
    typedef boost::interprocess::managed_shared_memory
        Segment;
    typedef boost::interprocess::managed_shared_memory::segment_manager
        SegmentManager;
    typedef boost::interprocess::allocator< void, SegmentManager >
        Allocator;
    typedef boost::interprocess::allocator< char, SegmentManager >
        CharAllocator;
    typedef boost::interprocess::basic_string< char, std::char_traits< char >, CharAllocator > 
        String;
    typedef std::less< String >
        StringComparator;
    // Definition of the shared map from String to String
    // (To avoid confusion, let's strictly use Python-like definitions of "key", "value" and "item")
    typedef std::pair< const String, String >
        MapItem;
    typedef boost::interprocess::allocator< MapItem, SegmentManager >
        MapItemAllocator;
    typedef boost::interprocess::map< String, String, StringComparator, MapItemAllocator >
        Map;
}
int main( void )
{
    struct shm_remove
    {
        shm_remove() { boost::interprocess::shared_memory_object::remove( "MySharedMemory" ); }
        ~shm_remove(){ boost::interprocess::shared_memory_object::remove( "MySharedMemory" ); }
    } remover;
    // Create shared memory
    Shared::Segment seg( boost::interprocess::create_only, "MySharedMemory", 65536 );
    // An allocator instance that can be converted to any allocator< T, Shared::SegmentManager > type
    Shared::Allocator alloc( seg.get_segment_manager() );
    // An instance of the string comparator, to construct the map
    Shared::StringComparator cmp;
    // Construct the shared memory map
    Shared::Map * myMapPtr = seg.construct< Shared::Map >( "myMap" )( cmp, alloc );
    // Here's the problem:
    // std::string key( "foo" );            // Compilation fails if you use this.
    // char key[] = "foo";                  // Compilation fails if you use this.
    Shared::String key( "foo", alloc );     // This the only version I can get to work.
                                            // But it forces you to create a copy of
                                            // the key you are searching for, in
                                            // the managed segment.
    // This is the point of the exercise:
    Shared::Map::iterator it = myMapPtr->find( key );
    return 0;
}

std::stringkey

$ g++ -o shmap2 -D BOOST_ALL_NO_LIB  -I ../boost_1_57_0  shmap2.cpp
shmap2.cpp:79:40: error: no matching member function for call to 'find'
                Shared::Map::iterator it = myMapPtr->find( key );
                                           ~~~~~~~~~~^~~~
../boost_1_57_0/boost/container/detail/tree.hpp:1089:13: note: candidate function not
      viable: no known conversion from 'std::string' (aka 'basic_string<char,
      char_traits<char>, allocator<char> >') to 'const key_type' (aka 'const
      boost::container::basic_string<char, std::__1::char_traits<char>,
      boost::interprocess::allocator<char, boost::interprocess::segment_manager<char,
      boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,
      boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> >
      >') for 1st argument
   iterator find(const key_type& k)
            ^
../boost_1_57_0/boost/container/detail/tree.hpp:1092:19: note: candidate function not
      viable: no known conversion from 'std::string' (aka 'basic_string<char,
      char_traits<char>, allocator<char> >') to 'const key_type' (aka 'const
      boost::container::basic_string<char, std::__1::char_traits<char>,
      boost::interprocess::allocator<char, boost::interprocess::segment_manager<char,
      boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,
      boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> >
      >') for 1st argument
   const_iterator find(const key_type& k) const
                  ^
1 error generated.

const char *key

$ g++ -o shmap2 -D BOOST_ALL_NO_LIB  -I ../boost_1_57_0  shmap2.cpp
In file included from shmap2.cpp:17:
In file included from ../boost_1_57_0/boost/interprocess/containers/string.hpp:19:
../boost_1_57_0/boost/container/string.hpp:676:59: error: no matching constructor for
      initialization of 'allocator_type' (aka 'boost::interprocess::allocator<char,
      boost::interprocess::segment_manager<char,
      boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,
      boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> >')
   basic_string(const CharT* s, const allocator_type& a = allocator_type())
                                                          ^
shmap2.cpp:79:46: note: in instantiation of default function argument expression for
      'basic_string<char, std::__1::char_traits<char>, boost::interprocess::allocator<char,
      boost::interprocess::segment_manager<char,
      boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,
      boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >'
      required here
                Shared::Map::iterator it = myMapPtr->find( key );
                                                           ^
../boost_1_57_0/boost/interprocess/allocators/allocator.hpp:140:4: note: candidate
      constructor template not viable: requires single argument 'other', but no
      arguments were provided
   allocator(const allocator<T2, SegmentManager> &other)
   ^
../boost_1_57_0/boost/interprocess/allocators/allocator.hpp:129:4: note: candidate
     constructor not viable: requires single argument 'segment_mngr', but no arguments
     were provided
   allocator(segment_manager *segment_mngr)
   ^
../boost_1_57_0/boost/interprocess/allocators/allocator.hpp:134:4: note: candidate
     constructor not viable: requires single argument 'other', but no arguments were
     provided
   allocator(const allocator &other)
   ^
1 error generated.

更新:根据 sehe 的建议,下面,我尝试替换

typedef std::less< String >
    StringComparator;

typedef struct
{
    template< typename T, typename U >
    bool operator()( const T & t, const U & u )
        const { return t < u; }
} StringComparator;

但得到了相同的两个编译器错误。

您可以使用自定义比较器

   struct MyLess {
        template <typename T, typename U>
            bool operator()(const T&t, const U&u) const
        {
            return t<u;
        }
    };

在你的代码中,你可以把它类型化为StringComparator

更新到评论


多索引救援

如果要将std::map/boost::container::map替换为 Boost Multi Index 容器(支持按 CompatibleKey 查找(,下面是如何执行此操作的演示:

我从文档部分借用了一些想法 multi_index_container模拟标准容器 .

请注意,std::string查找键仍然不起作用,但您可以在该事件中轻松使用.c_strio()

住在科里鲁

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
namespace emulation {
    template <typename T1,typename T2,typename Alloc>
        struct mutable_pair
        {
            typedef T1 first_type;
            typedef T2 second_type;
            mutable_pair(Alloc alloc):first(T1(alloc)),second(T2(alloc)){}
            mutable_pair(const T1& f,const T2& s):first(f),second(s){}
            mutable_pair(const std::pair<T1,T2>& p):first(p.first),second(p.second){}
            T1         first;
            mutable T2 second;
        };
    using namespace boost::multi_index;
    template <typename Key, typename T, typename Compare, typename Allocator, typename Element = mutable_pair<Key, T, Allocator> >
        using map = multi_index_container<
            Element,
            indexed_by<
                ordered_unique<member<Element,Key,&Element::first>,Compare>
            >,
            typename Allocator::template rebind<Element>::other
        >;
  template <typename Key, typename T, typename Compare, typename Allocator, typename Element = mutable_pair<Key, T, Allocator> >
    using multimap = multi_index_container<
        Element,
        indexed_by<
            ordered_non_unique<member<Element,Key,&Element::first>,Compare>
        >,
        typename Allocator::template rebind<Element>::other
    >;
  template <typename Key, typename T, typename Compare, typename Allocator> 
      struct wrap_map : map<Key, T, Compare, Allocator> {
          typedef map<Key, T, Compare, Allocator> base_type;
          typedef typename base_type::template nth_index<0>::type index_type;
          wrap_map(Allocator alloc) : base_type({}, alloc)
          {
          }
          wrap_map(Compare cmp, Allocator alloc) : base_type(
                  typename base_type::ctor_args_list{
                    typename index_type::ctor_args { typename index_type::key_from_value {}, cmp }
                  },
                  alloc)
          {
          }
      };
}
// Typedefs of allocators and containers
namespace Shared {
    typedef boost::interprocess::managed_shared_memory Segment;
    typedef boost::interprocess::managed_shared_memory::segment_manager SegmentManager;
    typedef boost::interprocess::allocator<void, SegmentManager> Allocator;
    typedef boost::interprocess::allocator<char, SegmentManager> CharAllocator;
    typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> String;
    struct MyLess {
        template <typename T, typename U> bool operator()(const T &t, const U &u) const { return t < u; }
    };
    typedef MyLess StringComparator;

    typedef boost::interprocess::allocator<char, SegmentManager> StringAlloc;
    typedef emulation::mutable_pair<const String, String, StringAlloc> MapItem;
    typedef boost::interprocess::allocator<MapItem, SegmentManager> MapItemAllocator;
    typedef emulation::wrap_map<String, String, StringComparator, MapItemAllocator> Map;
}
int main(void) {
    struct shm_remove {
        shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
        ~shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
    } remover;
    // Create shared memory
    Shared::Segment seg(boost::interprocess::create_only, "MySharedMemory", 65536);
    Shared::Allocator alloc(seg.get_segment_manager());
    // An instance of the string comparator, to construct the map
    Shared::StringComparator cmp;
    // Construct the shared memory map
    Shared::Map *myMapPtr = seg.construct<Shared::Map>("myMap")(cmp, alloc);
    myMapPtr->emplace(Shared::String("foo", alloc), Shared::String("bar", alloc));
    myMapPtr->emplace(Shared::String("goo", alloc), Shared::String("car", alloc));
    myMapPtr->emplace(Shared::String("hoo", alloc), Shared::String("dar", alloc));
    Shared::String key("foo", alloc);
    // This is the point of the exercise:
    auto it = myMapPtr->find(key);
    if (it!=myMapPtr->end())
        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'n";
    // this is now okay too
    char szkey[] = "foo";
    it = myMapPtr->find(szkey);
    if (it!=myMapPtr->end())
        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'n";
    // this is now okay too
    std::string skey("foo");
    it = myMapPtr->find(skey.c_str());
    if (it!=myMapPtr->end())
        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'n";
    return 0;
}

指纹:

Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'

范围分配器以获得额外的真棒酱?

现在,有趣的是,Boost Container 支持作用域分配器,因此您可以取消分配器的重复传递,但是,遗憾的是,Boost Multi Index 并不完全支持它。这是一个中途方法,大约是我所能得到的(仍然有点用户友好(:

住在科里鲁

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
namespace emulation {
    template <typename T1,typename T2,typename Alloc>
        struct mutable_pair
        {
            typedef Alloc allocator_type;
            typedef T1 first_type;
            typedef T2 second_type;
            mutable_pair(Alloc alloc):first(T1(alloc)),second(T2(alloc)){}
            mutable_pair(const T1& f,const T2& s):first(f),second(s){}
            mutable_pair(const std::pair<T1,T2>& p):first(p.first),second(p.second){}
            template <typename U, typename V, typename Alloc2>
            mutable_pair(const U& f,const V& s, Alloc2 alloc):first(f, alloc),second(s, alloc){}
            T1         first;
            mutable T2 second;
        };
    using namespace boost::multi_index;
    template <typename Key, typename T, typename Compare, typename Allocator, typename Element = mutable_pair<Key, T, Allocator> >
        using map = multi_index_container<
            Element,
            indexed_by<
                ordered_unique<member<Element,Key,&Element::first>,Compare>
            >,
            typename Allocator::template rebind<Element>::other
        >;
  template <typename Key, typename T, typename Compare, typename Allocator, typename Element = mutable_pair<Key, T, Allocator> >
    using multimap = multi_index_container<
        Element,
        indexed_by<
            ordered_non_unique<member<Element,Key,&Element::first>,Compare>
        >,
        typename Allocator::template rebind<Element>::other
    >;
  template <typename Key, typename T, typename Compare, typename Allocator> 
      struct wrap_map : map<Key, T, Compare, Allocator> {
          typedef map<Key, T, Compare, Allocator> base_type;
          typedef typename base_type::template nth_index<0>::type index_type;
          wrap_map(Allocator alloc) : base_type({}, alloc)
          { 
          }
          wrap_map(Compare cmp, Allocator alloc) : base_type(
                  typename base_type::ctor_args_list{
                    typename index_type::ctor_args { typename index_type::key_from_value {}, cmp }
                  },
                  alloc)
          { 
          }
      };
}

// Typedefs of allocators and containers
namespace Shared {
    typedef boost::interprocess::managed_shared_memory Segment;
    typedef Segment::segment_manager SegmentManager;
    typedef boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<void, SegmentManager> > Allocator;
    typedef Allocator::rebind<char>::other CharAllocator;
    typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> String;
    struct MyLess {
        template <typename T, typename U> bool operator()(const T &t, const U &u) const { return t < u; }
    };
    typedef MyLess StringComparator;
    typedef emulation::mutable_pair<String, String, CharAllocator> MapItem;
    typedef Allocator::rebind<MapItem>::other MapItemAllocator;
    typedef emulation::wrap_map<String, String, StringComparator, MapItemAllocator> Map;
}
int main(void) {
    struct shm_remove {
        shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
        ~shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
    } remover;
    // Create shared memory
    Shared::Segment seg(boost::interprocess::create_only, "MySharedMemory", 65536);
    Shared::Allocator alloc(seg.get_segment_manager());
    // An instance of the string comparator, to construct the map
    Shared::StringComparator cmp;
    // Construct the shared memory map
    Shared::Map *myMapPtr = seg.construct<Shared::Map>("myMap")(cmp, alloc);
    myMapPtr->emplace("foo", "bar", alloc);
    myMapPtr->emplace("goo", "car", alloc);
    myMapPtr->emplace("hoo", "dar", alloc);
    // This the only version I can get to work.  But it forces you to create a
    // copy of the key you are searching for, in the managed segment.
    Shared::String key("foo", alloc);     
    // This is the point of the exercise:
    auto it = myMapPtr->find(key);
    if (it!=myMapPtr->end())
        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'n";
    // this is now okay too
    char szkey[] = "foo";
    it = myMapPtr->find(szkey);
    if (it!=myMapPtr->end())
        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'n";
    // this is now okay too
    std::string skey("foo");
    it = myMapPtr->find(skey.c_str());
    if (it!=myMapPtr->end())
        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'n";
    return 0;
}

还印刷

Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'

以下内容改编自@sehe对自己答案的评论链接的C++03兼容代码。 虽然我真的不知道我在做什么,但我通过向mutable_pairmap_gen定义添加第二个分配器类型,以似乎适合键与其映射值不属于同一类型的映射的方式对其进行了概括。 我已经明确了所有内容都来自哪个命名空间,并为简洁起见删除了multimap_gen定义。 它编译以允许使用const char *查询共享内存中的map< string vector<int> >类型对象。

我不确定的一件事是ValueType是否需要出现在rebind行中,但它似乎可以像目前一样编译和工作......

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
namespace sehe
{
    template < typename T1, typename T2, typename Alloc1, typename Alloc2 >
        struct mutable_pair
        {
            typedef T1 first_type;
            typedef T2 second_type;
            mutable_pair( Alloc1 alloc1, Alloc2 alloc2 ) : first( T1( alloc1 ) ), second( T2( alloc2 ) ) {}
            mutable_pair( const T1 & f, const T2 & s ) : first( f ), second( s ) {}
            mutable_pair( const std::pair< T1, T2 > & p ) : first( p.first ), second( p.second ) {}
            T1         first;
            mutable T2 second;
        };

    template <typename KeyType, typename ValueType, typename Compare, typename KeyAllocator, typename ValueAllocator, typename Element = mutable_pair< KeyType, ValueType, KeyAllocator, ValueAllocator > >
        struct map_gen
        {
            typedef boost::multi_index::multi_index_container
            <
                Element,
                boost::multi_index::indexed_by
                <
                    boost::multi_index::ordered_unique< boost::multi_index::member< Element, KeyType, &Element::first >, Compare >
                >,
                typename KeyAllocator::template rebind<Element>::other
            > type;
        };
    typedef struct {
        template <typename T, typename U> bool operator()(const T &t, const U &u) const { return t < u; }
    } Comparator;
}
// Typedefs of allocators and containers
namespace Shared
{
    typedef boost::interprocess::managed_shared_memory Segment;
    typedef boost::interprocess::managed_shared_memory::segment_manager SegmentManager;
    typedef boost::interprocess::allocator<void, SegmentManager> Allocator;

    typedef boost::interprocess::allocator<char, SegmentManager> CharAlloc;
    typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAlloc> String;
    typedef boost::interprocess::allocator< String, SegmentManager > StringAlloc;
    typedef boost::interprocess::allocator< int, SegmentManager > IntAlloc;
    typedef boost::interprocess::vector< int, IntAlloc > IntVector;
    typedef boost::interprocess::allocator< IntVector, SegmentManager > IntVectorAlloc;
    typedef sehe::map_gen< String, String,    sehe::Comparator, StringAlloc, StringAlloc >::type StringStringMap;
    typedef sehe::map_gen< String, IntVector, sehe::Comparator, StringAlloc, IntVectorAlloc >::type StringIntVectorMap;
}