ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
commandline.hpp
Go to the documentation of this file.
1/** ************************************************************************************************
2 * \file
3 * This header file is part of module \alib_cli of the \aliblong.
4 *
5 * \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6 * Published under \ref mainpage_license "Boost Software License".
7 **************************************************************************************************/
8#ifndef HPP_ALIB_CLI_COMMANDLINE
9#define HPP_ALIB_CLI_COMMANDLINE 1
10
11#if !defined (HPP_ALIB_CLI_ARGUMENTS)
12 #include "alib/cli/arguments.hpp"
13#endif
14
15#if !defined(HPP_ALIB_COMPATIBILITY_STD_BOXING_FUNCTIONAL)
17#endif
18
19#if !defined (HPP_ALIB_MONOMEM_HASHMAP)
21#endif
22
23#if !defined (HPP_ALIB_MONOMEM_STDCONTAINERMA)
25#endif
26
27
28namespace alib { namespace cli {
29
30class CLIUtil;
31
32
33/** ************************************************************************************************
34 * This class provides a foundation for software executables that processes command line
35 * parameters.
36 *
37 * \see
38 * "Utility" methods which could have been implemented as an interface of this
39 * class have instead been located as static methods in friend class \alib{cli;CLIUtil} which
40 * receive a pointer to an instance of this type.
41 *
42 * ## Friends ##
43 * class \alib{cli;CLIUtil}
44 *
45\I{################################################################################################}
46 * @throws alib::cli::Exceptions
47 **************************************************************************************************/
49{
50 // #############################################################################################
51 // Type definitions
52 // #############################################################################################
53 #if !defined(ALIB_DOX)
54 // This friend provides utility methods for using this class.
55 friend class CommandDecl;
56 friend struct Command;
57 friend struct Option;
58 friend struct Parameter;
59
60 friend class CLIUtil;
61 #endif
62
63 // #############################################################################################
64 // Type definitions
65 // #############################################################################################
66 public:
67
68 // #############################################################################################
69 // protected fields
70 // #############################################################################################
71 protected:
72 /** Monotonic allocator used for all resourced static definitions as well as the data
73 * used during parsing. */
75
76 /** The element recycler shared between lists of strings. */
78
79 /** The element recycler shared between fields \alib{cli;Command::ParametersMandatory} and
80 * \alib{cli;Command::ParametersOptional}. */
82
83 // #############################################################################################
84 // Fields
85 // #############################################################################################
86 public:
87 /**
88 * Application information text.
89 * Used as a sort of "header" output by class \alib{cli;CLIUtil} .
90 */
92
93 // ################################## Arguments ############################################
94 /**
95 * A vector of args. If the type of CLI argument strings provided with the constructor does
96 * not match the \ref ALIB_CHARACTERS_WIDE "default ALib string width", the strings get
97 * converted.<br>
98 * Values that are 'consumed' by options that get defined, are \b not removed from this
99 * list. Instead, they are removed from index vector #ArgsLeft.
100 */
101 std::vector<String, StdContMA<String>> ArgStrings;
102
103 /**
104 * A vector of args. If constructor variant accepting \b wchar strings is used,
105 * those unicode strings get converted to 1-byte strings using the current locale.<br>
106 * Values that are 'consumed' by options that get defined, are removed.
107 */
108 std::vector<integer, StdContMA<integer>> ArgsLeft;
109
110 // ############################ Declarations (from custom enums) ###########################
111 /** Commands defined. */
113
114 /** Possible Options. */
116
117 /** Possible Parameters. */
119
120 /** Possible Errors. */
122
123 // ################################ Parsed CLI objects ########################################
124
125 /** The options parsed in the order of their appearance. */
127
128 /**
129 * List of arguments that start with a hyphen and are ignored by this class due to the
130 * fact that they were not recognized.
131 *
132 * \see Method #ReadOptions for details on this.
133 */
135
136 /** A list of commands actually parsed. Filled with method #ReadNextCommands. */
138
139 /** The next command in #CommandsParsed to be processed. Used with method #NextCommand. */
141
142 /**
143 * The maximum length of token names:
144 * - 0: Commands
145 * - 1: Options
146 *
147 * Note: Used for formatted help/dump output. (0= command, 1= option, 2= param) */
148 integer MaxNameLength[3] = { 0,0, 0 };
149
150 /**
151 * The resource pool used to fetch resources from.
152 * Several resources are loaded from this in addition
153 * to what is loaded as enum meta information of the cli declaration objects.
154 *
155 * It is recommended to have the main application implement a custom module, as
156 * \ref alib_manual_bootstrapping_custommods "described here".
157 */
159
160 /**
161 * The resource category to fetch CLI resources within field #Resources.
162 */
164
165 /**
166 * Specifies if a "dry run" should be performed.
167 * For more information, see \alib{cli;CLIUtil::GetDryOpt}.
168 */
170
171 // #############################################################################################
172 // Constructor/destructor
173 // #############################################################################################
174 public:
175 /**
176 * Constructor.
177 */
179 : allocator ( 2048 )
180 , AppInfo (A_CHAR("<AppInfo not set>") )
182 , ArgsLeft ( allocator )
186 , ExitCodeDecls ( &allocator, 3.0, 10.0 )
187 , Options ( &allocator )
190 , NextCommandIt ( CommandsParsed.end() )
191 {}
192
193 /**
194 * Virtual empty destructor.
195 */
196 virtual ~CommandLine()
197 {}
198
199 // #############################################################################################
200 // Definition interface
201 // #############################################################################################
202 public:
203 /** ****************************************************************************************
204 * Returns the allocator used for all command parsing, resourced enum record creation
205 * and so on. This allocator might be used for allocations that align with (or is shorter
206 * as) the lifecylce of the instance of this class
207 *
208 * @return The internal allocator
209 ******************************************************************************************/
211
212 /** ****************************************************************************************
213 * Helper function that uses fields #Resources and #ResourceCategory to fetch a
214 * resourced string.<br>
215 *
216 * With debug builds, this method asserts that a resource was found. If this is not
217 * wanted, use #TryResource.
218 *
219 * @param name The resource name.
220 * @return The resource string, respectively a \e nulled string on failure.
221 ******************************************************************************************/
222 const String& GetResource( const NString& name )
223 {
224 return Resources->Get( ResourceCategory, name ALIB_DBG(, true) );
225 }
226
227 /** ****************************************************************************************
228 * Helper function that uses fields #Resources and #ResourceCategory to fetch a
229 * resourced string.<br>
230 *
231 * \note
232 * Usually, it is recommended to use #GetResource, which asserts with debug builds
233 * if a resource was not found.
234 *
235 * @param name The resource name.
236 * @return The resource string, respectively a \e nulled string on failure.
237 ******************************************************************************************/
238 const String& TryResource( const NString& name )
239 {
240 return Resources->Get( ResourceCategory, name ALIB_DBG(, false) );
241 }
242
243
244 /** ****************************************************************************************
245 * Simple helper method that invokes #Init(lang::resources::ResourcePool*, NCString )
246 * providing the resource pool and categery of the given \p{resModule}.
247 *
248 * @param resModule The module used to load resource strings.
249 ******************************************************************************************/
250 void Init( lang::Camp* resModule )
251 {
252 Init( &resModule->GetResourcePool(), resModule->ResourceCategory );
253 }
254
255 /** ****************************************************************************************
256 * Initializes this class. This function has to be invoked after construction and
257 * after this object \alib \ref alib_manual_bootstrapping "was bootstrapped".
258 *
259 * This method accesses global \alib variables \alib{ArgC}, \alib{ArgVN} and
260 * \alib{ArgVW}, and thus those have to be set by the user's <c>main()</c>-function
261 * properly.
262 *
263 * A resource pool has to be provided along with a corresponding resource category
264 * to use. While it is not necessary to do, it is recommended to create a custom
265 * \alib module, which holds such resource pool. For this case, overloaded
266 * helper method #Init(lang::Camp*) is provided which calls this method by forwarding
267 * the pool and category name from that module.
268 *
269 * @param resourcePool The resource pool used to load resource strings.
270 * @param resCategory The resource category used to load resource strings.
271 ******************************************************************************************/
273 virtual
274 void Init( lang::resources::ResourcePool* resourcePool, NCString resCategory );
275
276 #if defined(ALIB_DOX)
277 /** ****************************************************************************************
278 * Defines parameters given with enumeration \p{TEnum}.
279 * \ref alib_enums_records "ALib Enum Records" of type \alib{cli;ERParameterDecl} need to
280 * associated to given enumeration type \p{TEnum}.
281 *
282 * @tparam TEnum The enum type.
283 ******************************************************************************************/
284 template<typename TEnum>
285 inline
287 #else
288 template<typename TEnum>
289 ATMP_VOID_IF( EnumRecords<TEnum>::template AreOfType<ERParameterDecl>() )
291 {
292 for( auto recordIt= EnumRecords<TEnum>::begin()
293 ; recordIt!= EnumRecords<TEnum>::end ()
294 ; ++ recordIt )
295 {
296 ParameterDecls.EmplaceBack( allocator.Emplace<ParameterDecl>( recordIt.Enum() ) );
297
298 if ( MaxNameLength[2] < recordIt->EnumElementName.Length() )
299 MaxNameLength[2]= recordIt->EnumElementName.Length();
300 }
301 }
302 #endif
303
304
305 #if defined(ALIB_DOX)
306 /** ****************************************************************************************
307 * Defines commands given with enumeration \p{TEnum}.
308 * \ref alib_enums_records "ALib Enum Records" of type \alib{cli;ERCommandDecl} need to
309 * associated to given enumeration type \p{TEnum}.
310 *
311 * @tparam TEnum The enum type.
312 ******************************************************************************************/
313 template<typename TEnum>
314 inline
316 #else
317 template<typename TEnum>
318 ATMP_VOID_IF( EnumRecords<TEnum>::template AreOfType<ERCommandDecl>() )
320 {
321 for( auto recordIt= EnumRecords<TEnum>::begin()
322 ; recordIt!= EnumRecords<TEnum>::end ()
323 ; ++ recordIt )
324 {
325 CommandDecls.EmplaceBack( allocator.Emplace<CommandDecl>( recordIt.Enum(), *this ) );
326
327 auto& name= CommandDecls.Back()->Identifier();
328 if ( MaxNameLength[0] < name.Length() )
329 MaxNameLength[0]= name.Length();
330 }
331 }
332 #endif
333
334
335 #if defined(ALIB_DOX)
336 /** ****************************************************************************************
337 * Defines options given with enumeration \p{TEnum}.
338 * \ref alib_enums_records "ALib Enum Records" of type \alib{cli;EROptionDecl} need to
339 * associated to given enumeration type \p{TEnum}.
340 *
341 * @tparam TEnum The enum type.
342 ******************************************************************************************/
343 template<typename TEnum>
344 inline
346 #else
347 template<typename TEnum>
348 ATMP_VOID_IF( EnumRecords<TEnum>::template AreOfType<EROptionDecl>() )
350 {
351 for( auto recordIt= EnumRecords<TEnum>::begin()
352 ; recordIt!= EnumRecords<TEnum>::end ()
353 ; ++ recordIt )
354 {
355 OptionDecls.EmplaceBack( allocator.Emplace<OptionDecl>( recordIt.Enum() ) );
356
357 if ( MaxNameLength[1] < recordIt->EnumElementName.Length() )
358 MaxNameLength[1]= recordIt->EnumElementName.Length();
359 }
360 }
361 #endif
362
363
364
365
366 #if defined(ALIB_DOX)
367 /** ****************************************************************************************
368 * Defines errors given with enumeration \p{TEnum}.
369 * \ref alib_enums_records "ALib Enum Records" of type \alib{cli;ERExitCodeDecl} need to
370 * associated to given enumeration type \p{TEnum}.
371 *
372 * @tparam TEnum The enum type.
373 ******************************************************************************************/
374 template<typename TEnum>
375 inline
377 #else
378 template<typename TEnum>
379 ATMP_VOID_IF( EnumRecords<TEnum>::template AreOfType<ERExitCodeDecl>() )
381 {
382 for( auto recordIt= EnumRecords<TEnum>::begin()
383 ; recordIt!= EnumRecords<TEnum>::end ()
384 ; ++ recordIt )
385 ExitCodeDecls.EmplaceUnique( recordIt.Enum(),
386 allocator.Emplace<ExitCodeDecl>(recordIt.Enum()) );
387 }
388 #endif
389
390
391 // #############################################################################################
392 // Parsing interface
393 // #############################################################################################
394 public:
395 /** ****************************************************************************************
396 * Finalizes Initialization and has to be called after all invocations of:
397 * - DefineCommands,
398 * - DefineOptions,
399 * - DefineParameters and
400 * - DefineExitCodes,
401 *
402 * have been performed. All options recognized get collected in list #Options
403 * The arguments of the options get removed from #ArgsLeft.
404 *
405 * In case of options that have own parameter arguments, such arguments may not be fully
406 * removed. This depends on whether it is possible with the simple flags and values
407 * provided in \alib{cli;OptionDecl} to enable class \alib{cli;Option} to fully detect
408 * them. Therefore, after this method is invoked, for options with more complex syntax,
409 * custom code may be needed to pull the "remainders" of option arguments from #ArgsLeft.
410 * For this, field \alib{cli;Option::Position} is quite useful, as well as method
411 * #RemoveArg.
412 *
413 * It has to be assured that before the next step, which is the invocation of
414 * #ReadNextCommands, all option-related CLI arguments are cleaned away!
415 *
416 * For this reason, this method removes all arguments that start with a
417 * hyphen \c '-' from the #ArgsLeft, even if they got \e not recognized. Those CLI arguments
418 * get collected in #OptionArgsIgnored.
419 * Finding unrecognized options is not considered an error, because other libraries used
420 * with the software might read CLI options autonomously.
421 *
422 * \note
423 * A good sample for this is class \alib{config;CLIArgs} used with \alib
424 * configuration variables. After this method has been invoked, vector #OptionArgsIgnored
425 * may/should be passed to the plug-in \b %CLIArgs of (all)
426 * \alib{config;Configuration} object(s) used with the application. For this, an
427 * invocation, similar to this may be used with all \alibmods that use an own configuration
428 * object:
429 *
430 * XYZModule.GetConfig().GetPluginTypeSafe<alib::config::CLIArgs>()->SetArgs( &OptionArgsIgnored );
431 *
432 * In the case that other libraries have more complex option syntax, e.g. options
433 * consisting of multiple arguments or such that do not even start with a hyphen character,
434 * then, this method should be called <b>only after a custom code removes such 3rd party
435 * options in a reliable way!</b>
436 *
437 * If all this was not done, the "remainder" of custom options might confuse parsing
438 * of commands and its parameters and most probably would lead to an
439 * "unknown" command error when the remainders of non-removed 3rd party option arguments
440 * are consumed later.
441 *
442 * As a consequence of this approach, a subsequent call to this method has no effect.
443 ******************************************************************************************/
445 virtual
446 void ReadOptions();
447
448 /** ****************************************************************************************
449 * Searches and returns the last occurrence of the specified option.
450 *
451 * This method is to be used with options that overwrite previous values in case
452 * that it was given multiple times as a the CLI argument. Instead, options that may occur
453 * multiple times without overriding a previous occurrence, are to be processed
454 * "manually" by examining field #Options.
455 *
456 * @param element The option's declaration enum element.
457 * @return A pointer to the parsed option, respectively \c nullptr if not given.
458 ******************************************************************************************/
460 Option* GetOption( Enum element );
461
462 /** ****************************************************************************************
463 * Parses as many commands as possible and stores them in #CommandsParsed. Prior
464 * to invoking this methods, all CLI declarations have to be made. Furthermore, usually
465 * method #ReadOptions has to be invoked prior to this method.
466 *
467 * The name of the method indicates that one or more, but maybe not all commands are read.
468 * The reason for this behavior lies in the fact that commands may be defined that
469 * do their own, specifically coded parsing. As a matter of the fact that the flags and
470 * options of structs \alib{cli;CommandDecl} and \alib{cli;ParameterDecl} are kept rather
471 * simple to match the most usual cases, the parsing of arguments of a command often
472 * has to be left to be done by custom code. Mostly just when processing (executing) a
473 * command. In contrast to the need of parsing (and processing) all CLI options,
474 * given prior to processing commands, this is not a problem. The usual inner part of a
475 * command processing loop then looks like this:
476 * - Check if #CommandsParsed is empty
477 * - Invoke this method (\b %ReadNextCommands )
478 * - If still no command is found, break loop
479 * - Remove and process first command in #CommandsParsed.
480 *
481 * A similar parsing approach is supported with method #NextCommand. The only difference
482 * is that the parsed commands stay in #CommandsParsed and instead field #NextCommandIt
483 * indicates the position of the next command to read. This way, commands may refer
484 * to previous ones, if this is needed.
485 *
486 * As a final note it should be mentioned, that implementing a "dry run" option on the
487 * level of command parsing, for the reasons explained above, might need some specific
488 * custom code to be able to parse all commands (without processing them). If such
489 * functionality is wanted, it is best to separate custom command parsing from
490 * command execution (the opposite was recommended above). Only the last command in the list
491 * has to be 'manually' processed, as the previous ones obviously got parsed well.
492 * With this approach, all commands can be parsed without executing them. Static utility
493 * method \alib{cli;CLIUtil::DumpParseResults} is a predefined way to then write information
494 * about all options and commands parsed.<br>
495 * A lower level "dry run", that receives information about the concrete actions that the
496 * processing of commands would perform, is of-course a different, application specific
497 * task.
498 ******************************************************************************************/
500 virtual
501 void ReadNextCommands();
502
503 /** ****************************************************************************************
504 * Returns the next item in vector #NextCommand. If needed, #ReadNextCommands is
505 * invoked.
506 * @return The next command parsed from CLI argument list. \c nullptr, if no more commands
507 * are found.
508 ******************************************************************************************/
510 virtual
512
513 /** ****************************************************************************************
514 * Retrieves the number of arguments.
515 *
516 * @return The number of arguments given.
517 ******************************************************************************************/
518 virtual
520 {
521 return static_cast<integer>(ArgStrings.size());
522 }
523
524 /** ****************************************************************************************
525 * Retrieves the argument at the given position.<br>
526 * In debug builds, this method assert the index is in the available range.
527 * @param idx The requested argument's index.
528 * @return The element \p{idx} of vector #ArgStrings.
529 ******************************************************************************************/
530 virtual
532 {
533 ALIB_ASSERT_ERROR( idx >= 0 && static_cast<size_t>(idx) < ArgStrings.size(),
534 "CLI", "Argument index out of bounds" )
535 return ArgStrings[static_cast<size_t>(idx)];
536 }
537
538 /** ****************************************************************************************
539 * Retrieves the next argument from the list without removing it.
540 *
541 * \see Method #PopArg, #RemoveArg and #ReadNextCommands.
542 *
543 * @return The first argument of (respectively remaining in) the list.
544 * If no argument is available, a \e nulled string is returned.
545 ******************************************************************************************/
546 virtual
548 {
549 return ArgsLeft.size() > 0 ? ArgStrings[static_cast<size_t>(ArgsLeft[0])]
550 : String();
551 }
552
553 /** ****************************************************************************************
554 * Retrieves the next argument and removes it from list #ArgsLeft.
555 *
556 * \see Method #PeekArg, #RemoveArg and #ReadNextCommands.
557 *
558 * @return The first argument of vector #ArgsLeft.
559 * If no argument is available, a \e nulled string is returned.
560 ******************************************************************************************/
562 virtual
563 String PopArg();
564
565 /** ****************************************************************************************
566 * Removes the argument at position \p{argNo}.
567 * If the argument is not in #ArgsLeft, an \alib assertion is raised.
568 *
569 * \see Method #PeekArg, #PopArg and #ReadNextCommands.
570 *
571 * @param argNo The argument number to remove.
572 ******************************************************************************************/
574 void RemoveArg( integer argNo );
575}; // class CommandLine
576
577
578
579// #############################################################################################
580// Definitions of methods of related objects that can be only mede after CommandLine is defined
581// #############################################################################################
582inline
584: Parsed( cmdLine )
585, Args ( &CmdLine->allocator, cmdLine->stringListRecycler )
586{}
587
589: Parsed( cmdLine )
590, Args ( &CmdLine->allocator, cmdLine->stringListRecycler )
591{}
592
593template<typename TEnum>
594CommandDecl::CommandDecl( TEnum element, CommandLine& cmdLine )
595: declElement (element)
596, resourceInfo(element)
597, CmdLine (cmdLine )
598, Parameters (&cmdLine.allocator)
599{
600 // make a copy of the resourced record
601 record= enums::GetRecord(element);
602
604}
605
607: Parsed ( cmdLine )
608, ParametersMandatory( &cmdLine->allocator, cmdLine->paramListRecycler )
609, ParametersOptional ( &cmdLine->allocator, cmdLine->paramListRecycler )
610{}
611
612
613} // namespace alib[::basecamp]
614
615/// Type alias in namespace \b alib.
617
618
619} // namespace [alib]
620
621
622
623#endif // HPP_ALIB_CLI_COMMANDLINE
ALIB_API void addParamDecls()
Definition arguments.cpp:38
CommandDecl(TEnum element, CommandLine &cmdLine)
lang::resources::ResourcePool * Resources
List< OptionDecl * > OptionDecls
void Init(lang::Camp *resModule)
List< ParameterDecl * > ParameterDecls
List< Parameter *, Recycling::Shared >::TSharedRecycler paramListRecycler
virtual ALIB_API Command * NextCommand()
List< Command * >::Iterator NextCommandIt
virtual String GetArg(integer idx)
HashMap< Enum, ExitCodeDecl * > ExitCodeDecls
List< Command * > CommandsParsed
MonoAllocator allocator
const String & TryResource(const NString &name)
virtual String PeekArg()
List< Option * > Options
virtual ALIB_API void ReadOptions()
std::vector< String, StdContMA< String > > ArgStrings
virtual ALIB_API String PopArg()
MonoAllocator & GetAllocator()
virtual integer ArgCount()
ALIB_API void RemoveArg(integer argNo)
ALIB_API Option * GetOption(Enum element)
const String & GetResource(const NString &name)
virtual ALIB_API void ReadNextCommands()
List< String, Recycling::Shared >::TSharedRecycler stringListRecycler
List< String, Recycling::Shared > OptionArgsIgnored
List< CommandDecl * > CommandDecls
std::vector< integer, StdContMA< integer > > ArgsLeft
resources::ResourcePool & GetResourcePool()
Definition camp.hpp:266
NCString ResourceCategory
Definition camp.hpp:142
virtual const String & Get(const NString &category, const NString &name, bool dbgAssert)=0
ALIB_FORCE_INLINE T * Emplace(TArgs &&... args)
#define A_CHAR(STR)
#define ATMP_VOID_IF(Cond)
Definition tmp.hpp:52
#define ALIB_API
Definition alib.hpp:538
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
#define ALIB_DBG(...)
Definition alib.hpp:457
DryRunModes
Definition cli.hpp:97
const T_EnumRecords< TEnum >::Type & GetRecord(TEnum element)
Definition alib.cpp:57
cli::CLIUtil CLIUtil
Type alias in namespace alib.
Definition cliutil.hpp:240
strings::TString< character > String
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:286
Command(CommandLine *cmdLine)
Option(CommandLine *cmdLine)
Parameter(CommandLine *cmdLine)
static constexpr ForwardIterator end()
Definition records.hpp:434