您的位置:首页 > 编程语言 > C语言/C++

[c++,boost] 使用 boost 中 multi_index

2018-03-01 09:10 1691 查看
这篇文章简单介绍一下 boost 中十分实用的库函数 --- 多重索引 multi_index ,它的底层结构是用来存放 结构体的 map 的数据结构,
但不同于 stl,boost 中普通的map,它可以根据用户的需要来指定索引的类型,即 key 的值可以根据需要的不同而变化的。



比如说,现在我定义了一个 fruit 的结构体,其中包含属性 { fruit_name , fruit_color , fruit_shape }
然后使用 fruit 结构体分别存放三种水果,将其设定为 fruit_table

{apple    |  fruit_name = apple   ,   fruit_color = red    , fruit_shape = cycle  }
{banana |  fruit_name = banana ,  fruit_color = yellow , fruit_shape = line   }
{grape   |  fruit_name = grape   ,   fruit_colo r= purple , fruit_shape = cycle }

对于 multi_index 来说,仅仅为每个fruit /水果 实例提供一份存储空间,但是却可以分别将 fruit_name , fruit_color , fruit_shape 设定为该结构体的索引,
不同的索引对应的结构体中的 fruit 排序方式是不同的。

这里定义的多重索引的功能与含义完全可以等同于数据库中所创建的索引,那么通过访问 映射底层存储数据上的不同类型的索引
所直接获取的并不是存储底层数据实体,而是该存储实体的一个视图,相当于是获取到一个对象实体的指针一样。

不同的视图中元素属性排列的方式是不同的,
如果我们将 fruit_name 设为多重索引之一,那么我们通过 fruit_name 属性访问 fruit_table 将会得到一个按照 fruit_name 属性进行排列的 fruit_table 的视图 "view" 即
1.{apple| ...} , 2. {banana|...} , 3. {grape|...}
如果按照 fruit_color 我们将会获取到一个按照 fruit_color 进行排序的 fruit_table 的视图 "view"
1.{grape| fruit_color=purple} 2. {apple|fruit_color= red} 3.{banana | fruit_color = yellow }

fruit_shape 属性建立索引也是同理

索引的一点好处就是,可以根据需要来设定,在不修改底层存储列表顺序的基础上,可以将列表中的元素快速的按照属性进行顺序访问。
不然的话,就只能将底层存放的数据一一全部读取到内存中,再根据某一个属性进行升序、降序的排列。
因为将底层数据(存放到计算机硬盘上的文件)通过 I/O 读入到内存中,一是需要耗费大量的时间进行扫描硬盘,而是内存空间可能容纳不下
列表项中全部的信息条目的(实际工作中的信息条目可并不像是 fruit_node 中的属性那样少,有可能一个条目就对应上 GB 的视频文件),
这样读入本身就是问题,在跑个排序算法,后果就可想而知了。

这就是我们为何要使用索引的原因,为何要使用多重索引呢?
1.实际工作中需要频繁的针对数据列表中的多个属性进行排序,这样便有了针对该数据列表创建一个多重索引。
2.用于排序的索引属性并不全都主索引(也就是索引与数据记录/表项 之间是一一映射的关系)不能够确定数据列表的排序顺序,
   就像是 fruit 例子中的仅仅通过 fruit_shape 属性是无法将 grape 和 apple 进行排序的,因为没有其他的索引属性可供参考了。
   这种情况,就需要添加其他的索引{fruit_name ,fruit_color }用作参考,当然这种情况下是优先参考有着 unique 性质的索引。
 
现在我们要通过创建一个文件列表的多重索引来介绍使用 boost::multi_index 的规则和如何调用自定义的多重索引来获取视图,通过视图来访问底层存储实体的各类属性

如果运行平台是 windows 直接运行源码即可,不过要根据需要修改一下对应读入的文件路径
如果运行平台是 linux ,
请结合后续的 configure.in 和 Makefile 以及 build.sh 文件来使用 autotools 流程来编译, 参考文档
linux平台上,要配合程序中的设定在 /tmp/路径下面创建名称为 rsa_key.pub,rsa_key{0|1|2|3|4}.txt 的文件,如果不想改源程序的话

点击(此处)折叠或打开
#if !defined(NDEBUG)

#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING

#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE

#endif

#include <cstdio>

#include <boost/multi_index_container.hpp>

#include <boost/multi_index/member.hpp>

#include <boost/multi_index/ordered_index.hpp>

#include <boost/shared_ptr.hpp>

#include <algorithm>

#include <iostream>

#include <iterator>

#include <string>

using boost::multi_index_container ;

using namespace boost::multi_index ;

struct file_node

{

    int id ;                             // ---- 这个是文件的 id 号码

    std::string path_name ;              // ---- 这个是文件的 路径, 在设定索引的时候,我们将其设定为非重复的主索引项

    long length ;                        // ---- 这个是文件的 长度, 在本实验中没有用到

    std::string creator ;                // ---- 这个是文件的 作者, 在设定索引的时候,我们将其设定为可重复的辅助索引项

    

    file_node( int id , std::string path_name , std::string creator ):    // 这个是 文件-节点 结构体的构造函数

        id(id) , path_name(path_name) , length(0), creator(creator)

    {

         

            

    }

    friend std::ostream & operator<<(std::ostream &os , const file_node & f ) ;  // 这里重载了 operator<< 并将其设定为友元方法,
                                                                                //  为了方便将 file-node 中的属性信息转换为流

} ;

std::ostream &operator<< ( std::ostream &os , const file_node &f )            // 在这里根据文件路径打开文件,并读取文件的内容

{                                                                             // 将文件中的内容进行格式化并转换为 流 对象,并返回

     char file_buffer[1024] ;

     std::string file_content ;

     bool isOpen = false ;

     FILE *fp = fopen(f.path_name.c_str() , "r") ;

        if ( fp != NULL )

             isOpen = true ;

     if ( isOpen )

     {

     int ret = fread((void*)file_buffer , sizeof(char) , sizeof(file_buffer)-1 , fp );

         file_buffer[ret] = '\0' ;

        

         file_content+= file_buffer ;

         fclose (fp) ;

     }

     else

     {

         file_content = "failed open file "+ f.path_name ;

     }

    

    os<<"[file path]     " << f.path_name << std::endl

        << "[file index]     " << f.id << std::endl

        <<"[file creator] " << f.creator << std::endl

        <<"[file contents] " << file_content << std::endl << std::endl ; ;

    return os ;

}

/**

 here we tags for accessing the corresponding indices of file_node_table

 要将 struct file-node 中的某个属性字段设定为 file-node 的索引项的话,需要定义与该
 属性字段同名的空的结构体

*/

struct path_name {} ;  

struct creator {} ;

/**

  here we define a multi_index_container of file_node, with

  following indices:

  - a unique index sorted by file_node::path_name

  - a non-unique index sorted by file_node::creator

*/

typedef multi_index_container

<

  file_node , // table element , 要将什么作为创建表的元素,传入该结构体名称

  indexed_by <

         ordered_unique   // 唯一索引,主索引,与表项一一映射, tag<传入刚刚定义好的 struct path_name {} 对应的名称 >

         <                // BOOST_MULTI_INDEX_MEMBER( 创建表中的元素结构体名称, 索引的类型, 索引的名称 等于 tag<> 中设定的名称 )

            tag<path_name> , BOOST_MULTI_INDEX_MEMBER(file_node, std::string , path_name)

         > , // this is for file's path_name , like primary-key in database table

        

         ordered_non_unique

         <

            tag<creator> , BOOST_MULTI_INDEX_MEMBER(file_node, std::string , creator)

         > // second index file node's creator

  > // end indexed by

> file_node_table ;

typedef boost::shared_ptr<file_node_table> ftPtr ; // 在这里我们使用 typedef 定义一个用来封装刚刚定义好的多重索引类型的 智能指针 类型

int main ()

{

    // try create obj by shared_ptr

    ftPtr file_table_ptr( new file_node_table ) ;  // shared_ptr 智能指针固有的创建对象的方法

    std::string file_path , author ;

    file_path = "/tmp/rsa_key" ;

    author = "kokia" ;

    // add additional node with creator/author name as 'Aimer'

    file_table_ptr->insert( file_node (1200 , "/tmp/rsa_key.pub" , "Aimer")) ; // 插入 file-node 文件-节点 作为表项

    for ( int i = 0 ; i < 5 ; i++ )

    {

        char no = i+'0' ;    

        file_table_ptr->insert(file_node(i , file_path+no+".txt" , author)) ;

    }

    std::cout << "+++++++++++++++++++++ print files by author ++++++++++++++++++++ "<< std::endl ;

   

    file_node_table::index<creator>::type & index_by_author = file_table_ptr->get<creator>() ; 
    /*
      恭喜你,获得指向以 creator 属性排序的 文件表视图 , 该视图,通过 index_by_author 变量关联

      创建一个视图的迭代器,有了它便可以顺序遍历视图中 以 creator 属性进行排序的各个表项了,
      并访问表项中,也就是每个 file-node 中的各个属性了
 

    */                                                                                                        

    file_node_table::index<creator>::type::iterator creator_it = index_by_author.begin() ;    

                                                                                               

    while ( creator_it != index_by_author.end())                                               

    {

        std::cout << *creator_it << std::endl ;

        creator_it++ ;

    }

 

    std::cout << "+++++++++++ print files by path name +++++++++++++++++++++" << std::endl ;
// 这里和上面的原理是一样的,首先是获得 file-table 的按照 path_name 属性进行排序的视图 (注意视图与表的区别,视图仅仅是表的一个映像

// 在系统中并不为视图中的元素提供实际的存储空间,类似于对象实体和指向对象的指针) ,通过该视图创建遍历视图的迭代器,通过迭代器
// 逐个访问以 path_name 属性排序的视图中的各个 file-node 元素,并可以访问各个 file-node 中的属性值

 

    file_node_table::index<path_name>::type & file_table_view_index_by_path_name =

            file_table_ptr->get<path_name>() ;

    // path_it points to the first element in the VIEW which sorted by path_name

    file_node_table::index<path_name>::type::iterator

             path_it = file_table_view_index_by_path_name.begin () ;

    for ( ; path_it != file_table_view_index_by_path_name.end() ; path_it++ )

    {

        // call operator<< method of each object which pointed by iterator of path name

        std::cout << *path_it << std::endl ;

    }

    std::cout << "----------------------- test find method --------------------------------------" << std::endl ;

    //  在下面的演示中,我们首先通过 file_table_ptr->get 获得一个以 creator 属性排序的 file-node-table 的视图

    // 然后创建该 creator-视图调用 find 方法来查找 file-node.creator = "Aimer" 的 file-node

    // 如果找到的话, 迭代器是不会指向 creator-视图 的结尾空元素项的,通过该点即可判断, 因为 itr 类型特殊,不可使用 itr == NULL 判断
    // 

    //在成功找到之后,将找到的对象通过特定类型(creator) 的迭代器指向,然后通过该迭代器即可访问 file-node 中的元素值了

    file_node_table::index<creator>::type::iterator

                 itr(file_table_ptr->get<creator>().find("Aimer")) ;

    if ( itr != file_table_ptr->get<creator>().end() )

    {

        std::cout<< " find file node " << std::endl ;

        std::cout << *itr << std::endl ;

    }

    else

    {

        std::cout << " file node not find" << std::endl ;

    }

    return 0 ;

}

//================== compile scripts for linux =============================
// build.sh , do not forget chmod 755

点击(此处)折叠或打开
#!/bin/sh

autoscan

aclocal

autoconf

autoheader

automake --add-missing

./configure CXXFLAGS= CFLAGS=

make

// configure.in
点击(此处)折叠或打开
# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

AC_INIT(multi_index_demo)

AC_USE_SYSTEM_EXTENSIONS

AM_INIT_AUTOMAKE( multi_index_demo, 1.0 )

AC_CONFIG_SRCDIR([multi_index_demo.cpp])

AC_CONFIG_HEADERS([config.h])

# Checks for programs.

AC_PROG_CXX

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

AC_HEADER_STDBOOL

# Checks for library functions.

AC_OUTPUT(Makefile)
// Makefile.am 点击(此处)折叠或打开
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=multi_index_demo
BOOST_LIB_PATH=/unixC/Boost/boost_1_58_0

#what source files multi_index_demo need ?

multi_index_demo_SOURCES=\
multi_index_demo.cpp

multi_index_demo_CXXFLAGS=\
-I$(BOOST_LIB_PATH) -D_FILE_OFFSET_BITS=64 \
-ggdb -Wall -O0

multi_index_demo_LDADD=\
-lpthread -lm -lboost_system -lboost_thread \
-lboost_program_options -lrt

multi_index_demo_LDFLAGS=\
-fPIC -rdynamic -L$(BOOST_LIB_PATH)/stage/lib -pthre
运行结果



没错,我读入的文件是前几篇文章中的程序生成的 rsa_key.pub,并不是实际使用中的 rsa-public-key ,在这里读入的文件可以根据用户喜好而定

end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C boost