From 8fd42a13c7814988f820aa64d1b6cc2b4eb1b7a9 Mon Sep 17 00:00:00 2001 From: mor0146 <vojtech.moravec.st@vsb.cz> Date: Tue, 30 Jul 2019 21:41:19 +0200 Subject: [PATCH] Added CliOptionGroup. Improved help print. --- czi/source_code/app/main.cpp | 37 ++-- .../include/azgra/cli/cli_arguments.h | 12 ++ .../azgra_lib/include/azgra/cli/cli_option.h | 40 ++++- .../include/azgra/string/simple_string.h | 16 +- .../azgra_lib/src/cli/cli_arguments.cpp | 158 ++++++++++++------ .../azgra_lib/src/string/simple_string.cpp | 98 ++++++++--- 6 files changed, 263 insertions(+), 98 deletions(-) diff --git a/czi/source_code/app/main.cpp b/czi/source_code/app/main.cpp index d0b02f4..8ff41a0 100644 --- a/czi/source_code/app/main.cpp +++ b/czi/source_code/app/main.cpp @@ -173,12 +173,15 @@ int main(azgra::i32 argc, const char **argv) 'b', "bins", false, 10); azgra::cli::CliFlag flagVerbose("Verbose", "Make program output verbose.", 'v', "verbose"); + + azgra::cli::CliFlag flagGzipCompressionOption("GZIP", "gzip (zlib) compression", '\0', "gzip"); azgra::cli::CliFlag flagLzmaCompressionOption("LZMA", "lzma compression", '\0', "lzma"); azgra::cli::CliFlag flagBzip2CompressionOption("BZIP2", "bzip2 compression", '\0', "bzip2"); azgra::cli::CliFlag flagB3dCompressionOption("B3D", "B3D cuda compression", '\0', "b3d"); - + azgra::cli::CliOptionGroup compressorGroup("CompressorGroup", {&flagGzipCompressionOption, &flagLzmaCompressionOption, + &flagBzip2CompressionOption, &flagB3dCompressionOption}); // Methods azgra::cli::CliMethod methodVersion("version", @@ -188,42 +191,40 @@ int main(azgra::i32 argc, const char **argv) "Report information about loaded czi file."); azgra::cli::CliMethod methodDump("dump", - "Dump raw image data. [subblock]", - {&flagFolder}); + "Dump raw image data.", + {&flagFolder}, {&valFlagSubblock}); azgra::cli::CliMethod methodCompressionBenchmark("compressors-benchmark", - "Compressors benchmark for subblocks in czi file. [compression-level] [subblock]" - " [compressor]", - {&flagReportFile}); + "Compressors benchmark for subblocks in czi file.", + {&flagReportFile}, {&valFlagCompressionLevel, &valFlagSubblock, &compressorGroup}); azgra::cli::CliMethod methodFrameDiffCompression("frame-diff-compression", - "Compress frames by their difference. [compression-level] [compressor]", - {&flagReportFile}); + "Compress frames by their difference.", + {&flagReportFile}, {&valFlagCompressionLevel, &compressorGroup}); azgra::cli::CliMethod methodFrameDiffHistogram("frame-diff-histogram", - "Save histograms of frame differences. [compression-level] [bin-count]", - {&flagFolder}); + "Save histograms of frame differences.", + {&flagFolder}, {&valFlagCompressionLevel, &valFlagHistogramBinCount}); azgra::cli::CliMethod methodHistogram("histogram", - "Save histograms of all frames or just one specific frame. [subblock] [bin-count]", - {&flagFolder}); + "Save histograms of all frames or just one specific frame.", + {&flagFolder}, {&valFlagSubblock, &valFlagHistogramBinCount}); azgra::cli::CliMethod methodCudaCompress("b3d", - "Test only b3d compression. [subblock]", - {&flagReportFile}); + "Test only b3d compression.", + {&flagReportFile}, {&valFlagSubblock}); azgra::cli::CliMethod methodCudaCompressLossyTest("b3d-quantization-test", - "Test quantization steps of lossy compression. <report-file> <subblock>", + "Test quantization steps of lossy compression.", {&flagFolder, &flagReportFile}); args.flags = {&flagCziFile, &flagFolder, &flagReportFile, &valFlagSubblock, &valFlagCompressionLevel, &valFlagHistogramBinCount, - &flagVerbose, &flagGzipCompressionOption, &flagLzmaCompressionOption, &flagBzip2CompressionOption, - &flagB3dCompressionOption}; + &flagVerbose}; + args.add_group(compressorGroup); args.methods = {&methodVersion, &methodReport, &methodDump, &methodCompressionBenchmark, &methodFrameDiffCompression, &methodFrameDiffHistogram, &methodHistogram, &methodCudaCompress, &methodCudaCompressLossyTest}; - if (!args.parse(argc, argv)) { fprintf(stderr, "%s\n", args.get_error().c_str()); diff --git a/czi/source_code/azgra_lib/include/azgra/cli/cli_arguments.h b/czi/source_code/azgra_lib/include/azgra/cli/cli_arguments.h index d9176af..0bf68a4 100644 --- a/czi/source_code/azgra_lib/include/azgra/cli/cli_arguments.h +++ b/czi/source_code/azgra_lib/include/azgra/cli/cli_arguments.h @@ -18,22 +18,33 @@ namespace azgra class CliArguments { private: + const int FIRST_COLUMN_WIDTH = 30; + const int SECOND_COLUMN_WIDTH = 60; std::stringstream errorStream; string::SmartStringView<char> appName; string::SmartStringView<char> appDescription; int outputWidth = 80; bool someMethodMatched = false; + std::vector<CliOptionGroup> groups; bool process_matched_flag(const string::SmartStringView<char> &match, bool shortMatch, const char **arguments, int &parseIndex); + bool process_matched_value_flag(CliOption *matchedFlag, const char *rawFlagValue); + bool process_matched_method(const string::SmartStringView<char> &match); + bool process_multiflag(const string::SmartStringView<char> &match); + + void print_flags(std::stringstream &outStream, const std::vector<CliOption *> &flags) const; + std::vector<CliOption*> get_flags_not_in_group() const; + public: std::vector<CliMethod *> methods; std::vector<CliOption *> flags; CliArguments(const string::SmartStringView<char> &name, const string::SmartStringView<char> &description, int width = 80); + void add_group(const CliOptionGroup &flagGroup); bool parse(const int argc, const char **argv); @@ -42,6 +53,7 @@ namespace azgra std::string get_error() const; bool is_any_method_matched() const; + }; }; }; \ No newline at end of file diff --git a/czi/source_code/azgra_lib/include/azgra/cli/cli_option.h b/czi/source_code/azgra_lib/include/azgra/cli/cli_option.h index 63b8ba4..2feb3f4 100644 --- a/czi/source_code/azgra_lib/include/azgra/cli/cli_option.h +++ b/czi/source_code/azgra_lib/include/azgra/cli/cli_option.h @@ -16,11 +16,12 @@ namespace azgra bool isMatched = false; bool isRequired = false; bool hasMatchCharacter = true; + bool isInGroup = false; string::SmartStringView<char> name; string::SmartStringView<char> description; - CliOption(const string::SmartStringView<char>& name, const string::SmartStringView<char>& description, - char matchCharacter, const string::SmartStringView<char>& matchString, bool required = false) + CliOption(const string::SmartStringView<char> &name, const string::SmartStringView<char> &description, + char matchCharacter, const string::SmartStringView<char> &matchString, bool required = false) { this->name = name; this->description = description; @@ -48,12 +49,15 @@ namespace azgra { private: std::vector<CliOption *> requiredFlags; + std::vector<CliOption *> optionalFlags; public: CliMethod(string::SmartStringView<char> methodMatchName, string::SmartStringView<char> description, - const std::vector<CliOption *> &requiredFlags = std::vector<CliOption *>()) : + const std::vector<CliOption *> &requiredFlags = std::vector<CliOption *>(), + const std::vector<CliOption *> &optionalFlags = std::vector<CliOption *>()) : CliOption(methodMatchName, description, '\0', methodMatchName, false) { this->requiredFlags = requiredFlags; + this->optionalFlags = optionalFlags; hasMatchCharacter = false; } @@ -62,14 +66,19 @@ namespace azgra return requiredFlags; } + std::vector<CliOption *> const &get_optional_flags() const + { + return optionalFlags; + } + }; class CliFlag : public CliOption { public: - CliFlag(const string::SmartStringView<char>& flagName, const string::SmartStringView<char>& description, char matchChar, - const string::SmartStringView<char>& matchString, bool required = false) : + CliFlag(const string::SmartStringView<char> &flagName, const string::SmartStringView<char> &description, char matchChar, + const string::SmartStringView<char> &matchString, bool required = false) : CliOption(flagName, description, matchChar, matchString, required) { if (matchChar == '\0') @@ -88,8 +97,8 @@ namespace azgra private: ValueType internalValue; public: - CliValueFlag(const string::SmartStringView<char>& flagName, const string::SmartStringView<char>& description, char matchChar, - const string::SmartStringView<char>& matchString, bool required = false, ValueType defaultValue = ValueType()) : + CliValueFlag(const string::SmartStringView<char> &flagName, const string::SmartStringView<char> &description, char matchChar, + const string::SmartStringView<char> &matchString, bool required = false, ValueType defaultValue = ValueType()) : CliOption(flagName, description, matchChar, matchString, required) { internalValue = defaultValue; @@ -104,5 +113,22 @@ namespace azgra return internalValue; } }; + + class CliOptionGroup : public CliOption + { + friend class CliArguments; + + private: + //NOTE: We can have group rules. Like AtLeastOne, OnlyOne, All + std::vector<CliOption *> options; + public: + CliOptionGroup(const string::SmartStringView<char> &name, const std::vector<CliOption *> &flagsInGroup) : + CliOption(name, "", '\0', "", false) + { + hasMatchCharacter = false; + isRequired = false; + options = flagsInGroup; + } + }; }; }; \ No newline at end of file diff --git a/czi/source_code/azgra_lib/include/azgra/string/simple_string.h b/czi/source_code/azgra_lib/include/azgra/string/simple_string.h index c85f797..f6b7b9c 100644 --- a/czi/source_code/azgra_lib/include/azgra/string/simple_string.h +++ b/czi/source_code/azgra_lib/include/azgra/string/simple_string.h @@ -16,6 +16,7 @@ namespace azgra class SimpleString { private: + bool _isEmpty = true; /** * @brief Internal string memory. */ @@ -73,6 +74,11 @@ namespace azgra SimpleString(char *cString, const size_t length, bool noAlloc = false); public: + /** + * Empty string constructor. + */ + SimpleString(); + /** * @brief Construct a new Simple String object. * @@ -80,6 +86,8 @@ namespace azgra */ SimpleString(const char *cString); + SimpleString(const std::vector<const char *> &strings); + ~SimpleString(); /** @@ -324,8 +332,10 @@ namespace azgra bool operator==(const SimpleString &string) const; void operator+=(const char *cString); + void operator+=(const char c); SimpleString operator+(const char *cString) const; + SimpleString operator+(const char c) const; /** * @brief Create string by replicating. @@ -336,11 +346,11 @@ namespace azgra */ static SimpleString replicate(const char *cString, const azgra::i32 replicationCount); - void pad_left(const char padChar, const size_t padCount); + void pad_left(const char padChar, const size_t desiredWidth); - void pad_right(const char padChar, const size_t padCount); + void pad_right(const char padChar, const size_t desiredWidth); - void centerize(const char padChar, const size_t outputWidth); + void centerize(const char padChar, const size_t desiredWidth); }; }; // namespace string } // namespace azgra diff --git a/czi/source_code/azgra_lib/src/cli/cli_arguments.cpp b/czi/source_code/azgra_lib/src/cli/cli_arguments.cpp index 30f40be..63e78d9 100644 --- a/czi/source_code/azgra_lib/src/cli/cli_arguments.cpp +++ b/czi/source_code/azgra_lib/src/cli/cli_arguments.cpp @@ -138,6 +138,83 @@ namespace azgra return allFound; } + std::vector<CliOption *> CliArguments::get_flags_not_in_group() const + { + std::vector<CliOption *> result; + for (CliOption *flag : flags) + { + if (!flag->isInGroup) + { + result.push_back(flag); + } + } + return result; + } + + void CliArguments::print_flags(std::stringstream &outStream, const std::vector<CliOption *> &flagsToPrint) const + { + + + for (const CliOption *flag : flagsToPrint) + { + const char *req = (flag->isRequired ? "Required" : "Optional"); + + + if (flag->hasMatchCharacter) + { + char matchCharString[2] = {flag->matchCharacter, '\0'}; + + string::SimpleString matcherString({"{-", matchCharString, ", --", + flag->matchString.data(), "} "}); + matcherString.pad_right(' ', FIRST_COLUMN_WIDTH); + outStream << '\t' << matcherString.get_c_string(); + } + else + { + string::SimpleString matcherString({"{--", flag->matchString.data(), "} "}); + matcherString.pad_right(' ', FIRST_COLUMN_WIDTH); + outStream << '\t' << matcherString.get_c_string(); + } + + + string::SimpleString flagNameDesc({flag->name.data(), " - ", flag->description.data()}); + flagNameDesc.pad_right(' ', SECOND_COLUMN_WIDTH); + + outStream << flagNameDesc.get_c_string() << " [" << req;; + + + auto *intFlag = dynamic_cast<const CliValueFlag<int> *> (flag); + if (intFlag) + outStream << ", int"; + + auto *uintFlag = dynamic_cast<const CliValueFlag<unsigned int> *> (flag); + if (uintFlag) + { + outStream << ", uint"; + } + + auto *floatFlag = dynamic_cast<const CliValueFlag<float> *> (flag); + if (floatFlag) + { + outStream << ", float"; + } + + auto *constCharFlag = dynamic_cast<const CliValueFlag<const char *> *> (flag); + if (constCharFlag) + { + outStream << ", const char*"; + } + + auto *stringFlag = dynamic_cast<const CliValueFlag<std::string> *> (flag); + if (stringFlag) + { + outStream << ", std::string"; + } + outStream << "]" << std::endl; + } + } + + bool CliArguments::parse(const int argc, const char **argv) { const char *helpIdentifier = "--help"; @@ -234,65 +311,36 @@ namespace azgra helpStream << "Methods:" << std::endl; for (const auto &method : methods) { - helpStream << "\t" << method->name.string_view() << " - " << method->description.string_view(); - auto methodReqFlags = method->get_required_flags(); - if (!methodReqFlags.empty()) - { - helpStream << "\tRequired flags: "; - for (size_t i = 0; i < methodReqFlags.size(); ++i) - { - helpStream << '<' << methodReqFlags[i]->name.string_view() << "> "; - } - } - helpStream << std::endl; - } - - helpStream << "Flags:" << std::endl; - for (const CliOption *flag : flags) - { - const char *req = (flag->isRequired ? "Required" : "Optional"); - - if (flag->hasMatchCharacter) - { - helpStream << "\t -" << flag->matchCharacter << ", --" - << flag->matchString.string_view() << "=" << - flag->name.string_view() << " - " << flag->description.string_view() << " [" << req; - } - else - { - helpStream << "\t --" << flag->matchString.string_view() << "=" << - flag->name.string_view() << " - " << flag->description.string_view() << " [" << req; - } - + string::SimpleString methodName(method->name.data()); + string::SimpleString methodDesc(method->description.data()); + methodName.pad_right(' ', FIRST_COLUMN_WIDTH); + methodDesc.pad_right(' ', SECOND_COLUMN_WIDTH); + helpStream << "\t" << methodName.get_c_string() << methodDesc.get_c_string(); - auto *intFlag = dynamic_cast<const CliValueFlag<int> *> (flag); - if (intFlag) - helpStream << ", int"; - - auto *uintFlag = dynamic_cast<const CliValueFlag<unsigned int> *> (flag); - if (uintFlag) + auto methodReqFlags = method->get_required_flags(); + auto methodOptionalFlags = method->get_optional_flags(); + if (!methodReqFlags.empty() || !methodOptionalFlags.empty()) { - helpStream << ", uint"; + helpStream << " Flags: "; } - auto *floatFlag = dynamic_cast<const CliValueFlag<float> *> (flag); - if (floatFlag) + for (const auto &methodReqFlag : methodReqFlags) { - helpStream << ", float"; + helpStream << '<' << methodReqFlag->name.string_view() << "> "; } - - auto *constCharFlag = dynamic_cast<const CliValueFlag<const char *> *> (flag); - if (constCharFlag) + for (const auto &methodOptionalFlag : methodOptionalFlags) { - helpStream << ", const char*"; + helpStream << '[' << methodOptionalFlag->name.string_view() << "] "; } + helpStream << std::endl; + } - auto *stringFlag = dynamic_cast<const CliValueFlag<std::string> *> (flag); - if (stringFlag) - { - helpStream << ", std::string"; - } - helpStream << "]" << std::endl; + helpStream << "Flags:" << std::endl; + print_flags(helpStream, get_flags_not_in_group()); + for (const CliOptionGroup &group : groups) + { + helpStream << "Flags - " << group.name.string_view() << std::endl; + print_flags(helpStream, group.options); } fprintf(stdout, "%s", helpStream.str().c_str()); @@ -308,5 +356,15 @@ namespace azgra { return someMethodMatched; } + + void CliArguments::add_group(const CliOptionGroup &flagGroup) + { + groups.push_back(flagGroup); + for (CliOption *flag : flagGroup.options) + { + flag->isInGroup = true; + flags.push_back(flag); + } + } }; }; \ No newline at end of file diff --git a/czi/source_code/azgra_lib/src/string/simple_string.cpp b/czi/source_code/azgra_lib/src/string/simple_string.cpp index 46cecbe..c4a8746 100644 --- a/czi/source_code/azgra_lib/src/string/simple_string.cpp +++ b/czi/source_code/azgra_lib/src/string/simple_string.cpp @@ -25,7 +25,7 @@ namespace azgra void SimpleString::free_string(char *memory) { - if (memory != nullptr) + if (memory != nullptr && memory[0] != '\0') { ::operator delete(memory); memory = nullptr; @@ -47,6 +47,7 @@ namespace azgra { size_t inputStringLen = c_string_length(string); + _isEmpty = (inputStringLen == 0); _length = inputStringLen; _string = alloc_string(_length + 1); @@ -56,11 +57,51 @@ namespace azgra _string[_length] = '\0'; } + SimpleString::SimpleString(const char *cString) + { + internal_initalize(cString); + } + SimpleString::SimpleString(char *cString, const size_t length, bool noAlloc) { assert(noAlloc); _string = cString; _length = length; + _isEmpty = (length == 0); + } + + SimpleString::SimpleString(const std::vector<const char *> &strings) + { + _isEmpty = true; + size_t finalLen = 0; + std::vector<size_t> lengths; + for (const char *string : strings) + { + size_t len = c_string_length(string); + lengths.push_back(len); + finalLen += len; + } + if (finalLen > 0) + { + _isEmpty = false; + _string = alloc_string(finalLen + 1); + size_t offset = 0; + for (size_t stringIndex = 0; stringIndex < strings.size(); ++stringIndex) + { + memcpy(_string + offset, strings[stringIndex], lengths[stringIndex] ); + offset += lengths[stringIndex]; + } + + _string[finalLen] = '\0'; + _length = finalLen; + } + } + + SimpleString::SimpleString() + { + _string = nullptr; + _length = 0; + _isEmpty = true; } SimpleString::operator const char *() const @@ -84,6 +125,12 @@ namespace azgra concat(cString); } + void SimpleString::operator+=(const char c) + { + char stringToAdd[1] = {c}; + this->operator+=(stringToAdd); + } + SimpleString SimpleString::operator+(const char *cString) const { SimpleString result(_string); @@ -91,6 +138,14 @@ namespace azgra return result; } + SimpleString SimpleString::operator+(const char c) const + { + SimpleString result(_string); + char stringToAdd[1] = {c}; + result.concat(stringToAdd); + return result; + } + bool SimpleString::operator==(const char *cString) const { return equals(cString); @@ -101,11 +156,6 @@ namespace azgra return equals(string); } - SimpleString::SimpleString(const char *cString) - { - internal_initalize(cString); - } - SimpleString::~SimpleString() { free_string(_string); @@ -540,40 +590,48 @@ namespace azgra return result; } - void SimpleString::pad_left(const char padChar, const size_t padCount) + void SimpleString::pad_left(const char padChar, const size_t desiredWidth) { - size_t resultLen = _length + padCount; - char *result = alloc_string(resultLen); + assert(_length <= desiredWidth); + if (_length == desiredWidth) + return; - for (int i = 0; i < padCount; ++i) + char *result = alloc_string(desiredWidth + 1); + int fillSize = desiredWidth - _length; + for (int i = 0; i < fillSize; ++i) { result[i] = padChar; } - for (int j = 0; j < _length; ++j) { - result[padCount + j] = _string[j]; + result[fillSize + j] = _string[j]; } + result[desiredWidth] = '\0'; + free_string(_string); _string = result; - _length = resultLen; + _length = desiredWidth; } - void SimpleString::pad_right(const char padChar, const size_t padCount) + void SimpleString::pad_right(const char padChar, const size_t desiredWidth) { - size_t resultLen = _length + padCount; - _string = realloc_string(resultLen + 1, _string, _length); - for (int i = 0; i < padCount; ++i) + assert(_length <= desiredWidth); + if (_length == desiredWidth) + return; + + _string = realloc_string(desiredWidth + 1, _string, _length); + int fillSize = desiredWidth - _length; + for (int i = 0; i < fillSize; ++i) { _string[_length + i] = padChar; } - _length = resultLen; - _string[resultLen] = '\0'; + _length = desiredWidth; + _string[desiredWidth] = '\0'; } void SimpleString::centerize(const char padChar, const size_t outputWidth) { - size_t actualOutputWidth = (_length < outputWidth) ? outputWidth : (_length + 4); + size_t actualOutputWidth = (_length < outputWidth) ? outputWidth : (_length + 4); size_t halfPadSize = (actualOutputWidth - 2 - static_cast<int>(_length)) / 2; char *result = alloc_string(actualOutputWidth + 1); -- GitLab