libcyberradio 22.01.24
App.cpp
1/***************************************************************************
2 * \file App.h
3 *
4 * \brief Defines basic application-level constructs.
5 *
6 * \author DA
7 * \copyright Copyright (c) 2015-2021 CyberRadio Solutions, Inc.
8 *
9 */
10
11#include "LibCyberRadio/Common/App.h"
12#include "LibCyberRadio/Common/Pythonesque.h"
13#include <iostream>
14#include <sstream>
15#include <iomanip>
16#include <algorithm>
17#include <stdlib.h>
18#include <getopt.h>
19
20
21namespace LibCyberRadio
22{
23
25 // AppOption
27
36
38 {
40 valuePtr = (void*)0;
41 showDefault = false;
42 }
43
44 AppOption::AppOption(const std::string& shortName, const std::string& longName,
45 int valueType, void *valuePtr,
46 const std::string& helpArgName, const std::string& helpText,
47 bool showDefault)
48 {
49 this->shortName = shortName;
50 this->longName = longName;
51 this->valueType = valueType;
52 this->valuePtr = valuePtr;
53 this->helpArgName = helpArgName;
54 this->helpText = helpText;
55 this->showDefault = showDefault;
56 }
57
59 {
60 this->shortName = opt.shortName;
61 this->longName = opt.longName;
62 this->valueType = opt.valueType;
63 this->valuePtr = opt.valuePtr;
64 this->helpArgName = opt.helpArgName;
65 this->helpText = opt.helpText;
66 this->showDefault = opt.showDefault;
67 }
68
70 {
71 if ( this != &opt )
72 {
73 this->shortName = opt.shortName;
74 this->longName = opt.longName;
75 this->valueType = opt.valueType;
76 this->valuePtr = opt.valuePtr;
77 this->helpArgName = opt.helpArgName;
78 this->helpText = opt.helpText;
79 this->showDefault = opt.showDefault;
80 }
81 return *this;
82 }
83
87
88
89
91 // AppHelpTextFormatter
93
95 {
96 _maxOptionWidth = 5;
97 _displayWidth = displayWidth;
98 }
99
103
104 void AppHelpTextFormatter::addPreOptionText(const std::string& text)
105 {
106 if ( text != "" )
107 _preOptionText.push_back(text);
108 }
109
110 void AppHelpTextFormatter::addOptionText(const std::string& option,
111 const std::string& text)
112 {
113 if ( option != "" )
114 {
115 _options.push_back(option);
116 _optionText.push_back(text);
117 if ( (int)option.length() > _maxOptionWidth )
118 _maxOptionWidth = option.length();
119 }
120 }
121
122 void AppHelpTextFormatter::addPostOptionText(const std::string& text)
123 {
124 if ( text != "" )
125 _postOptionText.push_back(text);
126 }
127
129 {
130 std::ostringstream oss, ossTmp;
131 // Format pre-option text
132 for (BasicStringListIterator it = _preOptionText.begin(); it != _preOptionText.end();
133 it++)
134 {
135 oss << wordWrappedText(*it, _displayWidth, 0);
136 }
137 // Format option text
138 for (BasicStringListIterator ito = _options.begin(), itt = _optionText.begin();
139 ito != _options.end();
140 ito++, itt++)
141 {
142 ossTmp.str("");
143 ossTmp.clear();
144 ossTmp << std::left << std::setw(_maxOptionWidth) << *ito
145 << std::setw(0) << " "
146 << std::setw(0) << *itt;
147 oss << wordWrappedText(ossTmp.str(), _displayWidth, _maxOptionWidth + 2);
148 }
149 // Format post-option text
150 for (BasicStringListIterator it = _postOptionText.begin(); it != _postOptionText.end();
151 it++)
152 {
153 oss << wordWrappedText(*it, _displayWidth, 0);
154 }
155 return oss.str();
156 }
157
158 std::string AppHelpTextFormatter::wordWrappedText(const std::string& text, int width,
159 int hangingIndent)
160 {
161 std::string ret;
162 std::string hangingIndentText(hangingIndent, ' ');
163 BasicStringList textLines = Pythonesque::Split(text, "\n");
164 for (BasicStringListIterator itl = textLines.begin(); itl != textLines.end(); itl++)
165 {
166 if ( itl == textLines.begin() )
167 {
168 std::string::size_type curPos = 0;
169 int curLine = 0;
170 BasicStringList words = Pythonesque::Split(*itl, " ");
171 for (BasicStringListIterator itw = words.begin(); itw != words.end(); itw++)
172 {
173 // See if printing the next word will go beyond the allowed width
174 if ( curPos + 1 + itw->length() > (std::string::size_type)width )
175 {
176 ret.append("\n");
177 curLine++;
178 // apply hanging indent if needed
179 if ( curLine != 0 )
180 {
181 ret.append(hangingIndentText);
182 curPos = hangingIndentText.length();
183 }
184 // then print the word
185 ret.append(*itw);
186 curPos += itw->length();
187 }
188 else
189 {
190 // Print a space then print the word
191 if (curPos != 0)
192 ret.append(" ");
193 ret.append(*itw);
194 curPos += itw->length() + 1;
195 }
196 }
197 ret.append("\n");
198 }
199 else
200 {
201 ret.append( wordWrappedText(*itl, width, hangingIndent) );
202 }
203 }
204 if ( textLines.size() == 0 )
205 ret.append("\n");
206 return ret;
207 }
208
209
211 // AppOptionParser
213
215 {
216 _allowUnknownOption = false;
217 _displayWidth = 75;
218 // Configure options that we support automatically:
219 // --help
220 addOption("", "help", AppOption::TYPE_NONE, (void*)0, "",
221 "Print this help text", false);
222 // --version
223 addOption("", "version", AppOption::TYPE_NONE, (void*)0, "",
224 "Print version information and exit", false);
225 // Dispatch map for option handler functions. Each option type
226 // (AppOption::TYPE_* constant) should have a corresponding handler
227 // function.
228 // To add a new option type:
229 // (1) Define a member function with this signature to handle that
230 // option type:
231 // int handler(int optindex, const std::string& optarg);
232 // (2) Add the new handler the dispatch map below.
233 _optionHelpers[AppOption::TYPE_NONE] = &AppOptionParser::handleOptionNone;
234 _optionHelpers[AppOption::TYPE_INTEGER] = &AppOptionParser::handleOptionInt;
235 _optionHelpers[AppOption::TYPE_FLOAT] = &AppOptionParser::handleOptionFloat;
236 _optionHelpers[AppOption::TYPE_DOUBLE] = &AppOptionParser::handleOptionDouble;
237 _optionHelpers[AppOption::TYPE_STRING] = &AppOptionParser::handleOptionString;
238 _optionHelpers[AppOption::TYPE_BOOLEAN] = &AppOptionParser::handleOptionBool;
239 _optionHelpers[AppOption::TYPE_BOOLEAN_SET_TRUE] = &AppOptionParser::handleOptionBooleanSetTrue;
240 _optionHelpers[AppOption::TYPE_BOOLEAN_SET_FALSE] = &AppOptionParser::handleOptionBooleanSetFalse;
241 }
242
246
248 {
249 _optionList.push_back( opt );
250 }
251
252 void AppOptionParser::addOption(const std::string& shortName,
253 const std::string& longName,
254 int valueType, void *valuePtr,
255 const std::string& helpArgName, const std::string& helpText,
256 bool showDefault)
257 {
258 addOption( AppOption(shortName, longName, valueType, valuePtr,
259 helpArgName, helpText, showDefault) );
260 }
261
263 {
264 _allowUnknownOption = allow;
265 }
266
267 void AppOptionParser::setDescription(const std::string& description)
268 {
269 _description = description;
270 }
271
272 void AppOptionParser::setDisplayWidth(int displayWidth)
273 {
274 _displayWidth = displayWidth;
275 }
276
277 void AppOptionParser::setEpilogText(const std::string& epilogText)
278 {
279 _epilogText = epilogText;
280 }
281
282 void AppOptionParser::setExecutable(const std::string& executable)
283 {
284 _executable = executable;
285 }
286
287 void AppOptionParser::setUnparsedArgText(const std::string& unparsedArgText)
288 {
289 _unparsedArgText = unparsedArgText;
290 }
291
292 void AppOptionParser::setVersion(const std::string& version)
293 {
294 _version = version;
295 }
296
297 int AppOptionParser::parse(int argc, char** argv)
298 {
299 int ret = 0;
300 // Form short argument list and long option structure list
301 std::string shargs;
302 struct option longOptions[_optionList.size() + 1];
303 for (int i = 0; i < (int)_optionList.size(); i++)
304 {
305 // If a long option has a corresponding short option,
306 // set the return value to the short option character.
307 if ( _optionList[i].shortName.length() == 1 )
308 {
309 shargs.append(_optionList[i].shortName);
310 if ( optionValueArg(_optionList[i].valueType) )
311 shargs.append(":");
312 longOptions[i].val = (int)_optionList[i].shortName[0];
313 }
314 // Otherwise, encode the index in the return value.
315 else
316 {
317 longOptions[i].val = 32768+i;
318 }
319 longOptions[i].name = _optionList[i].longName.c_str();
320 longOptions[i].has_arg = optionValueArg(_optionList[i].valueType);
321 longOptions[i].flag = (int*)0;
322 }
323 // Terminate long option structure list with an empty element
324 longOptions[_optionList.size()].name = (char*)0;
325 longOptions[_optionList.size()].has_arg = 0;
326 longOptions[_optionList.size()].flag = (int*)0;
327 longOptions[_optionList.size()].val = 0;
328#ifdef DEBUG
329 std::cerr << "[DBG] Short option list: " << shargs << std::endl;
330#endif
331
332 // Parse options
333 int opt = 0;
334 int optindex = 0;
335 while (1)
336 {
337 opt = getopt_long(/* int */ argc, /* char * const argv[] */ argv,
338 /* const char *optstring */ shargs.c_str(),
339 /* const struct option *longopts */ (const struct option *)longOptions,
340 /* int *longindex */ &optindex);
341 if (opt == -1)
342 break;
343#ifdef DEBUG
344 std::cerr << "Option parsed: " << opt << " (" << (char)opt << ")" << std::endl;
345#endif
346
347 switch(opt)
348 {
349 case '?':
350 // Option is unknown.
351 if ( !_allowUnknownOption )
352 ret = 2;
353 break;
354 default:
355 // Option is known but we haven't handled it yet.
356 ret = handleOptionReturn(opt, optarg ? optarg : "");
357 if ( ret == 1 )
358 std::cerr << "ERROR: Improperly formatted argument!" << std::endl;
359 break;
360 }
361 if ( ret != 0 )
362 break;
363 }
364 // Collect unparsed arguments. Use of unparsed arguments is application-specific.
365 if ( (ret == 0) && (optind < argc) )
366 {
367 while (optind < argc)
368 unparsedArgs.push_back( argv[optind++] );
369 }
370
371 return ret;
372 }
373
374 int AppOptionParser::handleOptionReturn(int opt, const std::string& optarg)
375 {
376 int ret = 0;
377 // We need to find the option index that corresponds to our option
378 // return value.
379 int our_optindex = -1;
380 // -- Short options, and long options w/ short options, have return
381 // values < 32768; return value = short option value
382 if ( opt < 32768 )
383 {
384 // Find the option where return value = short option value
385 for (int i = 0; i < (int)_optionList.size(); i++)
386 {
387 if ( (_optionList[i].shortName.length() == 1) &&
388 (_optionList[i].shortName[0] == (char)opt) )
389 {
390 our_optindex = i;
391 break;
392 }
393 }
394 }
395 // -- Long options w/o short options have return values >= 32768; option
396 // index = return value - 32768
397 else
398 {
399 our_optindex = opt - 32768;
400 }
401 // Dispatch option handler if we can
402 if ( our_optindex != -1 )
403 {
404 if ( _optionList[our_optindex].longName == "version" ) // Version information request
405 {
406 std::cerr << _description << ", Version " << _version << std::endl;
407 ret = 3;
408 }
409 else if ( _optionList[our_optindex].longName == "help" ) // Help requested
410 {
411 printUsage();
412 ret = 2;
413 }
414 else if ( _optionHelpers.count(_optionList[our_optindex].valueType) > 0 )
415 {
416 ret = (this->*_optionHelpers[_optionList[our_optindex].valueType])(our_optindex, optarg);
417 }
418 else
419 ret = 1;
420 }
421 else
422 ret = 1;
423 return ret;
424 }
425
426 void AppOptionParser::printUsage()
427 {
428 std::ostringstream ossPre, ossPost;
429 AppHelpTextFormatter fmtr(_displayWidth);
430 ossPre << _description << ", Version " << _version << "\n\n" << "Usage:\n" << _executable;
431 if ( _optionList.size() > 0 )
432 ossPre << " [options]";
433 if ( _unparsedArgText.length() > 0 )
434 ossPre << " " << _unparsedArgText;
435 ossPre << "\n\n";
436 if ( _optionList.size() > 0 )
437 {
438 ossPre << "Options:\n\n";
439 std::string opt, optText;
440 for (int i = 0; i < (int)_optionList.size(); i++)
441 {
442 opt = "";
443 if ( _optionList[i].shortName.length() == 1 )
444 {
445 opt.append("-");
446 opt.append(_optionList[i].shortName);
447 if ( optionValueArg(_optionList[i].valueType) > no_argument )
448 {
449 opt.append(" ");
450 opt.append(_optionList[i].helpArgName);
451 }
452 }
453 if ( _optionList[i].longName.length() > 0 )
454 {
455 if ( opt != "" )
456 opt.append(", ");
457 opt.append("--");
458 opt.append(_optionList[i].longName);
459 if ( optionValueArg(_optionList[i].valueType) > no_argument )
460 {
461 opt.append("=");
462 opt.append(_optionList[i].helpArgName);
463 }
464 }
465 // std::cerr << std::left << std::setw(25) << opt
466 // << std::setw(0) << " "
467 // << std::setw(0) << _optionList[i].help_text
468 // << "\n";
469 optText = _optionList[i].helpText;
470 optText.append( getDefault(_optionList[i]) );
471 fmtr.addOptionText(opt, optText);
472 }
473 }
474 if ( _epilogText.length() > 0 )
475 ossPost << "\n" << _epilogText << "\n";
476 fmtr.addPreOptionText(ossPre.str());
477 fmtr.addPostOptionText(ossPost.str());
478 //std::cerr << "[DBG] Formatter loaded" << std::endl;
479 std::cerr << fmtr.getFormattedText() << std::endl;
480 }
481
482 int AppOptionParser::optionValueArg(int valueType)
483 {
484 int ret = no_argument;
485 if ( (valueType >= AppOption::TYPE_INTEGER) &&
486 (valueType <= AppOption::TYPE_BOOLEAN) )
487 ret = required_argument;
488 return ret;
489 }
490
491 std::string AppOptionParser::getDefault(const AppOption& opt)
492 {
493 std::ostringstream oss;
494 int *iptr;
495 float *fptr;
496 double *dptr;
497 bool *bptr;
498 std::string *sptr;
499 if ( opt.showDefault && (opt.valuePtr != (void*)0) )
500 {
501 oss << " Default: ";
502 if ( opt.valueType == AppOption::TYPE_INTEGER )
503 {
504 iptr = (int *)opt.valuePtr;
505 oss << *iptr << ".";
506 }
507 else if ( opt.valueType == AppOption::TYPE_FLOAT )
508 {
509 fptr = (float *)opt.valuePtr;
510 oss << *fptr << ".";
511 }
512 else if ( opt.valueType == AppOption::TYPE_DOUBLE )
513 {
514 dptr = (double *)opt.valuePtr;
515 oss << *dptr << ".";
516 }
517 else if ( opt.valueType == AppOption::TYPE_STRING )
518 {
519 sptr = (std::string *)opt.valuePtr;
520 oss << *sptr << ".";
521 }
522 else if ( (opt.valueType >= AppOption::TYPE_BOOLEAN) &&
523 (opt.valueType <= AppOption::TYPE_BOOLEAN_SET_FALSE) )
524 {
525 bptr = (bool *)opt.valuePtr;
526 oss << (*bptr ? "True" : "False") << ".";
527 }
528 else
529 {
530 oss << "None.";
531 }
532 }
533 return oss.str();
534 }
535
536 int AppOptionParser::handleOptionNone(int optindex, const std::string& optarg)
537 {
538 return 0;
539 }
540
541 int AppOptionParser::handleOptionInt(int optindex, const std::string& optarg)
542 {
543 int ret = 0;
544 if ( _optionList[optindex].valuePtr != (void*)0 )
545 {
546 char *endptr;
547 int *vptr = (int *)_optionList[optindex].valuePtr;
548 int tmp = (int)strtol(optarg.c_str(), &endptr, 10);
549 if ( *endptr == '\0' )
550 *vptr = tmp;
551 else
552 ret = 1;
553 }
554 return ret;
555 }
556
557 int AppOptionParser::handleOptionFloat(int optindex, const std::string& optarg)
558 {
559 int ret = 0;
560 if ( _optionList[optindex].valuePtr != (void*)0 )
561 {
562 char *endptr;
563 float *vptr = (float *)_optionList[optindex].valuePtr;
564 float tmp = (float)strtod(optarg.c_str(), &endptr);
565 if ( *endptr == '\0' )
566 *vptr = tmp;
567 else
568 ret = 1;
569 }
570 return ret;
571 }
572
573 int AppOptionParser::handleOptionDouble(int optindex, const std::string& optarg)
574 {
575 int ret = 0;
576 if ( _optionList[optindex].valuePtr != (void*)0 )
577 {
578 char *endptr;
579 double *vptr = (double *)_optionList[optindex].valuePtr;
580 double tmp = strtod(optarg.c_str(), &endptr);
581 if ( *endptr == '\0' )
582 *vptr = tmp;
583 else
584 ret = 1;
585 }
586 return ret;
587 }
588
589 int AppOptionParser::handleOptionString(int optindex, const std::string& optarg)
590 {
591 int ret = 0;
592 if ( _optionList[optindex].valuePtr != (void*)0 )
593 {
594 std::string *vptr = (std::string *)_optionList[optindex].valuePtr;
595 *vptr = optarg;
596 }
597 return ret;
598 }
599
600 int AppOptionParser::handleOptionBool(int optindex, const std::string& optarg)
601 {
602 int ret = 0;
603 if ( _optionList[optindex].valuePtr != (void*)0 )
604 {
605 std::string val = optarg;
606 bool *vptr = (bool *)_optionList[optindex].valuePtr;
607 std::transform(val.begin(), val.end(), val.begin(), ::tolower);
608 if ( (val == "true") || (val == "yes") || (val == "on") || (val == "1") )
609 *vptr = true;
610 else if ( (val == "false") || (val == "no") || (val == "off") || (val == "0") )
611 *vptr = false;
612 else
613 ret = 1;
614 }
615 return ret;
616 }
617
618 int AppOptionParser::handleOptionBooleanSetTrue(int optindex, const std::string& optarg)
619 {
620 int ret = 0;
621 if ( _optionList[optindex].valuePtr != (void*)0 )
622 {
623 bool *vptr = (bool *)_optionList[optindex].valuePtr;
624 *vptr = true;
625 }
626 return ret;
627 }
628
629 int AppOptionParser::handleOptionBooleanSetFalse(int optindex, const std::string& optarg)
630 {
631 int ret = 0;
632 if ( _optionList[optindex].valuePtr != (void*)0 )
633 {
634 bool *vptr = (bool *)_optionList[optindex].valuePtr;
635 *vptr = false;
636 }
637 return ret;
638 }
639
640
641
643 // App
645
647 {
648 description = "Generic Application";
649 version = "0.0.1";
650 }
651
652 int App::run(int argc, char *argv[])
653 {
654 defineOptions(argv[0]);
655 int ret = parseCommandLine(argc, argv);
656 if ( ret == 0 )
657 ret = mainLoop();
658 return ret;
659 }
660
661 void App::defineOptions(const char *argv0)
662 {
663 // Base-class implementation configures basic app info and usage info
664 _optParser.setDescription(description);
665 _optParser.setVersion(version);
666 _optParser.setExecutable(argv0);
667 _optParser.setUnparsedArgText("");
668 _optParser.setEpilogText("");
669 // Derived classes should call base-class version, then extend/override with their
670 // own supported options.
671 }
672
674 {
675 // Base-class implementation does nothing; derived classes should override
676 // with their own implementations.
677 int ret = 0;
678 return ret;
679 }
680
681 int App::parseCommandLine(int argc, char* argv[])
682 {
683 return _optParser.parse(argc, argv);
684 }
685
686}
virtual void addPostOptionText(const std::string &text)
Adds text that gets displayed after the list of options.
Definition App.cpp:122
AppHelpTextFormatter(int displayWidth=75)
Constructs an AppHelpTextFormatter object.
Definition App.cpp:94
virtual ~AppHelpTextFormatter()
Destroys an AppHelpTextFormatter object.
Definition App.cpp:100
virtual void addPreOptionText(const std::string &text)
Adds text that gets displayed before the list of options.
Definition App.cpp:104
virtual void addOptionText(const std::string &option, const std::string &text)
Adds an option and its help text to the list of options.
Definition App.cpp:110
virtual std::string getFormattedText()
Gets the formatted help text.
Definition App.cpp:128
void setEpilogText(const std::string &epilogText)
Sets the epilog portion of the usage information; that is, the part that comes after the list of supp...
Definition App.cpp:277
void allowUnknownOption(bool allow=true)
Determines whether or not the parser will allow unknown options to pass through the parser without ge...
Definition App.cpp:262
void setExecutable(const std::string &executable)
Sets the portion of the usage information where the executable name is displayed.
Definition App.cpp:282
void setDescription(const std::string &description)
Sets the portion of the usage information where the application description is displayed.
Definition App.cpp:267
virtual void addOption(const AppOption &opt)
Adds an allowed option to the parser.
Definition App.cpp:247
AppOptionParser()
Constructs an AppOptionParser object.
Definition App.cpp:214
virtual ~AppOptionParser()
Destroys an AppOptionParser object.
Definition App.cpp:243
BasicStringList unparsedArgs
The collection of arguments that were not dealt with by the parser during option parsing.
Definition App.h:407
void setDisplayWidth(int displayWidth=75)
Sets the display width of the usage information.
Definition App.cpp:272
void setUnparsedArgText(const std::string &unparsedArgText)
Sets the portion of the usage information that represents the collection of unparsed arguments.
Definition App.cpp:287
void setVersion(const std::string &version)
Sets the portion of the usage information that represents the application version.
Definition App.cpp:292
virtual int parse(int argc, char **argv)
Parses the command-line options.
Definition App.cpp:297
Defines a command-line option that is supported by an application.
Definition App.h:29
std::string shortName
Short option name, without leading hyphen.
Definition App.h:86
bool showDefault
Show the default value in the option help text.
Definition App.h:113
static int TYPE_NONE
Option does not set a value.
Definition App.h:119
static int TYPE_DOUBLE
Option sets a double-precision floating-point value.
Definition App.h:131
std::string longName
Long option name, without leading hyphens.
Definition App.h:91
virtual ~AppOption()
Destroys an AppOption object.
Definition App.cpp:84
static int TYPE_INTEGER
Option sets an integer value.
Definition App.h:123
static int TYPE_BOOLEAN_SET_TRUE
Option sets a boolean value that is False by default but becomes True if this option is specified.
Definition App.h:144
void * valuePtr
Pointer to a variable that holds the option value.
Definition App.h:100
int valueType
Type of value that this option sets.
Definition App.h:96
virtual AppOption & operator=(const AppOption &opt)
Makes one AppOption object equivalent to another.
Definition App.cpp:69
static int TYPE_BOOLEAN_SET_FALSE
Option sets a boolean value that is True by default but becomes False if this option is specified.
Definition App.h:149
static int TYPE_BOOLEAN
Option explicitly sets a boolean value.
Definition App.h:139
AppOption()
Constructs an empty AppOption object.
Definition App.cpp:37
static int TYPE_FLOAT
Option sets a floating-point value.
Definition App.h:127
static int TYPE_STRING
Option sets a string value.
Definition App.h:135
std::string helpArgName
Option argument name to display in the help text.
Definition App.h:104
std::string helpText
Option help text.
Definition App.h:108
std::string description
The application description.
Definition App.h:492
virtual int mainLoop()
Defines the application's main processing loop.
Definition App.cpp:673
virtual int run(int argc, char *argv[])
Runs the application.
Definition App.cpp:652
std::string version
The application version.
Definition App.h:496
virtual void defineOptions(const char *argv0)
Defines which command-line options are supported by the application, as well as any help text that is...
Definition App.cpp:661
App()
Constructs an App object.
Definition App.cpp:646
static BasicStringList Split(const std::string &str, const std::string &sep, int maxsplit=INT_MAX)
Splits the given string into a list of string tokens.
Defines functionality for LibCyberRadio applications.
Definition App.h:24
BASIC_LIST_CONTAINER< std::string > BasicStringList
Type representing a list of strings.
Definition BasicList.h:25