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