Created
February 22, 2018 13:54
-
-
Save wieslawsoltes/417fe0c736b1c7a3af53cc98c7db4bfd to your computer and use it in GitHub Desktop.
Command-line parser for C++ apps.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include <vector> | |
#include <algorithm> | |
#include <iterator> | |
#include <utility> | |
#include <iostream> | |
namespace util | |
{ | |
class Option | |
{ | |
public: | |
int Id; | |
std::vector<std::wstring> Args; | |
int nParams; | |
}; | |
class Result | |
{ | |
public: | |
int Id; | |
std::wstring Arg; | |
std::vector<std::wstring> Params; | |
}; | |
class ArgvParser | |
{ | |
public: | |
bool bVerbose = false; | |
public: | |
int FindOption(const std::wstring& arg, const std::vector<Option>& options) | |
{ | |
for (size_t i = 0; i < options.size(); i++) | |
{ | |
auto& args = options[i].Args; | |
auto it = std::find(std::begin(args), std::end(args), arg); | |
if (it != std::end(args)) | |
return i; | |
} | |
return -1; | |
} | |
public: | |
bool HasDuplicates(std::vector<Result>& results) | |
{ | |
std::sort(results.begin(), results.end(), [](Result& a, Result &b) { return a.Id < b.Id; }); | |
auto unique = std::unique(results.begin(), results.end(), [](Result& a, Result &b) { return a.Id == b.Id; }); | |
return unique != results.end(); | |
} | |
public: | |
int ParseOptions(const int argc, const wchar_t *argv[], const std::vector<Option> options, std::vector<Result>& results) | |
{ | |
for (int i = 1; i < argc; i++) | |
{ | |
std::wstring arg = argv[i]; | |
if (bVerbose) | |
std::wcout << L"Input arg: " << arg << std::endl; | |
int index = FindOption(arg, options); | |
if (index != -1) | |
{ | |
auto& option = options[index]; | |
if (bVerbose) | |
std::wcout << L"Found option: " << arg << L", id: " << option.Id << L", params: " << option.nParams << std::endl; | |
std::vector<std::wstring> params; | |
if (option.nParams == -1) | |
{ | |
// unlimited number of params expected, minimum one required | |
int j = i + 1; | |
while (j < argc) | |
{ | |
std::wstring param = argv[j]; | |
if (param[0] == '-') | |
break; | |
if (bVerbose) | |
std::wcout << L"Input param: " << param << std::endl; | |
params.emplace_back(param); | |
j++; | |
i++; | |
} | |
if (params.size() < 1) | |
{ | |
if (bVerbose) | |
std::wcout << L"At leat one param is required" << std::endl; | |
return -1; | |
} | |
} | |
else if (option.nParams > 0) | |
{ | |
// specific number of params required | |
if (i + option.nParams < argc) | |
{ | |
for (int j = i + 1; j < i + option.nParams + 1; j++) | |
{ | |
std::wstring param = argv[j]; | |
if (bVerbose) | |
std::wcout << L"Input param: " << param << std::endl; | |
if (param[0] == '-') | |
{ | |
if (bVerbose) | |
std::wcout << L"Invalid param: " << param << std::endl; | |
return -1; | |
} | |
if (bVerbose) | |
std::wcout << L"Found param: " << param << std::endl; | |
params.emplace_back(param); | |
} | |
i += option.nParams; | |
} | |
else | |
{ | |
if (bVerbose) | |
std::wcout << L"Invalid number of params for: " << arg << std::endl; | |
return -1; | |
} | |
} | |
if (bVerbose) | |
{ | |
std::wcout << L"Result: " << arg << L", id: " << option.Id << L", params:"; | |
for (auto& param : params) | |
std::wcout << L" " << param; | |
std::wcout << std::endl << std::endl; | |
} | |
Result result { option.Id, arg, params }; | |
results.emplace_back(result); | |
} | |
else | |
{ | |
if (bVerbose) | |
std::wcout << L"Invalid option: " << argv[i] << std::endl; | |
return -1; | |
} | |
} | |
bool bHasDuplicates = HasDuplicates(results); | |
if (bHasDuplicates) | |
{ | |
if (bVerbose) | |
std::wcout << L"Duplicate options found." << std::endl; | |
return -1; | |
} | |
return 0; | |
} | |
}; | |
} | |
int main() | |
{ | |
// --presets <input.presets> Input presets file. | |
// --preset,-p <index> Set current preset, default: 0. | |
// --engines <input.engines> Input engines file. | |
// --engine,-e <index> Set current engine, default: 0. | |
// --files <input.files|input.mux> Input files or mux file. | |
// --mono,-m Set multi-mono input files flag. | |
// --input,-i <filename,...> Input file names (one or more). | |
// --output,-o <path|file.ac3> Output path or output file name. | |
// --help,-h Show command-line help. | |
enum OptionId : int | |
{ | |
Presets, | |
Preset, | |
Engines, | |
Engine, | |
Files, | |
Mono, | |
Input, | |
Output, | |
Help | |
}; | |
std::vector<util::Option> options | |
{ | |
{ OptionId::Presets, { L"--presets" }, 1 }, | |
{ OptionId::Preset, { L"--preset", L"-p" }, 1 }, | |
{ OptionId::Engines, { L"--engines" }, 1 }, | |
{ OptionId::Engine, { L"--engine", L"-e" }, 1 }, | |
{ OptionId::Files, { L"--files" }, 1 }, | |
{ OptionId::Mono, { L"--mono", L"-m" }, 0 }, | |
{ OptionId::Input, { L"--input", L"-i" }, -1 }, | |
{ OptionId::Output, { L"--output", L"-o" }, 1 }, | |
{ OptionId::Help, { L"--help", L"-h" }, 0 } | |
}; | |
/* | |
const int argc = 2; | |
const wchar_t *argv[] { | |
L"program.exe", L"--files" }; | |
//*/ | |
///* | |
const int argc = 6; | |
const wchar_t *argv[] { | |
L"program.exe", L"--files", L"input.files", L"--preset", L"0", L"-m" }; | |
//*/ | |
/* | |
const int argc = 6; | |
const wchar_t *argv[] { | |
L"program.exe", L"--input", L"L.wav", L"R.wav", L"C.wav", L"-m" }; | |
//*/ | |
/* | |
const int argc = 5; | |
const wchar_t *argv[] { | |
L"program.exe", L"-m", L"-m", L"-i", L"input.wav" }; | |
//*/ | |
std::vector<util::Result> results; | |
util::ArgvParser parser { true }; | |
int nResult = parser.ParseOptions(argc, argv, options, results); | |
std::wcout << L"Parse result: " << nResult << std::endl; | |
if (nResult == 0) | |
{ | |
for (auto& result : results) | |
{ | |
switch (result.Id) | |
{ | |
case OptionId::Presets: | |
{ | |
std::wcout << L"Option: Presets" << std::endl; | |
} | |
break; | |
case OptionId::Preset: | |
{ | |
std::wcout << L"Option: Preset" << std::endl; | |
} | |
break; | |
case OptionId::Engines: | |
{ | |
std::wcout << L"Option: Engines" << std::endl; | |
} | |
break; | |
case OptionId::Engine: | |
{ | |
std::wcout << L"Option: Engine" << std::endl; | |
} | |
break; | |
case OptionId::Files: | |
{ | |
std::wcout << L"Option: Files" << std::endl; | |
} | |
break; | |
case OptionId::Mono: | |
{ | |
std::wcout << L"Option: Mono" << std::endl; | |
} | |
break; | |
case OptionId::Input: | |
{ | |
std::wcout << L"Option: Input" << std::endl; | |
} | |
break; | |
case OptionId::Output: | |
{ | |
std::wcout << L"Option: Output" << std::endl; | |
} | |
break; | |
case OptionId::Help: | |
{ | |
std::wcout << L"Option: Help" << std::endl; | |
} | |
break; | |
default: | |
{ | |
std::wcout << L"Unknown option!" << std::endl; | |
} | |
break; | |
} | |
} | |
return 0; | |
} | |
return -1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment