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