OPAL (Object Oriented Parallel Accelerator Library) 2022.1
OPAL
DiscConfig.cpp
Go to the documentation of this file.
1// -*- C++ -*-
2/***************************************************************************
3 *
4 * The IPPL Framework
5 *
6 * This program was prepared by PSI.
7 * All rights in the program are reserved by PSI.
8 * Neither PSI nor the author(s)
9 * makes any warranty, express or implied, or assumes any liability or
10 * responsibility for the use of this software
11 *
12 * Visit www.amas.web.psi for more details
13 *
14 ***************************************************************************/
15
16// -*- C++ -*-
17/***************************************************************************
18 *
19 * The IPPL Framework
20 *
21 *
22 * Visit http://people.web.psi.ch/adelmann/ for more details
23 *
24 ***************************************************************************/
25
26// include files
27#include "Utility/DiscConfig.h"
28#include "Utility/IpplInfo.h"
29#include "Utility/PAssert.h"
30#include "Message/Communicate.h"
31#include "Message/Message.h"
32
33
34#include <algorithm>
35using namespace std;
36#include <cstring>
37#include <unistd.h>
38#include <cstdio>
39
40
42// a simple routine to take an input string and a list of token separators,
43// and return the number of tokens plus fill in a new array of strings
44// with the words. We had a nicer way to do this with a vector of strings,
45// but a #*#^@($ bug in KCC requires this workaround.
46int DiscConfig::dc_tokenize_string(const char *s, const char *tok,
47 string *&slist) {
48 // first determine how many words there are
49 int num = 0;
50 char *tempstring = new char[strlen(s) + 1];
51 strcpy(tempstring, s);
52 slist = 0;
53 char* tokenp = strtok(tempstring, tok);
54 while (tokenp) {
55 num++;
56 tokenp = strtok(0, tok);
57 }
58
59 // create an array of strings, and find the words and copy them into strings
60 // but only if we have any words at all
61 if (num > 0) {
62 slist = new string[num];
63 num = 0;
64 strcpy(tempstring, s);
65 tokenp = strtok(tempstring, tok);
66 while (tokenp) {
67 slist[num++] = tokenp;
68 tokenp = strtok(0, tok);
69 }
70 }
71
72 delete [] tempstring;
73 return num;
74}
75
76
78// Constructor: read in and parse the given config file. We must know
79// whether the configuration file is being used to read or write data,
80// and the base filename for the input/output file.
81DiscConfig::DiscConfig(const char *config, const char *BaseFile,
82 bool WritingFile)
83 : NumSMPs(0), FileSMPs(0), MySMP(0), ConfigOK(false) {
84
85 if (config != 0)
86 ConfigFile = config;
87
88 if (BaseFile == 0) {
89 ERRORMSG("Null base filename in DiscConfig constructor." << endl);
90 Ippl::abort("Exiting due to DiscConfig error.");
91 ConfigOK = false;
92 } else {
93 ConfigOK = parse_config(BaseFile, WritingFile);
94 }
95}
96
97
99// Destructor
101
102 // delete the SMP structures
103 vector<SMPData *>::iterator smpiter = SMPList.begin();
105 while (smpiter != smpend) {
106 if ((*smpiter)->BaseFileNum > 0)
107 delete [] (*smpiter)->BaseFileName;
108 delete (*smpiter++);
109 }
110
111 // delete the node structures
112 vector<NodeData *>::iterator nodeiter = NodeList.begin();
114 while (nodeiter != nodeend)
115 delete (*nodeiter++);
116}
117
118
120// compute how many physical nodes there are on the same SMP as the given
121// pnode. This returns the total number of nodes which are writing data
122// to this SMP.
123unsigned int DiscConfig::pNodesPerSMP(unsigned int node) const {
124 unsigned int nodesmp = NodeList[node]->SMPIndex;
125 if (getNumFiles(nodesmp) == 0)
126 return 0;
127
128 unsigned int numnodes = SMPList[nodesmp]->NodeList.size();
129 //unsigned int extrasmps = SMPList[nodesmp]->InformSMPList.size();
130 //for (unsigned int i=0; i < extrasmps; ++i)
131 // numnodes += SMPList[SMPList[nodesmp]->InformSMPList[i]]->NodeList.size();
132 return numnodes;
133}
134
135
137// take a string with configuration filename wildcards, and substitute
138// in the specific values.
139// The first argument is the original string with wildcards (listed below),
140// and the second is the machine name to use when substituting in the
141// machine name.
142//
143// Possible wildcards (can be upper or lower case, must start with $ and
144// have the name enclosed in () ):
145// $(*) ... use the machine name given in the second argument
146// $(n) ... our node number
147// $(env var name) ... environment variable, if not one of the above names
148//
149// Return a new string with the changes in place.
150string DiscConfig::replace_wildcards(const string& s,
151 const string& machine) {
152
153 // the return string
154 string retval;
155
156 // make sure we have non-null input
157 if (s.length() == 0 || machine.length() == 0)
158 return retval;
159
160 // copy of input string
161 string scpy(s);
162 char *sptrbase = (char*) scpy.c_str();
163
164 // skip leading "./" if necessary
165 if (s.length() > 2 && s[0] == '.' && s[1] == '/')
166 sptrbase += 2;
167
168 // start moving along the string until we get to a $
169 char *sptr = sptrbase;
170 while (*sptr != '\0') {
171 if (*sptr != '$') {
172 ++sptr;
173 } else {
174 // append previous text to return value
175 if (sptr != sptrbase) {
176 *sptr = '\0';
177 retval += sptrbase;
178 sptrbase = sptr + 1;
179 }
180 // find name of wildcard, enclosed by ()'s
181 ++sptr;
182 char *tok1 = sptr;
183 char *tok2 = sptr;
184 while (*tok1 != '(' && *tok1 != '\0') ++tok1;
185 while (*tok2 != ')' && *tok2 != '\0') ++tok2;
186 if (*tok1 == '\0' || *tok2 == '\0' || *tok2 <= *tok1) {
187 ERRORMSG("Unbalanced parenthesis in DiscConfig config file in line ");
188 ERRORMSG(s.c_str() << endl);
189 Ippl::abort("Exiting due to DiscConfig error.");
190 break;
191 }
192 // make string object with the wildcard name, and look for name.
193 // replace token with new name
194 *tok2 = '\0';
195 string token(tok1 + 1);
196 if (token == "*") {
197 token = machine;
198 } else if (token == "node" || token == "n" || token == "N") {
199 char buf[32];
200 sprintf(buf, "%d", Ippl::myNode());
201 token = buf;
202 } else {
203 // look for an env var with this name
204 char *env = getenv(token.c_str());
205 if (env != 0) {
206 token = env;
207 } else {
208 ERRORMSG("Unknown wildcard name '" << token.c_str()<<"' in line ");
209 ERRORMSG(s.c_str() << endl);
210 Ippl::abort("Exiting due to DiscConfig error.");
211 break;
212 }
213 }
214
215 // add this token to the return string, and move on past wildcard
216 retval += token;
217 sptr = sptrbase = tok2 + 1;
218 }
219 }
220
221 // append the final word to the return string
222 if (sptr != sptrbase)
223 retval += sptrbase;
224
225 // done with substitution; return the string
226 return retval;
227}
228
229
231// take the information about the directory and hostname for a given
232// SMP, and add it to the list of directories for that SMP. Make sure
233// the directory is not repeated. If it is, issue a warning and continue.
235 const string& s,
236 const string& machine,
237 bool WritingFile) {
238
239 // create a new smpd if necessary, and add it to the list
240 if (smpd == 0) {
241 smpd = new SMPData;
242 smpd->Box0Node = Ippl::getNodes();
243 smpd->HostName = machine;
244 smpd->BaseFileNum = 0;
245 smpd->BaseFileName = 0;
246 SMPMap.insert(vmap<string,SMPData *>::value_type(machine, smpd));
247 }
248
249 // if necessary, try to add a new directory. Buf if no directory
250 // was specified, we're done
251 if (s.length() == 0)
252 return;
253
254 // create a string with the wildcards replaced, etc.
255 string basename = replace_wildcards(s, machine);
256
257 // check to make sure it does already occur in the list of BaseFileName's
258 for (unsigned int sptr=0; sptr < smpd->BaseFileNum; ++sptr) {
259 if (strcmp(basename.c_str(), (smpd->BaseFileName[sptr]).c_str()) == 0) {
260 WARNMSG("DiscConfig: Duplicate configuration file entry '" << basename);
261 WARNMSG("' for host " << machine << " ... second one ignored." << endl);
262 return;
263 }
264 }
265
266 // check to make sure we're not trying to write to more than one output
267 // file on an SMP (multipple read files are OK, but we can only write
268 // to one file, since we cannot determine how to partition items among
269 // the files)
270 if (WritingFile && smpd->BaseFileNum > 0) {
271 WARNMSG("DiscConfig: Cannot write to more than one file per");
272 WARNMSG(" SMP. Only the first file listed for host '");
273 WARNMSG(smpd->HostName << "', " << smpd->BaseFileName[0]);
274 WARNMSG(", will be used." << endl);
275 return;
276 }
277
278 // if we're here, the entry is not duplicated, so add the name. We'll
279 // need to add the name, then regenerate our tokenized list
280 smpd->BaseFileNameStringList += " ";
281 smpd->BaseFileNameStringList += basename;
282 if (smpd->BaseFileNum > 0)
283 delete [] smpd->BaseFileName;
285 " ", smpd->BaseFileName);
286}
287
288
290// read in from configuration file - an ascii file of token pairs.
291// On each line, the first token is the hostname and the second
292// token is the directory where the file is to be placed on this
293// host.
294// This will construct the vector with SMP data and set how many SMP's
295// we expect to find. If this does not match later, an error will be
296// reported then.
297bool DiscConfig::parse_config(const char *BaseFile, bool WritingFile) {
298
299 const int bufferSize = 1024;
300 char bufferstore[bufferSize];
301 char *buffer;
302 FILE *inC;
303 string WildCard;
304 string ConfigItems;
305 string NodeNameItems;
306
307 // create a tag for use in sending info to/from other nodes
309
310 // save the number of nodes and which node we're on
311 NumSMPs = 0;
312 FileSMPs = 0;
313 MySMP = 0;
314
315 // initialize the list of node information
316 for (int i=0; i < Ippl::getNodes(); ++i)
317 NodeList.push_back(new NodeData);
318
319 // obtain the hostname and processor ID to send out
320 char name[1024];
321 if (gethostname(name, 1023) != 0) {
322 WARNMSG("DiscConfig: Could not get hostname. Using localhost." << endl);
323 strcpy(name, "localhost");
324 }
325 NodeNameItems = name;
326
327 // all other nodes send their hostname to node 0; node 0 gets the names,
328 // reads the config file, then broadcasts all the necessary info to all
329 // other nodes
330 if (Ippl::myNode() != 0) {
331 // other nodes send their node name to node 0
332 Message *msg = new Message;
333 ::putMessage(*msg,NodeNameItems);
334 Ippl::Comm->send(msg, 0, tag);
335
336 // receive back the config file and node name info
337 int node = 0;
338 msg = Ippl::Comm->receive_block(node, tag);
339 PAssert(msg);
340 PAssert_EQ(node, 0);
341 ::getMessage(*msg,ConfigItems);
342 ::getMessage(*msg,NodeNameItems);
343 delete msg;
344 } else {
345 // only node 0 reads config file - others get a broadcast message
346 // open the configuration file
347 if ((inC = fopen(ConfigFile.c_str(), "r")) != 0) {
348 // read in each line, and append it to end of broadcast string
349 while (fgets(bufferstore, bufferSize, inC) != 0) {
350 // skip leading spaces, and any comment lines starting with '#'
351 buffer = bufferstore;
352 while (*buffer == ' ' || *buffer == '\t' || *buffer == '\n')
353 ++buffer;
354 if (*buffer == '#' || *buffer == '\0')
355 continue;
356 ConfigItems += buffer;
357 ConfigItems += "\n";
358 }
359 fclose(inC);
360 }
361
362 // see if there was an error, or no config file was specified ...
363 // if so, use default
364 if (ConfigItems.length() == 0) {
365 ConfigItems = "* .";
366 ConfigItems += "\n";
367 }
368
369 // collect node names from everyone else, and then retransmit the collected
370 // list. The first name should be the node 0 name.
371 NodeNameItems += " 0";
372 int unreceived = Ippl::getNodes() - 1;
373 while (unreceived-- > 0) {
374 // get the hostname from the remote node, and append to a list
375 int node = COMM_ANY_NODE;
376 Message *msg = Ippl::Comm->receive_block(node, tag);
377 PAssert(msg);
378 string nodename;
379 ::getMessage(*msg,nodename);
380 sprintf(name, " %s %d", nodename.c_str(), node);
381 NodeNameItems += name;
382 delete msg;
383 }
384
385 // broadcast string to all other nodes
386 if (Ippl::getNodes() > 1) {
387 Message *msg = new Message;
388 ::putMessage(*msg,ConfigItems);
389 ::putMessage(*msg,NodeNameItems);
390 Ippl::Comm->broadcast_others(msg, tag);
391 }
392 }
393
394 // from the configuration string, break it up into single lines and parse.
395 // This sets up the SMP information list.
396 string *conflines;
397 int conflinenum = dc_tokenize_string(ConfigItems.c_str(), "\n", conflines);
398 for (int is=0; is < conflinenum; ++is) {
399
400 // tokenize string, and store values
401 string *tokens;
402 int ntok = dc_tokenize_string(conflines[is].c_str(), " \t,\n", tokens);
403 if (ntok != 2) {
404 ERRORMSG("Wrong number of parameters in DiscConfig config file ");
405 ERRORMSG("'" << ConfigFile << "' (" << ntok << " != 2)" << endl);
406 Ippl::abort("Exiting due to DiscConfig error.");
407 } else {
408 // append / to directory name if necessary, and also the base filename
409 if (tokens[1].c_str()[tokens[1].length() - 1] != '/')
410 tokens[1] += "/";
411 tokens[1] += BaseFile;
412 if (tokens[0] == "*") {
413 // save the wildcard string
414 WildCard = tokens[1];
415 } else {
416 // line was good ... store the values found there. If a line is
417 // repeated, we just replace the value.
418 SMPData *smpd = 0;
419 vmap<string,SMPData *>::iterator smpiter = SMPMap.find(tokens[0]);
420 if (smpiter != SMPMap.end())
421 smpd = (*smpiter).second;
422 add_SMP_directory(smpd, tokens[1], tokens[0], WritingFile);
423 }
424 }
425
426 // delete the tokens
427 if (tokens != 0)
428 delete [] tokens;
429 }
430
431 // delete the conf lines
432 if (conflines != 0)
433 delete [] conflines;
434
435 // make sure we found SOMETHING ...
436 if (SMPMap.size() < 1 && WildCard.length() == 0) {
437 ERRORMSG("No hostname/directory pairs found in DiscConfig config file ");
438 ERRORMSG("'" << ConfigFile << "' " << endl);
439 Ippl::abort("Exiting due to DiscConfig error.");
440 return false;
441 }
442
443 // set up the node information list
444 string *nodenames;
445 dc_tokenize_string(NodeNameItems.c_str(), " ", nodenames);
446 for (int in=0; in < Ippl::getNodes(); ++in) {
447 // get node number and node name from list of node information
448 int node = atoi(nodenames[2*in + 1].c_str());
449 string machine = nodenames[2*in];
450
451 // find the host name in our list of SMP's
452 SMPData *smpdata = 0;
453 vmap<string,SMPData *>::iterator smpiter = SMPMap.find(machine);
454 if (smpiter != SMPMap.end()) {
455 // this SMP has already been set up earlier
456 smpdata = (*smpiter).second;
457 } else {
458 // we must make a new info structure for this SMP, since it was
459 // not mentioned in the configuration file. The routine
460 // sets the value of smpdata to a newly allocated pointer
461 add_SMP_directory(smpdata, WildCard, machine, WritingFile);
462 }
463
464 // fill in the SMP info and node info
465 NodeList[node]->HostName = machine;
466 smpdata->NodeList.push_back(node);
467 }
468
469 // delete the node names
470 if (nodenames != 0)
471 delete [] nodenames;
472
473 // go through the SMP list, assign them numbers, and sort the node data
474 int firstSMPWithFiles = (-1);
476 for (smpa = SMPMap.begin() ; smpa != SMPMap.end(); ++smpa) {
477 // add this SMP info to our SMPList array (for fast access)
478 SMPData *smpdata = (*smpa).second;
479 smpdata->SMPIndex = NumSMPs++;
480 SMPList.push_back(smpdata);
481
482 // find out if this SMP is the first one we find with files. There
483 // must be at least one, since by this point we know the config
484 // file was not empty, or we used a wildcard.
485 if (firstSMPWithFiles < 0 && smpdata->BaseFileNum > 0)
486 firstSMPWithFiles = smpdata->SMPIndex;
487
488 // sort the list of nodes so that all nodes have them in the same order
489 // (this is needed in find_processors). But if an SMP has 0 nodes,
490 // it is an error since the configuration file lists an SMP on which
491 // we are not running.
492 if (!smpdata->NodeList.empty()) {
493 sort(smpdata->NodeList.begin(), smpdata->NodeList.end());
494 smpdata->Box0Node = smpdata->NodeList[0];
495 } else {
496 ERRORMSG("DiscConfig: The SMP '" << smpdata->HostName);
497 ERRORMSG("' was listed in the config file\n");
498 ERRORMSG("'" << ConfigFile << "' but you are not running on that SMP.");
499 ERRORMSG(endl);
500 Ippl::abort("Exiting due to DiscConfig error.");
501 }
502
503 // tell the proper nodes which SMP they're on
504 vector<int>::iterator nodea = smpdata->NodeList.begin();
505 for ( ; nodea != smpdata->NodeList.end(); ++nodea)
506 NodeList[*nodea]->SMPIndex = smpdata->SMPIndex;
507
508
509 // increment how many file sets we're dealing with
510 FileSMPs += smpdata->BaseFileNum;
511 }
512
513 // determine our parent SMP
514 MySMP = NodeList[Ippl::myNode()]->SMPIndex;
515
516 // determine Box0 nodes, and whether we need to make sure to send
517 // layout and other info to other SMP's
518 for (smpa = SMPMap.begin() ; smpa != SMPMap.end(); ++smpa) {
519 SMPData *smpdata = (*smpa).second;
520 if (smpdata->BaseFileNum == 0) {
521 // we'll need to send data to another SMP's Node 0
522 smpdata->Box0Node = SMPList[firstSMPWithFiles]->Box0Node;
523 SMPList[firstSMPWithFiles]->InformSMPList.push_back(smpdata->SMPIndex);
524 }
525 }
526
527 return true;
528}
529
530
532// print out debugging information for this DiscConfig
534 msg << "ConfigFile = " << getConfigFile() << endl;
535 msg << "Num Filesets = " << fileSMPs() << endl;
536 msg << "NumSMPs = " << numSMPs() << endl;
537 msg << "MySMP = " << mySMP() << " (" << getSMPHost(mySMP()) << ")" << endl;
538 msg << "MyBox0 = " << getSMPBox0() << endl;
539 msg << "MyNode = " << Ippl::myNode() << endl;
540
541 // print out summary of SMP info
542 msg << "DiscConfig SMP Summary:" << endl;
543 for (unsigned int smp=0; smp < numSMPs(); ++smp) {
544 msg << " SMP host=" << getSMPHost(smp);
545 msg << ", numnodes=" << getNumSMPNodes(smp);
546 msg << ", box0=" << getSMPBox0(smp) << endl;
547 msg << " FileList =";
548 for (unsigned int fl=0; fl < getNumFiles(smp); ++fl)
549 msg << " " << getFilename(smp, fl);
550 msg << endl;
551 msg << " OtherSMPList =";
552 for (unsigned int sl=0; sl < getNumOtherSMP(smp); ++sl)
553 msg << " " << getOtherSMP(smp, sl);
554 msg << endl;
555 msg << " NodeList =";
556 for (unsigned int nl=0; nl < getNumSMPNodes(smp); ++nl)
557 msg << " " << getSMPNode(smp,nl);
558 msg << endl;
559 }
560
561 // print out summary of node info
562 msg << "DiscConfig Node Summary:" << endl;
563 for (unsigned int n=0; n < getNumNodes(); ++n) {
564 msg << " Node " << n << " on SMP " << getNodeSMPIndex(n);
565 msg << " (" << getNodeHost(n) << ")" << endl;
566 }
567}
568
569
570/***************************************************************************
571 * $RCSfile: DiscConfig.cpp,v $ $Author: adelmann $
572 * $Revision: 1.1.1.1 $ $Date: 2003/01/23 07:40:33 $
573 * IPPL_VERSION_ID: $Id: DiscConfig.cpp,v 1.1.1.1 2003/01/23 07:40:33 adelmann Exp $
574 ***************************************************************************/
void putMessage(Message &m, const T &t)
Definition: Message.h:549
void getMessage(Message &m, T &t)
Definition: Message.h:572
const int COMM_ANY_NODE
Definition: Communicate.h:40
#define DF_TAG_CYCLE
Definition: Tags.h:74
#define DF_MAKE_HOST_MAP_TAG
Definition: Tags.h:68
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
#define PAssert_EQ(a, b)
Definition: PAssert.h:104
#define PAssert(c)
Definition: PAssert.h:102
#define ERRORMSG(msg)
Definition: IpplInfo.h:350
#define WARNMSG(msg)
Definition: IpplInfo.h:349
const std::string name
std::string::iterator iterator
Definition: MSLang.h:16
bool send(Message *, int node, int tag, bool delmsg=true)
virtual int broadcast_others(Message *, int, bool delmsg=true)
Message * receive_block(int &node, int &tag)
int next_tag(int t, int s=1000)
Definition: TagMaker.h:39
unsigned int getNodeSMPIndex(unsigned int n) const
Definition: DiscConfig.h:157
unsigned int getOtherSMP(unsigned int sn) const
Definition: DiscConfig.h:139
std::vector< SMPData * > SMPList
Definition: DiscConfig.h:221
std::string replace_wildcards(const std::string &s, const std::string &machine)
Definition: DiscConfig.cpp:150
vmap< std::string, SMPData * > SMPMap
Definition: DiscConfig.h:220
const std::string & getConfigFile() const
Definition: DiscConfig.h:76
unsigned int getSMPBox0() const
Definition: DiscConfig.h:113
unsigned int getNumNodes() const
Definition: DiscConfig.h:154
unsigned int fileSMPs() const
Definition: DiscConfig.h:81
unsigned int getNumOtherSMP() const
Definition: DiscConfig.h:133
unsigned int numSMPs() const
Definition: DiscConfig.h:80
std::vector< NodeData * > NodeList
Definition: DiscConfig.h:222
unsigned int getSMPNode(unsigned int n) const
Definition: DiscConfig.h:107
const std::string & getSMPHost() const
Definition: DiscConfig.h:95
unsigned int getNumSMPNodes() const
Definition: DiscConfig.h:101
const std::string & getFilename(unsigned int fn) const
Definition: DiscConfig.h:125
unsigned int MySMP
Definition: DiscConfig.h:211
unsigned int FileSMPs
Definition: DiscConfig.h:210
unsigned int mySMP() const
Definition: DiscConfig.h:82
bool ConfigOK
Definition: DiscConfig.h:215
std::string ConfigFile
Definition: DiscConfig.h:205
void add_SMP_directory(SMPData *&, const std::string &s, const std::string &m, bool)
Definition: DiscConfig.cpp:234
void printDebug(Inform &)
Definition: DiscConfig.cpp:533
const std::string & getNodeHost(unsigned int n) const
Definition: DiscConfig.h:162
DiscConfig(const char *, const char *, bool)
Definition: DiscConfig.cpp:81
static int dc_tokenize_string(const char *s, const char *tok, std::string *&)
Definition: DiscConfig.cpp:46
unsigned int getNumFiles() const
Definition: DiscConfig.h:119
bool parse_config(const char *, bool)
Definition: DiscConfig.cpp:297
unsigned int NumSMPs
Definition: DiscConfig.h:209
unsigned int pNodesPerSMP(unsigned int node) const
Definition: DiscConfig.cpp:123
std::string BaseFileNameStringList
Definition: DiscConfig.h:190
std::string * BaseFileName
Definition: DiscConfig.h:189
unsigned int SMPIndex
Definition: DiscConfig.h:194
std::string HostName
Definition: DiscConfig.h:188
std::vector< int > NodeList
Definition: DiscConfig.h:191
unsigned int BaseFileNum
Definition: DiscConfig.h:195
unsigned int Box0Node
Definition: DiscConfig.h:193
Definition: Inform.h:42
static void abort(const char *=0)
Definition: IpplInfo.cpp:616
static int getNodes()
Definition: IpplInfo.cpp:670
static int myNode()
Definition: IpplInfo.cpp:691
static Communicate * Comm
Definition: IpplInfo.h:84
rep_type::iterator iterator
Definition: vmap.h:98
std::pair< Key, T > value_type
Definition: vmap.h:64