1 /* Boost.MultiIndex example of use of Boost.Interprocess allocators.
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/config.hpp> /* keep it first to prevent nasty warns in MSVC */
17 #include <algorithm>
18 #include <boost/interprocess/allocators/allocator.hpp>
19 #include <boost/interprocess/containers/string.hpp>
20 #include <boost/interprocess/managed_mapped_file.hpp>
21 #include <boost/interprocess/sync/named_mutex.hpp>
22 #include <boost/interprocess/sync/scoped_lock.hpp>
23 #include <boost/multi_index_container.hpp>
24 #include <boost/multi_index/ordered_index.hpp>
25 #include <boost/multi_index/member.hpp>
26 #include <iostream>
27 #include <iterator>
28 #include <sstream>
29 #include <string>
30 
31 using boost::multi_index_container;
32 using namespace boost::multi_index;
33 namespace bip=boost::interprocess;
34 
35 /* shared_string is a string type placeable in shared memory,
36  * courtesy of Boost.Interprocess.
37  */
38 
39 typedef bip::basic_string<
40   char,std::char_traits<char>,
41   bip::allocator<char,bip::managed_mapped_file::segment_manager>
42 > shared_string;
43 
44 /* Book record. All its members can be placed in shared memory,
45  * hence the structure itself can too.
46  */
47 
48 struct book
49 {
50   shared_string name;
51   shared_string author;
52   unsigned      pages;
53   unsigned      prize;
54 
bookbook55   book(const shared_string::allocator_type& al):
56     name(al),author(al),pages(0),prize(0)
57   {}
58 
operator <<(std::ostream & os,const book & b)59   friend std::ostream& operator<<(std::ostream& os,const book& b)
60   {
61     os<<b.author<<": \""<<b.name<<"\", $"<<b.prize<<", "<<b.pages<<" pages\n";
62     return os;
63   }
64 };
65 
66 /* partial_str_less allows for partial searches taking into account
67  * only the first n chars of the strings compared against. See
68  * Tutorial: Basics: Special lookup operations for more info on this
69  * type of comparison functors.
70  */
71 
72 /* partial_string is a mere string holder used to differentiate from
73  * a plain string.
74  */
75 
76 struct partial_string
77 {
partial_stringpartial_string78   partial_string(const shared_string& str):str(str){}
79   shared_string str;
80 };
81 
82 struct partial_str_less
83 {
operator ()partial_str_less84   bool operator()(const shared_string& x,const shared_string& y)const
85   {
86     return x<y;
87   }
88 
operator ()partial_str_less89   bool operator()(const shared_string& x,const partial_string& y)const
90   {
91     return x.substr(0,y.str.size())<y.str;
92   }
93 
operator ()partial_str_less94   bool operator()(const partial_string& x,const shared_string& y)const
95   {
96     return x.str<y.substr(0,x.str.size());
97   }
98 };
99 
100 /* Define a multi_index_container of book records with indices on
101  * author, name and prize. The index on names allows for partial
102  * searches. This container can be placed in shared memory because:
103  *   * book can be placed in shared memory.
104  *   * We are using a Boost.Interprocess specific allocator.
105  */
106 
107 /* see Compiler specifics: Use of member_offset for info on
108  * BOOST_MULTI_INDEX_MEMBER
109  */
110 
111 typedef multi_index_container<
112   book,
113   indexed_by<
114     ordered_non_unique<
115       BOOST_MULTI_INDEX_MEMBER(book,shared_string,author)
116     >,
117     ordered_non_unique<
118       BOOST_MULTI_INDEX_MEMBER(book,shared_string,name),
119       partial_str_less
120     >,
121     ordered_non_unique<
122       BOOST_MULTI_INDEX_MEMBER(book,unsigned,prize)
123     >
124   >,
125   bip::allocator<book,bip::managed_mapped_file::segment_manager>
126 > book_container;
127 
128 /* A small utility to get data entered via std::cin */
129 
130 template<typename T>
enter(const char * msg,T & t)131 void enter(const char* msg,T& t)
132 {
133   std::cout<<msg;
134   std::string str;
135   std::getline(std::cin,str);
136   std::istringstream iss(str);
137   iss>>t;
138 }
139 
enter(const char * msg,std::string & str)140 void enter(const char* msg,std::string& str)
141 {
142   std::cout<<msg;
143   std::getline(std::cin,str);
144 }
145 
enter(const char * msg,shared_string & str)146 void enter(const char* msg,shared_string& str)
147 {
148   std::cout<<msg;
149   std::string stdstr;
150   std::getline(std::cin,stdstr);
151   str=stdstr.c_str();
152 }
153 
main()154 int main()
155 {
156   /* Create (or open) the memory mapped file where the book container
157    * is stored, along with a mutex for synchronized access.
158    */
159 
160   bip::managed_mapped_file seg(
161     bip::open_or_create,"./book_container.db",
162     65536);
163   bip::named_mutex mutex(
164     bip::open_or_create,"7FD6D7E8-320B-11DC-82CF-F0B655D89593");
165 
166   /* create or open the book container in shared memory */
167 
168   book_container* pbc=seg.find_or_construct<book_container>("book container")(
169     book_container::ctor_args_list(),
170     book_container::allocator_type(seg.get_segment_manager()));
171 
172   std::string command_info=
173     "1. list books by author\n"
174     "2. list all books by prize\n"
175     "3. insert a book\n"
176     "4. delete a book\n"
177     "0. exit\n";
178 
179   std::cout<<command_info;
180 
181   /* main loop */
182 
183   for(bool exit=false;!exit;){
184     int command=-1;
185     enter("command: ",command);
186 
187     switch(command){
188       case 0:{ /* exit */
189         exit=true;
190         break;
191       }
192       case 1:{ /* list books by author */
193         std::string author;
194         enter("author (empty=all authors): ",author);
195 
196         /* operations with the container must be mutex protected */
197 
198         bip::scoped_lock<bip::named_mutex> lock(mutex);
199 
200         std::pair<book_container::iterator,book_container::iterator> rng;
201         if(author.empty()){
202           rng=std::make_pair(pbc->begin(),pbc->end());
203         }
204         else{
205           rng=pbc->equal_range(
206             shared_string(
207               author.c_str(),
208               shared_string::allocator_type(seg.get_segment_manager())));
209         }
210 
211         if(rng.first==rng.second){
212           std::cout<<"no entries\n";
213         }
214         else{
215           std::copy(
216             rng.first,rng.second,std::ostream_iterator<book>(std::cout));
217         }
218         break;
219       }
220       case 2:{ /* list all books by prize */
221         bip::scoped_lock<bip::named_mutex> lock(mutex);
222 
223         std::copy(
224           get<2>(*pbc).begin(),get<2>(*pbc).end(),
225           std::ostream_iterator<book>(std::cout));
226         break;
227       }
228       case 3:{ /* insert a book */
229         book b(shared_string::allocator_type(seg.get_segment_manager()));
230 
231         enter("author: ",b.author);
232         enter("name: "  ,b.name);
233         enter("prize: " ,b.prize);
234         enter("pages: " ,b.pages);
235 
236         std::cout<<"insert the following?\n"<<b<<"(y/n): ";
237         char yn='n';
238         enter("",yn);
239         if(yn=='y'||yn=='Y'){
240           bip::scoped_lock<bip::named_mutex> lock(mutex);
241           pbc->insert(b);
242         }
243 
244         break;
245       }
246       case 4:{ /* delete a book */
247         shared_string name(
248           shared_string::allocator_type(seg.get_segment_manager()));
249         enter(
250           "name of the book (you can enter\nonly the first few characters): ",
251           name);
252 
253         typedef nth_index<book_container,1>::type index_by_name;
254         index_by_name&          idx=get<1>(*pbc);
255         index_by_name::iterator it;
256         book b(shared_string::allocator_type(seg.get_segment_manager()));
257 
258         {
259           /* Look for a book whose title begins with name. Note that we
260            * are unlocking after doing the search so as to not leave the
261            * container blocked during user prompting. That is also why a
262            * local copy of the book is done.
263            */
264 
265           bip::scoped_lock<bip::named_mutex> lock(mutex);
266 
267           it=idx.find(partial_string(name));
268           if(it==idx.end()){
269             std::cout<<"no such book found\n";
270             break;
271           }
272           b=*it;
273         }
274 
275         std::cout<<"delete the following?\n"<<b<<"(y/n): ";
276         char yn='n';
277         enter("",yn);
278         if(yn=='y'||yn=='Y'){
279           bip::scoped_lock<bip::named_mutex> lock(mutex);
280           idx.erase(it);
281         }
282 
283         break;
284       }
285       default:{
286         std::cout<<"select one option:\n"<<command_info;
287         break;
288       }
289     }
290   }
291 
292   return 0;
293 }
294