さくらんぼのlambda日記

lambdaちっくなことからゲーム開発までいろいろ書きます。

multi_indexが便利な件について その2

前回のmulti_indexの話の続きです。

multi_indexを作ってみる!

ここでは3つのインデックスをもつmulti_indexを作ってみます。
ちょっと複雑というか、長いコードなのでびっくりするかもしれませんが
落ち着いて読めば大丈夫です。

typedef multi_index_container<
  Charactor,
  indexed_by<
    // 挿入順アクセス
    sequenced<>,
    // Charactor::operatro<順でアクセス
    ordered_unique<identity<Charactor> >,
    // Charactor::nameの順番
    ordered_non_unique<member<Charactor,std::string,&Charactor::name> >
    > 
  > charactor_set;

単なるtypedef文です。
typedef multi_index_container<テンプレートパラメータ> charactor_set;
という文なわけですね。


じゃあ、multi_index_containerのテンプレートパラメータはそれぞれどういう意味なの?ということですが
本来は3つテンプレートパラメータがあって

multi_index_container< 挿入したいオブジェクトの型 , ソートの方法 , アロケータ >

となっています。

3番目のアロケータは省略が可能ですのでここでは説明を省略します。
気になる方は公式のドキュメントを参照してください。

ここで大事なのは2番目のパラメータである
ソートの方法です。

これにはindex_by<>で括った複数のソート方法を指定することができます。
上のtypedef文の場合ですと

  indexed_by<
    // 挿入順アクセス
    sequenced<>,
    // Charactor::operatro<順でアクセス
    ordered_unique<identity<Charactor> >,
    // Charactor::nameの順番
    ordered_non_unique<member<Charactor,std::string,&Charactor::name> >
    > 

と3種類指定しています。

アクセスの方法!

宣言は先ほどの例のとおりです。
では、どうやってアクセスすることが出来るのかを示します。

  charactor_set charactors;
  
  // キャラクタを順番に追加
  charactors.push_back( Charactor(0,0,"d"));
  charactors.push_back( Charactor(1,0,"c"));
  charactors.push_back( Charactor(3,0,"a"));
  charactors.push_back( Charactor(2,0,"b"));
  
  
  // まずは、挿入順でアクセス
  std::cout << "orderd by inserted\n";
  charactor_set::nth_index<0>::type& c0 = charactors.get<0>();
  BOOST_FOREACH( const Charactor& s, c0 ) {
    s.print();
  }
  std::cout<<std::endl;
  
  // operator<の順番でアクセス
  std::cout << "orderd by operator<\n";
  charactor_set::nth_index<1>::type& c1 = charactors.get<1>();
  BOOST_FOREACH( const Charactor& s, c1 ) {
    s.print();
  }
  std::cout<<std::endl;

上記のコードを実行した結果としては

orderd by inserted
[name:d , x:0 , y:0]
[name:c , x:1 , y:0]
[name:a , x:3 , y:0]
[name:b , x:2 , y:0]

orderd by operator<
[name:d , x:0 , y:0]
[name:c , x:1 , y:0]
[name:b , x:2 , y:0]
[name:a , x:3 , y:0]

のような出力が得られます。
確かに、最初の出力では挿入された順番、2番目の出力ではx順番と複数のソート順で出力されていますね!


コードの中で注目するべきところは

  charactor_set::nth_index<1>::type& c1 = charactors.get<1>();

この部分ですね。

charactor.get<>でindex_by<>で指定したソート順になっているコンテナを取得できます。
テンプレート引数には数字を与えることができ、その数字はindex_byで指定したソート順の
番号です(0から始まります)。
コンテナに収められている型情報はcharactor_set::nth_index<>::typeで取得できます。
ここでの注意点はgetは参照を返すということですね。

まとめ

駆け足ですがmulti_index_container<>の紹介をしてみました!
足りない情報満載ですが、そこは各自調べてください。

簡単ですがまとめておきます。

multi_index_container<>を利用することで
複数のインデックスをもったコンテナを宣言することができます。

multi_indexcontainer<>のテンプレート引数は

  1. 格納したい型
  2. index_by<>で囲まれた、ソートしたい順番のリスト

を指定する。

get<数字>を呼ぶことで、index_byの中の指定したソート順になっているコンテナを取得できる。
数字は0から始まる。


まとめると簡単ですね!

これで快適な生活をぜひ!

おまけ

実行できる(と思う)サンプルコードを載せておきます。

#include <algorithm>
#include <iterator>
#include <ostream>
#include <iostream>
#include <string>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/foreach.hpp> 

using namespace std;
using namespace boost::multi_index;

struct Charactor {
  int         x;
  int         y;
  std::string name;
  
  Charactor(int x,int y,const std::string& name):x(x),y(y),name(name){}
  
  // 標準の順序は、xの大きさで決定する
  bool operator<(const Charactor& e) const {
    return x < e.x;
  }
  
  // デバッグ用の出力関数
  void print() const {
    std::cout << "[name:" << this->name 
              << " , x:"  << this->x 
              << " , y:"  << this->y 
              << "]"      << std::endl;
  }
  
};

typedef multi_index_container<
  Charactor,
  indexed_by<
    // 挿入順アクセス
    sequenced<>,
    // Charactor::operatro<順でアクセス
    ordered_unique<identity<Charactor> >,
    // Charactor::nameの順番
    ordered_non_unique<member<Charactor,std::string,&Charactor::name> >
    > 
  > charactor_set;



int main(int argc,char** argv) {
  // multi_indexを作成  
  charactor_set charactors;
  
  // キャラクタを順番に追加
  charactors.push_back( Charactor(0,0,"d"));
  charactors.push_back( Charactor(1,0,"c"));
  charactors.push_back( Charactor(3,0,"a"));
  charactors.push_back( Charactor(2,0,"b"));
  
  
  // まずは、挿入順でアクセス
  std::cout << "orderd by inserted\n";
  charactor_set::nth_index<0>::type& c0 = charactors.get<0>();
  BOOST_FOREACH( const Charactor& s, c0 ) {
    s.print();
  }
  std::cout<<std::endl;
  
  // operator<の順番でアクセス
  std::cout << "orderd by operator<\n";
  charactor_set::nth_index<1>::type& c1 = charactors.get<1>();
  BOOST_FOREACH( const Charactor& s, c1 ) {
    s.print();
  }
  std::cout<<std::endl;
  
  // Charactor::name順でのアクセス
  std::cout << "orderd by name\n";
  charactor_set::nth_index<2>::type& c2 = charactors.get<2>();
  BOOST_FOREACH( const Charactor& s, c2 ) {
    s.print();
  }
  std::cout<<std::endl;
  
  // 削除してみる
  c2.erase("a");
  
  // c2の変更がc1にも波及していか確認
  std::cout << "after deleted" << std::endl;
  BOOST_FOREACH( const Charactor& s, c1 ) {
    s.print();
  }
  std::cout<<std::endl;
  
  BOOST_FOREACH( const Charactor& s, c2 ) {
    s.print();
  }
  std::cout<<std::endl;
}


簡単なデモなのですが長いコードになってしまいましたね…。
このプログラムを実行した時に実行結果は

orderd by inserted
[name:d , x:0 , y:0]
[name:c , x:1 , y:0]
[name:a , x:3 , y:0]
[name:b , x:2 , y:0]

orderd by operator<
[name:d , x:0 , y:0]
[name:c , x:1 , y:0]
[name:b , x:2 , y:0]
[name:a , x:3 , y:0]

orderd by name
[name:a , x:3 , y:0]
[name:b , x:2 , y:0]
[name:c , x:1 , y:0]
[name:d , x:0 , y:0]

after deleted
[name:d , x:0 , y:0]
[name:c , x:1 , y:0]
[name:b , x:2 , y:0]

[name:b , x:2 , y:0]
[name:c , x:1 , y:0]
[name:d , x:0 , y:0]