<i id='fyIQf'><tr id='fyIQf'><dt id='fyIQf'><q id='fyIQf'><span id='fyIQf'><b id='fyIQf'><form id='fyIQf'><ins id='fyIQf'></ins><ul id='fyIQf'></ul><sub id='fyIQf'></sub></form><legend id='fyIQf'></legend><bdo id='fyIQf'><pre id='fyIQf'><center id='fyIQf'></center></pre></bdo></b><th id='fyIQf'></th></span></q></dt></tr></i><div id='fyIQf'><tfoot id='fyIQf'></tfoot><dl id='fyIQf'><fieldset id='fyIQf'></fieldset></dl></div>
    • <bdo id='fyIQf'></bdo><ul id='fyIQf'></ul>

    <small id='fyIQf'></small><noframes id='fyIQf'>

    <legend id='fyIQf'><style id='fyIQf'><dir id='fyIQf'><q id='fyIQf'></q></dir></style></legend>

    1. <tfoot id='fyIQf'></tfoot>

    2. 想要有效地克服 Boost.Interprocess 共享内存中映射中键类型之间的不匹配

      want to efficiently overcome mismatch between key types in a map in Boost.Interprocess shared memory(想要有效地克服 Boost.Interprocess 共享内存中映射中键类型之间的不匹配)
    3. <i id='hdnVp'><tr id='hdnVp'><dt id='hdnVp'><q id='hdnVp'><span id='hdnVp'><b id='hdnVp'><form id='hdnVp'><ins id='hdnVp'></ins><ul id='hdnVp'></ul><sub id='hdnVp'></sub></form><legend id='hdnVp'></legend><bdo id='hdnVp'><pre id='hdnVp'><center id='hdnVp'></center></pre></bdo></b><th id='hdnVp'></th></span></q></dt></tr></i><div id='hdnVp'><tfoot id='hdnVp'></tfoot><dl id='hdnVp'><fieldset id='hdnVp'></fieldset></dl></div>
            • <bdo id='hdnVp'></bdo><ul id='hdnVp'></ul>

              <small id='hdnVp'></small><noframes id='hdnVp'>

              <legend id='hdnVp'><style id='hdnVp'><dir id='hdnVp'><q id='hdnVp'></q></dir></style></legend>

              <tfoot id='hdnVp'></tfoot>
                  <tbody id='hdnVp'></tbody>
              • 本文介绍了想要有效地克服 Boost.Interprocess 共享内存中映射中键类型之间的不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

                问题描述

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

                I'm creating a map (from string to string in this example) in shared memory using Boost.Interprocess. The compiler seems to want to force me, during retrieval from the map, to allocate memory in the managed segment just to (unnecessarily) contain the query term.

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

                I'd like to be able to look up values in a shared map more efficiently, by matching the map's keys against instances that are already in non-shared memory, without performing this extra allocation. But it's refusing to compile if I try to use a std::string or const char * as the argument to the map's find method. (see compiler error messages at bottom).

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

                Do I need to define some sort of comparator method between my shared-memory key type and its non-shared equivalent (std::string in this example)? If so, what should this look like and how should I make the map use it? If not, what should I do?

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

                Here's the code, followed by the compiler errors. The problem is towards the bottom of 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::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.
                

                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的建议,我尝试更换

                UPDATE: following the suggestion of sehe, below, I tried replacing

                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;
                

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

                but got the same two compiler errors.

                推荐答案

                您可以使用自定义比较器

                You can use a custom comparator

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

                在您的代码中,您可以将其定义为 StringComparator

                In your code you can just typedef it as StringComparator

                更新评论

                如果要将 std::map/boost::container::map 替换为 Boost Multi Index 容器(支持通过 CompatibleKey 查找),这是一个如何做到的演示:

                If you want to replace the std::map/boost::container::map with a Boost Multi Index container (which supports lookup by CompatibleKey), here's a demo of how to do it:

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

                I've borrowed some of the idea's from the documentation section Emulating standard containers with multi_index_container.

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

                Note that std::string as the lookup key still won't work, but you can easily use .c_strio() in that event.

                生活在 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 << "'
                ";
                
                    // this is now okay too
                    char szkey[] = "foo";
                    it = myMapPtr->find(szkey);
                    if (it!=myMapPtr->end())
                        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'
                ";
                
                    // 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 << "'
                ";
                
                    return 0;
                }
                

                打印:

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

                <小时>

                额外的Awesomesauce的范围分配器?

                现在,有趣的是,Boost Container 支持作用域分配器,因此您可以取消分配器的重复传递,但是遗憾的是,Boost Multi Index 并不完全支持它.这是我所能得到的一种半途而废的方法(仍然对用户更友好):


                Scoped Allocators For Extra Awesomesauce?

                Now, interestingly, Boost Container supports Scoped Allocators, so you could do away with the repeated passing of the allocators, however, Boost Multi Index sadly doesn't support it fully. Here's a halfway approach that's about as far as I could get it (still somewhat user friendlier):

                生活在 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 << "'
                ";
                
                    // this is now okay too
                    char szkey[] = "foo";
                    it = myMapPtr->find(szkey);
                    if (it!=myMapPtr->end())
                        std::cout << "Found: '" << it->first << "' -> '" << it->second << "'
                ";
                
                    // 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 << "'
                ";
                
                    return 0;
                }
                

                也打印

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

                这篇关于想要有效地克服 Boost.Interprocess 共享内存中映射中键类型之间的不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

                本站部分内容来源互联网,如果有图片或者内容侵犯了您的权益,请联系我们,我们会在确认后第一时间进行删除!

                相关文档推荐

                How to limit the number of running instances in C++(C++中如何限制运行实例的数量)
                Using boost::asio::async_read with stdin?(将 boost::asio::async_read 与 stdin 一起使用?)
                How to find out what dependencies (i.e other Boost libraries) a particular Boost library requires?(如何找出特定 Boost 库需要哪些依赖项(即其他 Boost 库)?)
                What#39;s the purpose of a leading quot;::quot; in a C++ method call(引导“::的目的是什么?在 C++ 方法调用中)
                Boost Spirit x3: parse into structs(Boost Spirit x3:解析为结构体)
                How boost auto-linking makes choice?(boost自动链接如何做出选择?)
                  <legend id='IOTDW'><style id='IOTDW'><dir id='IOTDW'><q id='IOTDW'></q></dir></style></legend>

                  <small id='IOTDW'></small><noframes id='IOTDW'>

                  1. <i id='IOTDW'><tr id='IOTDW'><dt id='IOTDW'><q id='IOTDW'><span id='IOTDW'><b id='IOTDW'><form id='IOTDW'><ins id='IOTDW'></ins><ul id='IOTDW'></ul><sub id='IOTDW'></sub></form><legend id='IOTDW'></legend><bdo id='IOTDW'><pre id='IOTDW'><center id='IOTDW'></center></pre></bdo></b><th id='IOTDW'></th></span></q></dt></tr></i><div id='IOTDW'><tfoot id='IOTDW'></tfoot><dl id='IOTDW'><fieldset id='IOTDW'></fieldset></dl></div>
                    • <tfoot id='IOTDW'></tfoot>
                        <tbody id='IOTDW'></tbody>

                      • <bdo id='IOTDW'></bdo><ul id='IOTDW'></ul>