Initial commit for ArgParser

This commit is contained in:
Thraix
2026-05-12 23:31:16 +02:00
commit 50bd1722d2
16 changed files with 1382 additions and 0 deletions
+334
View File
@@ -0,0 +1,334 @@
---
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: true
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AllowShortNamespacesOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AttributeMacros:
- __capability
- absl_nonnull
- absl_nullable
- absl_nullability_unknown
BinPackArguments: false
BinPackLongBracedList: true
BinPackParameters: false
BitFieldColonSpacing: Both
BracedInitializerIndentWidth: -1
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: None
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Allman
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTemplateCloser: false
BreakBeforeTernaryOperators: true
BreakBinaryOperations: Never
BreakConstructorInitializers: BeforeColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakTemplateDeclarations: Yes
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
EnumTrailingComma: Leave
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExportBlock: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: false
AtStartOfFile: true
KeepFormFeed: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
OneLineFormatOffRegex: ''
PackConstructorInitializers: Never
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakBeforeMemberAccess: 150
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Leave
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: Always
RemoveBracesLLVM: false
RemoveEmptyLinesInUnwrappedLines: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Always
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes:
Enabled: true
IgnoreCase: false
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterOperatorKeyword: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterNot: false
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TableGenBreakInsideDAGArg: DontBreak
TabWidth: 2
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
WrapNamespaceBodyWithEmptyLines: Leave
...
+3
View File
@@ -0,0 +1,3 @@
bin/
compile_flags.txt
Makefile
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Tim Håkansson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+32
View File
@@ -0,0 +1,32 @@
<makegen>
<configuration name="Test">
<generatehfile>false</generatehfile>
<outputdir>bin/Test/</outputdir>
<outputname>arg_parser</outputname>
<outputtype>executable</outputtype>
<projectname>ArgParser</projectname>
<srcdir>src/</srcdir>
</configuration>
<configuration name="Release">
<generatehfile>false</generatehfile>
<outputdir>bin/Release</outputdir>
<outputname>libarg_parser.so</outputname>
<outputtype>sharedlibrary</outputtype>
<sourceexclude>main.cpp</sourceexclude>
<projectname>ArgParser</projectname>
<srcdir>src/</srcdir>
</configuration>
<configuration name="Debug">
<cflag>-g3</cflag>
<cflag>-w</cflag>
<define>_DEBUG</define>
<generatehfile>false</generatehfile>
<outputdir>bin/Debug/</outputdir>
<outputname>arg_parser</outputname>
<outputtype>executable</outputtype>
<projectname>ArgParser</projectname>
<srcdir>src/</srcdir>
</configuration>
<target>Release</target>
<version>v1.3.8</version>
</makegen>
+54
View File
@@ -0,0 +1,54 @@
#include "ActionArg.h"
#include <sstream>
namespace ap
{
ActionArg::ActionArg(const std::string& argument, const std::string& helpText, std::function<void()> action)
: Arg{argument, helpText, false, true},
action{action}
{
}
bool ActionArg::PartialMatch(const std::string& arg) const
{
for (const auto& argument : arguments)
{
if (argument == arg)
return true;
}
return false;
}
bool ActionArg::Parse(const std::vector<std::string>& arguments, size_t& index)
{
action();
return true;
}
std::string ActionArg::GetHelpArgument(bool onlyFirst) const
{
std::stringstream ss;
if (onlyFirst)
{
if (required)
{
ss << arguments.front();
}
else
{
ss << "[" << arguments.front() << "]";
}
}
else
{
ss << arguments.front();
for (int i = 1; i < arguments.size(); i++)
{
ss << ", " << arguments[i];
}
}
return ss.str();
}
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <functional>
#include "Arg.h"
namespace ap
{
struct ActionArg : public Arg
{
std::function<void()> action;
ActionArg(const std::string& argument, const std::string& helpText, std::function<void()> action);
bool PartialMatch(const std::string& arg) const override;
bool Parse(const std::vector<std::string>& arguments, size_t& index) override;
std::string GetHelpArgument(bool onlyFirst) const override;
};
}
+41
View File
@@ -0,0 +1,41 @@
#include "Arg.h"
namespace ap
{
Arg::Arg(const std::string& argument, const std::string& helpText, bool required, bool overridesMandatory)
: helpText{helpText},
required{required},
overridesMandatory{overridesMandatory}
{
size_t lastPos = 0;
size_t pos = argument.find(',');
while (pos != std::string::npos)
{
arguments.emplace_back(argument.substr(lastPos, (pos - lastPos)));
lastPos = pos + 1;
pos = argument.find(',', lastPos);
}
arguments.emplace_back(argument.substr(lastPos));
}
bool Arg::Match(const std::string& arg) const
{
for (const auto& argument : arguments)
{
if (argument == arg)
return true;
}
return MatchAdditional(arg);
}
bool Arg::PartialMatch(const std::string& arg) const
{
return false;
}
bool Arg::MatchAdditional(const std::string& arg) const
{
return false;
}
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <string>
#include <vector>
namespace ap
{
struct Arg
{
std::vector<std::string> arguments;
std::string helpText;
bool required;
bool overridesMandatory;
bool wasSpecified{false};
Arg(const std::string& argument, const std::string& helpText, bool required, bool overridesMandatory);
bool Match(const std::string& arg) const;
virtual bool PartialMatch(const std::string& arg) const;
virtual bool MatchAdditional(const std::string& arg) const;
virtual bool Parse(const std::vector<std::string>& arguments, size_t& index) = 0;
virtual std::string GetHelpArgument(bool onlyFirst) const = 0;
};
}
+228
View File
@@ -0,0 +1,228 @@
#include "ArgParser.h"
#include <algorithm>
#include <iomanip>
#include "ActionArg.h"
#include "FlagArg.h"
const int HELP_WIDTH = 25;
const int MAX_WIDTH = 80;
namespace ap
{
ArgParser::ArgParser(const std::string& commandName,
const std::string& description,
const std::string& additionalHelp,
const std::vector<std::string>& arguments)
: commandName{commandName},
description{description},
additionalHelp{additionalHelp},
argumentStrings{arguments}
{
AddAction("--help,-h", "Displays this information", [&]() { PrintHelp(); });
}
ArgParser::ArgParser(const std::string& commandName,
const std::string& description,
const std::string& additionalHelp,
int argc,
char** argv)
: commandName{commandName},
description{description},
additionalHelp{additionalHelp}
{
AddAction("--help,-h", "Displays this information", [&]() { PrintHelp(); });
for (int i = 0; i < argc; i++)
{
argumentStrings.emplace_back(argv[i]);
}
}
void ArgParser::AddAction(const std::string& arg, const std::string& helpText, std::function<void()> action)
{
args.emplace_back(std::make_shared<ActionArg>(arg, helpText, action));
}
void ArgParser::AddFlag(const std::string& arg, bool& flag, const std::string& helpText)
{
args.emplace_back(std::make_shared<FlagArg>(flag, arg, helpText));
}
void ArgParser::PrintHelp() const
{
size_t pos = 0;
size_t offset = 0;
Utils::PrintMaxWidthSplit(description, pos, offset, MAX_WIDTH, " ");
std::cout << std::endl;
std::cout << "Usage: " << std::endl;
PrintCommandHelp(false);
PrintCommandHelp(true);
std::cout << std::endl;
std::cout << "Options: " << std::endl;
size_t width = 0;
for (auto& arg : args)
{
std::string helpArg = arg->GetHelpArgument(false);
if (helpArg.size() + 4 < HELP_WIDTH)
width = std::max(width, helpArg.size());
}
width = std::min(size_t(HELP_WIDTH), width + 4);
for (const auto& arg : args)
{
PrintArgHelp(*arg, width);
}
std::cout << std::endl;
pos = 0;
offset = 0;
Utils::PrintMaxWidthSplit(additionalHelp, pos, offset, MAX_WIDTH, " ");
}
void ArgParser::PrintError() const
{
std::cout << "Use --help for more info" << std::endl;
}
bool ArgParser::Parse()
{
bool anyOverridesMandatory = false;
for (size_t i = 0; i < argumentStrings.size(); i++)
{
auto it = std::find_if(args.begin(),
args.end(),
[&](const std::shared_ptr<Arg>& arg)
{ return arg->overridesMandatory && arg->Match(argumentStrings[i]); });
if (it != args.end())
{
(*it)->Parse(argumentStrings, i);
anyOverridesMandatory = true;
}
}
if (anyOverridesMandatory)
{
return false;
}
for (size_t i = 0; i < argumentStrings.size(); i++)
{
auto it = std::find_if(
args.begin(), args.end(), [&](const std::shared_ptr<Arg>& arg) { return arg->Match(argumentStrings[i]); });
if (it != args.end())
{
if (!(*it)->Parse(argumentStrings, i))
{
PrintError();
return false;
}
continue;
}
if (Utils::IsMultiFlag(argumentStrings[i]))
{
std::vector<std::vector<std::shared_ptr<Arg>>::iterator> iterators;
for (size_t j = 1; j < argumentStrings[i].size(); j++)
{
std::string flag = std::string("-") + argumentStrings[i][j];
auto it =
std::find_if(args.begin(), args.end(), [&](const std::shared_ptr<Arg>& arg) { return arg->Match(flag); });
if (it == args.end())
{
std::cout << "Unknown flag: \"" << flag << "\" in \"" << argumentStrings[i] << "\"" << std::endl
<< std::endl
<< std::endl;
PrintError();
return false;
}
iterators.emplace_back(it);
}
for (auto it : iterators)
{
if (!(*it)->Parse(argumentStrings, i))
{
PrintError();
return false;
}
if ((*it)->overridesMandatory)
{
anyOverridesMandatory = true;
}
}
continue;
}
std::cout << "Unknown argment: \"" << argumentStrings[i] << "\"" << std::endl << std::endl;
PrintError();
return false;
}
if (anyOverridesMandatory)
{
return false;
}
for (const auto& arg : args)
{
if (arg->required && !arg->wasSpecified)
{
std::cout << "Mandatory argument not specified: " << arg->arguments.front() << std::endl << std::endl;
PrintError();
return false;
}
}
return true;
}
void ArgParser::PrintCommandHelp(bool actions) const
{
std::cout << " " << commandName;
size_t offset = 4;
if (commandName.size() - 3 > HELP_WIDTH)
std::cout << std::endl << " ";
else
{
std::cout << " ";
offset = commandName.size() + 3;
}
size_t pos = offset;
PrintCommandArgsHelp(pos, offset, actions, true);
PrintCommandArgsHelp(pos, offset, actions, false);
std::cout << std::endl;
}
void ArgParser::PrintCommandArgsHelp(size_t& pos, size_t offset, bool actions, bool required) const
{
for (auto& arg : args)
{
if (arg->overridesMandatory == actions && arg->required == required)
{
Utils::PrintMaxWidth(arg->GetHelpArgument(true), pos, offset, MAX_WIDTH);
}
}
}
void ArgParser::PrintArgHelp(const Arg& arg, size_t offset) const
{
std::string helpArg = arg.GetHelpArgument(false);
if (helpArg.size() + 4 >= HELP_WIDTH)
{
std::cout << " " << helpArg << std::endl;
std::cout << std::setw(offset) << "";
size_t pos = offset;
Utils::PrintMaxWidthSplit(arg.helpText, pos, offset, MAX_WIDTH, " ");
}
else
{
std::cout << " " << std::setw(offset - 2) << std::left << helpArg;
size_t pos = offset;
Utils::PrintMaxWidthSplit(arg.helpText, pos, offset, MAX_WIDTH, " ");
}
}
}
+70
View File
@@ -0,0 +1,70 @@
#include <math.h>
#include <functional>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "Arg.h"
#include "ValueArg.h"
namespace ap
{
struct ArgParser
{
std::string commandName;
std::string description;
std::string additionalHelp;
std::vector<std::string> argumentStrings;
std::vector<std::shared_ptr<Arg>> args;
ArgParser(const std::string& commandName,
const std::string& description,
const std::string& additionalHelp,
const std::vector<std::string>& arguments);
ArgParser(const std::string& commandName,
const std::string& description,
const std::string& additionalHelp,
int argc,
char** argv);
void AddAction(const std::string& arg, const std::string& helpText, std::function<void()> action);
template <typename T>
void Add(const std::string& arg, T& value, const std::string& helpText)
{
args.emplace_back(std::make_shared<ValueArg<T>>(value, arg, helpText, true));
}
template <typename T>
void Add(const std::string& arg, T& value, const std::string& helpText, const T& defaultValue)
{
value = defaultValue;
std::stringstream ss;
ss << helpText << " (default=" << defaultValue << ")";
args.emplace_back(std::make_shared<ValueArg<T>>(value, arg, ss.str(), false));
}
template <typename T>
void AddOptional(const std::string& arg, std::optional<T>& value, const std::string& helpText)
{
value = std::nullopt;
std::stringstream ss;
ss << helpText << " (default=null)";
args.emplace_back(std::make_shared<ValueArg<std::optional<T>>>(value, arg, ss.str(), false));
}
void AddFlag(const std::string& arg, bool& flag, const std::string& helpText);
bool Parse();
private:
void PrintCommandHelp(bool actions) const;
void PrintCommandArgsHelp(size_t& pos, size_t offset, bool actions, bool required) const;
void PrintArgHelp(const Arg& arg, size_t offset) const;
void PrintHelp() const;
void PrintError() const;
};
}
+55
View File
@@ -0,0 +1,55 @@
#include "FlagArg.h"
#include <sstream>
namespace ap
{
FlagArg::FlagArg(bool& flag, const std::string& argument, const std::string& helpText)
: Arg{argument, helpText, false, false},
flag{flag}
{
flag = false;
}
bool FlagArg::PartialMatch(const std::string& arg) const
{
for (const auto& argument : arguments)
{
if (argument == arg)
return true;
}
return false;
}
bool FlagArg::Parse(const std::vector<std::string>& arguments, size_t& index)
{
flag = true;
return true;
}
std::string FlagArg::GetHelpArgument(bool onlyFirst) const
{
std::stringstream ss;
if (onlyFirst)
{
if (required)
{
ss << arguments.front();
}
else
{
ss << "[" << arguments.front() << "]";
}
}
else
{
ss << arguments.front();
for (int i = 1; i < arguments.size(); i++)
{
ss << ", " << arguments[i];
}
}
return ss.str();
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "Arg.h"
namespace ap
{
struct FlagArg : public Arg
{
bool& flag;
FlagArg(bool& flag, const std::string& argument, const std::string& helpText);
bool PartialMatch(const std::string& arg) const override;
bool Parse(const std::vector<std::string>& arguments, size_t& index) override;
std::string GetHelpArgument(bool onlyFirst) const override;
};
}
+173
View File
@@ -0,0 +1,173 @@
#include "Utils.h"
#include <iomanip>
#include <iostream>
namespace ap
{
template <>
std::pair<bool, int> ParseValue<int>(const std::string_view& str)
{
try
{
size_t index = 0;
int i = std::stoi(std::string(str), &index);
if (index != str.size())
return {false, 0};
return {true, i};
}
catch (...)
{
return {false, 0};
}
}
template <>
std::pair<bool, float> ParseValue<float>(const std::string_view& str)
{
try
{
size_t index = 0;
float f = std::stof(std::string(str), &index);
if (index != str.size())
return {false, 0};
return {true, f};
}
catch (...)
{
return {false, 0};
}
}
template <>
std::pair<bool, std::string> ParseValue<std::string>(const std::string_view& str)
{
return {true, std::string(str)};
}
template <>
std::pair<bool, bool> ParseValue<bool>(const std::string_view& str)
{
if (Utils::CaseInsensitiveEqual(str, "true") || Utils::CaseInsensitiveEqual(str, "yes") || str == "y" || str == "Y")
{
return {true, true};
}
if (Utils::CaseInsensitiveEqual(str, "false") || Utils::CaseInsensitiveEqual(str, "no") || str == "n" || str == "N")
{
return {true, false};
}
return {false, false};
}
template <>
std::string GetTypeHelpName<bool>()
{
return "<boolean>";
}
template <>
std::string GetTypeHelpName<int>()
{
return "<int>";
}
template <>
std::string GetTypeHelpName<float>()
{
return "<float>";
}
template <>
std::string GetTypeHelpName<std::string>()
{
return "<string>";
}
namespace Utils
{
bool CaseInsensitiveEqual(const std::string_view& sv, const std::string& str)
{
if (sv.size() != str.size())
return false;
for (size_t i = 0; i < sv.size(); i++)
{
if (std::tolower(sv[i]) != str[i])
{
return false;
}
}
return true;
}
void PrintMaxWidth(
const std::vector<std::string>& strings, size_t pos, size_t offset, size_t maxWidth, const std::string& delim)
{
if (strings.empty())
return;
for (auto& string : strings)
{
PrintMaxWidth(strings.front(), pos, offset, maxWidth);
}
std::cout << std::endl;
}
void PrintMaxWidthSplit(
const std::string& str, size_t pos, size_t offset, size_t maxWidth, const std::string& delim)
{
size_t lastPos = 0;
size_t strPos = str.find(delim);
while (strPos != std::string::npos)
{
PrintMaxWidth(std::string_view(str.c_str() + lastPos, strPos - lastPos), pos, offset, maxWidth);
lastPos = strPos + 1;
strPos = str.find(' ', lastPos);
}
PrintMaxWidth(std::string_view(str.c_str() + lastPos, str.size() - lastPos), pos, offset, maxWidth);
std::cout << std::endl;
}
void PrintMaxWidth(const std::string_view& view, size_t& pos, size_t offset, size_t maxWidth)
{
if (pos + view.size() > maxWidth && pos != offset)
{
std::cout << std::endl << std::setw(offset) << "";
pos = offset;
}
size_t lastPos = 0;
size_t newline = view.find('\n');
while (newline != std::string::npos)
{
std::cout << view.substr(lastPos, newline - lastPos) << std::endl << std::setw(offset) << "";
pos = offset;
lastPos = newline + 1;
newline = view.find('\n', lastPos);
}
std::cout << view.substr(lastPos);
if (pos + view.size() != maxWidth)
std::cout << " ";
pos += view.size() + 1;
}
bool IsMultiFlag(const std::string& arg)
{
if (arg[0] != '-')
return false;
for (auto i = 1; i < arg.size(); i++)
{
if (!(arg[i] >= 'a' && arg[i] <= 'z') && !(arg[i] >= 'A' && arg[i] <= 'Z') && !(arg[i] >= '0' && arg[i] <= '9'))
return false;
}
return true;
}
}
}
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <optional>
#include <ostream>
#include <string>
#include <vector>
namespace ap
{
template <typename T>
struct dependent_false
{
enum
{
value = false
};
};
template <typename T>
struct is_optional : std::false_type
{
};
template <typename T>
struct is_optional<std::optional<T>> : std::true_type
{
};
template <typename T>
std::string GetTypeHelpName()
{
static_assert(dependent_false<T>::value, "No GetTypeHelpName function created for type T");
return {false, T{}};
}
template <typename T>
std::pair<bool, T> ParseValue(const std::string_view& str)
{
static_assert(dependent_false<T>::value, "No ParseValue function created for type T");
return {false, T{}};
}
template <>
std::pair<bool, int> ParseValue<int>(const std::string_view& str);
template <>
std::pair<bool, float> ParseValue<float>(const std::string_view& str);
template <>
std::pair<bool, std::string> ParseValue<std::string>(const std::string_view& str);
template <>
std::pair<bool, bool> ParseValue<bool>(const std::string_view& str);
template <>
std::string GetTypeHelpName<bool>();
template <>
std::string GetTypeHelpName<int>();
template <>
std::string GetTypeHelpName<float>();
template <>
std::string GetTypeHelpName<std::string>();
namespace Utils
{
bool CaseInsensitiveEqual(const std::string_view& sv, const std::string& str);
void PrintMaxWidth(
const std::vector<std::string>& strings, size_t pos, size_t offset, size_t maxWidth, const std::string& delim);
void PrintMaxWidthSplit(
const std::string& view, size_t pos, size_t offset, size_t maxWidth, const std::string& delim);
void PrintMaxWidth(const std::string_view& view, size_t& pos, size_t offset, size_t maxWidth);
bool IsMultiFlag(const std::string& arg);
}
}
+122
View File
@@ -0,0 +1,122 @@
#include <iostream>
#include <sstream>
#include "Arg.h"
#include "Utils.h"
namespace ap
{
template <typename T>
struct ValueArg : public Arg
{
T& value;
ValueArg(T& value, const std::string& argument, const std::string& helpText, bool required)
: Arg{argument, helpText, required, false},
value{value}
{
}
bool MatchAdditional(const std::string& arg) const override
{
size_t pos = arg.find('=');
if (pos == std::string::npos)
return false;
for (const auto& argument : arguments)
{
if (std::string_view(arg.c_str(), pos) == argument)
return true;
}
return false;
}
bool Parse(const std::vector<std::string>& arguments, size_t& index) override
{
wasSpecified = true;
size_t pos = arguments[index].find('=');
std::string valStr;
std::string argStr;
if (pos == std::string::npos)
{
argStr = arguments[index];
if (arguments.size() <= index + 1)
{
std::cout << "No argument specified for " << arguments[index] << std::endl;
return false;
}
index++;
valStr = arguments[index];
}
else
{
argStr = arguments[index].substr(0, pos);
valStr = arguments[index].substr(pos + 1, arguments[index].size() - pos - 1);
}
std::pair<bool, T> res;
if constexpr (is_optional<T>::value)
{
res = ParseValue<typename T::value_type>(valStr);
}
else
{
res = ParseValue<T>(valStr);
}
const auto& [result, val] = res;
if (!result)
{
std::cout << "Could not parse value for argument=\"" << argStr << "\"" << " value=\"" << valStr << "\""
<< std::endl
<< std::endl;
return false;
}
value = val;
return true;
}
std::string GetHelpArgument(bool onlyFirst) const override
{
std::stringstream ss;
if (!onlyFirst)
{
ss << arguments.front();
for (int i = 1; i < arguments.size(); i++)
{
ss << ", " << arguments[i];
}
OutputTypeHelpName(ss);
return ss.str();
}
if (!required)
ss << "[";
ss << arguments.front();
OutputTypeHelpName(ss);
if (!required)
ss << "]";
return ss.str();
}
private:
void OutputTypeHelpName(std::ostream& os) const
{
if constexpr (is_optional<T>::value)
{
os << " " << GetTypeHelpName<typename T::value_type>();
}
else
{
os << " " << GetTypeHelpName<T>();
}
}
};
}
+109
View File
@@ -0,0 +1,109 @@
#include <optional>
#include "ArgParser.h"
void PrintVersion()
{
std::cout << "command v1.2.3" << std::endl;
}
int main(int argc, char** argv)
{
ap::ArgParser parser(
"makegen",
"MakeGen is a utility tool to generate and run Makefiles in a simple manner. By default it "
"always compiles code with parallel jobs",
"If no option is given it will run \"make all\"\n\nIf multiple make options are given it will "
"run in the following order:\nclean all install run, rebuild will be translated to \"clean all\"",
argc - 1,
argv + 1);
struct Args
{
bool make;
bool install;
bool clean;
bool rebuild;
bool run;
bool single;
bool simple;
bool conf;
std::optional<std::string> target;
int test;
};
Args args;
parser.AddAction("--version,-v", "Displays the version of this program", PrintVersion);
parser.AddFlag("make,all,-m,-a", args.make, "Generates a Makefile and runs \"make all\"");
parser.AddFlag("install,-i", args.install, "Generates a Makefile and runs \"make all && make install\"");
parser.AddFlag("clean,-c", args.clean, "Generates a Makefile and runs \"make clean\"");
parser.AddFlag("rebuld,-r", args.rebuild, "Generates a Makefile and runs \"make clean && make all\"");
parser.AddFlag("run,execute,-e", args.run, "Generates a Makefile and runs \"make all && make run\"");
parser.AddFlag("single,-s", args.single, "Runs additional makegen options as single thread\n(no --jobs=X flag)");
parser.AddFlag("--simple", args.simple, "Creates a simple Makefile without include dependencies");
parser.AddOptional("--target,-t", args.target, "Run the makegen.xml file with the specified target");
if (parser.Parse())
{
if (args.target)
std::cout << args.target.value() << std::endl;
else
std::cout << "null" << std::endl;
}
return 0;
#if 0
ap::ArgParser parser("command",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut "
"labore et dolore magna aliqua",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut "
"labore et dolore magna aliqua",
argc - 1,
argv + 1);
// ArgParser parser("command",
// "This text explains what the command does",
// "Additional help information",
// {{"--test", "10", "--test2=20", "--test4=tesssda", "--test5", "--test6=Y"}});
int test;
int test2;
float test3;
std::string test4;
bool flag;
bool b;
bool version;
parser.AddInfo("--version", version, "Displays the version of the program");
parser.Add<int>(
"--test",
test,
"some1 help2 text3 some4 help5 text6 some7 help8 text9 some10 "
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
parser.Add<int>("--test2,-t", test2, "some help text 2");
parser.Add<float>("--test3", test3, "some help text", 0.1f);
parser.Add<std::string>("--test4", test4, "some help text");
parser.Add<bool>("--test6", b, "some help text");
parser.Add<bool>("--test7", b, "some help text");
parser.AddFlag("--test5", flag, "some help text");
parser.AddFlag("--very_very_very_very_long", flag, "some help text");
parser.AddFlag("--very_very_very_very_longxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
flag,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore "
"et dolore magna aliqua");
parser.AddFlag("--very_very_very_very", flag, "some help text");
parser.AddFlag("--very_very_very_ver", flag, "some help text");
if (parser.Parse())
{
std::cout << test << std::endl;
std::cout << test2 << std::endl;
std::cout << test3 << std::endl;
std::cout << test4 << std::endl;
std::cout << b << std::endl;
std::cout << flag << std::endl;
}
if (version)
{
std::cout << "Command 1.2.3" << std::endl;
return 0;
}
return 1;
#endif
}