diff --git a/INCLUDE/Hazelnupp.cpp b/INCLUDE/Hazelnupp.cpp new file mode 100644 index 0000000..1b00aa0 --- /dev/null +++ b/INCLUDE/Hazelnupp.cpp @@ -0,0 +1,1008 @@ +#include "merged.h" + +/*** FloatValue.cpp ***/ + +#include + +using namespace Hazelnp; + +FloatValue::FloatValue(const long double& value) + : + Value(DATA_TYPE::FLOAT), + value { value } +{ + return; +} + +Value* FloatValue::Deepcopy() const +{ + return new FloatValue(value); +} + +std::string FloatValue::GetAsOsString() const +{ + std::stringstream ss; + ss << "FloatValue: " << value; + return ss.str(); +} + +const long double& FloatValue::GetValue() const +{ + return value; +} + +FloatValue::operator long double() const +{ + return value; +} + +FloatValue::operator double() const +{ + return (double)value; +} + + + +long long int FloatValue::GetInt64() const +{ + return (long long int)value; +} + +int FloatValue::GetInt32() const +{ + return (int)value; +} + +long double FloatValue::GetFloat64() const +{ + return value; +} + +double FloatValue::GetFloat32() const +{ + return (double)value; +} + +std::string FloatValue::GetString() const +{ + std::stringstream ss; + ss << value; + + return ss.str(); +} + +const std::vector& FloatValue::GetList() const +{ + throw HazelnuppValueNotConvertibleException(); +} + + +/*** Hazelnupp.cpp ***/ + +#include +#include + +using namespace Hazelnp; + +Hazelnupp::Hazelnupp() +{ + return; +} + +Hazelnupp::Hazelnupp(const int argc, const char* const* argv) +{ + Parse(argc, argv); + return; +} + +Hazelnupp::~Hazelnupp() +{ + for (auto& it : parameters) + delete it.second; + + parameters.clear(); + + return; +} + +void Hazelnupp::Parse(const int argc, const char* const* argv) +{ + try + { + // Populate raw arguments + PopulateRawArgs(argc, argv); + + // Expand abbreviations + ExpandAbbreviations(); + + executableName = std::string(rawArgs[0]); + + std::size_t i = 1; + while (i < rawArgs.size()) + { + if ((rawArgs[i].length() > 2) && (rawArgs[i].substr(0, 2) == "--")) + { + Parameter* param = nullptr; + i = ParseNextParameter(i, param); + + parameters.insert(std::pair(param->Key(), param)); + } + else + i++; + } + + // Apply constraints such as default values, and required parameters. + // Types have already been enforced. + ApplyConstraints(); + } + catch (const HazelnuppConstraintTypeMissmatch& hctm) + { + if (crashOnFail) + { + std::cerr << "Fatal error: Command-line parameter value-type mismatch at \"" << hctm.What() << "\"!"; + quick_exit(-1009); + } + else + throw hctm; // yeet + } + catch (const HazelnuppConstraintMissingValue& hctm) + { + if (crashOnFail) + { + std::cerr << "Fatal error: Missing required command-line parameter \"" << hctm.What() << "\"!"; + quick_exit(-1010); + } + else + throw hctm; // yeet + } + + return; +} + +std::size_t Hazelnupp::ParseNextParameter(const std::size_t parIndex, Parameter*& out_Par) +{ + std::size_t i = parIndex; + const std::string key = rawArgs[parIndex]; + std::vector values; + + // Get values + for (i++; i < rawArgs.size(); i++) + // If not another parameter + if ((rawArgs[i].length() < 2) || (rawArgs[i].substr(0, 2) != "--")) + values.emplace_back(rawArgs[i]); + else + { + break; + } + + // Fetch constraint info + const ParamConstraint* pcn = GetConstraintForKey(key); + + Value* parsedVal = ParseValue(values, pcn); + if (parsedVal != nullptr) + { + out_Par = new Parameter(key, parsedVal); + + delete parsedVal; + parsedVal = nullptr; + } + else + throw std::runtime_error("Unable to parse parameter!"); + + return i; +} + +void Hazelnupp::PopulateRawArgs(const int argc, const char* const* argv) +{ + rawArgs.clear(); + rawArgs.reserve(argc); + + for (int i = 0; i < argc; i++) + rawArgs.emplace_back(std::string(argv[i])); + + return; +} + +void Hazelnupp::ExpandAbbreviations() +{ + // Abort if no abbreviations + if (abbreviations.size() == 0) + return; + + for (std::string& arg : rawArgs) + { + // Is arg registered as an abbreviation? + auto abbr = abbreviations.find(arg); + if (abbr != abbreviations.end()) + { + // Yes: replace arg with the long form + arg = abbr->second; + } + } + + return; +} + +bool Hazelnupp::HasParam(const std::string& key) const +{ + return parameters.find(key) != parameters.end(); +} + +Value* Hazelnupp::ParseValue(const std::vector& values, const ParamConstraint* constraint) +{ + // Constraint values + const bool constrainType = (constraint != nullptr) && (constraint->constrainType); + + // Void-type + if (values.size() == 0) + { + // Is a list forced via a constraint? If yes, return an empty list + if ((constrainType) && + (constraint->wantedType == DATA_TYPE::LIST)) + return new ListValue(); + + return new VoidValue; + } + + // Force void type by constraint + if ((constrainType) && + (constraint->wantedType == DATA_TYPE::VOID)) + { + return new VoidValue; + } + + // List-type + else if (values.size() > 1) + { + // Should the type be something other than list? + if ((constrainType) && + (constraint->wantedType != DATA_TYPE::LIST)) + { + throw HazelnuppConstraintTypeMissmatch(values[0] + " " + values[1]); + } + + ListValue* newList = new ListValue(); + for (const std::string& val : values) + { + Value* tmp = ParseValue({ val }); + newList->AddValue(tmp); + delete tmp; + } + return newList; + } + + // Now we're only dealing with a single value + const std::string& val = values[0]; + + // String + if (!StringTools::IsNumeric(val, true)) + { + // Is the type not supposed to be a string? + // void and list are already sorted out + if ((constrainType) && + (constraint->wantedType != DATA_TYPE::STRING)) + { + // We can only force a list-value from here + if (constraint->wantedType == DATA_TYPE::LIST) + { + ListValue* list = new ListValue(); + Value* tmp = ParseValue({ val }); + list->AddValue(tmp); + delete tmp; + tmp = nullptr; + return list; + } + // Else it not possible to convert to a numeric + else + throw HazelnuppConstraintTypeMissmatch(val); + } + + return new StringValue(val); + } + + // In this case we have a numeric value. + // We should still produce a string if requested + if ((constrainType) && + (constraint->wantedType == DATA_TYPE::STRING)) + return new StringValue(val); + + // Numeric + bool isInt; + long double num; + + if (StringTools::ParseNumber(val, isInt, num)) + { + // Is the type constrained? + // (only int and float left) + if (constrainType) + { + // Must it be an integer? + if (constraint->wantedType == DATA_TYPE::INT) + return new IntValue((long long int)num); + // Must it be a floating point? + else if (constraint->wantedType == DATA_TYPE::FLOAT) + return new FloatValue(num); + // Else it must be a List + else + { + ListValue* list = new ListValue(); + Value* tmp = ParseValue({ val }); + list->AddValue(tmp); + delete tmp; + tmp = nullptr; + return list; + } + } + // Type is not constrained + else + { + // Integer + if (isInt) + return new IntValue((long long int)num); + + // Double + return new FloatValue(num); + } + } + + // Failed + return nullptr; +} + +bool Hazelnupp::GetCrashOnFail() const +{ + return crashOnFail; +} + +void Hazelnupp::ApplyConstraints() +{ + // Enforce required parameters / default values + for (const auto& pc : constraints) + // Parameter in question is not supplied + if (!HasParam(pc.second.key)) + { + // Do we have a default value? + if (pc.second.defaultValue.size() > 0) + { + // Then create it now, by its default value + + Value* tmp = ParseValue(pc.second.defaultValue, &pc.second); + parameters.insert(std::pair( + pc.second.key, + new Parameter(pc.second.key, tmp) + )); + + delete tmp; + tmp = nullptr; + } + // So we do not have a default value... + else + { + // Is it important to have the missing parameter? + if (pc.second.required) + // Throw an error message then + throw HazelnuppConstraintMissingValue(pc.second.key); + } + } + + return; +} + +const std::string& Hazelnupp::GetExecutableName() const +{ + return executableName; +} + +const Value& Hazelnupp::operator[](const std::string& key) const +{ + // Throw exception if param is unknown + if (!HasParam(key)) + throw HazelnuppInvalidKeyException(); + + return *parameters.find(key)->second->GetValue(); +} + +void Hazelnupp::RegisterAbbreviation(const std::string& abbrev, const std::string& target) +{ + abbreviations.insert(std::pair(abbrev, target)); + return; +} + +const std::string& Hazelnupp::GetAbbreviation(const std::string& abbrev) const +{ + return abbreviations.find(abbrev)->second; +} + +bool Hazelnupp::HasAbbreviation(const std::string& abbrev) const +{ + return abbreviations.find(abbrev) != abbreviations.end(); +} + +void Hazelnupp::ClearAbbreviations() +{ + abbreviations.clear(); + return; +} + +void Hazelnupp::RegisterConstraints(const std::vector& constraints) +{ + for (const ParamConstraint& pc : constraints) + { + // Does this constraint already exist? + const auto constraint = this->constraints.find(pc.key); + // If yes, replace it. + if (constraint != this->constraints.end()) + constraint->second = pc; + + // Else, create a new pair + else + this->constraints.insert(std::pair( + pc.key, + pc + )); + } + + return; +} + +void Hazelnupp::ClearConstraints() +{ + constraints.clear(); + return; +} + +void Hazelnupp::SetCrashOnFail(bool crashOnFail) +{ + this->crashOnFail = crashOnFail; + return; +} + +const ParamConstraint* Hazelnupp::GetConstraintForKey(const std::string& key) const +{ + const auto constraint = constraints.find(key); + + if (constraint == constraints.end()) + return nullptr; + + return &constraint->second; +} + + +/*** IntValue.cpp ***/ + +#include + +using namespace Hazelnp; + +IntValue::IntValue(const long long int& value) + : + Value(DATA_TYPE::INT), + value { value } +{ + return; +} + +Value* IntValue::Deepcopy() const +{ + return new IntValue(value); +} + +std::string IntValue::GetAsOsString() const +{ + std::stringstream ss; + ss << "IntValue: " << value; + return ss.str(); +} + +const long long int& IntValue::GetValue() const +{ + return value; +} + +IntValue::operator long long int() const +{ + return value; +} + +IntValue::operator int() const +{ + return (int)value; +} + + + +long long int IntValue::GetInt64() const +{ + return value; +} + +int IntValue::GetInt32() const +{ + return (int)value; +} + +long double IntValue::GetFloat64() const +{ + return (long double)value; +} + +double IntValue::GetFloat32() const +{ + return (double)value; +} + +std::string IntValue::GetString() const +{ + std::stringstream ss; + ss << value; + + return ss.str(); +} + +const std::vector& IntValue::GetList() const +{ + throw HazelnuppValueNotConvertibleException(); +} + + +/*** ListValue.cpp ***/ + +#include + +using namespace Hazelnp; + +ListValue::ListValue() : + Value(DATA_TYPE::LIST) +{ + return; +} + +ListValue::~ListValue() +{ + for (Value* val : value) + delete val; + + value.clear(); + + return; +} + +Value* ListValue::Deepcopy() const +{ + ListValue* newList = new ListValue(); + + for (const Value* val : value) + newList->AddValue(val); + + return newList; +} + +void ListValue::AddValue(const Value* value) +{ + this->value.emplace_back(value->Deepcopy()); + return; +} + +const std::vector& ListValue::GetValue() const +{ + return value; +} + +std::string ListValue::GetAsOsString() const +{ + std::stringstream ss; + + ss << "ListValue: ["; + + for (const Value* val : value) + { + ss << *val; + if (val != value.back()) + ss << ", "; + } + + ss << "]"; + + return ss.str(); +} + +ListValue::operator std::vector() const +{ + return value; +} + + + +long long int ListValue::GetInt64() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +int ListValue::GetInt32() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +long double ListValue::GetFloat64() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +double ListValue::GetFloat32() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +std::string ListValue::GetString() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +const std::vector& ListValue::GetList() const +{ + return value; +} + + +/*** Parameter.cpp ***/ + + +using namespace Hazelnp; + +Parameter::Parameter(const std::string& key, const ::Value* value) + : + key{ key } +{ + this->value = value->Deepcopy(); + return; +} + +Parameter::~Parameter() +{ + delete value; + value = nullptr; + + return; +} + +const std::string& Parameter::Key() const +{ + return key; +} + +const ::Value* Parameter::GetValue() const +{ + return value; +} + + +/*** StringTools.cpp ***/ + + +using namespace Hazelnp; + +bool StringTools::Contains(const std::string& str, const char c) +{ + for (const char& i : str) + if (i == c) + return true; + + return false; +} + +std::string StringTools::Replace(const std::string& str, const char find, const std::string& subst) +{ + std::stringstream ss; + + for (std::size_t i = 0; i < str.length(); i++) + { + if (str[i] != find) ss << str[i]; + else ss << subst; + } + + return ss.str(); +} + +std::string StringTools::Replace(const std::string& str, const std::string& find, const std::string& subst) +{ + if (find.length() == 0) return str; + + std::stringstream ss; + + std::size_t posFound = 0; + std::size_t lastFound = 0; + + while (posFound != std::string::npos) + { + lastFound = posFound; + posFound = str.find(find, posFound); + + if (posFound != std::string::npos) + { + ss << str.substr(lastFound, posFound - lastFound) << subst; + posFound += find.length(); + } + else + { + ss << str.substr(lastFound, (str.length()) - lastFound); + } + } + + return ss.str(); +} + + +bool StringTools::IsNumeric(const std::string& str, const bool allowDecimalPoint) +{ + if (str.length() == 0) return false; + + bool alreadyParsedDecimalPoint = false; + std::size_t digitCount = 0; + + for (std::size_t i = 0; i < str.length(); i++) + { + if (!( + ((str[i] >= '0') && (str[i] <= '9')) || + ((str[i] == '-') && (i == 0)) || + ((str[i] == '.') && (allowDecimalPoint) && (!alreadyParsedDecimalPoint) && (digitCount > 0)) + )) return false; + + + // Here we just have to check for the character. Not for any other conditions. + // Why? Because if these conditions failed, the function would have already returned false. + if (((str[i] >= '0') && (str[i] <= '9'))) digitCount++; + if (str[i] == '.') alreadyParsedDecimalPoint = true; + } + + // Even if we did not find any invalid chars, we should still return false, if we found no digits at all. + return digitCount > 0; +} + +bool StringTools::ParseNumber(const std::string& str, bool& out_isInt, long double& out_number) +{ + bool isDecimal = false; + + if (str.length() == 0) return false; + if (Contains(str, '.')) isDecimal = true; + + if (isDecimal) + { + try + { + out_number = std::stold(str); + out_isInt = false; + } + catch (std::invalid_argument&) + { + return false; + } + catch (std::out_of_range&) + { + return false; + } + } + else + { + try + { + out_number = (long double)std::stoll(str); + out_isInt = true; + } + catch (std::invalid_argument&) + { + return false; + } + catch (std::out_of_range&) + { + return false; + } + } + + return true; +} + +std::vector StringTools::SplitString(const std::string& str, const char delimiter) +{ + if (str.length() == 0) return std::vector(); + + return SplitString(str, delimiter); +} + +std::vector StringTools::SplitString(const std::string& str, const std::string& delimiter) +{ + if (str.length() == 0) return std::vector(); + + std::vector parts; + + if (delimiter.length() == 0) // If the delimiter is "" (empty), just split between every single char. Not useful, but logical + { + for (std::size_t i = 0; i < str.length(); i++) + { + parts.push_back(std::string({ str[i] })); + } + return parts; + } + + std::size_t posFound = 0; + std::size_t lastFound = 0; + + while (posFound != std::string::npos) + { + lastFound = posFound; + posFound = str.find(delimiter, posFound); + + std::string found; + + if (posFound != std::string::npos) + { + found = str.substr(lastFound, posFound - lastFound); + posFound += delimiter.length(); + } + else + { + found = str.substr(lastFound, str.length() - lastFound); + } + + parts.push_back(found); + } + + return parts; +} + +std::string StringTools::ToLower(const std::string& str) +{ + std::stringstream ss; + for (std::size_t i = 0; i < str.length(); i++) + { + if ((str[i] >= 'A') && (str[i] <= 'Z')) ss << (char)(((int)str[i]) + 32); + else if (str[i] == -60) ss << (char)-28; // AE => ae + else if (str[i] == -42) ss << (char)-10; // OE => oe + else if (str[i] == -36) ss << (char)-4; // UE => ue + else ss << str[i]; + } + + return ss.str(); +} + + +/*** StringValue.cpp ***/ + +#include + +using namespace Hazelnp; + +StringValue::StringValue(const std::string& value) + : + Value(DATA_TYPE::STRING), + value { value } +{ + return; +} + +Value* StringValue::Deepcopy() const +{ + return new StringValue(value); +} + +std::string StringValue::GetAsOsString() const +{ + std::stringstream ss; + ss << "StringValue: " << value; + return ss.str(); +} + +const std::string& StringValue::GetValue() const +{ + return value; +} + +StringValue::operator std::string() const +{ + return value; +} + + + +long long int StringValue::GetInt64() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +int StringValue::GetInt32() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +long double StringValue::GetFloat64() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +double StringValue::GetFloat32() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +std::string StringValue::GetString() const +{ + return value; +} + +const std::vector& StringValue::GetList() const +{ + throw HazelnuppValueNotConvertibleException(); +} + + +/*** Value.cpp ***/ + + +using namespace Hazelnp; + +Value::Value(DATA_TYPE type) + : + type{ type } +{ + return; +} + +DATA_TYPE Value::GetDataType() const +{ + return type; +} + + +/*** VoidValue.cpp ***/ + + +using namespace Hazelnp; + +VoidValue::VoidValue() + : + Value(DATA_TYPE::VOID) +{ + return; +} + +Value* VoidValue::Deepcopy() const +{ + return new VoidValue(); +} + +std::string VoidValue::GetAsOsString() const +{ + return "VoidValue"; +} + + + +long long int VoidValue::GetInt64() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +int VoidValue::GetInt32() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +long double VoidValue::GetFloat64() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +double VoidValue::GetFloat32() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +std::string VoidValue::GetString() const +{ + throw HazelnuppValueNotConvertibleException(); +} + +const std::vector& VoidValue::GetList() const +{ + throw HazelnuppValueNotConvertibleException(); +} + diff --git a/INCLUDE/Hazelnupp.h b/INCLUDE/Hazelnupp.h new file mode 100644 index 0000000..6ab6c05 --- /dev/null +++ b/INCLUDE/Hazelnupp.h @@ -0,0 +1,596 @@ +#pragma once + +/*** StringTools.h ***/ + +#include +#include +#include +#include + +namespace Hazelnp +{ + /** Internal helper class. Feel free to use it tho. + */ + class StringTools + { + public: + //! Will return wether or not a given char is in a string + static bool Contains(const std::string& str, const char c); + + //! Will replace a part of a string with another string + static std::string Replace(const std::string& str, const char find, const std::string& subst); + + //! Will replace a part of a string with another string + static std::string Replace(const std::string& str, const std::string& find, const std::string& subst); + + //! Will return true if the given string consists only of digits (including signage) + static bool IsNumeric(const std::string& str, const bool allowDecimalPoint = false); + + //! Will convert the number in str to a number. + //! Returns wether or not the operation was successful. + //! Also returns wether the number is an integer, or floating point. If int, cast out_number to int. + static bool ParseNumber(const std::string& str, bool& out_isInt, long double& out_number); + + //! Will split a string by a delimiter char. The delimiter will be excluded! + static std::vector SplitString(const std::string& str, const char delimiter); + + //! Will split a string by a delimiter string. The delimiter will be excluded! + static std::vector SplitString(const std::string& str, const std::string& delimiter); + + //! Will make a string all lower-case + static std::string ToLower(const std::string& str); + }; +} + +/*** HazelnuppException.h ***/ + +#include + +namespace Hazelnp +{ + /** Generic hazelnupp exception + */ + class HazelnuppException : public std::exception + { + public: + HazelnuppException() {}; + HazelnuppException(const std::string& msg) : message{ msg } {}; + + //! Will return an error message + const std::string& What() const + { + return message; + } + + protected: + std::string message; + }; + + /** Gets thrown when an non-existent key gets dereferenced + */ + class HazelnuppInvalidKeyException : public HazelnuppException + { + public: + HazelnuppInvalidKeyException() : HazelnuppException() {}; + HazelnuppInvalidKeyException(const std::string& msg) : HazelnuppException(msg) {}; + }; + + /** Gets thrown when an attempt is made to retrieve the wrong data type from a value, when the value not convertible + */ + class HazelnuppValueNotConvertibleException : public HazelnuppException + { + public: + HazelnuppValueNotConvertibleException() : HazelnuppException() {}; + HazelnuppValueNotConvertibleException(const std::string& msg) : HazelnuppException(msg) {}; + }; + + /** Gets thrown something bad happens because of parameter constraints + */ + class HazelnuppConstraintException : public HazelnuppException + { + public: + HazelnuppConstraintException() : HazelnuppException() {}; + HazelnuppConstraintException(const std::string& msg) : HazelnuppException(msg) {}; + }; + + /** Gets thrown when a parameter is of a type that does not match the required type, and is not convertible to it + */ + class HazelnuppConstraintTypeMissmatch : public HazelnuppConstraintException + { + public: + HazelnuppConstraintTypeMissmatch() : HazelnuppConstraintException() {}; + HazelnuppConstraintTypeMissmatch(const std::string& msg) : HazelnuppConstraintException(msg) {}; + }; + + /** Gets thrown when a parameter constrained to be required is not provided, and has no default value set + */ + class HazelnuppConstraintMissingValue : public HazelnuppConstraintException + { + public: + HazelnuppConstraintMissingValue() : HazelnuppConstraintException() {}; + HazelnuppConstraintMissingValue(const std::string& msg) : HazelnuppConstraintException(msg) {}; + }; +} + +/*** DataType.h ***/ + + +namespace Hazelnp +{ + /** The different data types a paramater can be + */ + enum class DATA_TYPE + { + VOID, + INT, + FLOAT, + STRING, + LIST + }; +} + +/*** ParamConstraint.h ***/ + +#include +#include + +namespace Hazelnp +{ + struct ParamConstraint + { + public: + //! Empty constructor + ParamConstraint() = default; + + //! Constructs a require constraint. + //! Think of the default value like of a list ofparameters. Like {"--width", "800"} + static ParamConstraint Require(const std::string& key, const std::vector& defaultValue = {}, bool required = true) + { + ParamConstraint pc; + pc.key = key; + pc.defaultValue = defaultValue; + pc.required = required; + + return pc; + } + + //! Constructs a type-safety constraint + static ParamConstraint TypeSafety(const std::string& key, DATA_TYPE wantedType, bool constrainType = true) + { + ParamConstraint pc; + pc.key = key; + pc.constrainType = constrainType; + pc.wantedType = wantedType; + + return pc; + } + + //! Whole constructor + ParamConstraint(const std::string& key, bool constrainType, DATA_TYPE wantedType, const std::vector& defaultValue, bool required) + : + key{ key }, + constrainType{ constrainType }, + wantedType{ wantedType }, + defaultValue{ defaultValue }, + required{ required } + { + return; + } + + //! The key of the parameter to constrain + std::string key; + + //! Should this parameter be forced to be of a certain type? + //! Remember to set `constrainTo` to the wanted type + bool constrainType = false; + + //! Constrain the parameter to this value. Requires `constrainType` to be set to true. + DATA_TYPE wantedType = DATA_TYPE::VOID; + + //! The default value for this parameter. + //! Gets applied if this parameter was not given. + //! Think of this like a list of parameters. Like {"--width", "800"} + std::vector defaultValue; + + //! If set to true, and no default value set, + //! an error will be produced if this parameter is not supplied by the user. + bool required = false; + }; +} + +/*** Value.h ***/ + +#include +#include + +namespace Hazelnp +{ + /** Abstract class for values + */ + class Value + { + public: + virtual ~Value() {}; + + //! Will return a deeopopy of this object + virtual Value* Deepcopy() const = 0; + + //! Will return a string suitable for an std::ostream + virtual std::string GetAsOsString() const = 0; + + //! Will return the data type of this value + DATA_TYPE GetDataType() const; + + friend std::ostream& operator<< (std::ostream& os, const Value& v) + { + return os << v.GetAsOsString(); + } + + //! Will attempt to return the integer data (long long) + virtual long long int GetInt64() const = 0; + //! Will attempt to return the integer data (int) + virtual int GetInt32() const = 0; + + //! Will attempt to return the floating-point data (long double) + virtual long double GetFloat64() const = 0; + //! Will attempt to return the floating-point data (double) + virtual double GetFloat32() const = 0; + + //! Will attempt to return the string-data + virtual std::string GetString() const = 0; + + //! Will attempt to return the list-data + virtual const std::vector& GetList() const = 0; + + protected: + Value(DATA_TYPE type); + + DATA_TYPE type; + }; +} + +/*** ListValue.h ***/ + +#include + +namespace Hazelnp +{ + /** Specializations for list values (uses std::vector) + */ + class ListValue : public Value + { + public: + ListValue(); + ~ListValue() override; + + //! Will return a deeopopy of this object + Value* Deepcopy() const override; + + //! Will return a string suitable for an std::ostream; + std::string GetAsOsString() const override; + + //! Will add this value to the list + void AddValue(const Value* value); + + //! Will return the raw value + const std::vector& GetValue() const; + + operator std::vector() const; + + //! Throws HazelnuppValueNotConvertibleException + long long int GetInt64() const override; + //! Throws HazelnuppValueNotConvertibleException + int GetInt32() const override; + + //! Throws HazelnuppValueNotConvertibleException + long double GetFloat64() const override; + //! Throws HazelnuppValueNotConvertibleException + double GetFloat32() const override; + + //! Throws HazelnuppValueNotConvertibleException + std::string GetString() const override; + + //! Will return this values list + const std::vector& GetList() const override; + + private: + std::vector value; + }; +} + +/*** VoidValue.h ***/ + + +namespace Hazelnp +{ + /** Specializations for void values. These house no value whatsoever, but only communicate information by merely existing. + */ + class VoidValue : public Value + { + public: + VoidValue(); + ~VoidValue() override {}; + + //! Will return a deeopopy of this object + Value* Deepcopy() const override; + + //! Will return a string suitable for an std::ostream; + std::string GetAsOsString() const override; + + //! Throws HazelnuppValueNotConvertibleException + long long int GetInt64() const override; + //! Throws HazelnuppValueNotConvertibleException + int GetInt32() const override; + + //! Throws HazelnuppValueNotConvertibleException + long double GetFloat64() const override; + //! Throws HazelnuppValueNotConvertibleException + double GetFloat32() const override; + + //! Throws HazelnuppValueNotConvertibleException + std::string GetString() const override; + + //! Throws HazelnuppValueNotConvertibleException + const std::vector& GetList() const; + }; +} + +/*** Parameter.h ***/ + +#include +#include + +namespace Hazelnp +{ + class Parameter + { + public: + explicit Parameter(const std::string& key, const Value* value); + ~Parameter(); + + //! Will return the key of this parameter + const std::string& Key() const; + + //! Will return the value of this parameter + const Value* GetValue() const; + + friend std::ostream& operator<< (std::ostream& os, const Parameter& p) + { + return os << "{ Key: \"" << p.key << "\" -> " << *p.value << " }"; + } + + private: + std::string key; + Hazelnp::Value* value; + }; +} + +/*** StringValue.h ***/ + +#include + +namespace Hazelnp +{ + /** Specializations for string values (uses std::string) + */ + class StringValue : public Value + { + public: + StringValue(const std::string& value); + ~StringValue() override {}; + + //! Will return a deeopopy of this object + Value* Deepcopy() const override; + + //! Will return a string suitable for an std::ostream; + std::string GetAsOsString() const override; + + //! Will return the raw value + const std::string& GetValue() const; + + operator std::string() const; + + //! Throws HazelnuppValueNotConvertibleException + long long int GetInt64() const override; + //! Throws HazelnuppValueNotConvertibleException + int GetInt32() const override; + + //! Throws HazelnuppValueNotConvertibleException + long double GetFloat64() const override; + //! Throws HazelnuppValueNotConvertibleException + double GetFloat32() const override; + + //! Will return this value as a string + std::string GetString() const override; + + //! Throws HazelnuppValueNotConvertibleException + const std::vector& GetList() const override; + + private: + std::string value; + }; +} + +/*** IntValue.h ***/ + + +namespace Hazelnp +{ + /** Specializations for integer values (uses long long int) + */ + class IntValue : public Value + { + public: + IntValue(const long long int& value); + ~IntValue() override {}; + + //! Will return a deeopopy of this object + Value* Deepcopy() const override; + + //! Will return a string suitable for an std::ostream; + std::string GetAsOsString() const override; + + //! Will return the raw value + const long long int& GetValue() const; + + operator long long int() const; + operator int() const; + + + //! Will return the data as a long long int + long long int GetInt64() const override; + //! Will return the data as an int + int GetInt32() const override; + + //! Will return the data as a long double + long double GetFloat64() const override; + //! Will return the data as a double + double GetFloat32() const override; + + //! Will return the data as a string + std::string GetString() const override; + + //! Throws HazelnuppValueNotConvertibleException + const std::vector& GetList() const override; + + private: + long long int value; + }; +} + +/*** Hazelnupp.h ***/ + +#include +#include + +namespace Hazelnp +{ + /** The main class to interface with + */ + class Hazelnupp + { + public: + Hazelnupp(); + Hazelnupp(const int argc, const char* const* argv); + + ~Hazelnupp(); + + //! Will parse command line arguments + void Parse(const int argc, const char* const* argv); + + //! Will return argv[0], the name of the executable. + const std::string& GetExecutableName() const; + + //! Will return the value given a key + const Value& operator[](const std::string& key) const; + + //! Will check wether a parameter exists given a key, or not + bool HasParam(const std::string& key) const; + + // Abbreviations + //! Will register an abbreviation (like -f for --force) + void RegisterAbbreviation(const std::string& abbrev, const std::string& target); + + //! Will return the long form of an abbreviation (like --force for -f) + const std::string& GetAbbreviation(const std::string& abbrev) const; + + //! Will check wether or not an abbreviation is registered + bool HasAbbreviation(const std::string& abbrev) const; + + //! Will delete all abbreviations + void ClearAbbreviations(); + + //! Will register parameter constraints + void RegisterConstraints(const std::vector& constraints); + + //! Will delete all constraints + void ClearConstraints(); + + //! Sets whether to crash the application, and print to stderr, when an exception is + //! raised whilst parsing, or not. + void SetCrashOnFail(bool crashOnFail); + + //! Gets whether the application crashes on an exception whilst parsing, and prints to stderr. + bool GetCrashOnFail() const; + + private: + //! Will translate the c-like args to an std::vector + void PopulateRawArgs(const int argc, const char* const* argv); + + //! Will replace all args matching an abbreviation with their long form (like -f for --force) + void ExpandAbbreviations(); + + //! Will parse the next parameter. Returns the index of the next parameter. + std::size_t ParseNextParameter(const std::size_t parIndex, Parameter*& out_Par); + + //! Will convert a vector of string-values to an actual Value + Value* ParseValue(const std::vector& values, const ParamConstraint* constraint = nullptr); + + //! Will apply the loaded constraints on the loaded values, exluding types. + void ApplyConstraints(); + + //! Will return a pointer to a paramConstraint given a key. If there is no, it returns nullptr + const ParamConstraint* GetConstraintForKey(const std::string& key) const; + + std::string executableName; //! The path of the executable. Always argv[0] + std::unordered_map parameters; + + // These are abbreviations. Like, -f for --force. + std::unordered_map abbreviations; + + // Parameter constraints, mapped to keys + std::unordered_map constraints; + + std::vector rawArgs; + + //! If set to true, Hazelnupp will crash the application with output to stderr when an exception is thrown whilst parsing. + bool crashOnFail = true; + }; +} + +/*** FloatValue.h ***/ + +#include + +namespace Hazelnp +{ + /** Specializations for floating point values (uses long double) + */ + class FloatValue : public Value + { + public: + FloatValue(const long double& value); + ~FloatValue() override {}; + + //! Will return a deeopopy of this object + Value* Deepcopy() const override; + + //! Will return a string suitable for an std::ostream; + std::string GetAsOsString() const override; + + //! Will return the raw value + const long double& GetValue() const; + + operator long double() const; + operator double() const; + + //! Will return the data as a long long int + long long int GetInt64() const override; + //! Will return the data as an int + int GetInt32() const override; + + //! Will return the data as a long double + long double GetFloat64() const override; + //! Will return the data as a double + double GetFloat32() const override; + + //! Will return the data as a string + std::string GetString() const override; + + //! Throws HazelnuppValueNotConvertibleException + const std::vector& GetList() const override; + + private: + long double value; + }; +} diff --git a/readme.md b/readme.md index 40150f6..0733c52 100644 --- a/readme.md +++ b/readme.md @@ -29,12 +29,8 @@ args.SetCrashOnFail(false); ## Importing into a project > How do i actually import this into my existing project? -I am working on a proper way to make this a fast-and-easy include. -I am probably going to make it a single-header--single-cpp file solution. -A namespace will obviously also be used. - -If you want to use it NOW, the best idea would probably be to either compile a lib from source -or set the entire Visual Studio project as a dependency, if you are using VS. +Super easily! Just grab the latest files (2) from [/INCLUDE](https://github.com/Leonetienne/Hazelnupp/tree/master/INCLUDE) and put then into your project! +You may have to add the .cpp to your compile list, but most IDEs should do this automatically. ## What's the concept? The concept is that each parameter must be one of five types. These are: