feat: add arg parser feature but not finished
This commit is contained in:
parent
f997990af6
commit
35318505e4
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 3.23)
|
cmake_minimum_required(VERSION 3.23)
|
||||||
project(YYCC
|
project(YYCC
|
||||||
VERSION 1.1.0
|
VERSION 1.2.0
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
204
src/ArgParser.cpp
Normal file
204
src/ArgParser.cpp
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
#include "ArgParser.hpp"
|
||||||
|
|
||||||
|
#include "EncodingHelper.hpp"
|
||||||
|
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
#include "WinImportPrefix.hpp"
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <shellapi.h>
|
||||||
|
#include <processenv.h>
|
||||||
|
#include "WinImportSuffix.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace YYCC::ArgParser {
|
||||||
|
|
||||||
|
#pragma region Arguments List
|
||||||
|
|
||||||
|
ArgumentList ArgumentList::CreateFromStd(int argc, char* argv[]) {
|
||||||
|
std::vector<yycc_u8string> args;
|
||||||
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
if (argv[i] != nullptr)
|
||||||
|
args.emplace_back(yycc_u8string(YYCC::EncodingHelper::ToUTF8(argv[i])));
|
||||||
|
}
|
||||||
|
return ArgumentList(std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
ArgumentList ArgumentList::CreateFromWin32() {
|
||||||
|
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
|
||||||
|
|
||||||
|
// prepare list
|
||||||
|
std::vector<yycc_u8string> args;
|
||||||
|
|
||||||
|
// try fetching from Win32 functions
|
||||||
|
int argc;
|
||||||
|
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||||
|
if (argv != NULL) {
|
||||||
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
if (argv[i] != nullptr) {
|
||||||
|
yycc_u8string u8_argv;
|
||||||
|
if (YYCC::EncodingHelper::WcharToUTF8(argv[i], u8_argv))
|
||||||
|
args.emplace_back(std::move(u8_argv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LocalFree(argv);
|
||||||
|
|
||||||
|
// return result
|
||||||
|
return ArgumentList(std::move(args));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ArgumentList::ArgumentList(std::vector<yycc_u8string>&& arguments) :
|
||||||
|
m_Arguments(arguments), m_ArgumentsIterator(m_Arguments.begin()) {}
|
||||||
|
|
||||||
|
void ArgumentList::Prev() {
|
||||||
|
if (m_ArgumentsIterator == m_Arguments.begin())
|
||||||
|
throw std::runtime_error("attempt to move on the head of iterator.");
|
||||||
|
--m_ArgumentsIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgumentList::Next() {
|
||||||
|
if (IsEOF()) throw std::runtime_error("attempt to move on the tail of iterator.");
|
||||||
|
++m_ArgumentsIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsLongName(const yycc_u8string& param, yycc_u8string_view* name_part) {
|
||||||
|
if (param.find(AbstractArgument::DOUBLE_DASH) != 0u) return false;
|
||||||
|
if (name_part != nullptr)
|
||||||
|
*name_part = yycc_u8string_view(param).substr(2u);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static bool IsShortName(const yycc_u8string& param, yycc_char8_t* name_part) {
|
||||||
|
if (param.size() != 2u ||
|
||||||
|
param[0] != AbstractArgument::DASH ||
|
||||||
|
param[1] == AbstractArgument::DASH ||
|
||||||
|
param[1] < AbstractArgument::MIN_SHORT_NAME || param[1] > AbstractArgument::MAX_SHORT_NAME) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (name_part != nullptr)
|
||||||
|
*name_part = param[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ArgumentList::IsSwitch(
|
||||||
|
bool* is_long_name = nullptr,
|
||||||
|
yycc_u8string_view* long_name = nullptr,
|
||||||
|
yycc_char8_t* short_name = nullptr) const {
|
||||||
|
// get argument first
|
||||||
|
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
||||||
|
const auto& param = *m_ArgumentsIterator;
|
||||||
|
// check long name first, then check short name
|
||||||
|
if (IsLongName(param, long_name)) {
|
||||||
|
if (is_long_name != nullptr) *is_long_name = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IsShortName(param, short_name)) {
|
||||||
|
if (is_long_name != nullptr) *is_long_name = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// not matched
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArgumentList::IsValue() const { return !IsSwitch(); }
|
||||||
|
|
||||||
|
bool ArgumentList::IsEOF() const { return m_ArgumentsIterator == m_Arguments.end(); }
|
||||||
|
|
||||||
|
void ArgumentList::Reset() { m_ArgumentsIterator = m_Arguments.begin(); }
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Abstract Argument
|
||||||
|
|
||||||
|
const yycc_u8string AbstractArgument::DOUBLE_DASH = YYCC_U8("--");
|
||||||
|
const yycc_char8_t AbstractArgument::DASH = YYCC_U8_CHAR('-');
|
||||||
|
const yycc_char8_t AbstractArgument::NO_SHORT_NAME = YYCC_U8_CHAR(0);
|
||||||
|
const yycc_char8_t AbstractArgument::MIN_SHORT_NAME = YYCC_U8_CHAR('!');
|
||||||
|
const yycc_char8_t AbstractArgument::MAX_SHORT_NAME = YYCC_U8_CHAR('~');
|
||||||
|
|
||||||
|
AbstractArgument::AbstractArgument(
|
||||||
|
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||||
|
const yycc_char8_t* description, const yycc_char8_t* argument_example,
|
||||||
|
bool is_optional) :
|
||||||
|
m_LongName(), m_ShortName(NO_SHORT_NAME), m_Description(), m_ArgumentExample(),
|
||||||
|
m_IsOptional(is_optional), m_IsCaptured(false) {
|
||||||
|
|
||||||
|
// try to assign long name
|
||||||
|
if (long_name != nullptr) m_LongName = long_name;
|
||||||
|
// try to assign short name
|
||||||
|
if (short_name == AbstractArgument::DASH ||
|
||||||
|
short_name < AbstractArgument::MIN_SHORT_NAME ||
|
||||||
|
short_name > AbstractArgument::MAX_SHORT_NAME) {
|
||||||
|
throw std::invalid_argument("given short name character is invalid.");
|
||||||
|
}
|
||||||
|
m_ShortName = short_name;
|
||||||
|
// check short name and long name
|
||||||
|
if (!HasShortName() && !HasLongName())
|
||||||
|
throw std::invalid_argument("you must specify an one of long name or short name.");
|
||||||
|
|
||||||
|
// try to assign other string values
|
||||||
|
if (description != nullptr) m_Description = description;
|
||||||
|
if (argument_example != nullptr) m_ArgumentExample = argument_example;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractArgument::~AbstractArgument() {}
|
||||||
|
|
||||||
|
bool AbstractArgument::HasLongName() const { return !m_LongName.empty(); }
|
||||||
|
const yycc_u8string& AbstractArgument::GetLongName() const { return m_LongName; }
|
||||||
|
bool AbstractArgument::HasShortName() const { return m_ShortName != NO_SHORT_NAME; }
|
||||||
|
yycc_char8_t AbstractArgument::GetShortName() const { return m_ShortName; }
|
||||||
|
bool AbstractArgument::HasDescription() const { return !m_Description.empty(); }
|
||||||
|
const yycc_u8string& AbstractArgument::GetDescription() const { return m_Description; }
|
||||||
|
bool AbstractArgument::HasArgumentExample() const { return !m_ArgumentExample.empty(); }
|
||||||
|
const yycc_u8string& AbstractArgument::GetArgumentExample() const { return m_ArgumentExample; }
|
||||||
|
bool AbstractArgument::IsOptional() const { return m_IsOptional; }
|
||||||
|
|
||||||
|
bool AbstractArgument::IsCaptured() const { return m_IsCaptured; }
|
||||||
|
void AbstractArgument::SetCaptured(bool is_captured) { m_IsCaptured = is_captured; }
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Option Context
|
||||||
|
|
||||||
|
OptionContext::OptionContext(
|
||||||
|
const yycc_char8_t* summary, const yycc_char8_t* description,
|
||||||
|
std::initializer_list<AbstractArgument*> arguments) :
|
||||||
|
m_Summary(), m_Description() {
|
||||||
|
// assign summary and description
|
||||||
|
if (summary != nullptr) m_Summary = summary;
|
||||||
|
if (description != nullptr) m_Description = description;
|
||||||
|
|
||||||
|
// insert argument list and check them
|
||||||
|
for (auto* arg : arguments) {
|
||||||
|
// insert into long name map if necessary
|
||||||
|
if (arg->HasLongName()) {
|
||||||
|
auto result = m_LongNameMap.try_emplace(arg->GetLongName(), arg);
|
||||||
|
if (!result.second) throw std::invalid_argument("duplicated long name!");
|
||||||
|
}
|
||||||
|
// insert into short name map if necessary
|
||||||
|
if (arg->HasShortName()) {
|
||||||
|
auto result = m_ShortNameMap.try_emplace(arg->GetShortName(), arg);
|
||||||
|
if (!result.second) throw std::invalid_argument("duplicated short name!");
|
||||||
|
}
|
||||||
|
// insert into argument list
|
||||||
|
m_Arguments.emplace_back(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionContext::~OptionContext() {}
|
||||||
|
|
||||||
|
bool OptionContext::Parse(ArgumentList* al) {
|
||||||
|
return false; //todo
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionContext::Reset() {
|
||||||
|
for (auto* arg : m_Arguments) {
|
||||||
|
// clear user data and unset captured
|
||||||
|
arg->Reset();
|
||||||
|
arg->SetCaptured(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
}
|
176
src/ArgParser.hpp
Normal file
176
src/ArgParser.hpp
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
#pragma once
|
||||||
|
#include "YYCCInternal.hpp"
|
||||||
|
|
||||||
|
#include "Constraints.hpp"
|
||||||
|
#include "EncodingHelper.hpp"
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace YYCC::ArgParser {
|
||||||
|
|
||||||
|
class ArgumentList {
|
||||||
|
public:
|
||||||
|
static ArgumentList CreateFromStd(int argc, char* argv[]);
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
static ArgumentList CreateFromWin32();
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
ArgumentList(std::vector<yycc_u8string>&& arguments);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Prev();
|
||||||
|
void Next();
|
||||||
|
bool IsSwitch(
|
||||||
|
bool* is_long_name = nullptr,
|
||||||
|
yycc_u8string_view* long_name = nullptr,
|
||||||
|
yycc_char8_t* short_name = nullptr) const;
|
||||||
|
bool IsValue() const;
|
||||||
|
bool IsEOF() const;
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<yycc_u8string> m_Arguments;
|
||||||
|
std::vector<yycc_u8string>::const_iterator m_ArgumentsIterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AbstractArgument {
|
||||||
|
friend class OptionContext;
|
||||||
|
public:
|
||||||
|
static const yycc_u8string DOUBLE_DASH;
|
||||||
|
static const yycc_char8_t DASH;
|
||||||
|
static const yycc_char8_t NO_SHORT_NAME;
|
||||||
|
static const yycc_char8_t MIN_SHORT_NAME;
|
||||||
|
static const yycc_char8_t MAX_SHORT_NAME;
|
||||||
|
public:
|
||||||
|
AbstractArgument(
|
||||||
|
const yycc_char8_t* long_name, yycc_char8_t short_name = AbstractArgument::NO_SHORT_NAME,
|
||||||
|
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
||||||
|
bool is_optional = false);
|
||||||
|
virtual ~AbstractArgument();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool Parse(ArgumentList& al) = 0;
|
||||||
|
virtual void Reset() = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool HasLongName() const;
|
||||||
|
const yycc_u8string& GetLongName() const;
|
||||||
|
bool HasShortName() const;
|
||||||
|
yycc_char8_t GetShortName() const;
|
||||||
|
bool HasDescription() const;
|
||||||
|
const yycc_u8string& GetDescription() const;
|
||||||
|
bool HasArgumentExample() const;
|
||||||
|
const yycc_u8string& GetArgumentExample() const;
|
||||||
|
bool IsOptional() const;
|
||||||
|
private:
|
||||||
|
yycc_u8string m_LongName;
|
||||||
|
yycc_char8_t m_ShortName;
|
||||||
|
yycc_u8string m_Description;
|
||||||
|
yycc_u8string m_ArgumentExample;
|
||||||
|
bool m_IsOptional;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool IsCaptured() const;
|
||||||
|
private:
|
||||||
|
void SetCaptured(bool is_captured);
|
||||||
|
bool m_IsCaptured;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OptionContext {
|
||||||
|
public:
|
||||||
|
OptionContext(
|
||||||
|
const yycc_char8_t* summary, const yycc_char8_t* description,
|
||||||
|
std::initializer_list<AbstractArgument*> arguments);
|
||||||
|
~OptionContext();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool Parse(ArgumentList* al);
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
yycc_u8string m_Summary;
|
||||||
|
yycc_u8string m_Description;
|
||||||
|
|
||||||
|
std::vector<AbstractArgument*> m_Arguments;
|
||||||
|
std::map<yycc_u8string, AbstractArgument*> m_LongNameMap;
|
||||||
|
std::map<yycc_char8_t, AbstractArgument*> m_ShortNameMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma region Argument Presets
|
||||||
|
|
||||||
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||||
|
class NumberArgument : public AbstractArgument {
|
||||||
|
public:
|
||||||
|
NumberArgument(
|
||||||
|
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||||
|
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
||||||
|
bool is_optional = false,
|
||||||
|
Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) :
|
||||||
|
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
||||||
|
virtual ~NumberArgument() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
_Ty Get() const {
|
||||||
|
if (!IsCaptured()) throw std::runtime_error("try fetching data from a not captured argument.");
|
||||||
|
return m_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool Parse(ArgumentList& al) override {} // todo
|
||||||
|
virtual void Reset() override {}// todo
|
||||||
|
|
||||||
|
protected:
|
||||||
|
_Ty m_Data;
|
||||||
|
Constraints::Constraint<_Ty> m_Constraint;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SwitchArgument : public AbstractArgument {
|
||||||
|
public:
|
||||||
|
SwitchArgument(
|
||||||
|
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||||
|
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr) :
|
||||||
|
AbstractArgument(long_name, short_name, description, argument_example, true), m_Data(false) {} // bool switch must be optional, because it is false if no given switch.
|
||||||
|
virtual ~SwitchArgument() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool Get() const { return m_Data; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool Parse(ArgumentList& al) override { m_Data = true; }
|
||||||
|
virtual void Reset() override { m_Data = false; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StringArgument : public AbstractArgument {
|
||||||
|
public:
|
||||||
|
StringArgument(
|
||||||
|
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||||
|
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
||||||
|
bool is_optional = false,
|
||||||
|
Constraints::Constraint<yycc_u8string_view> constraint = Constraints::Constraint<yycc_u8string_view> {}) :
|
||||||
|
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
||||||
|
virtual ~StringArgument() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const yycc_u8string& Get() const {
|
||||||
|
if (!IsCaptured()) throw std::runtime_error("try fetching data from a not captured argument.");
|
||||||
|
return m_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool Parse(ArgumentList& al) override {} // todo
|
||||||
|
virtual void Reset() override {}// todo
|
||||||
|
|
||||||
|
protected:
|
||||||
|
yycc_u8string m_Data;
|
||||||
|
Constraints::Constraint<yycc_u8string_view> m_Constraint;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ target_sources(YYCCommonplace
|
||||||
PRIVATE
|
PRIVATE
|
||||||
# Sources
|
# Sources
|
||||||
COMHelper.cpp
|
COMHelper.cpp
|
||||||
|
ArgParser.cpp
|
||||||
ConfigManager.cpp
|
ConfigManager.cpp
|
||||||
ConsoleHelper.cpp
|
ConsoleHelper.cpp
|
||||||
DialogHelper.cpp
|
DialogHelper.cpp
|
||||||
|
@ -23,7 +24,9 @@ FILE_SET HEADERS
|
||||||
FILES
|
FILES
|
||||||
# Headers
|
# Headers
|
||||||
# Common headers
|
# Common headers
|
||||||
|
Constraints.hpp
|
||||||
COMHelper.hpp
|
COMHelper.hpp
|
||||||
|
ArgParser.hpp
|
||||||
ConfigManager.hpp
|
ConfigManager.hpp
|
||||||
ConsoleHelper.hpp
|
ConsoleHelper.hpp
|
||||||
DialogHelper.hpp
|
DialogHelper.hpp
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "YYCCInternal.hpp"
|
#include "YYCCInternal.hpp"
|
||||||
|
|
||||||
|
#include "Constraints.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -17,46 +18,6 @@
|
||||||
*/
|
*/
|
||||||
namespace YYCC::ConfigManager {
|
namespace YYCC::ConfigManager {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The constraint applied to settings to limit its stored value.
|
|
||||||
* @tparam _Ty The internal data type stroed in corresponding setting.
|
|
||||||
*/
|
|
||||||
template<typename _Ty>
|
|
||||||
struct Constraint {
|
|
||||||
using CheckFct_t = std::function<bool(const _Ty&)>;
|
|
||||||
//using CorrectFct_t = std::function<_Ty(const _Ty&)>;
|
|
||||||
CheckFct_t m_CheckFct;
|
|
||||||
//CorrectFct_t m_CorrectFct;
|
|
||||||
|
|
||||||
bool IsValid() const {
|
|
||||||
return m_CheckFct != nullptr/* && m_CorrectFct != nullptr*/;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The namespace containing functions generating common used constraint.
|
|
||||||
*/
|
|
||||||
namespace ConstraintPresets {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get constraint for arithmetic values by minimum and maximum value range.
|
|
||||||
* @tparam _Ty The underlying arithmetic type.
|
|
||||||
* @param[in] min_value The minimum value of range (inclusive).
|
|
||||||
* @param[in] max_value The maximum value of range (inclusive).
|
|
||||||
* @return The generated constraint instance which can be directly applied.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_enum_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
Constraint<_Ty> GetNumberRangeConstraint(_Ty min_value, _Ty max_value) {
|
|
||||||
if (min_value > max_value)
|
|
||||||
throw std::invalid_argument("invalid min max value for NumberRangeConstraint");
|
|
||||||
return Constraint<_Ty> {
|
|
||||||
[min_value, max_value](const _Ty& val) -> bool { return (val <= max_value) && (val >= min_value); }
|
|
||||||
/*[min_value, max_value](const _Ty& val) -> _Ty { return std::clamp(val, min_value, max_value); }*/
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief The base class of every setting.
|
/// @brief The base class of every setting.
|
||||||
/// @details Programmer can inherit this class and implement essential to create custom setting.
|
/// @details Programmer can inherit this class and implement essential to create custom setting.
|
||||||
class AbstractSetting {
|
class AbstractSetting {
|
||||||
|
@ -114,7 +75,7 @@ namespace YYCC::ConfigManager {
|
||||||
private:
|
private:
|
||||||
std::vector<uint8_t> m_RawData;
|
std::vector<uint8_t> m_RawData;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Settings manager and config file reader writer.
|
/// @brief Settings manager and config file reader writer.
|
||||||
class CoreManager {
|
class CoreManager {
|
||||||
public:
|
public:
|
||||||
|
@ -166,10 +127,12 @@ namespace YYCC::ConfigManager {
|
||||||
* @param[in] default_value The default value of this setting.
|
* @param[in] default_value The default value of this setting.
|
||||||
* @param[in] constraint The constraint applied to this setting.
|
* @param[in] constraint The constraint applied to this setting.
|
||||||
*/
|
*/
|
||||||
NumberSetting(const yycc_char8_t* name, _Ty default_value, Constraint<_Ty> constraint = Constraint<_Ty> {}) :
|
NumberSetting(
|
||||||
|
const yycc_char8_t* name, _Ty default_value,
|
||||||
|
Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) :
|
||||||
AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constraint(constraint) {}
|
AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constraint(constraint) {}
|
||||||
virtual ~NumberSetting() {}
|
virtual ~NumberSetting() {}
|
||||||
|
|
||||||
/// @brief Get stored data in setting.
|
/// @brief Get stored data in setting.
|
||||||
_Ty Get() const { return m_Data; }
|
_Ty Get() const { return m_Data; }
|
||||||
/**
|
/**
|
||||||
|
@ -208,7 +171,7 @@ namespace YYCC::ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
_Ty m_Data, m_DefaultData;
|
_Ty m_Data, m_DefaultData;
|
||||||
Constraint<_Ty> m_Constraint;
|
Constraints::Constraint<_Ty> m_Constraint;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief String type setting
|
/// @brief String type setting
|
||||||
|
@ -220,13 +183,15 @@ namespace YYCC::ConfigManager {
|
||||||
* @param[in] default_value The default value of this setting.
|
* @param[in] default_value The default value of this setting.
|
||||||
* @param[in] constraint The constraint applied to this setting.
|
* @param[in] constraint The constraint applied to this setting.
|
||||||
*/
|
*/
|
||||||
StringSetting(const yycc_char8_t* name, const yycc_u8string_view& default_value, Constraint<yycc_u8string_view> constraint = Constraint<yycc_u8string_view> {}) :
|
StringSetting(
|
||||||
|
const yycc_char8_t* name, const yycc_u8string_view& default_value,
|
||||||
|
Constraints::Constraint<yycc_u8string_view> constraint = Constraints::Constraint<yycc_u8string_view> {}) :
|
||||||
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) {
|
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) {
|
||||||
m_Data = default_value;
|
m_Data = default_value;
|
||||||
m_DefaultData = default_value;
|
m_DefaultData = default_value;
|
||||||
}
|
}
|
||||||
virtual ~StringSetting() {}
|
virtual ~StringSetting() {}
|
||||||
|
|
||||||
/// @brief Get reference to stored string.
|
/// @brief Get reference to stored string.
|
||||||
const yycc_u8string& Get() const { return m_Data; }
|
const yycc_u8string& Get() const { return m_Data; }
|
||||||
/**
|
/**
|
||||||
|
@ -279,7 +244,7 @@ namespace YYCC::ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
yycc_u8string m_Data, m_DefaultData;
|
yycc_u8string m_Data, m_DefaultData;
|
||||||
Constraint<yycc_u8string_view> m_Constraint;
|
Constraints::Constraint<yycc_u8string_view> m_Constraint;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
84
src/Constraints.hpp
Normal file
84
src/Constraints.hpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#pragma once
|
||||||
|
#include "YYCCInternal.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <set>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The namespace containing constraint declaration
|
||||||
|
* and functions generating common used constraint.
|
||||||
|
*/
|
||||||
|
namespace YYCC::Constraints {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The constraint applied to settings to limit its stored value.
|
||||||
|
* @tparam _Ty The internal data type stroed in corresponding setting.
|
||||||
|
*/
|
||||||
|
template<typename _Ty>
|
||||||
|
struct Constraint {
|
||||||
|
/// @brief Return true if value is legal, otherwise false.
|
||||||
|
using CheckFct_t = std::function<bool(const _Ty&)>;
|
||||||
|
/// @brief The function pointer used for checking whether given value is valid.
|
||||||
|
CheckFct_t m_CheckFct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether this Constraint is valid for using.
|
||||||
|
* @return
|
||||||
|
* True if this Constraint is valid, otherwise false.
|
||||||
|
* You should not use this Constraint if this function return false.
|
||||||
|
*/
|
||||||
|
bool IsValid() const {
|
||||||
|
return m_CheckFct != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get constraint for arithmetic or enum values by minimum and maximum value range.
|
||||||
|
* @tparam _Ty An arithmetic or enum type (except bool) of underlying stored value.
|
||||||
|
* @param[in] min_value The minimum value of range (inclusive).
|
||||||
|
* @param[in] max_value The maximum value of range (inclusive).
|
||||||
|
* @return The generated constraint instance which can be directly applied.
|
||||||
|
*/
|
||||||
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||||
|
Constraint<_Ty> GetMinMaxRangeConstraint(_Ty min_value, _Ty max_value) {
|
||||||
|
if (min_value > max_value)
|
||||||
|
throw std::invalid_argument("invalid min max value for NumberRangeConstraint");
|
||||||
|
return Constraint<_Ty> {
|
||||||
|
[min_value, max_value](const _Ty& val) -> bool { return (val <= max_value) && (val >= min_value); }
|
||||||
|
/*[min_value, max_value](const _Ty& val) -> _Ty { return std::clamp(val, min_value, max_value); }*/
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get constraint for enum values by enumerate all possible values.
|
||||||
|
* @tparam _Ty An enum type (except bool) of underlying stored value.
|
||||||
|
* @param[in] il An initializer list storing all possible values.
|
||||||
|
* @return The generated constraint instance which can be directly applied.
|
||||||
|
*/
|
||||||
|
template<typename _Ty, std::enable_if_t<std::is_enum_v<_Ty>, int> = 0>
|
||||||
|
Constraint<_Ty> GetEnumEnumerationConstraint(const std::initializer_list<_Ty>& il) {
|
||||||
|
std::set<_Ty> data(il);
|
||||||
|
return Constraint<_Ty> {
|
||||||
|
[data](const _Ty& val) -> bool { return data.find(val) != data.end(); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get constraint for string values by enumerate all possible values.
|
||||||
|
* @param[in] il An initializer list storing all possible values.
|
||||||
|
* @return The generated constraint instance which can be directly applied.
|
||||||
|
* @remarks
|
||||||
|
* Caller must make sure that the string view passed in initializer list is valid until this Constraint life time gone.
|
||||||
|
* Becasue this generator will not copy your given string view into string.
|
||||||
|
*/
|
||||||
|
Constraint<yycc_u8string_view> GetStringEnumerationConstraint(const std::initializer_list<yycc_u8string_view>& il) {
|
||||||
|
std::set<yycc_u8string_view> data(il);
|
||||||
|
return Constraint<yycc_u8string_view> {
|
||||||
|
[data](const yycc_u8string_view& val) -> bool { return data.find(val) != data.end(); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -402,7 +402,7 @@ namespace YYCCTestbench {
|
||||||
m_FloatSetting(YYCC_U8("float-setting"), 0.0f),
|
m_FloatSetting(YYCC_U8("float-setting"), 0.0f),
|
||||||
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
|
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
|
||||||
m_BoolSetting(YYCC_U8("bool-setting"), false),
|
m_BoolSetting(YYCC_U8("bool-setting"), false),
|
||||||
m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::ConfigManager::ConstraintPresets::GetNumberRangeConstraint<float>(-1.0f, 1.0f)),
|
m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
|
||||||
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
|
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
|
||||||
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
||||||
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
|
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
|
||||||
|
|
Loading…
Reference in New Issue
Block a user