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