Brian Silverman | 3cbbaca | 2018-08-04 23:38:07 -0700 | [diff] [blame^] | 1 | /* Boost.MultiIndex example of composite keys. |
| 2 | * |
| 3 | * Copyright 2003-2008 Joaquin M Lopez Munoz. |
| 4 | * Distributed under the Boost Software License, Version 1.0. |
| 5 | * (See accompanying file LICENSE_1_0.txt or copy at |
| 6 | * http://www.boost.org/LICENSE_1_0.txt) |
| 7 | * |
| 8 | * See http://www.boost.org/libs/multi_index for library home page. |
| 9 | */ |
| 10 | |
| 11 | #if !defined(NDEBUG) |
| 12 | #define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING |
| 13 | #define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE |
| 14 | #endif |
| 15 | |
| 16 | #include <boost/call_traits.hpp> |
| 17 | #include <boost/multi_index_container.hpp> |
| 18 | #include <boost/multi_index/composite_key.hpp> |
| 19 | #include <boost/multi_index/member.hpp> |
| 20 | #include <boost/multi_index/ordered_index.hpp> |
| 21 | #include <boost/next_prior.hpp> |
| 22 | #include <boost/tokenizer.hpp> |
| 23 | #include <functional> |
| 24 | #include <iostream> |
| 25 | #include <iterator> |
| 26 | #include <map> |
| 27 | #include <string> |
| 28 | |
| 29 | using namespace boost::multi_index; |
| 30 | |
| 31 | /* A file record maintains some info on name and size as well |
| 32 | * as a pointer to the directory it belongs (null meaning the root |
| 33 | * directory.) |
| 34 | */ |
| 35 | |
| 36 | struct file_entry |
| 37 | { |
| 38 | file_entry( |
| 39 | std::string name_,unsigned size_,bool is_dir_,const file_entry* dir_): |
| 40 | name(name_),size(size_),is_dir(is_dir_),dir(dir_) |
| 41 | {} |
| 42 | |
| 43 | std::string name; |
| 44 | unsigned size; |
| 45 | bool is_dir; |
| 46 | const file_entry* dir; |
| 47 | |
| 48 | friend std::ostream& operator<<(std::ostream& os,const file_entry& f) |
| 49 | { |
| 50 | os<<f.name<<"\t"<<f.size; |
| 51 | if(f.is_dir)os<<"\t <dir>"; |
| 52 | return os; |
| 53 | } |
| 54 | }; |
| 55 | |
| 56 | /* A file system is just a multi_index_container of entries with indices on |
| 57 | * file and size. These indices are firstly ordered by directory, as commands |
| 58 | * work on a current directory basis. Composite keys are just fine to model |
| 59 | * this. |
| 60 | * NB: The use of derivation here instead of simple typedef is explained in |
| 61 | * Compiler specifics: type hiding. |
| 62 | */ |
| 63 | |
| 64 | struct name_key:composite_key< |
| 65 | file_entry, |
| 66 | BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry*,dir), |
| 67 | BOOST_MULTI_INDEX_MEMBER(file_entry,std::string,name) |
| 68 | >{}; |
| 69 | |
| 70 | struct size_key:composite_key< |
| 71 | file_entry, |
| 72 | BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry* const,dir), |
| 73 | BOOST_MULTI_INDEX_MEMBER(file_entry,unsigned,size) |
| 74 | >{}; |
| 75 | |
| 76 | /* see Compiler specifics: composite_key in compilers without partial |
| 77 | * template specialization, for info on composite_key_result_less |
| 78 | */ |
| 79 | |
| 80 | typedef multi_index_container< |
| 81 | file_entry, |
| 82 | indexed_by< |
| 83 | /* primary index sorted by name (inside the same directory) */ |
| 84 | ordered_unique<name_key>, |
| 85 | /* secondary index sorted by size (inside the same directory) */ |
| 86 | ordered_non_unique<size_key> |
| 87 | > |
| 88 | > file_system; |
| 89 | |
| 90 | /* typedef's of the two indices of file_system */ |
| 91 | |
| 92 | typedef nth_index<file_system,0>::type file_system_by_name; |
| 93 | typedef nth_index<file_system,1>::type file_system_by_size; |
| 94 | |
| 95 | /* We build a rudimentary file system simulation out of some global |
| 96 | * info and a map of commands provided to the user. |
| 97 | */ |
| 98 | |
| 99 | static file_system fs; /* the one and only file system */ |
| 100 | static file_system_by_name& fs_by_name=fs; /* name index to fs */ |
| 101 | static file_system_by_size& fs_by_size=get<1>(fs); /* size index to fs */ |
| 102 | static const file_entry* current_dir=0; /* root directory */ |
| 103 | |
| 104 | /* command framework */ |
| 105 | |
| 106 | /* A command provides an execute memfun fed with the corresponding params |
| 107 | * (first param is stripped off as it serves to identify the command |
| 108 | * currently being used.) |
| 109 | */ |
| 110 | |
| 111 | typedef boost::tokenizer<boost::char_separator<char> > command_tokenizer; |
| 112 | |
| 113 | class command |
| 114 | { |
| 115 | public: |
| 116 | virtual ~command(){} |
| 117 | virtual void execute( |
| 118 | command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)=0; |
| 119 | }; |
| 120 | |
| 121 | /* available commands */ |
| 122 | |
| 123 | /* cd: syntax cd [.|..|<directory>] */ |
| 124 | |
| 125 | class command_cd:public command |
| 126 | { |
| 127 | public: |
| 128 | virtual void execute( |
| 129 | command_tokenizer::iterator tok1,command_tokenizer::iterator tok2) |
| 130 | { |
| 131 | if(tok1==tok2)return; |
| 132 | std::string dir=*tok1++; |
| 133 | |
| 134 | if(dir==".")return; |
| 135 | if(dir==".."){ |
| 136 | if(current_dir)current_dir=current_dir->dir; |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | file_system_by_name::iterator it=fs.find( |
| 141 | boost::make_tuple(current_dir,dir)); |
| 142 | if(it==fs.end()){ |
| 143 | std::cout<<"non-existent directory"<<std::endl; |
| 144 | return; |
| 145 | } |
| 146 | if(!it->is_dir){ |
| 147 | std::cout<<dir<<" is not a directory"<<std::endl; |
| 148 | return; |
| 149 | } |
| 150 | current_dir=&*it; |
| 151 | } |
| 152 | }; |
| 153 | static command_cd cd; |
| 154 | |
| 155 | /* ls: syntax ls [-s] */ |
| 156 | |
| 157 | class command_ls:public command |
| 158 | { |
| 159 | public: |
| 160 | virtual void execute( |
| 161 | command_tokenizer::iterator tok1,command_tokenizer::iterator tok2) |
| 162 | { |
| 163 | std::string option; |
| 164 | if(tok1!=tok2)option=*tok1++; |
| 165 | |
| 166 | if(!option.empty()){ |
| 167 | if(option!="-s"){ |
| 168 | std::cout<<"incorrect parameter"<<std::endl; |
| 169 | return; |
| 170 | } |
| 171 | |
| 172 | /* list by size */ |
| 173 | |
| 174 | file_system_by_size::iterator it0,it1; |
| 175 | boost::tie(it0,it1)=fs_by_size.equal_range( |
| 176 | boost::make_tuple(current_dir)); |
| 177 | std::copy(it0,it1,std::ostream_iterator<file_entry>(std::cout,"\n")); |
| 178 | |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | /* list by name */ |
| 183 | |
| 184 | file_system_by_name::iterator it0,it1; |
| 185 | boost::tie(it0,it1)=fs.equal_range(boost::make_tuple(current_dir)); |
| 186 | std::copy(it0,it1,std::ostream_iterator<file_entry>(std::cout,"\n")); |
| 187 | } |
| 188 | }; |
| 189 | static command_ls ls; |
| 190 | |
| 191 | /* mkdir: syntax mkdir <directory> */ |
| 192 | |
| 193 | class command_mkdir:public command |
| 194 | { |
| 195 | public: |
| 196 | virtual void execute( |
| 197 | command_tokenizer::iterator tok1,command_tokenizer::iterator tok2) |
| 198 | { |
| 199 | std::string dir; |
| 200 | if(tok1!=tok2)dir=*tok1++; |
| 201 | |
| 202 | if(dir.empty()){ |
| 203 | std::cout<<"missing parameter"<<std::endl; |
| 204 | return; |
| 205 | } |
| 206 | |
| 207 | if(dir=="."||dir==".."){ |
| 208 | std::cout<<"incorrect parameter"<<std::endl; |
| 209 | return; |
| 210 | } |
| 211 | |
| 212 | if(!fs.insert(file_entry(dir,0,true,current_dir)).second){ |
| 213 | std::cout<<"directory already exists"<<std::endl; |
| 214 | return; |
| 215 | } |
| 216 | } |
| 217 | }; |
| 218 | static command_mkdir mkdir; |
| 219 | |
| 220 | /* table of commands, a map from command names to class command pointers */ |
| 221 | |
| 222 | typedef std::map<std::string,command*> command_table; |
| 223 | static command_table cmt; |
| 224 | |
| 225 | int main() |
| 226 | { |
| 227 | /* fill the file system with some data */ |
| 228 | |
| 229 | file_system::iterator it0,it1; |
| 230 | |
| 231 | fs.insert(file_entry("usr.cfg",240,false,0)); |
| 232 | fs.insert(file_entry("memo.txt",2430,false,0)); |
| 233 | it0=fs.insert(file_entry("dev",0,true,0)).first; |
| 234 | fs.insert(file_entry("tty0",128,false,&*it0)); |
| 235 | fs.insert(file_entry("tty1",128,false,&*it0)); |
| 236 | it0=fs.insert(file_entry("usr",0,true,0)).first; |
| 237 | it1=fs.insert(file_entry("bin",0,true,&*it0)).first; |
| 238 | fs.insert(file_entry("bjam",172032,false,&*it1)); |
| 239 | it0=fs.insert(file_entry("home",0,true,0)).first; |
| 240 | it1=fs.insert(file_entry("andy",0,true,&*it0)).first; |
| 241 | fs.insert(file_entry("logo.jpg",5345,false,&*it1)).first; |
| 242 | fs.insert(file_entry("foo.cpp",890,false,&*it1)).first; |
| 243 | fs.insert(file_entry("foo.hpp",93,false,&*it1)).first; |
| 244 | fs.insert(file_entry("foo.html",750,false,&*it1)).first; |
| 245 | fs.insert(file_entry("a.obj",12302,false,&*it1)).first; |
| 246 | fs.insert(file_entry(".bash_history",8780,false,&*it1)).first; |
| 247 | it1=fs.insert(file_entry("rachel",0,true,&*it0)).first; |
| 248 | fs.insert(file_entry("test.py",650,false,&*it1)).first; |
| 249 | fs.insert(file_entry("todo.txt",241,false,&*it1)).first; |
| 250 | fs.insert(file_entry(".bash_history",9510,false,&*it1)).first; |
| 251 | |
| 252 | /* fill the command table */ |
| 253 | |
| 254 | cmt["cd"] =&cd; |
| 255 | cmt["ls"] =&ls; |
| 256 | cmt["mkdir"]=&mkdir; |
| 257 | |
| 258 | /* main looop */ |
| 259 | |
| 260 | for(;;){ |
| 261 | /* print out the current directory and the prompt symbol */ |
| 262 | |
| 263 | if(current_dir)std::cout<<current_dir->name; |
| 264 | std::cout<<">"; |
| 265 | |
| 266 | /* get an input line from the user: if empty, exit the program */ |
| 267 | |
| 268 | std::string com; |
| 269 | std::getline(std::cin,com); |
| 270 | command_tokenizer tok(com,boost::char_separator<char>(" \t\n")); |
| 271 | if(tok.begin()==tok.end())break; /* null command, exit */ |
| 272 | |
| 273 | /* select the corresponding command and execute it */ |
| 274 | |
| 275 | command_table::iterator it=cmt.find(*tok.begin()); |
| 276 | if(it==cmt.end()){ |
| 277 | std::cout<<"invalid command"<<std::endl; |
| 278 | continue; |
| 279 | } |
| 280 | |
| 281 | it->second->execute(boost::next(tok.begin()),tok.end()); |
| 282 | } |
| 283 | |
| 284 | return 0; |
| 285 | } |