Skip to content

Instantly share code, notes, and snippets.

@wieslawsoltes
Created February 22, 2018 13:54
Show Gist options
  • Save wieslawsoltes/417fe0c736b1c7a3af53cc98c7db4bfd to your computer and use it in GitHub Desktop.
Save wieslawsoltes/417fe0c736b1c7a3af53cc98c7db4bfd to your computer and use it in GitHub Desktop.
Command-line parser for C++ apps.
#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