diff --git a/judges/recodex_token_judge/bpplib/algo/lcs.hpp b/judges/recodex_token_judge/bpplib/algo/lcs.hpp index 643427e2..472710ad 100644 --- a/judges/recodex_token_judge/bpplib/algo/lcs.hpp +++ b/judges/recodex_token_judge/bpplib/algo/lcs.hpp @@ -1,8 +1,8 @@ /* -* Author: Martin Krulis -* Last Modification: 24.5.2018 -* License: CC 3.0 BY-NC (http://creativecommons.org/) -*/ + * Author: Martin Krulis + * Last Modification: 24.5.2018 + * License: CC 3.0 BY-NC (http://creativecommons.org/) + */ #ifndef BPPLIB_ALGO_LCS_HPP #define BPPLIB_ALGO_LCS_HPP @@ -21,27 +21,24 @@ namespace bpp * \tparma COMPARATOR Comparator class holds a static method compare(seq1, i1, seq2, i2) -> bool. * I.e., the comparator is also responsible for fetching values from the seq. containers. */ - template + template RES longest_common_subsequence_length(const CONTAINER &sequence1, const CONTAINER &sequence2, COMPARATOR comparator) { - if (sequence1.size() == 0 || sequence2.size() == 0) return (RES)0; + if (sequence1.size() == 0 || sequence2.size() == 0) return (RES) 0; // Make sure in seq1 is the longer sequence ... const CONTAINER &seq1 = sequence1.size() >= sequence2.size() ? sequence1 : sequence2; const CONTAINER &seq2 = sequence1.size() < sequence2.size() ? sequence1 : sequence2; - std::vector row((std::size_t)seq2.size()); - std::size_t rows = (std::size_t)seq1.size(); + std::vector row((std::size_t) seq2.size()); + std::size_t rows = (std::size_t) seq1.size(); // Dynamic programming - matrix traversal that keeps only the last row. for (std::size_t r = 0; r < rows; ++r) { RES lastUpperLeft = 0, lastLeft = 0; for (std::size_t i = 0; i < row.size(); ++i) { RES upper = row[i]; - row[i] = - (comparator(seq1, r, seq2, i)) - ? lastUpperLeft + 1 - : std::max(lastLeft, upper); + row[i] = (comparator(seq1, r, seq2, i)) ? lastUpperLeft + 1 : std::max(lastLeft, upper); lastLeft = row[i]; lastUpperLeft = upper; } @@ -52,29 +49,30 @@ namespace bpp // Only an overload that uses default comparator. - template + template RES longest_common_subsequence_length(const CONTAINER &sequence1, const CONTAINER &sequence2) { - return longest_common_subsequence_length(sequence1, sequence2, + return longest_common_subsequence_length(sequence1, + sequence2, [](const CONTAINER &seq1, std::size_t i1, const CONTAINER &seq2, std::size_t i2) -> bool { return seq1[i1] == seq2[i2]; - } - ); + }); } - /** - * Implements a longes common subsequence algorithm, which founds only the length of the LCS itself. - * \tparam RES The result type (must be an integral type). - * \tparam CONTAINER Class holding the sequence. The class must have size() method - * and the comparator must be able to get values from the container based on their indices. - * \tparma COMPARATOR Comparator class holds a static method compare(seq1, i1, seq2, i2) -> bool. - * I.e., the comparator is also responsible for fetching values from the seq. containers. - */ - template - void longest_common_subsequence(const CONTAINER &sequence1, const CONTAINER &sequence2, - std::vector> &common, COMPARATOR comparator) + * Implements a longes common subsequence algorithm, which founds only the length of the LCS itself. + * \tparam RES The result type (must be an integral type). + * \tparam CONTAINER Class holding the sequence. The class must have size() method + * and the comparator must be able to get values from the container based on their indices. + * \tparma COMPARATOR Comparator class holds a static method compare(seq1, i1, seq2, i2) -> bool. + * I.e., the comparator is also responsible for fetching values from the seq. containers. + */ + template + void longest_common_subsequence(const CONTAINER &sequence1, + const CONTAINER &sequence2, + std::vector> &common, + COMPARATOR comparator) { struct Node { std::pair previous; @@ -90,33 +88,27 @@ namespace bpp // Prepare vector representing the LCS matrix ... std::vector matrix((size1 + 1) * (size2 + 1)); - for (std::size_t i = 0; i < size1; ++i) { - matrix[i + 1].previous.first = 1; - } - for (std::size_t i = 0; i < size2; ++i) { - matrix[(i + 1)*(size1 + 1)].previous.second = 1; - } + for (std::size_t i = 0; i < size1; ++i) { matrix[i + 1].previous.first = 1; } + for (std::size_t i = 0; i < size2; ++i) { matrix[(i + 1) * (size1 + 1)].previous.second = 1; } // Fill in the LCS matrix by dynamic programming std::size_t i = size1 + 2; // current position in matrix (i == (c+1)*(size1+1) + (r+1)) for (std::size_t r = 0; r < size2; ++r) { // iterate over rows for (std::size_t c = 0; c < size1; ++c) { // iterate over cols matrix[i].match = comparator(sequence1, c, sequence2, r); - + if (matrix[i].match) { // Matching tokens should prolong the sequence... matrix[i].length = matrix[i - size1 - 2].length + 1; matrix[i].previous.first = 1; matrix[i].previous.second = 1; - } - else { + } else { IDX leftLength = matrix[i - 1].length; IDX upperLength = matrix[i - size1 - 1].length; if (leftLength >= upperLength) { matrix[i].previous.first = 1; matrix[i].length = leftLength; - } - else { + } else { matrix[i].previous.second = 1; matrix[i].length = upperLength; } @@ -131,9 +123,7 @@ namespace bpp std::size_t r = size2; while (c > 0 && r > 0) { const Node &node = matrix[r * (size1 + 1) + c]; - if (node.match) { - common.push_back(std::make_pair(c - 1, r - 1)); - } + if (node.match) { common.push_back(std::make_pair(c - 1, r - 1)); } c -= node.previous.first; r -= node.previous.second; @@ -145,16 +135,19 @@ namespace bpp // Only an overload that uses default comparator. - template - void longest_common_subsequence(const CONTAINER &sequence1, const CONTAINER &sequence2, std::vector> &common) + template + void longest_common_subsequence( + const CONTAINER &sequence1, const CONTAINER &sequence2, std::vector> &common) { - longest_common_subsequence(sequence1, sequence2, common, + longest_common_subsequence(sequence1, + sequence2, + common, [](const CONTAINER &seq1, std::size_t i1, const CONTAINER &seq2, std::size_t i2) -> bool { - return seq1[i1] == seq2[i2]; - }); + return seq1[i1] == seq2[i2]; + }); } -} +} // namespace bpp #endif diff --git a/judges/recodex_token_judge/bpplib/cli/args.hpp b/judges/recodex_token_judge/bpplib/cli/args.hpp index dadcb589..e2e958b4 100644 --- a/judges/recodex_token_judge/bpplib/cli/args.hpp +++ b/judges/recodex_token_judge/bpplib/cli/args.hpp @@ -29,1038 +29,1097 @@ namespace bpp { -/** - * \brief Specific exception thrown from argument processor when internal error occurs. - */ -class ArgumentException : public RuntimeError -{ -public: - ArgumentException() : RuntimeError() {} - ArgumentException(const char *msg) : RuntimeError(msg) {} - ArgumentException(const std::string &msg) : RuntimeError(msg) {} - virtual ~ArgumentException() throw() {} - - /* - * Overloading << operator that uses stringstream to append data to mMessage. - * Note that this overload is necessary so the operator returns object of exactly this class. + /** + * \brief Specific exception thrown from argument processor when internal error occurs. */ - template ArgumentException& operator<<(const T &data) + class ArgumentException : public RuntimeError { - RuntimeError::operator<<(data); - return *this; - } -}; + public: + ArgumentException() : RuntimeError() + { + } + ArgumentException(const char *msg) : RuntimeError(msg) + { + } + ArgumentException(const std::string &msg) : RuntimeError(msg) + { + } + virtual ~ArgumentException() throw() + { + } + /* + * Overloading << operator that uses stringstream to append data to mMessage. + * Note that this overload is necessary so the operator returns object of exactly this class. + */ + template ArgumentException &operator<<(const T &data) + { + RuntimeError::operator<<(data); + return *this; + } + }; -/** - * \brief Wrapper class that keeps specifications, constraints, and after processing - * also the values of program arguments. - * - * The wrapper can process both named arguments (with standard '-' or '--' prefix notation) - * and nameless arguments (like a list of files to be processed). This argument processor - * does not distinguish between short arguments like (-v) or long arguments like (--verbose). - * The length of the name does not matter and it can be prefixed with one or two dashes. - * Furthermore, arguments with value (int, float and string) are supported besides boolean arguments. - */ -class ProgramArguments -{ -public: /** - * \brief Base class for internal argument-representing ojects. + * \brief Wrapper class that keeps specifications, constraints, and after processing + * also the values of program arguments. + * + * The wrapper can process both named arguments (with standard '-' or '--' prefix notation) + * and nameless arguments (like a list of files to be processed). This argument processor + * does not distinguish between short arguments like (-v) or long arguments like (--verbose). + * The length of the name does not matter and it can be prefixed with one or two dashes. + * Furthermore, arguments with value (int, float and string) are supported besides boolean arguments. */ - class ArgBase + class ProgramArguments { - friend class ProgramArguments; - - protected: - ProgramArguments *args; - std::string mName; ///< Name of the argument (both for identification and parsing). - std::string mComment; ///< Comment used for help printing. - - bool mMandatory; ///< Whether the argument must be present on the command line. - bool mPresent; ///< True if the argumen was present on the command line (after parsing). - - std::set mConflictsWith; ///< List of arguments with which this argument conflicts with. - std::set mRequiresAlso; ///< List of arguments that are required if this argument is present. - - + public: /** - * \brief Internal method called by friend class to verify that all constraints - * (mandatory, conflictsWith, requiresAlso) hold. - * \param arguments A map indexed by names of all arguments defined. - * \throws ArgumentException if any of the constraints are broken. + * \brief Base class for internal argument-representing ojects. */ - void checkConstraints(const std::map> &arguments) const + class ArgBase { - if (isMandatory() && !isPresent()) - throw (ArgumentException() << "The argument '" << mName << "' is mandatory but missing."); - - // Constraint checks are done only if the argument is present. - if (isPresent()) { - // Check for collisions. - for (auto it = mConflictsWith.begin(); it != mConflictsWith.end(); ++it) { - if (arguments.find(*it) == arguments.end()) - throw (ArgumentException() << "Internal Error: Argument '" << mName - << "' has unspecified argument '" << *it << "' on its collision list."); - - if (arguments.find(*it)->second->isPresent()) - throw (ArgumentException() << "The argument '" << mName << "' conflicts with argument '" << *it << "'."); + friend class ProgramArguments; + + protected: + ProgramArguments *args; + std::string mName; ///< Name of the argument (both for identification and parsing). + std::string mComment; ///< Comment used for help printing. + + bool mMandatory; ///< Whether the argument must be present on the command line. + bool mPresent; ///< True if the argumen was present on the command line (after parsing). + + std::set mConflictsWith; ///< List of arguments with which this argument conflicts with. + std::set mRequiresAlso; ///< List of arguments that are required if this argument is present. + + + /** + * \brief Internal method called by friend class to verify that all constraints + * (mandatory, conflictsWith, requiresAlso) hold. + * \param arguments A map indexed by names of all arguments defined. + * \throws ArgumentException if any of the constraints are broken. + */ + void checkConstraints(const std::map> &arguments) const + { + if (isMandatory() && !isPresent()) + throw(ArgumentException() << "The argument '" << mName << "' is mandatory but missing."); + + // Constraint checks are done only if the argument is present. + if (isPresent()) { + // Check for collisions. + for (auto it = mConflictsWith.begin(); it != mConflictsWith.end(); ++it) { + if (arguments.find(*it) == arguments.end()) + throw(ArgumentException() + << "Internal Error: Argument '" << mName << "' has unspecified argument '" << *it + << "' on its collision list."); + + if (arguments.find(*it)->second->isPresent()) + throw(ArgumentException() + << "The argument '" << mName << "' conflicts with argument '" << *it << "'."); + } + + // Check for requirements. + for (auto it = mRequiresAlso.begin(); it != mRequiresAlso.end(); ++it) { + if (arguments.find(*it) == arguments.end()) + throw(ArgumentException() + << "Internal Error: Argument '" << mName << "' has unspecified argument '" << *it + << "' on its requirements list."); + + if (!arguments.find(*it)->second->isPresent()) + throw(ArgumentException() << "The argument '" << *it << "' is also required when '" << mName + << "' was specified."); + } } + } - // Check for requirements. - for (auto it = mRequiresAlso.begin(); it != mRequiresAlso.end(); ++it) { - if (arguments.find(*it) == arguments.end()) - throw (ArgumentException() << "Internal Error: Argument '" << mName - << "' has unspecified argument '" << *it << "' on its requirements list."); - if (!arguments.find(*it)->second->isPresent()) - throw (ArgumentException() << "The argument '" << *it << "' is also required when '" << mName << "' was specified."); - } + /** + * \brief Abstract method that is derived by final classes to parse the argument + * value and update internal status. + * \param argc Intermediate copy of argc which is updated during parsing process. + * \param argv Intermediate copy of argv which is updated during parsing process. + * \note If the arguments parses any values from the command line, the argc and + * argv values should be updated accordingly. + */ + virtual void process(int &argc, const char **(&argv)) = 0; + + + public: + /** + * \brief Initialize the argument object. + * \param name Human-readable identification of the argument. + * It is used for both programming and parsing. + * The name must not start with '-'. + * \param comment Text commentary used for printing the documentation. + * \param mandatory Flag that indicates whether the argument must be present on commandline. + */ + ArgBase(const std::string &name, const std::string &comment, bool mandatory) + : mName(name), mComment(comment), mMandatory(mandatory), mPresent(false) + { + if (mName.empty()) throw(ArgumentException() << "Argument name must not be empty."); + + if (mName[0] == '-') throw(ArgumentException() << "Argument name must not start with dash '-'."); } - } - - /** - * \brief Abstract method that is derived by final classes to parse the argument - * value and update internal status. - * \param argc Intermediate copy of argc which is updated during parsing process. - * \param argv Intermediate copy of argv which is updated during parsing process. - * \note If the arguments parses any values from the command line, the argc and - * argv values should be updated accordingly. - */ - virtual void process(int &argc, const char **(&argv)) = 0; + // Enforce virtual destructor for descendants. + virtual ~ArgBase() + { + } - public: - /** - * \brief Initialize the argument object. - * \param name Human-readable identification of the argument. - * It is used for both programming and parsing. - * The name must not start with '-'. - * \param comment Text commentary used for printing the documentation. - * \param mandatory Flag that indicates whether the argument must be present on commandline. - */ - ArgBase(const std::string &name, const std::string &comment, bool mandatory) - : mName(name), mComment(comment), mMandatory(mandatory), mPresent(false) - { - if (mName.empty()) - throw (ArgumentException() << "Argument name must not be empty."); + /** + * \brief Return the name of the argument (used both for access to its object + * and to write it on command line). + * \note The name is without '-' prefix, but on the command line is written as + * '-name' or '--name'. + */ + const std::string &getName() const + { + return mName; + } - if (mName[0] == '-') - throw (ArgumentException() << "Argument name must not start with dash '-'."); - } - // Enforce virtual destructor for descendants. - virtual ~ArgBase() {} + /** + * \brief Get short description of the argument (e.g., for usage and help printing). + */ + const std::string &getComment() const + { + return mComment; + } - /** - * \brief Return the name of the argument (used both for access to its object - * and to write it on command line). - * \note The name is without '-' prefix, but on the command line is written as - * '-name' or '--name'. - */ - const std::string& getName() const { return mName; } + /** + * \brief Check whether the argument is mandatory (always must be present). + */ + bool isMandatory() const + { + return mMandatory; + } - /** - * \brief Get short description of the argument (e.g., for usage and help printing). - */ - const std::string& getComment() const { return mComment; } + /** + * \brief Check whether the argument was present in processed arguments. + */ + bool isPresent() const + { + return mPresent; + } - /** - * \brief Check whether the argument is mandatory (always must be present). - */ - bool isMandatory() const { return mMandatory; } + /** + * \brief Adds conflict constraint. + * \param argName Name of an argument that must not be present after processing + * if this argument is present. + * \return Reference to self, so the conflictWith() calls may be chained. + * \note The user is responsible for the ambiguity of conflict and requirement constraints. + */ + ArgBase &conflictsWith(const std::string &argName) + { + mConflictsWith.insert(argName); + return *this; + } - /** - * \brief Check whether the argument was present in processed arguments. - */ - bool isPresent() const { return mPresent; } + /** + * \brief Adds dependency constraint. + * \param argName Name of an argument that must be also present after processing + * if this argument is present. + * \return Reference to self, so the requiresAlso() calls may be chained. + * \note The user is responsible for the ambiguity of conflict and requirement constraints. + */ + ArgBase &requiresAlso(const std::string &argName) + { + mRequiresAlso.insert(argName); + return *this; + } + }; /** - * \brief Adds conflict constraint. - * \param argName Name of an argument that must not be present after processing - * if this argument is present. - * \return Reference to self, so the conflictWith() calls may be chained. - * \note The user is responsible for the ambiguity of conflict and requirement constraints. + * \brief Argument without value (simple option switch). + * The value is true if the argument is present and false otherwise. + * \note Switch options are never mandatory. */ - ArgBase& conflictsWith(const std::string &argName) + class ArgBool : public ArgBase { - mConflictsWith.insert(argName); - return *this; - } + public: + typedef bool value_t; + protected: + virtual void process(int &, const char **&) + { + this->mPresent = true; + } - /** - * \brief Adds dependency constraint. - * \param argName Name of an argument that must be also present after processing - * if this argument is present. - * \return Reference to self, so the requiresAlso() calls may be chained. - * \note The user is responsible for the ambiguity of conflict and requirement constraints. - */ - ArgBase& requiresAlso(const std::string &argName) - { - mRequiresAlso.insert(argName); - return *this; - } - }; + public: + ArgBool(const std::string &name, const std::string &comment) : ArgBase(name, comment, false) + { + } + value_t getValue() const + { + return isPresent(); + } + }; - /** - * \brief Argument without value (simple option switch). - * The value is true if the argument is present and false otherwise. - * \note Switch options are never mandatory. - */ - class ArgBool : public ArgBase - { - public: - typedef bool value_t; - protected: - virtual void process(int&, const char**&) + /** + * \brief Argument with one numerical value. The value is stored as int64. + */ + class ArgIntBase : public ArgBase { - this->mPresent = true; - } + public: + typedef std::int64_t value_t; + + protected: + value_t mMin; ///< Range constraint for the value. + value_t mMax; ///< Range constraint for the value. + + /** + * \brief Internal method that tests whether given string is valid + * decimal representation of an int. + */ + bool isInt(const char *str) const + { + if (*str == '-') ++str; // optional sign + if (!*str) return false; + while (*str && isdigit(*str)) // digits + ++str; + if (*str == 'k' || *str == 'M' || *str == 'G' || *str == 'T') // optional multiplier + ++str; + return !*str; + } - public: - ArgBool(const std::string &name, const std::string &comment) - : ArgBase(name, comment, false) {} + value_t processInt(int &argc, const char **(&argv)) const + { + // Check whether the argument value is present and valid. + if (argc == 0) + throw(ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); + if (!isInt(argv[0])) + throw(ArgumentException() << "Value '" << argv[0] << "' of argument '" << this->getName() + << "' cannot be converted to int!"); + + value_t multiplier = 1; + const char *last = argv[0]; + while (last[1] != 0) ++last; + switch (*last) { + case 'k': multiplier = 1024; break; + case 'M': multiplier = 1024 * 1024; break; + case 'G': multiplier = 1024 * 1024 * 1024; break; + case 'T': multiplier = 1024LL * 1024LL * 1024LL * 1024LL; break; + } - value_t getValue() const - { - return isPresent(); - } - }; + value_t value = std::atol(argv[0]) * multiplier; + // Verify constraints ... + if (value < mMin) + throw(ArgumentException() + << "Value of argument '" << this->getName() << "' is below limits (" << mMin << ")."); + if (value > mMax) + throw(ArgumentException() + << "Value of argument '" << this->getName() << "' is above limits (" << mMax << ")."); + // Update parsing status. + --argc; + ++argv; + return value; + } - /** - * \brief Argument with one numerical value. The value is stored as int64. - */ - class ArgIntBase : public ArgBase - { - public: - typedef std::int64_t value_t; + public: + ArgIntBase(const std::string &name, + const std::string &comment, + bool mandatory = false, + value_t min = 0, + value_t max = std::numeric_limits::max()) + : ArgBase(name, comment, mandatory), mMin(min), mMax(max) + { + if (mMin > mMax) + throw(ArgumentException() << "Argument '" << this->getName() << "' has invalid limits (min=" << mMin + << ", max=" << mMax << ")."); + } + }; - protected: - value_t mMin; ///< Range constraint for the value. - value_t mMax; ///< Range constraint for the value. /** - * \brief Internal method that tests whether given string is valid - * decimal representation of an int. + * \brief Argument with one numerical value. The value is stored as int64. */ - bool isInt(const char *str) const + class ArgInt : public ArgIntBase { - if (*str == '-') ++str; // optional sign - if (!*str) return false; - while (*str && isdigit(*str)) // digits - ++str; - if (*str == 'k' || *str == 'M' || *str == 'G' || *str == 'T') // optional multiplier - ++str; - return !*str; - } + private: + value_t mValue; ///< Parsed value of the argument. + + protected: + virtual void process(int &argc, const char **(&argv)) + { + mValue = this->processInt(argc, argv); + this->mPresent = true; + } + public: + ArgInt(const std::string &name, + const std::string &comment, + bool mandatory = false, + value_t defaultValue = 0, + value_t min = 0, + value_t max = std::numeric_limits::max()) + : ArgIntBase(name, comment, mandatory, min, max), mValue(defaultValue) + { + } - value_t processInt(int &argc, const char **(&argv)) const - { - // Check whether the argument value is present and valid. - if (argc == 0) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); - if (!isInt(argv[0])) - throw (ArgumentException() << "Value '" << argv[0] << "' of argument '" << this->getName() << "' cannot be converted to int!"); - - value_t multiplier = 1; - const char *last = argv[0]; - while (last[1] != 0) ++last; - switch (*last) { - case 'k': multiplier = 1024; break; - case 'M': multiplier = 1024 * 1024; break; - case 'G': multiplier = 1024 * 1024 * 1024; break; - case 'T': multiplier = 1024LL * 1024LL * 1024LL * 1024LL; break; + value_t getValue() const + { + return mValue; } - value_t value = std::atol(argv[0]) * multiplier; + std::size_t getAsSize() const + { + static_assert( + sizeof(std::uint64_t) >= sizeof(std::size_t), "std::size_t is larger than 64-bit unsigned int"); - // Verify constraints ... - if (value < mMin) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is below limits (" << mMin << ")."); - if (value > mMax) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is above limits (" << mMax << ")."); + if (getAsUint() > (std::uint64_t) std::numeric_limits::max()) + throw(bpp::ArgumentException() + << "Unable to convert int argument '" << this->getName() << "' to size_t."); + return (std::size_t) mValue; + } - // Update parsing status. - --argc; ++argv; - return value; - } + std::uint64_t getAsUint() const + { + if (mValue < 0) + throw(bpp::ArgumentException() + << "Unable to convert int argument '" << this->getName() << "' to 64-bit unsigned int."); + return (std::uint64_t) mValue; + } - public: - ArgIntBase(const std::string &name, const std::string &comment, bool mandatory = false, - value_t min = 0, value_t max = std::numeric_limits::max()) - : ArgBase(name, comment, mandatory), mMin(min), mMax(max) - { - if (mMin > mMax) - throw (ArgumentException() << "Argument '" << this->getName() << "' has invalid limits (min=" - << mMin << ", max=" << mMax << ")."); - } - }; + std::int32_t getAsInt32() const + { + if (mValue > (value_t) std::numeric_limits::max()) + throw(bpp::ArgumentException() + << "Unable to convert int argument '" << this->getName() << "' to 32-bit int."); + return (std::int32_t) mValue; + } + std::uint32_t getAsUint32() const + { + if (mValue > (value_t) std::numeric_limits::max()) + throw(bpp::ArgumentException() + << "Unable to convert int argument '" << this->getName() << "' to 32-bit unsigned int."); + return (std::uint32_t) mValue; + } + }; - /** - * \brief Argument with one numerical value. The value is stored as int64. - */ - class ArgInt : public ArgIntBase - { - private: - value_t mValue; ///< Parsed value of the argument. - - protected: - virtual void process(int &argc, const char **(&argv)) + /** + * \brief Argument with list numerical value. The values are stored as int64. + * \note The values may be sequenced after the argument or the argument may be + * present multiple times. + */ + class ArgIntList : public ArgIntBase { - mValue = this->processInt(argc, argv); - this->mPresent = true; - } + private: + std::vector mValues; ///< Parsed values of the argument. - public: - ArgInt(const std::string &name, const std::string &comment, bool mandatory = false, - value_t defaultValue = 0, value_t min = 0, value_t max = std::numeric_limits::max()) - : ArgIntBase(name, comment, mandatory, min, max), mValue(defaultValue) {} + protected: + virtual void process(int &argc, const char **(&argv)) + { + if (!this->mPresent) mValues.clear(); - value_t getValue() const - { - return mValue; - } + mValues.push_back(this->processInt(argc, argv)); + while (argc && (argv[0][0] != '-' || (argv[0][0] && argv[0][1] != '-' && !args->isSingleDashAllowed()))) + mValues.push_back(this->processInt(argc, argv)); + this->mPresent = true; + } - std::size_t getAsSize() const - { - static_assert(sizeof(std::uint64_t) >= sizeof(std::size_t), "std::size_t is larger than 64-bit unsigned int"); + public: + ArgIntList(const std::string &name, + const std::string &comment, + bool mandatory = false, + value_t min = 0, + value_t max = std::numeric_limits::max()) + : ArgIntBase(name, comment, mandatory, min, max) + { + } - if (getAsUint() > (std::uint64_t)std::numeric_limits::max()) - throw (bpp::ArgumentException() << "Unable to convert int argument '" << this->getName() << "' to size_t."); - return (std::size_t)mValue; - } + value_t getValue(std::size_t idx) const + { + return mValues[idx]; + } - std::uint64_t getAsUint() const - { - if (mValue < 0) - throw (bpp::ArgumentException() << "Unable to convert int argument '" << this->getName() << "' to 64-bit unsigned int."); - return (std::uint64_t)mValue; - } - std::int32_t getAsInt32() const - { - if (mValue > (value_t)std::numeric_limits::max()) - throw (bpp::ArgumentException() << "Unable to convert int argument '" << this->getName() << "' to 32-bit int."); - return (std::int32_t)mValue; - } + /** + * \brief Get number of retrieved values in the list. + */ + std::size_t count() const + { + return mValues.size(); + } - std::uint32_t getAsUint32() const - { - if (mValue > (value_t)std::numeric_limits::max()) - throw (bpp::ArgumentException() << "Unable to convert int argument '" << this->getName() << "' to 32-bit unsigned int."); - return (std::uint32_t)mValue; - } - }; + /** + * \brief Push another default value at the end of the value list. + * \param value Value being added to the list. + * \return Reference to this argument object (for chaining method calls). + * \note The default value can be modified only before actual values has + * been successfully parsed from the commandline. + */ + ArgIntList &addDefault(value_t value) + { + if (this->mPresent) + throw(ArgumentException() << "Unable to modify default values of argument '" << this->mName + << "' when the actual values were parsed from the command line."); + mValues.push_back(value); + return *this; + } + }; - /** - * \brief Argument with list numerical value. The values are stored as int64. - * \note The values may be sequenced after the argument or the argument may be - * present multiple times. - */ - class ArgIntList : public ArgIntBase - { - private: - std::vector mValues; ///< Parsed values of the argument. - - protected: - virtual void process(int &argc, const char **(&argv)) + /** + * \brief Argument with one real number value. The value is stored as double. + */ + class ArgFloatBase : public ArgBase { - if (!this->mPresent) - mValues.clear(); + public: + typedef double value_t; + + protected: + value_t mMin; ///< Range constraint for the value. + value_t mMax; ///< Range constraint for the value. + + /** + * \brief Internal method that tests whether given string is valid + * decimal representation of a float number. + */ + bool isFloat(const char *str) const + { + if (*str == '-') ++str; + if (!*str) return false; + while (*str && (isdigit(*str) || *str == '.')) ++str; + return !*str; + } - mValues.push_back(this->processInt(argc, argv)); - while (argc && (argv[0][0] != '-' || (argv[0][0] && argv[0][1] != '-' && !args->isSingleDashAllowed()))) - mValues.push_back(this->processInt(argc, argv)); - this->mPresent = true; - } - public: - ArgIntList(const std::string &name, const std::string &comment, bool mandatory = false, - value_t min = 0, value_t max = std::numeric_limits::max()) - : ArgIntBase(name, comment, mandatory, min, max) {} + value_t processFloat(int &argc, const char **(&argv)) + { + // Check whether the argument value is present and valid. + if (argc == 0) + throw(ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); + if (!isFloat(argv[0])) + throw(ArgumentException() << "Value '" << argv[0] << "' of argument '" << this->getName() + << "' cannot be converted to float!"); + + // Get the value + value_t value = atof(argv[0]); + + // Verify constraints ... + if (value < mMin) + throw(ArgumentException() + << "Value of argument '" << this->getName() << "' is below limits (" << mMin << ")."); + if (value > mMax) + throw(ArgumentException() + << "Value of argument '" << this->getName() << "' is above limits (" << mMax << ")."); + + // Update parsing status. + --argc; + ++argv; + return value; + } - value_t getValue(std::size_t idx) const - { - return mValues[idx]; - } + public: + ArgFloatBase(const std::string &name, + const std::string &comment, + bool mandatory = false, + value_t min = 0.0, + value_t max = 1.0) + : ArgBase(name, comment, mandatory), mMin(min), mMax(max) + { + if (mMin > mMax) + throw(ArgumentException() << "Argument '" << this->getName() << "' has invalid limits (min=" << mMin + << ", max=" << mMax << ")."); + } + }; /** - * \brief Get number of retrieved values in the list. + * \brief Argument with one real number value. The value is stored as double. */ - std::size_t count() const + class ArgFloat : public ArgFloatBase { - return mValues.size(); - } + private: + value_t mValue; ///< Parsed value of the argument. + + protected: + virtual void process(int &argc, const char **(&argv)) + { + mValue = this->processFloat(argc, argv); + this->mPresent = true; + } + + public: + ArgFloat(const std::string &name, + const std::string &comment, + bool mandatory = false, + value_t defaultValue = 0.0, + value_t min = 0.0, + value_t max = 1.0) + : ArgFloatBase(name, comment, mandatory, min, max), mValue(defaultValue) + { + } + + value_t getValue() const + { + return mValue; + } + }; /** - * \brief Push another default value at the end of the value list. - * \param value Value being added to the list. - * \return Reference to this argument object (for chaining method calls). - * \note The default value can be modified only before actual values has - * been successfully parsed from the commandline. + * \brief Argument with list of real number values. The values are stored as double. + * \note The values may be sequenced after the argument or the argument may be + * present multiple times. */ - ArgIntList& addDefault(value_t value) + class ArgFloatList : public ArgFloatBase { - if (this->mPresent) - throw (ArgumentException() << "Unable to modify default values of argument '" << this->mName - << "' when the actual values were parsed from the command line."); - mValues.push_back(value); - return *this; - } - }; + private: + std::vector mValues; ///< Parsed values of the argument. + protected: + virtual void process(int &argc, const char **(&argv)) + { + if (!this->mPresent) mValues.clear(); + mValues.push_back(this->processFloat(argc, argv)); - /** - * \brief Argument with one real number value. The value is stored as double. - */ - class ArgFloatBase : public ArgBase - { - public: - typedef double value_t; + while (argc && (argv[0][0] != '-' || (argv[0][0] && argv[0][1] != '-' && !args->isSingleDashAllowed()))) + mValues.push_back(this->processFloat(argc, argv)); + this->mPresent = true; + } + + public: + ArgFloatList(const std::string &name, + const std::string &comment, + bool mandatory = false, + value_t min = 0.0, + value_t max = 1.0) + : ArgFloatBase(name, comment, mandatory, min, max) + { + } + + value_t getValue(std::size_t idx) const + { + return mValues[idx]; + } + + + /** + * \brief Get number of retrieved values in the list. + */ + std::size_t count() const + { + return mValues.size(); + } + + + /** + * \brief Push another default value at the end of the value list. + * \param value Value being added to the list. + * \return Reference to this argument object (for chaining method calls). + * \note The default value can be modified only before actual values has + * been successfully parsed from the commandline. + */ + ArgFloatList &addDefault(value_t value) + { + if (this->mPresent) + throw(ArgumentException() << "Unable to modify default values of argument '" << this->mName + << "' when the actual values were parsed from the command line."); + mValues.push_back(value); + return *this; + } + }; - protected: - value_t mMin; ///< Range constraint for the value. - value_t mMax; ///< Range constraint for the value. /** - * \brief Internal method that tests whether given string is valid - * decimal representation of a float number. + * \brief Argument with one string value. */ - bool isFloat(const char *str) const + class ArgString : public ArgBase { - if (*str == '-') ++str; - if (!*str) return false; - while (*str && (isdigit(*str) || *str == '.')) - ++str; - return !*str; - } - + public: + typedef std::string value_t; - value_t processFloat(int &argc, const char **(&argv)) - { - // Check whether the argument value is present and valid. - if (argc == 0) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); - if (!isFloat(argv[0])) - throw (ArgumentException() << "Value '" << argv[0] << "' of argument '" << this->getName() << "' cannot be converted to float!"); - - // Get the value - value_t value = atof(argv[0]); - - // Verify constraints ... - if (value < mMin) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is below limits (" << mMin << ")."); - if (value > mMax) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is above limits (" << mMax << ")."); - - // Update parsing status. - --argc; ++argv; - return value; - } + protected: + std::string mValue; ///< The value of the argument stored after parsing. - public: - ArgFloatBase(const std::string &name, const std::string &comment, bool mandatory = false, - value_t min = 0.0, value_t max = 1.0) - : ArgBase(name, comment, mandatory), mMin(min), mMax(max) - { - if (mMin > mMax) - throw (ArgumentException() << "Argument '" << this->getName() << "' has invalid limits (min=" - << mMin << ", max=" << mMax << ")."); - } - }; + virtual void process(int &argc, const char **(&argv)) + { + if (argc == 0) + throw(ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); + mValue = std::string(argv[0]); + --argc; + ++argv; + this->mPresent = true; + } - /** - * \brief Argument with one real number value. The value is stored as double. - */ - class ArgFloat : public ArgFloatBase - { - private: - value_t mValue; ///< Parsed value of the argument. + public: + ArgString(const std::string &name, + const std::string &comment, + bool mandatory = false, + const char *defaultValue = "") + : ArgBase(name, comment, mandatory), mValue(defaultValue) + { + } - protected: - virtual void process(int &argc, const char **(&argv)) - { - mValue = this->processFloat(argc, argv); - this->mPresent = true; - } + const std::string &getValue() const + { + return mValue; + } + }; - public: - ArgFloat(const std::string &name, const std::string &comment, bool mandatory = false, - value_t defaultValue = 0.0, value_t min = 0.0, value_t max = 1.0) - : ArgFloatBase(name, comment, mandatory, min, max), mValue(defaultValue) - {} - value_t getValue() const + /** + * \brief Argument with one string value which must be from a predefined enumeration. + */ + class ArgEnum : public ArgString { - return mValue; - } - }; + public: + typedef std::string value_t; + + private: + std::string mNormalizedValue; + std::set mOptions; + bool mCaseSensitive; + + protected: + static std::string toLower(const std::string &str) + { + std::string s = str; + std::transform(s.begin(), s.end(), s.begin(), (int (*)(int)) std::tolower); + return s; + } + void addOption(const std::string &str) + { + mOptions.insert(mCaseSensitive ? str : toLower(str)); + } + virtual void process(int &argc, const char **(&argv)) + { + ArgString::process(argc, argv); - /** - * \brief Argument with list of real number values. The values are stored as double. - * \note The values may be sequenced after the argument or the argument may be - * present multiple times. - */ - class ArgFloatList : public ArgFloatBase - { - private: - std::vector mValues; ///< Parsed values of the argument. + mNormalizedValue = (mCaseSensitive ? this->getValue() : toLower(this->getValue())); + if (mOptions.find(mNormalizedValue) == mOptions.end()) + throw(ArgumentException() + << "Invalid value '" << this->getValue() << "' for argument '" << this->getName() << "'."); + } - protected: - virtual void process(int &argc, const char **(&argv)) - { - if (!this->mPresent) - mValues.clear(); + public: + ArgEnum(const std::string &name, + const std::string &comment, + bool mandatory = false, + bool caseSensitive = true, + const char *defaultValue = "", + std::initializer_list options = {}) + : ArgString(name, comment, mandatory, defaultValue), mCaseSensitive(caseSensitive) + { + std::for_each( + options.begin(), options.end(), std::bind(&ArgEnum::addOption, this, std::placeholders::_1)); + + if (!this->getValue().empty()) mOptions.insert(this->getValue()); + } - mValues.push_back(this->processFloat(argc, argv)); - while (argc && (argv[0][0] != '-' || (argv[0][0] && argv[0][1] != '-' && !args->isSingleDashAllowed()))) - mValues.push_back(this->processFloat(argc, argv)); - this->mPresent = true; - } + /** + * \brief Add more enum options. + */ + void addOptions(std::initializer_list options) + { + std::for_each( + options.begin(), options.end(), std::bind(&ArgEnum::addOption, this, std::placeholders::_1)); + } - public: - ArgFloatList(const std::string &name, const std::string &comment, bool mandatory = false, - value_t min = 0.0, value_t max = 1.0) - : ArgFloatBase(name, comment, mandatory, min, max) - {} - value_t getValue(std::size_t idx) const - { - return mValues[idx]; - } + /** + * \brief Checks that the loaded value is equal to given value taking + * internal case-sensitive flag into account. + * \param value The value which is being tested to equality. + * \return True if the enum argument has the given value. + */ + bool is(const std::string &value) + { + std::string lower; + if (!mCaseSensitive) { lower = toLower(value); } + if (this->getValue() == (mCaseSensitive ? value : lower)) return true; - /** - * \brief Get number of retrieved values in the list. - */ - std::size_t count() const - { - return mValues.size(); - } + if (mOptions.find(mCaseSensitive ? value : lower) == mOptions.end()) + throw(ArgumentException() << "Value '" << value << "' is not a valid enum option of '" + << this->getName() << " argument."); + + return false; + } + }; /** - * \brief Push another default value at the end of the value list. - * \param value Value being added to the list. - * \return Reference to this argument object (for chaining method calls). - * \note The default value can be modified only before actual values has - * been successfully parsed from the commandline. + * \brief Argument with list of string values. */ - ArgFloatList& addDefault(value_t value) + class ArgStringList : public ArgBase { - if (this->mPresent) - throw (ArgumentException() << "Unable to modify default values of argument '" << this->mName - << "' when the actual values were parsed from the command line."); - mValues.push_back(value); - return *this; - } - }; + private: + std::vector mValues; ///< The list of values of the argument stored after parsing. + protected: + virtual void process(int &argc, const char **(&argv)) + { + if (argc == 0) + throw(ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); + if (!this->mPresent) mValues.clear(); - /** - * \brief Argument with one string value. - */ - class ArgString : public ArgBase - { - public: - typedef std::string value_t; + mValues.push_back(argv[0]); + --argc; + ++argv; + + while ( + argc && (argv[0][0] != '-' || (argv[0][0] && argv[0][1] != '-' && !args->isSingleDashAllowed()))) { + mValues.push_back(argv[0]); + --argc; + ++argv; + } - protected: - std::string mValue; ///< The value of the argument stored after parsing. + this->mPresent = true; + } - virtual void process(int &argc, const char **(&argv)) - { - if (argc == 0) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); + public: + ArgStringList(const std::string &name, const std::string &comment, bool mandatory = false) + : ArgBase(name, comment, mandatory) + { + } - mValue = std::string(argv[0]); - --argc; ++argv; + const std::string &getValue(std::size_t idx) const + { + return mValues[idx]; + } - this->mPresent = true; - } - public: - ArgString(const std::string &name, const std::string &comment, bool mandatory = false, const char *defaultValue = "") - : ArgBase(name, comment, mandatory), mValue(defaultValue) - { - } - - const std::string& getValue() const - { - return mValue; - } - }; + /** + * \brief Get number of retrieved values in the list. + */ + std::size_t count() const + { + return mValues.size(); + } + /** + * \brief Push another default value at the end of the value list. + * \param value Value being added to the list. + * \return Reference to this argument object (for chaining method calls). + * \note The default value can be modified only before actual values has + * been successfully parsed from the commandline. + */ + ArgStringList &addDefault(const std::string &value) + { + if (this->mPresent) + throw(ArgumentException() << "Unable to modify default values of argument '" << this->mName + << "' when the actual values were parsed from the command line."); + mValues.push_back(value); + return *this; + } + }; - /** - * \brief Argument with one string value which must be from a predefined enumeration. - */ - class ArgEnum : public ArgString - { - public: - typedef std::string value_t; private: - std::string mNormalizedValue; - std::set mOptions; - bool mCaseSensitive; - - protected: - static std::string toLower(const std::string &str) - { - std::string s = str; - std::transform(s.begin(), s.end(), s.begin(), (int(*)(int))std::tolower); - return s; - } + std::string mProgramName; ///< Name of the program taken as first record on command line. - void addOption(const std::string &str) - { - mOptions.insert(mCaseSensitive ? str : toLower(str)); - } + /** + * \brief Map of named arguments. The arguments are indexed by their names (no duplicates allowed). + */ + std::map> mArguments; - virtual void process(int &argc, const char **(&argv)) - { - ArgString::process(argc, argv); + std::size_t mNamelessMin; ///< Range constraing on number of nameless arguments. + std::size_t mNamelessMax; ///< Range constraing on number of nameless arguments. + std::vector mNamelessArguments; ///< Values of nameless arguments. + std::vector mNamelessCaptions; ///< Captions of nameless arguments (for documentation). - mNormalizedValue = (mCaseSensitive ? this->getValue() : toLower(this->getValue())); - if (mOptions.find(mNormalizedValue) == mOptions.end()) - throw (ArgumentException() << "Invalid value '" << this->getValue() << "' for argument '" << this->getName() << "'."); - } + bool mAllowSingleDash; ///< Whether single dash is allowed as attribute prefix (normally, two dashes prefix long + ///< arguments) - public: - ArgEnum(const std::string &name, const std::string &comment, bool mandatory = false, - bool caseSensitive = true, const char *defaultValue = "", std::initializer_list options = {}) - : ArgString(name, comment, mandatory, defaultValue), mCaseSensitive(caseSensitive) + /** + * \brief Provides access to named argument object. + * \tparam Type of the argument object (must be ArgBase or derived class) + * \param name Name of the argument. + * \return Reference to argument object. + * \throws ArgumentException if the argument is not found or has invalid type. + */ + template ARG_T &getArgTyped(const std::string &name) { - std::for_each(options.begin(), options.end(), - std::bind(&ArgEnum::addOption, this, std::placeholders::_1)); - - if (!this->getValue().empty()) - mOptions.insert(this->getValue()); + if (mArguments.find(name) == mArguments.end()) + throw(ArgumentException() << "Argument '" << name << "' was not specified."); + ARG_T *arg = dynamic_cast(mArguments.find(name)->second.get()); + if (arg == nullptr) throw(ArgumentException() << "Argument '" << name << "' type mismatch."); + return *arg; } /** - * \brief Add more enum options. + * \brief Provides read-only access to named argument object. + * \tparam Type of the argument object (must be ArgBase or derived class) + * \param name Name of the argument. + * \return Reference to argument object. + * \throws ArgumentException if the argument is not found or has invalid type. */ - void addOptions(std::initializer_list options) + template const ARG_T &getArgTyped(const std::string &name) const { - std::for_each(options.begin(), options.end(), - std::bind(&ArgEnum::addOption, this, std::placeholders::_1)); + if (mArguments.find(name) == mArguments.end()) + throw(ArgumentException() << "Argument '" << name << "' was not specified."); + const ARG_T *arg = dynamic_cast(mArguments.find(name)->second.get()); + if (arg == nullptr) throw(ArgumentException() << "Argument '" << name << "' type mismatch."); + return *arg; } + public: /** - * \brief Checks that the loaded value is equal to given value taking - * internal case-sensitive flag into account. - * \param value The value which is being tested to equality. - * \return True if the enum argument has the given value. + * \brief Initialize the arguments container. + * \param namelessMin Minimal number of required nameless arguments. + * \param namelessMax Maximal number of allowed nameless arguments. */ - bool is(const std::string &value) + ProgramArguments(std::size_t namelessMin = 0, std::size_t namelessMax = ~(std::size_t) 0) + : mNamelessMin(namelessMin), mNamelessMax(namelessMax), mAllowSingleDash(false) { - std::string lower; - if (!mCaseSensitive) { - lower = toLower(value); - } - - if (this->getValue() == (mCaseSensitive ? value : lower)) - return true; - - if (mOptions.find(mCaseSensitive ? value : lower) == mOptions.end()) - throw (ArgumentException() << "Value '" << value << "' is not a valid enum option of '" - << this->getName() << " argument."); - - return false; + if (namelessMin > namelessMax) + throw(ArgumentException() << "Nameless arguments minimum (" << namelessMin << ") exceeds the maximum (" + << namelessMax << ")."); } - }; - - /** - * \brief Argument with list of string values. - */ - class ArgStringList : public ArgBase - { - private: - std::vector mValues; ///< The list of values of the argument stored after parsing. - - protected: - virtual void process(int &argc, const char **(&argv)) + /** + * Allow or disable whether single dash is acceptable for argument name prefix. + * Typically, two dashes are required for long argument names. + */ + ProgramArguments &allowSingleDash(bool allow = true) { - if (argc == 0) - throw (ArgumentException() << "Value of argument '" << this->getName() << "' is missing!"); - - if (!this->mPresent) - mValues.clear(); - - mValues.push_back(argv[0]); - --argc; ++argv; - - while (argc && (argv[0][0] != '-' || (argv[0][0] && argv[0][1] != '-' && !args->isSingleDashAllowed()))) { - mValues.push_back(argv[0]); - --argc; ++argv; - } - - this->mPresent = true; + mAllowSingleDash = allow; + return *this; } - public: - ArgStringList(const std::string &name, const std::string &comment, bool mandatory = false) - : ArgBase(name, comment, mandatory) {} - const std::string& getValue(std::size_t idx) const + /** + * Whether a single dash is acceptable for argument name prefix. + */ + bool isSingleDashAllowed() const { - return mValues[idx]; + return mAllowSingleDash; } /** - * \brief Get number of retrieved values in the list. + * \brief Return name of the program as it was passed to command line. */ - std::size_t count() const + const std::string &getProgramName() const { - return mValues.size(); + return mProgramName; } /** - * \brief Push another default value at the end of the value list. - * \param value Value being added to the list. - * \return Reference to this argument object (for chaining method calls). - * \note The default value can be modified only before actual values has - * been successfully parsed from the commandline. + * \brief Typed accessor to argument object. */ - ArgStringList& addDefault(const std::string &value) + ArgBase &getArg(const std::string &name) { - if (this->mPresent) - throw (ArgumentException() << "Unable to modify default values of argument '" << this->mName - << "' when the actual values were parsed from the command line."); - mValues.push_back(value); - return *this; + return getArgTyped(name); } - }; -private: - std::string mProgramName; ///< Name of the program taken as first record on command line. - - /** - * \brief Map of named arguments. The arguments are indexed by their names (no duplicates allowed). - */ - std::map> mArguments; - - std::size_t mNamelessMin; ///< Range constraing on number of nameless arguments. - std::size_t mNamelessMax; ///< Range constraing on number of nameless arguments. - std::vector mNamelessArguments; ///< Values of nameless arguments. - std::vector mNamelessCaptions; ///< Captions of nameless arguments (for documentation). - - bool mAllowSingleDash; ///< Whether single dash is allowed as attribute prefix (normally, two dashes prefix long arguments) - - /** - * \brief Provides access to named argument object. - * \tparam Type of the argument object (must be ArgBase or derived class) - * \param name Name of the argument. - * \return Reference to argument object. - * \throws ArgumentException if the argument is not found or has invalid type. - */ - template - ARG_T& getArgTyped(const std::string &name) - { - if (mArguments.find(name) == mArguments.end()) - throw (ArgumentException() << "Argument '" << name << "' was not specified."); - ARG_T *arg = dynamic_cast(mArguments.find(name)->second.get()); - if (arg == nullptr) - throw (ArgumentException() << "Argument '" << name << "' type mismatch."); - return *arg; - } - - - /** - * \brief Provides read-only access to named argument object. - * \tparam Type of the argument object (must be ArgBase or derived class) - * \param name Name of the argument. - * \return Reference to argument object. - * \throws ArgumentException if the argument is not found or has invalid type. - */ - template - const ARG_T& getArgTyped(const std::string &name) const - { - if (mArguments.find(name) == mArguments.end()) - throw (ArgumentException() << "Argument '" << name << "' was not specified."); - const ARG_T *arg = dynamic_cast(mArguments.find(name)->second.get()); - if (arg == nullptr) - throw (ArgumentException() << "Argument '" << name << "' type mismatch."); - return *arg; - } - - -public: - /** - * \brief Initialize the arguments container. - * \param namelessMin Minimal number of required nameless arguments. - * \param namelessMax Maximal number of allowed nameless arguments. - */ - ProgramArguments(std::size_t namelessMin = 0, std::size_t namelessMax = ~(std::size_t)0) - : mNamelessMin(namelessMin), mNamelessMax(namelessMax), mAllowSingleDash(false) - { - if (namelessMin > namelessMax) - throw (ArgumentException() << "Nameless arguments minimum (" << namelessMin << ") exceeds the maximum (" << namelessMax << ")."); - } + /** + * \brief Typed accessor to argument object. + */ + const ArgBase &getArg(const std::string &name) const + { + return getArgTyped(name); + } - /** - * Allow or disable whether single dash is acceptable for argument name prefix. - * Typically, two dashes are required for long argument names. - */ - ProgramArguments& allowSingleDash(bool allow = true) - { - mAllowSingleDash = allow; - return *this; + // Macro that will help us define all the accessor methods. +#define ARG_ACCESSOR(TYPE) \ + Arg##TYPE &getArg##TYPE(const std::string &name) \ + { \ + return getArgTyped(name); \ + } \ + const Arg##TYPE &getArg##TYPE(const std::string &name) const \ + { \ + return getArgTyped(name); \ } - - /** - * Whether a single dash is acceptable for argument name prefix. - */ - bool isSingleDashAllowed() const { return mAllowSingleDash; } - - - /** - * \brief Return name of the program as it was passed to command line. - */ - const std::string& getProgramName() const - { - return mProgramName; +#define ARG_ACCESSOR_FULL(TYPE) \ + ARG_ACCESSOR(TYPE) \ + Arg##TYPE::value_t getValue##TYPE(const std::string &name) const \ + { \ + return getArgTyped(name).getValue(); \ } - /** - * \brief Typed accessor to argument object. - */ - ArgBase& getArg(const std::string &name) - { - return getArgTyped(name); - } - + // Definitions of typed accessors ... + ARG_ACCESSOR_FULL(Bool) + ARG_ACCESSOR_FULL(Int) + ARG_ACCESSOR(IntList) + ARG_ACCESSOR_FULL(Float) + ARG_ACCESSOR(FloatList) + ARG_ACCESSOR_FULL(String) + ARG_ACCESSOR_FULL(Enum) + ARG_ACCESSOR(StringList) - /** - * \brief Typed accessor to argument object. - */ - const ArgBase& getArg(const std::string &name) const - { - return getArgTyped(name); - } + /** + * \brief Nameless argument accessor. + */ + const std::string &operator[](std::size_t idx) const + { + if (idx >= mNamelessArguments.size()) + throw(ArgumentException() << "Unable to retieve nameless argument #" << idx << " (only " + << mNamelessArguments.size() << " provided)."); + return mNamelessArguments[idx]; + } - // Macro that will help us define all the accessor methods. -#define ARG_ACCESSOR(TYPE) \ - Arg##TYPE& getArg##TYPE(const std::string &name) { \ - return getArgTyped(name); \ - } \ - const Arg##TYPE& getArg##TYPE(const std::string &name) const { \ - return getArgTyped(name); \ - } -#define ARG_ACCESSOR_FULL(TYPE) \ - ARG_ACCESSOR(TYPE) \ - Arg##TYPE::value_t getValue##TYPE(const std::string &name) const { \ - return getArgTyped(name).getValue(); \ - } + /** + * \brief Set a documentation caption of nameless argument. + * \param idx A zero-based index of the nameless argument. It must fall + * into constrained range. + * \param caption A text content of the caption. + */ + void setNamelessCaption(std::size_t idx, const std::string &caption) + { + if (mNamelessCaptions.size() <= idx) mNamelessCaptions.resize(idx + 1); + mNamelessCaptions[idx] = caption; + } - // Definitions of typed accessors ... - ARG_ACCESSOR_FULL(Bool) - ARG_ACCESSOR_FULL(Int) - ARG_ACCESSOR(IntList) - ARG_ACCESSOR_FULL(Float) - ARG_ACCESSOR(FloatList) - ARG_ACCESSOR_FULL(String) - ARG_ACCESSOR_FULL(Enum) - ARG_ACCESSOR(StringList) + /** + * \brief Return the number of nameless arguments. + */ + std::size_t namelessCount() const + { + return mNamelessArguments.size(); + } - /** - * \brief Nameless argument accessor. - */ - const std::string& operator[](std::size_t idx) const - { - if (idx >= mNamelessArguments.size()) - throw (ArgumentException() << "Unable to retieve nameless argument #" << idx << " (only " << mNamelessArguments.size() << " provided)."); - return mNamelessArguments[idx]; - } + /** + * \brief Register new named argument to the parser. + * \param arg The argument object to be registred. + * + * After registration the object becomes a property of ProgramArguments object, + * thus the called should not care nor attempt to destroy the arg object passed + * to this method. Argument name is acquired from the object itself and it must + * not collide with any other registered arguments. + */ + void registerArg(std::unique_ptr &&arg) + { + if (!arg) { throw(RuntimeError("Argument object must not be NULL.")); } + if (mArguments.find(arg->getName()) != mArguments.end()) { + throw(ArgumentException() << "Attempting to register duplicate argument '" << arg->getName() << "'."); + } + arg->args = this; + mArguments[arg->getName()] = std::move(arg); + } - /** - * \brief Set a documentation caption of nameless argument. - * \param idx A zero-based index of the nameless argument. It must fall - * into constrained range. - * \param caption A text content of the caption. - */ - void setNamelessCaption(std::size_t idx, const std::string &caption) - { - if (mNamelessCaptions.size() <= idx) - mNamelessCaptions.resize(idx + 1); - mNamelessCaptions[idx] = caption; - } + /** + * \brief Parse the command line and fill registred named arguments and nameless argument values. + * \param argc A copy of a parameter of main() function. + * \param argv A copy of a parameter of main() function. + */ + void process(int argc, const char *argv[]) + { + // Get program name. + mProgramName.assign(argv[0]); + --argc; + ++argv; // skip program name + + // Process named arguments. + while (argc > 0 && argv[0] && argv[0][0] == '-' && (mAllowSingleDash || argv[0][1] == '-')) { + // Get argument name. + const char *name = argv[0] + 1; // +1 ~ skip leading dash + --argc; + ++argv; + + if (name[0] == '-') ++name; // skip second dash if present + if (!name[0]) break; // empty name ~ end of named argument; + + // Find corresponding argument object. + auto argIt = mArguments.find(name); + if (argIt == mArguments.end()) throw(ArgumentException() << "Unknown argument '" << name << "'."); + + // Process the argument value(s). + argIt->second->process(argc, argv); + } - /** - * \brief Return the number of nameless arguments. - */ - std::size_t namelessCount() const - { - return mNamelessArguments.size(); - } + // Verify argument constraints. + for (auto it = mArguments.begin(); it != mArguments.end(); ++it) it->second->checkConstraints(mArguments); + // Process nameless arguments. + while (argc > 0 && argv[0]) { + mNamelessArguments.push_back(argv[0]); + --argc; + ++argv; + } - /** - * \brief Register new named argument to the parser. - * \param arg The argument object to be registred. - * - * After registration the object becomes a property of ProgramArguments object, - * thus the called should not care nor attempt to destroy the arg object passed - * to this method. Argument name is acquired from the object itself and it must - * not collide with any other registered arguments. - */ - void registerArg(std::unique_ptr && arg) - { - if (!arg) { - throw (RuntimeError("Argument object must not be NULL.")); - } + // Check nameless arguments size. + if (mNamelessArguments.size() < mNamelessMin) + throw(ArgumentException() << "At least " << mNamelessMin << " nameless arguments expected, only " + << mNamelessArguments.size() << " were found."); - if (mArguments.find(arg->getName()) != mArguments.end()) { - throw (ArgumentException() << "Attempting to register duplicate argument '" << arg->getName() << "'."); + if (mNamelessArguments.size() > mNamelessMax) + throw(ArgumentException() << "Too many nameless arguments (" << mNamelessArguments.size() << " found, " + << mNamelessMax << " is the limit)."); } - arg->args = this; - mArguments[arg->getName()] = std::move(arg); - } - /** - * \brief Parse the command line and fill registred named arguments and nameless argument values. - * \param argc A copy of a parameter of main() function. - * \param argv A copy of a parameter of main() function. - */ - void process(int argc, const char *argv[]) - { - // Get program name. - mProgramName.assign(argv[0]); - --argc; ++argv; // skip program name - - // Process named arguments. - while (argc > 0 && argv[0] && argv[0][0] == '-' && (mAllowSingleDash || argv[0][1] == '-')) { - // Get argument name. - const char *name = argv[0] + 1; // +1 ~ skip leading dash - --argc; ++argv; - - if (name[0] == '-') ++name; // skip second dash if present - if (!name[0]) - break; // empty name ~ end of named argument; - - // Find corresponding argument object. - auto argIt = mArguments.find(name); - if (argIt == mArguments.end()) - throw (ArgumentException() << "Unknown argument '" << name << "'."); - - // Process the argument value(s). - argIt->second->process(argc, argv); + /** + * \brief Parse the command line and fill registred named arguments and nameless argument values. + * \param argc A copy of a parameter of main() function. + * \param argv A copy of a parameter of main() function. + * \note This overloaded function takes non-const char** as argv. The reason is that main() typically + * declares char **argv without const and C++ does not allow automatic conversion from + * char** to const char** as it may create a loophole for modifying constant chars. + * However, we are only reading argv, so no harm is done. + */ + void process(int argc, char *argv[]) + { + process(argc, (const char **) argv); } - // Verify argument constraints. - for (auto it = mArguments.begin(); it != mArguments.end(); ++it) - it->second->checkConstraints(mArguments); - // Process nameless arguments. - while (argc > 0 && argv[0]) { - mNamelessArguments.push_back(argv[0]); - --argc; ++argv; + /** + * \brief Print simple usage generated from the argument specifications to std. output. + */ + void printUsage() const + { + printUsage(std::cout); } - // Check nameless arguments size. - if (mNamelessArguments.size() < mNamelessMin) - throw (ArgumentException() << "At least " << mNamelessMin << " nameless arguments expected, only " - << mNamelessArguments.size() << " were found."); - if (mNamelessArguments.size() > mNamelessMax) - throw (ArgumentException() << "Too many nameless arguments (" << mNamelessArguments.size() << " found, " - << mNamelessMax << " is the limit)."); - } - - - /** - * \brief Parse the command line and fill registred named arguments and nameless argument values. - * \param argc A copy of a parameter of main() function. - * \param argv A copy of a parameter of main() function. - * \note This overloaded function takes non-const char** as argv. The reason is that main() typically - * declares char **argv without const and C++ does not allow automatic conversion from - * char** to const char** as it may create a loophole for modifying constant chars. - * However, we are only reading argv, so no harm is done. - */ - void process(int argc, char *argv[]) - { - process(argc, (const char**)argv); - } - - - /** - * \brief Print simple usage generated from the argument specifications to std. output. - */ - void printUsage() const - { - printUsage(std::cout); - } - - - /** - * \brief Print simple usage generated from the argument specifications. - * \param stream Output where the usage is printed (usually either cout or cerr). - */ - void printUsage(std::ostream &stream) const - { - stream << "Usage: " << Path::getFileName(getProgramName()) << std::endl; + /** + * \brief Print simple usage generated from the argument specifications. + * \param stream Output where the usage is printed (usually either cout or cerr). + */ + void printUsage(std::ostream &stream) const + { + stream << "Usage: " << Path::getFileName(getProgramName()) << std::endl; - stream << "Named arguments:" << std::endl; - for (auto it = mArguments.begin(); it != mArguments.end(); ++it) { - stream << " " << it->first << " - " << it->second->getComment() << std::endl; - } + stream << "Named arguments:" << std::endl; + for (auto it = mArguments.begin(); it != mArguments.end(); ++it) { + stream << " " << it->first << " - " << it->second->getComment() << std::endl; + } - stream << "Nameless arguments (" << mNamelessMin << ", " << mNamelessMax << "):"; - for (std::size_t i = 0; i < mNamelessCaptions.size(); ++i) { - stream << " " << mNamelessCaptions[i]; + stream << "Nameless arguments (" << mNamelessMin << ", " << mNamelessMax << "):"; + for (std::size_t i = 0; i < mNamelessCaptions.size(); ++i) { stream << " " << mNamelessCaptions[i]; } + stream << std::endl; } - stream << std::endl; - } -}; + }; -} +} // namespace bpp #endif diff --git a/judges/recodex_token_judge/bpplib/cli/logger.hpp b/judges/recodex_token_judge/bpplib/cli/logger.hpp index 1dae5aaa..3a5ce141 100644 --- a/judges/recodex_token_judge/bpplib/cli/logger.hpp +++ b/judges/recodex_token_judge/bpplib/cli/logger.hpp @@ -16,256 +16,270 @@ #include #include -namespace bpp { +namespace bpp +{ -/** - * Severity (priority) enum constants. - */ -enum class LogSeverity -{ - UNDEFINED = 0, - FATAL = 1, - ERROR = 2, - WARNING = 3, - INFO = 4, - NOTICE = 5, - DEBUG = 6, - ANY = 7, // any must be last, so we have the biggest number here -}; - - -/** - * A very simple implementation of logger with stream-like interface, which respects message severities and - * allows buffering and smart size-restrictions imposed on the contents. - */ -class Logger -{ -protected: /** - * Internal structure forming one data block of log. + * Severity (priority) enum constants. */ - struct Block - { - LogSeverity severity; - std::string data; + enum class LogSeverity { + UNDEFINED = 0, + FATAL = 1, + ERROR = 2, + WARNING = 3, + INFO = 4, + NOTICE = 5, + DEBUG = 6, + ANY = 7, // any must be last, so we have the biggest number here }; - std::ostream &mDefaultSink; ///< Default ostream, where the log is being flushed. - LogSeverity mSeverity; ///< Current severity (of data in accumulator). - LogSeverity mMaxSeverity; ///< Severity restriction. - std::size_t mMaxLength; ///< Log length restrictoin. - - std::stringstream mAccumulator; ///< String buffer where the data are accumulated. - std::size_t mAccumulatorSize; ///< Number of chars in accumulator. - std::vector mLog; ///< Log as a sequence of Blocks. - std::map mLengths; ///< How many bytes has each severity level of the log. - /** - * Flush current accumulator into another block in the log. + * A very simple implementation of logger with stream-like interface, which respects message severities and + * allows buffering and smart size-restrictions imposed on the contents. */ - void flushAccumulator() + class Logger { - if (mAccumulatorSize > 0) { - // Something was accumulated ... save another block. - std::string acu = mAccumulator.str(); - mLog.push_back(Block()); - mLog.back().severity = mSeverity; - mLog.back().data = acu; - mLengths[mSeverity] += acu.length(); - mAccumulator.str(std::string()); - mAccumulatorSize = 0; + protected: + /** + * Internal structure forming one data block of log. + */ + struct Block { + LogSeverity severity; + std::string data; + }; + + std::ostream &mDefaultSink; ///< Default ostream, where the log is being flushed. + LogSeverity mSeverity; ///< Current severity (of data in accumulator). + LogSeverity mMaxSeverity; ///< Severity restriction. + std::size_t mMaxLength; ///< Log length restrictoin. + + std::stringstream mAccumulator; ///< String buffer where the data are accumulated. + std::size_t mAccumulatorSize; ///< Number of chars in accumulator. + std::vector mLog; ///< Log as a sequence of Blocks. + std::map mLengths; ///< How many bytes has each severity level of the log. + + + /** + * Flush current accumulator into another block in the log. + */ + void flushAccumulator() + { + if (mAccumulatorSize > 0) { + // Something was accumulated ... save another block. + std::string acu = mAccumulator.str(); + mLog.push_back(Block()); + mLog.back().severity = mSeverity; + mLog.back().data = acu; + mLengths[mSeverity] += acu.length(); + mAccumulator.str(std::string()); + mAccumulatorSize = 0; + } } - } - /** - * Compute maximal severity level and how many bytes of that last level can be written out. - * \param severity Severity limit. Used both as input (maximal severity limit examined) and - * output (if the level needs adjustment, the value is modified). - * \return Length limit imposed on the severity level in the severity parameter. Levels with higher priority - * are returned completely. - */ - std::size_t applySizeLimit(LogSeverity &severity) - { - std::size_t total = 0; - for (std::size_t i = (std::size_t)LogSeverity::UNDEFINED; i < (std::size_t)severity; ++i) { - total += mLengths[(LogSeverity)i]; - if (total >= mMaxLength) { // max log length would be exceeded including current severity into output - severity = (LogSeverity)i; - break; + /** + * Compute maximal severity level and how many bytes of that last level can be written out. + * \param severity Severity limit. Used both as input (maximal severity limit examined) and + * output (if the level needs adjustment, the value is modified). + * \return Length limit imposed on the severity level in the severity parameter. Levels with higher priority + * are returned completely. + */ + std::size_t applySizeLimit(LogSeverity &severity) + { + std::size_t total = 0; + for (std::size_t i = (std::size_t) LogSeverity::UNDEFINED; i < (std::size_t) severity; ++i) { + total += mLengths[(LogSeverity) i]; + if (total >= mMaxLength) { // max log length would be exceeded including current severity into output + severity = (LogSeverity) i; + break; + } } + + return (total <= mMaxLength) ? ~(std::size_t) 0 : mMaxLength - (total - mLengths[(LogSeverity) severity]); } - return (total <= mMaxLength) ? ~(std::size_t)0 : mMaxLength - (total - mLengths[(LogSeverity)severity]); - } + public: + Logger(std::ostream &defaultSink = std::cerr) + : mDefaultSink(defaultSink), mSeverity(LogSeverity::UNDEFINED), mMaxSeverity(LogSeverity::ANY), + mMaxLength(~(std::size_t) 0) + { + } + virtual ~Logger() + { + } -public: - Logger(std::ostream &defaultSink = std::cerr) : mDefaultSink(defaultSink), - mSeverity(LogSeverity::UNDEFINED), mMaxSeverity(LogSeverity::ANY), mMaxLength(~(std::size_t)0) {} - virtual ~Logger() {} + /** + * Adjust current severity level. The adjustment also invokes internal accumulator flush. + */ + Logger &setSeverity(LogSeverity severity) + { + if (severity == mSeverity) return *this; - /** - * Adjust current severity level. The adjustment also invokes internal accumulator flush. - */ - Logger& setSeverity(LogSeverity severity) - { - if (severity == mSeverity) return *this; - - flushAccumulator(); - mSeverity = severity; - return *this; - } + flushAccumulator(); + mSeverity = severity; + return *this; + } - Logger& fatal() { return setSeverity(LogSeverity::FATAL); } - Logger& error() { return setSeverity(LogSeverity::ERROR); } - Logger& warning() { return setSeverity(LogSeverity::WARNING); } - Logger& info() { return setSeverity(LogSeverity::INFO); } - Logger& notice() { return setSeverity(LogSeverity::NOTICE); } - Logger& debug() { return setSeverity(LogSeverity::DEBUG); } + Logger &fatal() + { + return setSeverity(LogSeverity::FATAL); + } + Logger &error() + { + return setSeverity(LogSeverity::ERROR); + } + Logger &warning() + { + return setSeverity(LogSeverity::WARNING); + } + Logger &info() + { + return setSeverity(LogSeverity::INFO); + } + Logger ¬ice() + { + return setSeverity(LogSeverity::NOTICE); + } + Logger &debug() + { + return setSeverity(LogSeverity::DEBUG); + } - /** - * Set internal limit for severity level. Only messages with this level and higher are written out. - */ - void restrictSeverity(LogSeverity maxSeverity) - { - mMaxSeverity = maxSeverity; - } + /** + * Set internal limit for severity level. Only messages with this level and higher are written out. + */ + void restrictSeverity(LogSeverity maxSeverity) + { + mMaxSeverity = maxSeverity; + } - /** - * Impose a limit on the log size. The limit is applied in a smart way, so that messages - * with higher priorities are also prioritized in the output, but the actual order of all - * messages is preserved. - */ - void restrictSize(std::size_t maxLength) - { - mMaxLength = maxLength; - } + /** + * Impose a limit on the log size. The limit is applied in a smart way, so that messages + * with higher priorities are also prioritized in the output, but the actual order of all + * messages is preserved. + */ + void restrictSize(std::size_t maxLength) + { + mMaxLength = maxLength; + } - /** - * Add another message/value into the log under current severity. - * This function is also invoked by the << operator. - */ - template - Logger& write(const T& data) - { - mAccumulator << data; - mAccumulatorSize = (std::size_t)mAccumulator.tellp(); - return *this; - } + /** + * Add another message/value into the log under current severity. + * This function is also invoked by the << operator. + */ + template Logger &write(const T &data) + { + mAccumulator << data; + mAccumulatorSize = (std::size_t) mAccumulator.tellp(); + return *this; + } - /** - * Flush the log to the default sink. - */ - void flush() - { - flushAccumulator(); - LogSeverity limitSeverity = mMaxSeverity; - std::size_t limitSize = applySizeLimit(limitSeverity); // limitSize applies to limitSeverity level only, levels above are printed completely - - for (auto && block : mLog) { - if (block.severity > limitSeverity) continue; - if (block.severity == limitSeverity) { - if (limitSize == 0) continue; - if (limitSize < block.data.length()) { - // Special case -- print only part of the block. - bool newline = block.data.find("\n", limitSize) != std::string::npos; - mDefaultSink.write(block.data.c_str(), limitSize - (newline ? 1 : 0)); - if (newline) mDefaultSink.write("\n", 1); - limitSize = 0; - continue; + /** + * Flush the log to the default sink. + */ + void flush() + { + flushAccumulator(); + LogSeverity limitSeverity = mMaxSeverity; + std::size_t limitSize = applySizeLimit( + limitSeverity); // limitSize applies to limitSeverity level only, levels above are printed completely + + for (auto &&block : mLog) { + if (block.severity > limitSeverity) continue; + if (block.severity == limitSeverity) { + if (limitSize == 0) continue; + if (limitSize < block.data.length()) { + // Special case -- print only part of the block. + bool newline = block.data.find("\n", limitSize) != std::string::npos; + mDefaultSink.write(block.data.c_str(), limitSize - (newline ? 1 : 0)); + if (newline) mDefaultSink.write("\n", 1); + limitSize = 0; + continue; + } else + limitSize -= block.data.length(); } - else - limitSize -= block.data.length(); + mDefaultSink.write(block.data.c_str(), block.data.length()); } - mDefaultSink.write(block.data.c_str(), block.data.length()); + + clear(); } - - clear(); - } - /** - * Return number of chars logged so far. - * \param severity Applied severiry level. Only messages with this level and above are counted. - */ - std::size_t size(LogSeverity severity = LogSeverity::ANY) const - { - std::size_t size = 0; - for (std::size_t i = (std::size_t)LogSeverity::UNDEFINED; i <= (std::size_t)severity; ++i) { - LogSeverity s = (LogSeverity)i; - auto it = mLengths.find(s); - if (it != mLengths.end()) { - size += it->second; - } + /** + * Return number of chars logged so far. + * \param severity Applied severiry level. Only messages with this level and above are counted. + */ + std::size_t size(LogSeverity severity = LogSeverity::ANY) const + { + std::size_t size = 0; + for (std::size_t i = (std::size_t) LogSeverity::UNDEFINED; i <= (std::size_t) severity; ++i) { + LogSeverity s = (LogSeverity) i; + auto it = mLengths.find(s); + if (it != mLengths.end()) { size += it->second; } - if (s == mSeverity) { - size += mAccumulatorSize; + if (s == mSeverity) { size += mAccumulatorSize; } } + return size; } - return size; - } - /** - * Check whether the size of the log (for selected severity and above) is full (exceeds the size restriction). - * \param severity Applied severiry level. Only messages with this level and above are counted. - */ - bool isFull(LogSeverity severity = LogSeverity::ANY) const - { - return mMaxLength <= size(severity); - } + /** + * Check whether the size of the log (for selected severity and above) is full (exceeds the size restriction). + * \param severity Applied severiry level. Only messages with this level and above are counted. + */ + bool isFull(LogSeverity severity = LogSeverity::ANY) const + { + return mMaxLength <= size(severity); + } - /** - * Empty the log, discard all data. - */ - void clear() - { - mLog.clear(); - mAccumulator.str(std::string()); - mAccumulatorSize = 0; - mLengths.clear(); - } + /** + * Empty the log, discard all data. + */ + void clear() + { + mLog.clear(); + mAccumulator.str(std::string()); + mAccumulatorSize = 0; + mLengths.clear(); + } - template - Logger& operator<<(const T& data) - { - return write(data); - } -}; + template Logger &operator<<(const T &data) + { + return write(data); + } + }; -template<> -Logger& Logger::operator<< (const LogSeverity& data) -{ - return setSeverity(data); -} + template <> Logger &Logger::operator<<(const LogSeverity &data) + { + return setSeverity(data); + } + /** + * Injectable singleton holder and wrapper for logger entitiy. + */ + Logger &log(std::unique_ptr &&logger = std::unique_ptr()) + { + static std::unique_ptr residentLogger; + if (logger) { + // Register new logger ... + residentLogger = std::move(logger); + } -/** - * Injectable singleton holder and wrapper for logger entitiy. - */ -Logger& log(std::unique_ptr && logger = std::unique_ptr()) -{ - static std::unique_ptr residentLogger; - if (logger) { - // Register new logger ... - residentLogger = std::move(logger); - } + if (!residentLogger) { + // No logger, construct default + residentLogger = bpp::make_unique(); + } - if (!residentLogger) { - // No logger, construct default - residentLogger = bpp::make_unique(); + return (*residentLogger.get()).setSeverity(LogSeverity::UNDEFINED); } - return (*residentLogger.get()).setSeverity(LogSeverity::UNDEFINED); -} - -} +} // namespace bpp #endif diff --git a/judges/recodex_token_judge/bpplib/misc/exception.hpp b/judges/recodex_token_judge/bpplib/misc/exception.hpp index 7cb6fd5e..95f5c428 100644 --- a/judges/recodex_token_judge/bpplib/misc/exception.hpp +++ b/judges/recodex_token_judge/bpplib/misc/exception.hpp @@ -14,112 +14,140 @@ namespace bpp { -/** - * \brief Specific exception that behaves like a stream, so it can cummulate - * error messages more easily. - */ -class StreamException : public std::exception -{ -protected: - std::string mMessage; ///< Internal buffer where the message is kept. - -public: - StreamException() : std::exception() {} - StreamException(const char *msg) : std::exception(), mMessage(msg) {} - StreamException(const std::string &msg) : std::exception(), mMessage(msg) {} - virtual ~StreamException() throw() {} - - virtual const char* what() const throw() + /** + * \brief Specific exception that behaves like a stream, so it can cummulate + * error messages more easily. + */ + class StreamException : public std::exception { - return mMessage.c_str(); - } - - // Overloading << operator that uses stringstream to append data to mMessage. - template - StreamException& operator<<(const T &data) + protected: + std::string mMessage; ///< Internal buffer where the message is kept. + + public: + StreamException() : std::exception() + { + } + StreamException(const char *msg) : std::exception(), mMessage(msg) + { + } + StreamException(const std::string &msg) : std::exception(), mMessage(msg) + { + } + virtual ~StreamException() throw() + { + } + + virtual const char *what() const throw() + { + return mMessage.c_str(); + } + + // Overloading << operator that uses stringstream to append data to mMessage. + template StreamException &operator<<(const T &data) + { + std::stringstream stream; + stream << mMessage << data; + mMessage = stream.str(); + return *this; + } + }; + + + /** + * \brief A stream exception that is base for all runtime errors. + */ + class RuntimeError : public StreamException { - std::stringstream stream; - stream << mMessage << data; - mMessage = stream.str(); - return *this; - } -}; - - -/** - * \brief A stream exception that is base for all runtime errors. - */ -class RuntimeError : public StreamException -{ -public: - RuntimeError() : StreamException() {} - RuntimeError(const char *msg) : StreamException(msg) {} - RuntimeError(const std::string &msg) : StreamException(msg) {} - virtual ~RuntimeError() throw() {} - - - // Overloading << operator that uses stringstream to append data to mMessage. - template - RuntimeError& operator<<(const T &data) + public: + RuntimeError() : StreamException() + { + } + RuntimeError(const char *msg) : StreamException(msg) + { + } + RuntimeError(const std::string &msg) : StreamException(msg) + { + } + virtual ~RuntimeError() throw() + { + } + + + // Overloading << operator that uses stringstream to append data to mMessage. + template RuntimeError &operator<<(const T &data) + { + std::stringstream stream; + stream << mMessage << data; + mMessage = stream.str(); + return *this; + } + }; + + + /** + * \brief A special type of runtime error representing error in logic of the + * application/algorithm/utility... (e.g., invokation of a method when + * the object is in indesirable state or an invalid configuration of + * an utility. + */ + class LogicError : public RuntimeError { - std::stringstream stream; - stream << mMessage << data; - mMessage = stream.str(); - return *this; - } -}; - - -/** - * \brief A special type of runtime error representing error in logic of the - * application/algorithm/utility... (e.g., invokation of a method when - * the object is in indesirable state or an invalid configuration of - * an utility. - */ -class LogicError : public RuntimeError -{ -public: - LogicError() : RuntimeError() {} - LogicError(const char *msg) : RuntimeError(msg) {} - LogicError(const std::string &msg) : RuntimeError(msg) {} - virtual ~LogicError() throw() {} - - - // Overloading << operator that uses stringstream to append data to mMessage. - template - LogicError& operator<<(const T &data) - { - std::stringstream stream; - stream << mMessage << data; - mMessage = stream.str(); - return *this; - } -}; - - -/** -* \brief Internal error that indicate missing implementation or virtual function override. -*/ -class NotImplementedError : public RuntimeError -{ -public: - NotImplementedError() : RuntimeError() {} - NotImplementedError(const char *msg) : RuntimeError(msg) {} - NotImplementedError(const std::string &msg) : RuntimeError(msg) {} - virtual ~NotImplementedError() throw() {} - - - // Overloading << operator that uses stringstream to append data to mMessage. - template - NotImplementedError& operator<<(const T &data) + public: + LogicError() : RuntimeError() + { + } + LogicError(const char *msg) : RuntimeError(msg) + { + } + LogicError(const std::string &msg) : RuntimeError(msg) + { + } + virtual ~LogicError() throw() + { + } + + + // Overloading << operator that uses stringstream to append data to mMessage. + template LogicError &operator<<(const T &data) + { + std::stringstream stream; + stream << mMessage << data; + mMessage = stream.str(); + return *this; + } + }; + + + /** + * \brief Internal error that indicate missing implementation or virtual function override. + */ + class NotImplementedError : public RuntimeError { - std::stringstream stream; - stream << mMessage << data; - mMessage = stream.str(); - return *this; - } -}; - - -} + public: + NotImplementedError() : RuntimeError() + { + } + NotImplementedError(const char *msg) : RuntimeError(msg) + { + } + NotImplementedError(const std::string &msg) : RuntimeError(msg) + { + } + virtual ~NotImplementedError() throw() + { + } + + + // Overloading << operator that uses stringstream to append data to mMessage. + template NotImplementedError &operator<<(const T &data) + { + std::stringstream stream; + stream << mMessage << data; + mMessage = stream.str(); + return *this; + } + }; + + +} // namespace bpp #endif diff --git a/judges/recodex_token_judge/bpplib/misc/ptr_fix.hpp b/judges/recodex_token_judge/bpplib/misc/ptr_fix.hpp index 9f94ddc5..940633e5 100644 --- a/judges/recodex_token_judge/bpplib/misc/ptr_fix.hpp +++ b/judges/recodex_token_judge/bpplib/misc/ptr_fix.hpp @@ -1,8 +1,8 @@ /* -* Author: Martin Krulis -* Last Modification: 23.12.2015 -* License: CC 3.0 BY-NC (http://creativecommons.org/) -*/ + * Author: Martin Krulis + * Last Modification: 23.12.2015 + * License: CC 3.0 BY-NC (http://creativecommons.org/) + */ #ifndef BPPLIB_MISC_PTR_FIX_HPP #define BPPLIB_MISC_PTR_FIX_HPP @@ -11,38 +11,35 @@ // Define pointer restrict keyword for various compilers. #if defined(__ICC) || defined(__ICL) - // Intel compiler - #define RESTRICT __restrict__ +// Intel compiler +#define RESTRICT __restrict__ #elif defined(_MSC_VER) - // MSVC - #define RESTRICT __restrict +// MSVC +#define RESTRICT __restrict #elif defined(__GNUG__) - // GNU Linux (g++) - #define RESTRICT __restrict__ +// GNU Linux (g++) +#define RESTRICT __restrict__ #else - // unknown compiler (define as empty) - #define RESTRICT +// unknown compiler (define as empty) +#define RESTRICT #endif - namespace bpp { // Replacement for std::make_unique for C++ < 14 - template - std::unique_ptr make_unique(Args&&... args) + template std::unique_ptr make_unique(Args &&... args) { return std::unique_ptr(new T(std::forward(args)...)); } // Replacement for std::make_shared for C++ < 14 - template - std::shared_ptr make_shared(Args&&... args) + template std::shared_ptr make_shared(Args &&... args) { return std::shared_ptr(new T(std::forward(args)...)); } -} +} // namespace bpp #endif diff --git a/judges/recodex_token_judge/bpplib/system/filesystem.hpp b/judges/recodex_token_judge/bpplib/system/filesystem.hpp index dbd37238..d98a6cd3 100644 --- a/judges/recodex_token_judge/bpplib/system/filesystem.hpp +++ b/judges/recodex_token_judge/bpplib/system/filesystem.hpp @@ -9,18 +9,18 @@ #include #ifdef _WIN32 - #define NOMINMAX - #include +#define NOMINMAX +#include - // This macro is defined in wingdi.h, I do non want ERROR macro in my projects! - #ifdef ERROR - #undef ERROR - #endif +// This macro is defined in wingdi.h, I do non want ERROR macro in my projects! +#ifdef ERROR +#undef ERROR +#endif #else - #include - #include - #include - #include +#include +#include +#include +#include #endif #include @@ -30,169 +30,167 @@ namespace bpp { -/** - * \brief A special type of runtime error representing error with file operations. - */ -class FileError : public RuntimeError -{ -public: - FileError() : RuntimeError() {} - FileError(const char *msg) : RuntimeError(msg) {} - FileError(const std::string &msg) : RuntimeError(msg) {} - virtual ~FileError() throw() {} - - - // Overloading << operator that uses stringstream to append data to mMessage. - template - FileError& operator<<(const T &data) + /** + * \brief A special type of runtime error representing error with file operations. + */ + class FileError : public RuntimeError { - std::stringstream stream; - stream << mMessage << data; - mMessage = stream.str(); - return *this; - } -}; + public: + FileError() : RuntimeError() + { + } + FileError(const char *msg) : RuntimeError(msg) + { + } + FileError(const std::string &msg) : RuntimeError(msg) + { + } + virtual ~FileError() throw() + { + } + // Overloading << operator that uses stringstream to append data to mMessage. + template FileError &operator<<(const T &data) + { + std::stringstream stream; + stream << mMessage << data; + mMessage = stream.str(); + return *this; + } + }; - -/** - * \brief Encapsulates various filesystem functions implemented for both Windows and Unix (Linux) OSes. - */ -class Path -{ -public: /** - * \brief Return a file name from the path string. - * \param path A path to a file. - * \return The filename part of the path (if the path ends with '/' or '\', empty string is returned). - * \note This function works for both windows and linux paths. + * \brief Encapsulates various filesystem functions implemented for both Windows and Unix (Linux) OSes. */ - static std::string getFileName(const std::string &path) + class Path { - size_t pos = path.find_last_of("/\\"); - return (pos != std::string::npos) - ? path.substr(pos+1) : path; - } + public: + /** + * \brief Return a file name from the path string. + * \param path A path to a file. + * \return The filename part of the path (if the path ends with '/' or '\', empty string is returned). + * \note This function works for both windows and linux paths. + */ + static std::string getFileName(const std::string &path) + { + size_t pos = path.find_last_of("/\\"); + return (pos != std::string::npos) ? path.substr(pos + 1) : path; + } - /** - * \brief Return a path to file without its last extension. - * \param path A path to a file. - * \return Remaining part of the path after the trailing .* was removed. - * \note This function works for both windows and linux paths. - */ - static std::string cropExtension(const std::string &path) - { - if (path.empty()) - throw RuntimeError("Empty path provided to crop_extension function."); - - if (path[path.length()-1] == '/' || path[path.length()-1] == '\\') - return path; // path ends with dir separator => no file name to crop - - std::size_t lastSlashPos = path.find_last_of("/\\"); - std::size_t pos = path.find_last_of('.'); - - // Avoid cases where last detected '.' is in directory name. - if (pos == std::string::npos || (lastSlashPos != std::string::npos && pos < lastSlashPos)) - return path; - - // Avoid croping the current/parent directory paths ('.' and '..'). - if (path[path.length()-1] == '.') { - if (path.length() == 1 || path[path.length()-2] == '/' || path[path.length()-2] == '\\') - return path; // The '.' represents current directory, not an extension separator. - - if (path[path.length()-2] == '.') { // There are two consecutive dots '..' - if (path.length() == 2 || path[path.length()-3] == '/' || path[path.length()-3] == '\\') - return path; // The '..' represents parent directory, not an extension separator. + /** + * \brief Return a path to file without its last extension. + * \param path A path to a file. + * \return Remaining part of the path after the trailing .* was removed. + * \note This function works for both windows and linux paths. + */ + static std::string cropExtension(const std::string &path) + { + if (path.empty()) throw RuntimeError("Empty path provided to crop_extension function."); + + if (path[path.length() - 1] == '/' || path[path.length() - 1] == '\\') + return path; // path ends with dir separator => no file name to crop + + std::size_t lastSlashPos = path.find_last_of("/\\"); + std::size_t pos = path.find_last_of('.'); + + // Avoid cases where last detected '.' is in directory name. + if (pos == std::string::npos || (lastSlashPos != std::string::npos && pos < lastSlashPos)) return path; + + // Avoid croping the current/parent directory paths ('.' and '..'). + if (path[path.length() - 1] == '.') { + if (path.length() == 1 || path[path.length() - 2] == '/' || path[path.length() - 2] == '\\') + return path; // The '.' represents current directory, not an extension separator. + + if (path[path.length() - 2] == '.') { // There are two consecutive dots '..' + if (path.length() == 2 || path[path.length() - 3] == '/' || path[path.length() - 3] == '\\') + return path; // The '..' represents parent directory, not an extension separator. + } } - } - return path.substr(0, pos); - } + return path.substr(0, pos); + } - /** - * \brief Verify that selected path exists. - * \param path A string representing a filesystem path. - */ - static bool exists(const std::string &path) - { + /** + * \brief Verify that selected path exists. + * \param path A string representing a filesystem path. + */ + static bool exists(const std::string &path) + { #ifdef _WIN32 - return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES; + return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES; #else - struct stat fileStatus; - if (stat(path.c_str(), &fileStatus) == 0) - return true; + struct stat fileStatus; + if (stat(path.c_str(), &fileStatus) == 0) return true; - int err = errno; - if (err != ENOENT && err != ENOTDIR) - throw (FileError() << "Error occured when reading status of file '" << path << "' (errno = " << err << ")."); + int err = errno; + if (err != ENOENT && err != ENOTDIR) + throw(FileError() << "Error occured when reading status of file '" << path << "' (errno = " << err + << ")."); - return false; + return false; #endif - } + } - /** - * \brief Verify that selected existing path points to a regular file. - * \param path A string representing a filesystem path. - */ - static bool isRegularFile(const std::string &path) - { + /** + * \brief Verify that selected existing path points to a regular file. + * \param path A string representing a filesystem path. + */ + static bool isRegularFile(const std::string &path) + { #ifdef _WIN32 - DWORD attrs = GetFileAttributesA(path.c_str()); - if (attrs == INVALID_FILE_ATTRIBUTES) - throw (FileError() << "Unable to retrieve the attributes of '" << path << "'."); + DWORD attrs = GetFileAttributesA(path.c_str()); + if (attrs == INVALID_FILE_ATTRIBUTES) + throw(FileError() << "Unable to retrieve the attributes of '" << path << "'."); - return ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) - && ((attrs & FILE_ATTRIBUTE_DEVICE) == 0) - && ((attrs & FILE_ATTRIBUTE_VIRTUAL) == 0); + return ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) && ((attrs & FILE_ATTRIBUTE_DEVICE) == 0) && + ((attrs & FILE_ATTRIBUTE_VIRTUAL) == 0); #else - struct stat fileStatus; - if (stat(path.c_str(), &fileStatus) != 0) - throw (FileError() << "Unable to determine the status of '" << path << "'."); + struct stat fileStatus; + if (stat(path.c_str(), &fileStatus) != 0) + throw(FileError() << "Unable to determine the status of '" << path << "'."); - return S_ISREG(fileStatus.st_mode); + return S_ISREG(fileStatus.st_mode); #endif - } + } - /** - * \brief Verify that selected existing path points to a directory. - * \param path A string representing a filesystem path. - */ - static bool isDirectory(const std::string &path) - { + /** + * \brief Verify that selected existing path points to a directory. + * \param path A string representing a filesystem path. + */ + static bool isDirectory(const std::string &path) + { #ifdef _WIN32 - DWORD attrs = GetFileAttributesA(path.c_str()); - if (attrs == INVALID_FILE_ATTRIBUTES) - throw (FileError() << "Unable to retrieve the attributes of '" << path << "'."); + DWORD attrs = GetFileAttributesA(path.c_str()); + if (attrs == INVALID_FILE_ATTRIBUTES) + throw(FileError() << "Unable to retrieve the attributes of '" << path << "'."); - return ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0); + return ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0); #else - struct stat fileStatus; - if (stat(path.c_str(), &fileStatus) != 0) - throw (FileError() << "Unable to determine the status of '" << path << "'."); + struct stat fileStatus; + if (stat(path.c_str(), &fileStatus) != 0) + throw(FileError() << "Unable to determine the status of '" << path << "'."); - return S_ISDIR(fileStatus.st_mode); + return S_ISDIR(fileStatus.st_mode); #endif - } - + } - /** - * \brief Unlink selected (existing) file. - * \param path A string representing a filesystem path to a file which is to be removed. - */ - static void unlink(const std::string &path) - { - if (std::remove(path.c_str()) != 0) - throw (FileError() << "Unable to remove '" << path << "'."); - } -}; + /** + * \brief Unlink selected (existing) file. + * \param path A string representing a filesystem path to a file which is to be removed. + */ + static void unlink(const std::string &path) + { + if (std::remove(path.c_str()) != 0) throw(FileError() << "Unable to remove '" << path << "'."); + } + }; -} +} // namespace bpp #endif diff --git a/judges/recodex_token_judge/bpplib/system/mmap_file.hpp b/judges/recodex_token_judge/bpplib/system/mmap_file.hpp index f46a9cd5..15877f18 100644 --- a/judges/recodex_token_judge/bpplib/system/mmap_file.hpp +++ b/judges/recodex_token_judge/bpplib/system/mmap_file.hpp @@ -12,198 +12,190 @@ #ifdef _WIN32 - #define NOMINMAX - #include +#define NOMINMAX +#include - // This macro is defined in wingdi.h, I do non want ERROR macro in my projects! - #ifdef ERROR - #undef ERROR - #endif +// This macro is defined in wingdi.h, I do non want ERROR macro in my projects! +#ifdef ERROR +#undef ERROR +#endif #else - #include - #include - #include - #include - #include +#include +#include +#include +#include +#include #endif -namespace bpp { +namespace bpp +{ -/** - * \brief MultiOS wrapper for read-only mmaped files. - * - * This class is used to map database files into memory, - * so we can get faster access to them. - */ -class MMapFile -{ -private: + /** + * \brief MultiOS wrapper for read-only mmaped files. + * + * This class is used to map database files into memory, + * so we can get faster access to them. + */ + class MMapFile + { + private: #ifdef _WIN32 - typedef LONGLONG length_t; + typedef LONGLONG length_t; #else - typedef size_t length_t; + typedef size_t length_t; #endif - void *mData; ///< Pointer to memory area where the file is mapped. - length_t mLength; ///< Total size of the mapped file. - std::string mFileName; + void *mData; ///< Pointer to memory area where the file is mapped. + length_t mLength; ///< Total size of the mapped file. + std::string mFileName; #ifdef _WIN32 - HANDLE mFile; ///< Windows file handle. - HANDLE mMappedFile; ///< Windows mapped object handle. + HANDLE mFile; ///< Windows file handle. + HANDLE mMappedFile; ///< Windows mapped object handle. #else - int mFile; ///< Unix file handle. + int mFile; ///< Unix file handle. #endif -public: - MMapFile() : mData(NULL), mLength(0), + public: + MMapFile() + : mData(NULL), mLength(0), #ifdef _WIN32 - mFile(NULL), mMappedFile(NULL) + mFile(NULL), mMappedFile(NULL) #else - mFile(0) + mFile(0) #endif - {} - - /** - * \brief When the object is destroyed, the file is unmapped and closed. - */ - ~MMapFile() - { - close(); - } - + { + } - /** - * \brief Open and map file into memory. - * \param fileName Path to a file being opened. - * \throws RuntimeError if error occurs. - * \note If called multiple times, current file is closed before another is opened. - */ - void open(const std::string &fileName) - { - close(); - mFileName = fileName; - - #ifdef _WIN32 - // Create file handle. - mFile = CreateFileA(mFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); - if (mFile == INVALID_HANDLE_VALUE) - throw RuntimeError("Cannot open selected file."); - - // Get the file size. - LARGE_INTEGER tmpSize; - if (!GetFileSizeEx(mFile, &tmpSize)) - throw RuntimeError("Cannot get file size."); - mLength = tmpSize.QuadPart; - - // Create read only mapping object. - mMappedFile = CreateFileMapping(mFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (mMappedFile == NULL) - throw RuntimeError("Cannot create mapped file object."); - - // Map the entire file to virtual memory space. - mData = MapViewOfFile(mMappedFile, FILE_MAP_READ, 0, 0, 0); - if (mData == NULL) - throw RuntimeError("Cannot map view of file."); - #else - // Create file handle. - mFile = ::open(mFileName.c_str(), O_RDONLY); - if (mFile == -1) - throw RuntimeError("Cannot open selected file."); - - // Get the file size. - struct stat fileStat; - if (::fstat(mFile, &fileStat) == -1) - throw RuntimeError("Cannot get file size."); - mLength = fileStat.st_size; - - // Map the entire file to virtual memory space. - mData = ::mmap(NULL, mLength, PROT_READ, MAP_PRIVATE, mFile, 0); - if (mData == MAP_FAILED) { - mData = NULL; - throw RuntimeError("Cannot mmap the file."); + /** + * \brief When the object is destroyed, the file is unmapped and closed. + */ + ~MMapFile() + { + close(); } - #endif - }; - /** - * \brief Get a pointer to memory block where the file is mapped. - * \return Valid pointer if the file was mapped, NULL pointer otherwise. - */ - void* getData() const - { - return mData; - } + /** + * \brief Open and map file into memory. + * \param fileName Path to a file being opened. + * \throws RuntimeError if error occurs. + * \note If called multiple times, current file is closed before another is opened. + */ + void open(const std::string &fileName) + { + close(); + mFileName = fileName; +#ifdef _WIN32 + // Create file handle. + mFile = CreateFileA(mFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); + if (mFile == INVALID_HANDLE_VALUE) throw RuntimeError("Cannot open selected file."); + + // Get the file size. + LARGE_INTEGER tmpSize; + if (!GetFileSizeEx(mFile, &tmpSize)) throw RuntimeError("Cannot get file size."); + mLength = tmpSize.QuadPart; + + // Create read only mapping object. + mMappedFile = CreateFileMapping(mFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (mMappedFile == NULL) throw RuntimeError("Cannot create mapped file object."); + + // Map the entire file to virtual memory space. + mData = MapViewOfFile(mMappedFile, FILE_MAP_READ, 0, 0, 0); + if (mData == NULL) throw RuntimeError("Cannot map view of file."); +#else + // Create file handle. + mFile = ::open(mFileName.c_str(), O_RDONLY); + if (mFile == -1) throw RuntimeError("Cannot open selected file."); + + // Get the file size. + struct stat fileStat; + if (::fstat(mFile, &fileStat) == -1) throw RuntimeError("Cannot get file size."); + mLength = fileStat.st_size; + + // Map the entire file to virtual memory space. + mData = ::mmap(NULL, mLength, PROT_READ, MAP_PRIVATE, mFile, 0); + if (mData == MAP_FAILED) { + mData = NULL; + throw RuntimeError("Cannot mmap the file."); + } +#endif + }; - /** - * Return the length of the file. - */ - std::size_t length() const - { - return (std::size_t)mLength; - } + /** + * \brief Get a pointer to memory block where the file is mapped. + * \return Valid pointer if the file was mapped, NULL pointer otherwise. + */ + void *getData() const + { + return mData; + } - /** - * \brief Check whether the file has been opened. - * \return True if the file was opened (and mapped), false otherwise. - */ - bool opened() const - { - return getData() != NULL; - } - - /** - * \brief Unmap the file and close it. - */ - void close() - { - if (!opened()) return; + /** + * Return the length of the file. + */ + std::size_t length() const + { + return (std::size_t) mLength; + } - #ifdef _WIN32 - if (!UnmapViewOfFile(mData)) - throw RuntimeError("Cannot unmap view of file."); - mData = NULL; - if (!CloseHandle(mMappedFile) || !CloseHandle(mFile)) - throw RuntimeError("Cannot close mapped file."); - mMappedFile = mFile = NULL; - #else - if (::munmap(mData, mLength) == -1) - throw RuntimeError("Cannot unmap file."); - mData = NULL; + /** + * \brief Check whether the file has been opened. + * \return True if the file was opened (and mapped), false otherwise. + */ + bool opened() const + { + return getData() != NULL; + } - if (::close(mFile) == -1) - throw RuntimeError("Cannot close mapped file."); - mFile = 0; - #endif - } + /** + * \brief Unmap the file and close it. + */ + void close() + { + if (!opened()) return; - /** - * \brief Try to prepopulate virt. memory pages by the file data. - * - * This function is useful only if the file fits the memory. - * It expects only 4kB pages. - */ - void populate() - { - if (!opened()) - throw RuntimeError("The file must be opened before prepopulation."); - - // Traverse the mapped file accessing first dword on each page. - unsigned x, *data = (unsigned*)getData(); - for (length_t i = 0; i < mLength / 4096; ++i) { - x ^= *data; // read from page - data += 4096 / sizeof(unsigned); // move to another page +#ifdef _WIN32 + if (!UnmapViewOfFile(mData)) throw RuntimeError("Cannot unmap view of file."); + mData = NULL; + + if (!CloseHandle(mMappedFile) || !CloseHandle(mFile)) throw RuntimeError("Cannot close mapped file."); + mMappedFile = mFile = NULL; +#else + if (::munmap(mData, mLength) == -1) throw RuntimeError("Cannot unmap file."); + mData = NULL; + + if (::close(mFile) == -1) throw RuntimeError("Cannot close mapped file."); + mFile = 0; +#endif + } + + + /** + * \brief Try to prepopulate virt. memory pages by the file data. + * + * This function is useful only if the file fits the memory. + * It expects only 4kB pages. + */ + void populate() + { + if (!opened()) throw RuntimeError("The file must be opened before prepopulation."); + + // Traverse the mapped file accessing first dword on each page. + unsigned x, *data = (unsigned *) getData(); + for (length_t i = 0; i < mLength / 4096; ++i) { + x ^= *data; // read from page + data += 4096 / sizeof(unsigned); // move to another page + } } - } -}; + }; -} +} // namespace bpp #endif diff --git a/judges/recodex_token_judge/comparator.hpp b/judges/recodex_token_judge/comparator.hpp index 7ca7b107..8e316df7 100644 --- a/judges/recodex_token_judge/comparator.hpp +++ b/judges/recodex_token_judge/comparator.hpp @@ -181,9 +181,7 @@ template class TokenComp TokenPair tokenPair(t1, len1, t2, len2); long int i1, i2; - if (tokenPair.tryGetInts(i1, i2)) { - return i1 == i2; - } + if (tokenPair.tryGetInts(i1, i2)) { return i1 == i2; } double d1, d2; if (tokenPair.tryGetFloats(d1, d2)) { @@ -229,9 +227,7 @@ template 1) { - bpp::log().warning() << " (" << std::abs(diff) << "x)"; - } + if (std::abs(diff) > 1) { bpp::log().warning() << " (" << std::abs(diff) << "x)"; } } @@ -272,9 +268,7 @@ template static void mapRemoveEmpty(std::map &m) { for (auto it = m.begin(); it != m.end(); ++it) { - if (it->second == 0) { - m.erase(it); - } + if (it->second == 0) { m.erase(it); } } } diff --git a/judges/recodex_token_judge/judge.hpp b/judges/recodex_token_judge/judge.hpp index 547c5c9f..ad1403fc 100644 --- a/judges/recodex_token_judge/judge.hpp +++ b/judges/recodex_token_judge/judge.hpp @@ -135,9 +135,7 @@ template class Judge const std::size_t MAX_CHARS = 10000; // amount of chars affect aggregated complexities of token comarisons // Fill in the first correct line which already has been loaded. - if (mCorrectLine) { - mCorrectLinesBuffer.insert(mCorrectLinesBuffer.begin(), std::move(mCorrectLine)); - } + if (mCorrectLine) { mCorrectLinesBuffer.insert(mCorrectLinesBuffer.begin(), std::move(mCorrectLine)); } // Count stats of actual state of the correct lines buffer... std::size_t tokens = 0, chars = 0; @@ -155,9 +153,7 @@ template class Judge } // Fill in the first result line which already has been loaded. - if (mResultLine) { - mResultLinesBuffer.insert(mResultLinesBuffer.begin(), std::move(mResultLine)); - } + if (mResultLine) { mResultLinesBuffer.insert(mResultLinesBuffer.begin(), std::move(mResultLine)); } // Count stats of actual state of the result lines buffer... tokens = chars = 0; diff --git a/judges/recodex_token_judge/reader.hpp b/judges/recodex_token_judge/reader.hpp index 77a49844..9ed618e8 100644 --- a/judges/recodex_token_judge/reader.hpp +++ b/judges/recodex_token_judge/reader.hpp @@ -313,9 +313,7 @@ template class Reader */ std::unique_ptr readLine() { - if (eof()) { - return std::unique_ptr(); - } + if (eof()) { return std::unique_ptr(); } offset_t startOffset = mOffset; auto line = bpp::make_unique(*this, mLineNumber, mData + mOffset); @@ -338,7 +336,7 @@ template class Reader if (mIgnoreLineEnds) continue; // new lines are ignored, lets continue read tokens if (!line->mTokens.empty() || (!mIgnoreEmptyLines && !comment)) break; // line is non-empty or we return empty lines - + // If we got here, an empty line or a comment line was read (which we skipped). line->mLineNumber = mLineNumber; line->mRawData = mData + mOffset;