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