Boost.Intrusive のコンテナを hook なしで使う
Boost.Intrusive を使いたい場面は結構あるものの、なんだ hook とか書かなくちゃいけないのか、既存のデータ構造には適用できないのか、と諦めていた。ところが、よくよくソースを読んでみると ValueTraits という trait クラスを指定することで完全にノードの形式をカスタマイズできるようだったので、試してみた。つか、書いてみて分かったけどリファレンスにさりげなく載ってた。なんだよこれ。
#define BOOST_TEST_MODULE "list" #include <boost/intrusive/list.hpp> #include <boost/test/included/unit_test.hpp> #include <iostream> class my_list_node { public: my_list_node(int v = 0): v_(v) {} bool operator<(my_list_node const& that) const { return v_ < that.v_; } operator int() const { return v_; } public: my_list_node* next; my_list_node* prev; private: int v_; }; struct my_list_value_traits { typedef my_list_node value_type; typedef value_type* pointer; typedef value_type const* const_pointer; typedef value_type& reference; typedef value_type const& const_reference; struct node_traits { typedef my_list_node node; typedef my_list_node* node_ptr; typedef my_list_node const* const_node_ptr; static node_ptr get_previous(const_node_ptr n) { return n->prev; } static node_ptr get_next(const_node_ptr n) { return n->next; } static void set_previous(node_ptr n, node_ptr v) { n->prev = v; } static void set_next(node_ptr n, node_ptr v) { n->next = v; } }; typedef node_traits::node node; typedef node_traits::node_ptr node_ptr; typedef node_traits::const_node_ptr const_node_ptr; enum { link_mode = boost::intrusive::normal_link }; static pointer to_value_ptr(node_ptr n) { return n; } static const_pointer to_value_ptr(const_node_ptr n) { return n; } static node_ptr to_node_ptr(reference n) { return &n; } static const_node_ptr to_node_ptr(const_reference n) { return &n; } }; struct deleter { template<typename T_> void operator()(T_* ptr) { delete ptr; } }; typedef boost::intrusive::list<my_list_node, boost::intrusive::value_traits<my_list_value_traits> > list_type; BOOST_AUTO_TEST_CASE(insertion) { list_type l; my_list_node a[] = { 3, 1, 2 }; l.push_back(a[0]); BOOST_CHECK_EQUAL(l.size(), 1); l.push_back(a[1]); BOOST_CHECK_EQUAL(l.size(), 2); l.push_back(a[2]); BOOST_CHECK_EQUAL(l.size(), 3); } BOOST_AUTO_TEST_CASE(iteration_and_sorting) { list_type l; my_list_node a[] = { 3, 1, 2 }; l.push_back(a[0]); BOOST_CHECK_EQUAL(l.size(), 1); l.push_back(a[1]); BOOST_CHECK_EQUAL(l.size(), 2); l.push_back(a[2]); BOOST_CHECK_EQUAL(l.size(), 3); { list_type::const_iterator i(l.begin()); BOOST_CHECK(i != l.end()); BOOST_CHECK_EQUAL(static_cast<int>(*i), a[0]); ++i; BOOST_CHECK(i != l.end()); BOOST_CHECK_EQUAL(static_cast<int>(*i), a[1]); ++i; BOOST_CHECK(i != l.end()); BOOST_CHECK_EQUAL(static_cast<int>(*i), a[2]); ++i; BOOST_CHECK(i == l.end()); } l.sort(); { list_type::const_iterator i(l.begin()); BOOST_CHECK(i != l.end()); BOOST_CHECK_EQUAL(static_cast<int>(*i), 1); ++i; BOOST_CHECK(i != l.end()); BOOST_CHECK_EQUAL(static_cast<int>(*i), 2); ++i; BOOST_CHECK(i != l.end()); BOOST_CHECK_EQUAL(static_cast<int>(*i), 3); ++i; BOOST_CHECK(i == l.end()); } } BOOST_AUTO_TEST_CASE(removal) { list_type l; l.push_back(*new my_list_node(3)); BOOST_CHECK_EQUAL(l.size(), 1); l.push_back(*new my_list_node(1)); BOOST_CHECK_EQUAL(l.size(), 2); l.push_back(*new my_list_node(2)); BOOST_CHECK_EQUAL(l.size(), 3); l.pop_back_and_dispose(deleter()); BOOST_CHECK_EQUAL(l.size(), 2); l.pop_back_and_dispose(deleter()); BOOST_CHECK_EQUAL(l.size(), 1); l.pop_back_and_dispose(deleter()); BOOST_CHECK_EQUAL(l.size(), 0); }