Add command line flag processing.
This commit is contained in:
parent
e80f597ec8
commit
4d2abcf434
2
Arena.cc
2
Arena.cc
|
@ -346,7 +346,7 @@ uint8_t &
|
|||
Arena::operator[](size_t index)
|
||||
{
|
||||
if (index > this->size) {
|
||||
#if defined(SCSL_DESKTOP_BUILD) and !defined(SCSL_NO_ASSERT)
|
||||
#if defined(SCSL_DESKTOP_BUILD) and !defined(SCSL_NOEXCEPT)
|
||||
throw std::range_error("index out of range");
|
||||
#else
|
||||
abort();
|
||||
|
|
|
@ -330,7 +330,7 @@ uint8_t &
|
|||
Buffer::operator[](size_t index)
|
||||
{
|
||||
if (index > this->length) {
|
||||
#if defined(SCSL_DESKTOP_BUILD) and !defined(SCSL_NO_ASSERT)
|
||||
#if defined(SCSL_DESKTOP_BUILD) and !defined(SCSL_NOEXCEPT)
|
||||
throw std::range_error("array index out of bounds");
|
||||
#else
|
||||
abort();
|
||||
|
|
|
@ -5,6 +5,7 @@ if (${DOXYGEN_FOUND})
|
|||
set(DOXYGEN_GENERATE_MAN YES)
|
||||
set(DOXYGEN_GENERATE_LATEX YES)
|
||||
#set(DOXYGEN_EXTRACT_ALL YES)
|
||||
message(STATUS "Doxygen found, building docs.")
|
||||
|
||||
doxygen_add_docs(scsl_docs
|
||||
${HEADER_FILES} ${SOURCE_FILES}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
project(scsl LANGUAGES CXX
|
||||
VERSION 0.1.0
|
||||
VERSION 0.1.1
|
||||
DESCRIPTION "Shimmering Clarity Standard Library")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
@ -33,6 +33,7 @@ set(HEADER_FILES scsl.h
|
|||
Buffer.h
|
||||
Dictionary.h
|
||||
Exceptions.h
|
||||
Flag.h
|
||||
StringUtil.h
|
||||
TLV.h
|
||||
Test.h
|
||||
|
@ -45,6 +46,7 @@ set(SOURCE_FILES
|
|||
Commander.h
|
||||
Dictionary.cc
|
||||
Exceptions.cc
|
||||
Flag.cc
|
||||
StringUtil.cc
|
||||
TLV.cc
|
||||
Test.cc
|
||||
|
@ -66,6 +68,10 @@ target_link_libraries(phonebook scsl)
|
|||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
add_executable(buffer_test bufferTest.cc)
|
||||
target_link_libraries(buffer_test scsl)
|
||||
add_test(bufferTest buffer_test)
|
||||
|
||||
add_executable(tlv_test tlvTest.cc)
|
||||
target_link_libraries(tlv_test scsl)
|
||||
add_test(tlvTest tlv_test)
|
||||
|
@ -74,9 +80,9 @@ add_executable(dictionary_test dictionaryTest.cc)
|
|||
target_link_libraries(dictionary_test scsl)
|
||||
add_test(dictionaryTest dictionary_test)
|
||||
|
||||
add_executable(buffer_test bufferTest.cc)
|
||||
target_link_libraries(buffer_test scsl)
|
||||
add_test(bufferTest buffer_test)
|
||||
add_executable(flag_test flagTest.cc)
|
||||
target_link_libraries(flag_test scsl)
|
||||
add_test(flagTest flag_test)
|
||||
|
||||
add_executable(stringutil_test stringutil_test.cc)
|
||||
target_link_libraries(stringutil_test scsl)
|
||||
|
@ -94,7 +100,7 @@ add_custom_target(cloc
|
|||
COMMAND cloc ${SOURCE_FILES} ${HEADER_FILES}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
add_custom_target(deploy-docs
|
||||
COMMAND rsync --progress -auvz ${CMAKE_CURRENT_BINARY_DIR}/html/* docs.shimmering-clarity.net:sites/cc/
|
||||
COMMAND rsync --delete-after --progress -auvz ${CMAKE_CURRENT_BINARY_DIR}/html/* docs.shimmering-clarity.net:sites/cc/${PROJECT_NAME}/
|
||||
DEPENDS scsl_docs
|
||||
)
|
||||
|
||||
|
|
235
Flag.cc
235
Flag.cc
|
@ -30,18 +30,45 @@
|
|||
#include <vector>
|
||||
|
||||
#include "Flag.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
|
||||
namespace klib {
|
||||
namespace scsl {
|
||||
|
||||
|
||||
static const std::regex isFlag("^-[a-zA-Z0-9]+$",
|
||||
static const std::regex isFlag("^--?[a-zA-Z0-9][a-zA-Z0-9-_]*$",
|
||||
std::regex_constants::nosubs);
|
||||
|
||||
|
||||
Flag::Flag(FlagType fType, std::string fName, std::string fDescription)
|
||||
: Type(fType), WasSet(false), Name(fName), Description(fDescription)
|
||||
std::string
|
||||
ParseStatusToString(ParseStatus status)
|
||||
{
|
||||
switch (status) {
|
||||
case ParseStatus::OK:
|
||||
return "OK";
|
||||
case ParseStatus::EndOfFlags:
|
||||
return "end of flags";
|
||||
case ParseStatus::NotRegistered:
|
||||
return "flag not registered";
|
||||
case ParseStatus::NotEnoughArgs:
|
||||
return "not enough args passed to flags";
|
||||
default:
|
||||
return "unknown/unspecified parse error";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Flag *
|
||||
NewFlag(FlagType fType, std::string fName, std::string fDescription)
|
||||
{
|
||||
auto flag = new Flag;
|
||||
|
||||
flag->Type = fType;
|
||||
flag->WasSet = false;
|
||||
flag->Name = fName;
|
||||
flag->Description = fDescription;
|
||||
flag->Value = FlagValue{};
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,16 +95,79 @@ Flags::Register(std::string fName, FlagType fType, std::string fDescription)
|
|||
return false;
|
||||
}
|
||||
|
||||
auto flag = new Flag;
|
||||
flag->Type = fType;
|
||||
flag->WasSet = false;
|
||||
flag->name = name;
|
||||
this->desription = fDescription;
|
||||
auto flag = NewFlag(fType, fName, fDescription);
|
||||
this->flags[fName] = flag;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::Register(std::string fName, bool defaultValue, std::string fDescription)
|
||||
{
|
||||
if (!this->Register(fName, FlagType::Boolean, fDescription)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->flags[fName]->Value.b = defaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::Register(std::string fName, int defaultValue, std::string fDescription)
|
||||
{
|
||||
if (!this->Register(fName, FlagType::Integer, fDescription)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->flags[fName]->Value.i = defaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::Register(std::string fName, unsigned int defaultValue, std::string fDescription)
|
||||
{
|
||||
if (!this->Register(fName, FlagType::UnsignedInteger, fDescription)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->flags[fName]->Value.u = defaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::Register(std::string fName, size_t defaultValue, std::string fDescription)
|
||||
{
|
||||
if (!this->Register(fName, FlagType::SizeT, fDescription)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->flags[fName]->Value.size = defaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::Register(std::string fName, std::string defaultValue, std::string fDescription)
|
||||
{
|
||||
if (!this->Register(fName, FlagType::String, fDescription)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->flags[fName]->Value.s = new std::string(defaultValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
Flags::Size()
|
||||
{
|
||||
return this->flags.size();
|
||||
}
|
||||
|
||||
|
||||
Flag *
|
||||
Flags::Lookup(std::string fName)
|
||||
{
|
||||
|
@ -85,18 +175,19 @@ Flags::Lookup(std::string fName)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
return this->flags[fName];
|
||||
}
|
||||
|
||||
|
||||
OptFlagValue
|
||||
Flags::ValueOf(std::string fName)
|
||||
bool
|
||||
Flags::ValueOf(std::string fName, FlagValue &value)
|
||||
{
|
||||
if (this->flags.count(fName)) {
|
||||
return std::nullopt;
|
||||
return false;
|
||||
}
|
||||
|
||||
return OptFlagValue(this->flags[fName]->Value);
|
||||
value = this->flags[fName]->Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -106,6 +197,8 @@ Flags::parseArg(int argc, char **argv, int &index)
|
|||
{
|
||||
std::string arg(argv[index]);
|
||||
|
||||
U::S::TrimWhitespace(arg);
|
||||
|
||||
index++;
|
||||
if (!std::regex_search(arg, isFlag)) {
|
||||
return ParseStatus::EndOfFlags;
|
||||
|
@ -116,15 +209,33 @@ Flags::parseArg(int argc, char **argv, int &index)
|
|||
}
|
||||
|
||||
auto flag = flags[arg];
|
||||
if (flag->Type == FlagType::Boolean) {
|
||||
switch (flag->Type) {
|
||||
case FlagType::Boolean:
|
||||
flag->WasSet = true;
|
||||
flag->Value.b = true;
|
||||
return ParseStatus::OK;
|
||||
}
|
||||
|
||||
switch (flag->Type) {
|
||||
case FlagType::Integer:
|
||||
flag->WasSet = true;
|
||||
flag->Value.i = std::stoi(argv[++index], 0, 0);
|
||||
return ParseStatus::OK;
|
||||
case FlagType::UnsignedInteger:
|
||||
flag->WasSet = true;
|
||||
flag->Value.u = static_cast<unsigned int>(std::stoi(argv[index++], 0, 0));
|
||||
return ParseStatus::OK;
|
||||
case FlagType::String:
|
||||
flag->WasSet = true;
|
||||
flag->Value.s = new std::string(argv[index++]);
|
||||
return ParseStatus::OK;
|
||||
case FlagType::SizeT:
|
||||
flag->WasSet = true;
|
||||
flag->Value.size = static_cast<size_t>(std::stoull(argv[index++]));
|
||||
return ParseStatus::OK;
|
||||
default:
|
||||
throw std::runtime_error("not handled");
|
||||
#if defined(NDEBUG) or defined(SCSL_NOEXCEPT)
|
||||
return ParseStatus::Unknown;
|
||||
#else
|
||||
throw std::runtime_error("unhandled type");
|
||||
#endif
|
||||
}
|
||||
|
||||
return ParseStatus::OK;
|
||||
|
@ -148,12 +259,16 @@ Flags::Parse(int argc, char **argv)
|
|||
index++;
|
||||
}
|
||||
continue;
|
||||
case ParseStatus::EndOfFlags:
|
||||
case ParseStatus::NotEnoughArgs:
|
||||
case ParseStatus::NotRegistered:
|
||||
// fall through //
|
||||
return result;
|
||||
default:
|
||||
throw runtime_error("unhandled parse state");
|
||||
#if defined(NDEBUG) or defined(SCSL_NOEXCEPT)
|
||||
return ParseStatus::Unknown;
|
||||
#else
|
||||
throw std::runtime_error("unhandled parse state");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -176,16 +291,80 @@ Flags::Args()
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::GetBool(std::string name, bool &flagValue)
|
||||
Flag *
|
||||
Flags::checkGetArg(std::string fName, FlagType eType)
|
||||
{
|
||||
if (this->flags[name] == 0) {
|
||||
return false;
|
||||
if (this->flags[fName] == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::get<bool>(this->flags[name]->Value);
|
||||
auto flag = this->flags[fName];
|
||||
if (flag == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (flag->Type != eType) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
|
||||
} // namespace klib
|
||||
bool
|
||||
Flags::GetBool(std::string fName, bool &flagValue)
|
||||
{
|
||||
auto flag = this->checkGetArg(fName, FlagType::Boolean);
|
||||
|
||||
flagValue = flag->Value.b;
|
||||
return flag->WasSet;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::GetInteger(std::string fName, int &flagValue)
|
||||
{
|
||||
auto flag = this->checkGetArg(fName, FlagType::Integer);
|
||||
|
||||
flagValue = flag->Value.i;
|
||||
return flag->WasSet;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::GetUnsignedInteger(std::string fName, unsigned int &flagValue)
|
||||
{
|
||||
auto flag = this->checkGetArg(fName, FlagType::UnsignedInteger);
|
||||
|
||||
flagValue = flag->Value.u;
|
||||
return flag->WasSet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
Flags::GetSizeT(std::string fName, std::size_t &flagValue)
|
||||
{
|
||||
auto flag = this->checkGetArg(fName, FlagType::SizeT);
|
||||
|
||||
flagValue = flag->Value.size;
|
||||
return flag->WasSet;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Flags::GetString(std::string fName, std::string &flagValue)
|
||||
{
|
||||
auto flag = this->checkGetArg(fName, FlagType::String);
|
||||
|
||||
if (flag->Value.s == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
flagValue = *(flag->Value.s);
|
||||
return flag->WasSet;
|
||||
}
|
||||
|
||||
|
||||
} // namespace scsl
|
||||
|
||||
|
|
81
Flag.h
81
Flag.h
|
@ -26,49 +26,56 @@
|
|||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace klib {
|
||||
|
||||
namespace scsl {
|
||||
|
||||
|
||||
/// FlagType indicates the value held in a FlagValue.
|
||||
enum class FlagType : uint8_t {
|
||||
Unknown = 0,
|
||||
Boolean = 1,
|
||||
Integer = 2,
|
||||
UnsignedInteger = 3,
|
||||
String = 4,
|
||||
Integer = 2, ///< int32_t
|
||||
UnsignedInteger = 3, ///< uint32_t
|
||||
SizeT = 4, ///< size_t
|
||||
String = 5,
|
||||
};
|
||||
|
||||
|
||||
enum class ParseStatus : uint8_t {
|
||||
OK = 0,
|
||||
EndOfFlags = 1,
|
||||
NotRegistered = 2,
|
||||
NotEnoughArgs = 3,
|
||||
Unknown = 0,
|
||||
OK = 1,
|
||||
EndOfFlags = 2,
|
||||
NotRegistered = 3,
|
||||
NotEnoughArgs = 4,
|
||||
};
|
||||
|
||||
|
||||
typedef std::variant<
|
||||
std::string *,
|
||||
bool *,
|
||||
int64_t *,
|
||||
uint64_t *> FlagValue;
|
||||
std::string
|
||||
ParseStatusToString(ParseStatus status);
|
||||
|
||||
|
||||
struct Flag {
|
||||
Flag(FlagType fType, std::string fName, std::string fDescription);
|
||||
typedef union {
|
||||
unsigned int u;
|
||||
int i;
|
||||
std::size_t size;
|
||||
std::string *s;
|
||||
bool b;
|
||||
} FlagValue;
|
||||
|
||||
|
||||
typedef struct {
|
||||
FlagType Type;
|
||||
bool WasSet;
|
||||
std::string Name;
|
||||
std::string Description;
|
||||
FlagValue Value;
|
||||
};
|
||||
typedef std::optional<FlagValue> OptFlagValue;
|
||||
} Flag;
|
||||
|
||||
Flag *
|
||||
NewFlag(FlagType fType, std::string fName, std::string fDescription);
|
||||
|
||||
|
||||
class Flags {
|
||||
|
@ -79,11 +86,26 @@ public:
|
|||
bool Register(std::string fName,
|
||||
FlagType fType,
|
||||
std::string fDescription);
|
||||
bool Register(std::string fName,
|
||||
bool defaultValue,
|
||||
std::string fDescription);
|
||||
bool Register(std::string fName,
|
||||
int defaultValue,
|
||||
std::string fDescription);
|
||||
bool Register(std::string fName,
|
||||
unsigned int defaultValue,
|
||||
std::string fDescription);
|
||||
bool Register(std::string fName,
|
||||
size_t defaultValue,
|
||||
std::string fDescription);
|
||||
bool Register(std::string fName,
|
||||
std::string defaultValue,
|
||||
std::string fDescription);
|
||||
size_t Size();
|
||||
Flag *Lookup(std::string fName);
|
||||
OptFlagValue
|
||||
ValueOf(std::string fName);
|
||||
bool ValueOf(std::string fName, FlagValue &value);
|
||||
|
||||
int Parse(int argc, char **argv);
|
||||
ParseStatus Parse(int argc, char **argv);
|
||||
|
||||
void Usage(std::ostream &os, int exitCode);
|
||||
|
||||
|
@ -91,14 +113,16 @@ public:
|
|||
std::vector<std::string> Args();
|
||||
std::string Arg(int index);
|
||||
|
||||
bool GetBool(std::string name, bool &flagValue);
|
||||
// bool GetUnsignedInteger(std::string name, uint64_t &flagValue)
|
||||
// bool GetInteger(std::string name, int64_t &flagValue)
|
||||
// bool GetString(std::string name, std::string &flagValue)
|
||||
bool GetBool(std::string fName, bool &flagValue);
|
||||
bool GetUnsignedInteger(std::string fName, unsigned int &flagValue);
|
||||
bool GetInteger(std::string fName, int &flagValue);
|
||||
bool GetString(std::string fName, std::string &flagValue);
|
||||
bool GetSizeT(std::string fName, std::size_t &flagValue);
|
||||
|
||||
|
||||
private:
|
||||
ParseStatus parseArg(int argc, char **argv, int &index);
|
||||
Flag *checkGetArg(std::string fName, FlagType eType);
|
||||
|
||||
std::string name;
|
||||
std::string description;
|
||||
|
@ -107,5 +131,4 @@ private:
|
|||
};
|
||||
|
||||
|
||||
|
||||
} // namespace klib
|
||||
} // namespace scsl
|
||||
|
|
4
Test.cc
4
Test.cc
|
@ -15,7 +15,7 @@ namespace scsl {
|
|||
void
|
||||
TestAssert(bool condition, std::string message)
|
||||
{
|
||||
#if defined(NDEBUG) || defined(SCSL_NO_ASSERT)
|
||||
#if defined(NDEBUG) || defined(SCSL_NOEXCEPT)
|
||||
if (!condition) {
|
||||
throw AssertionFailed(message);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ TestAssert(bool condition)
|
|||
if (condition) {
|
||||
return;
|
||||
}
|
||||
#if defined(SCSL_NO_ASSERT)
|
||||
#if defined(SCSL_NOEXCEPT)
|
||||
std::cerr << "Assertion failed!\n";
|
||||
#else
|
||||
std::stringstream msg;
|
||||
|
|
2
Test.h
2
Test.h
|
@ -29,7 +29,7 @@ void TestAssert(bool condition);
|
|||
/// If NDEBUG is set, TestAssert will throw an exception if condition is false.
|
||||
/// Otherwise, it calls assert after printing the message.
|
||||
///
|
||||
/// In addition to NDEBUG, SCSL_NO_ASSERT will suppress assertions.
|
||||
/// In addition to NDEBUG, SCSL_NOEXCEPT will suppress assertions.
|
||||
///
|
||||
/// \throws AssertionFailed
|
||||
///
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Created by kyle on 10/15/23.
|
||||
//
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include "Flag.h"
|
||||
#include "Test.h"
|
||||
|
||||
|
||||
using namespace scsl;
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
bool testFlag = false;
|
||||
size_t testSize = 0;
|
||||
unsigned int testUnsigned = 0;
|
||||
int testInteger = 0;
|
||||
std::string testString;
|
||||
|
||||
auto flags = new Flags("flag_test", "this is a test of the flag functionality.");
|
||||
flags->Register("-b", FlagType::Boolean, "test boolean");
|
||||
flags->Register("-s", FlagType::String, "test string");
|
||||
flags->Register("-u", (unsigned int)42, "test unsigned integer");
|
||||
flags->Register("-i", -42, "test integer");
|
||||
flags->Register("-size", FlagType::SizeT, "test size_t");
|
||||
TestAssert(flags->Size() == 5, "flags weren't registered");
|
||||
|
||||
auto status = flags->Parse(argc, argv);
|
||||
|
||||
if (status != ParseStatus::OK) {
|
||||
std::cerr << "failed to parse flags: " << ParseStatusToString(status) << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto wasSet = flags->GetBool("-b", testFlag);
|
||||
std::cout << " (bool) test flag was set: " << wasSet << "\n";
|
||||
std::cout << " (bool) test flag value: " << testFlag << "\n";
|
||||
|
||||
wasSet = flags->GetInteger("-i", testInteger);
|
||||
std::cout << " (int) test flag was set: " << wasSet << "\n";
|
||||
std::cout << " (int) test flag value: " << testInteger << "\n";
|
||||
|
||||
wasSet = flags->GetUnsignedInteger("-u", testUnsigned);
|
||||
std::cout << " (uint) test flag was set: " << wasSet << "\n";
|
||||
std::cout << " (uint) test flag value: " << testUnsigned << "\n";
|
||||
|
||||
wasSet = flags->GetSizeT("-size", testSize);
|
||||
std::cout << "(size_t) test flag was set: " << wasSet << "\n";
|
||||
std::cout << "(size_t) test flag value: " << testSize << "\n";
|
||||
|
||||
wasSet = flags->GetString("-s", testString);
|
||||
std::cout << "(string) test flag was set: " << wasSet << "\n";
|
||||
std::cout << "(string) test flag value: " << testString << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue