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

jez*_*jez 2 c++ boost shared-memory

我正在使用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;
}
Run Code Online (Sandbox Code Playgroud)

有一个std::string如下key:

$ 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.
Run Code Online (Sandbox Code Playgroud)

随着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.
Run Code Online (Sandbox Code Playgroud)

更新:按照下面的建议,我尝试更换

typedef std::less< String >
    StringComparator;
Run Code Online (Sandbox Code Playgroud)

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

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

seh*_*ehe 5

您可以使用自定义比较器

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

在您的代码中,您只需将其定义为 StringComparator

更新到评论


多指数救援

如果你想用一个Boost Multi Index容器替换std::map/ boost::container::map(它支持查找CompatibleKey),这里有一个如何做的演示:

我从文档部分Emulating standard containers中multi_index_container借用了一些想法.

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

Live On Coliru

#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;
}
Run Code Online (Sandbox Code Playgroud)

打印:

Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'
Run Code Online (Sandbox Code Playgroud)

Scoped Allocators为Extra Awesomesauce?

现在,有趣的是,升压容器支持作用域分配器,所以你可以做掉与分配器的反复传球,然而,Boost多指数可悲的是不支持它充分.这是我可以得到它的中途方法(仍然有点用户友好):

Live On Coliru

#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;
}
Run Code Online (Sandbox Code Playgroud)

还印刷

Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'
Found: 'foo' -> 'bar'
Run Code Online (Sandbox Code Playgroud)