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