doc: migrate all old doc
This commit is contained in:
@@ -1,200 +0,0 @@
|
||||
namespace YYCC::ArgParser {
|
||||
/**
|
||||
|
||||
\page arg_parser Universal Argument Parser
|
||||
|
||||
YYCC::ArgParser provides an universal way to parsing command line arguments.
|
||||
|
||||
Universal argument parser has similar design with universal config manager,
|
||||
it is highly recommand that read \ref config_manager chapter first,
|
||||
because you will have a clear understanding of this namespace after reading universal config manager chapter.
|
||||
|
||||
There is an example about how to use universal argument parser.
|
||||
In following content, we will describe it in detail.
|
||||
|
||||
\code{.cpp}
|
||||
class TestArgParser {
|
||||
public:
|
||||
TestArgParser() :
|
||||
m_IntArgument(YYCC_U8("int"), YYCC_U8_CHAR('i'), YYCC_U8("integral argument"), YYCC_U8("114514")),
|
||||
m_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
|
||||
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
|
||||
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
|
||||
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
|
||||
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the test of argument parser."), {
|
||||
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
|
||||
&m_BoolArgument, &m_ClampedFloatArgument
|
||||
}) {}
|
||||
~TestArgParser() {}
|
||||
|
||||
YYCC::ArgParser::NumberArgument<int32_t> m_IntArgument;
|
||||
YYCC::ArgParser::NumberArgument<float> m_FloatArgument;
|
||||
YYCC::ArgParser::StringArgument m_StringArgument;
|
||||
|
||||
YYCC::ArgParser::SwitchArgument m_BoolArgument;
|
||||
YYCC::ArgParser::NumberArgument<float> m_ClampedFloatArgument;
|
||||
|
||||
YYCC::ArgParser::OptionContext m_OptionContext;
|
||||
};
|
||||
|
||||
// Initialize argument parser.
|
||||
TestArgParser test;
|
||||
// Get argument list for parsing from standard C main function.
|
||||
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
|
||||
// Start parsing
|
||||
test.Parse(al);
|
||||
// Get captured string argument
|
||||
if (test.m_StringArgument.IsCaptured())
|
||||
auto val = test.m_StringArgument.Get();
|
||||
\endcode
|
||||
|
||||
These code can resolve following command line:
|
||||
|
||||
\code{.sh}
|
||||
exec -i 114514 -f 2.0 --string fuck -b --clamped-float 0.5
|
||||
\endcode
|
||||
|
||||
For convenience, we define following terms used in this article.
|
||||
|
||||
\li Every items in command line: Argument.
|
||||
\li \c -i, \c --clamped-float: \b Switch / \b Option. the argument starts with dash or double dash.
|
||||
\li \c 114514: \b Value. the value of switch.
|
||||
|
||||
\section arg_parser__argument Argument
|
||||
|
||||
Argument is the leaf of argument parser.
|
||||
It has the same position as setting in universal config manager.
|
||||
|
||||
\subsection arg_parser__argument__presets Argument Presets
|
||||
|
||||
Like setting in universal config manager,
|
||||
we also provide various common used argument presets.
|
||||
Current'y we support following argument presets:
|
||||
|
||||
\li NumberArgument: The argument storing arithmetic type (except \c bool) inside. Such as <TT>-i 114514</TT> in example.
|
||||
\li StringArgument: The argument storing string inside. Such as <TT>--string fuck</TT> in example.
|
||||
\li SwitchArgument: The argument storing nothing. It is just a simple switch. Such as <TT>-b</TT> in example.
|
||||
|
||||
When constructing these argument,
|
||||
you need provide one from long name or short name, or both of them.
|
||||
Short name is the argument starting with dash and long name starts with double dash.
|
||||
You don't need add dash or double dash prefix when providing these names.
|
||||
Please note only ASCII characters, which can be displayed on screen, can be used in these names.
|
||||
|
||||
Optionally, you can provide description when constructing,
|
||||
which will tell user how this switch does and more infomation about this switch.
|
||||
And, you can add an example to tell user which value is valid.
|
||||
|
||||
Next, you can specify an argument to be optional.
|
||||
Optional argument can be absent in command line.
|
||||
Oppositely, non-optional argument must be presented in command line,
|
||||
otherwise parser will return false to indicate an error.
|
||||
For checking whether an optional argument is specified,
|
||||
please call AbstractArgument::IsCaptured().
|
||||
|
||||
Last, you can optionally assign a constraint to it,
|
||||
to help argument limit its value.
|
||||
|
||||
However SwitchArgument must be optional argument.
|
||||
Because it is true if user specify it explicit it,
|
||||
and will be false if user do not give this flag.
|
||||
SwitchArgument doesn't have constraint features,
|
||||
because it doesn't store any value inside.
|
||||
Thus no need to limit this.
|
||||
|
||||
\subsection arg_parser__argument__custom Custom Argument
|
||||
|
||||
In most cases, the combination use of argument presets and constraints is enough.
|
||||
However, if you still are urge to create your personal argument,
|
||||
please inherit AbstractArgument and implement essential class functions.
|
||||
For the class functions you need to implement,
|
||||
please refer to our argument presets.
|
||||
|
||||
\section arg_parser__argument_list Argument List
|
||||
|
||||
Argument list is a struct used by parser for parsing.
|
||||
It is a higher wrapper of a simple list containing argument items.
|
||||
We provide 2 ways to get argument list.
|
||||
|
||||
\li ArgumentList::CreateFromStd: Create argument list from standard C main function parameters.
|
||||
\li ArgumentList::CreateFromWin32: Create argument list from Win32 functions in Windows.
|
||||
You should use this function in Windows instead of ArgumentList::CreateFromStd.
|
||||
Because the command line passed in standard C main function has encoding issue in Windows.
|
||||
Use this function you will fetch correct argument list especially command including non-ASCII characters.
|
||||
|
||||
Please note the first argument in given command line will be stripped.
|
||||
Because in most cases it point to the executable self,
|
||||
and should not be seen as the part of argument list.
|
||||
|
||||
\section arg_parser__option_context Option Context
|
||||
|
||||
Please note any unknow argument will let the parser return false.
|
||||
This is different with other argument parsers.
|
||||
In other common argument parsers,
|
||||
they will collect all unknow argument as positional argument,
|
||||
or just simply ignore them.
|
||||
|
||||
OptionContext also will not add \c -h or \c --help switch automatically.
|
||||
This is also differnent with other parsers.
|
||||
You should manually add it.
|
||||
However, OptionContext provide a universal help print function, OptionContext::Help.
|
||||
You can directly call it to output help text if you needed (fail to parse or user order help).
|
||||
|
||||
\section arg_parser__limitation Limitation
|
||||
|
||||
This universal argument parser is a tiny parser.
|
||||
It only just fulfill my personal requirements.
|
||||
So it only accepts limited command line syntax.
|
||||
In following content I will tell you some syntaxes which this parser \b not accept.
|
||||
|
||||
\subsection arg_parser__limitation__flag_combination Flag Combination
|
||||
|
||||
\code{.sh}
|
||||
exec -l -s -h
|
||||
exec -lsh
|
||||
\endcode
|
||||
|
||||
Parser accept first line but not accept the second line.
|
||||
You must write these flags independently.
|
||||
|
||||
\subsection arg_parser__limitation__equal_symbol Equal Symbol
|
||||
|
||||
\code{.sh}
|
||||
exec --value 114514
|
||||
exec --value=114514
|
||||
exec --value:114514
|
||||
\endcode
|
||||
|
||||
Parser only accept first line command.
|
||||
You can not use equal symbol or any other symbol to assign value for specified argument.
|
||||
You must write value after the argument immediately please.
|
||||
|
||||
\subsection arg_parser__limitation__variable_argument Variable Argument
|
||||
|
||||
\code{.sh}
|
||||
exec -DSOME_VARABLE=SOME_VALUE
|
||||
exec -D SOME_VARIABLE=SOME_VALUE
|
||||
\endcode
|
||||
|
||||
Parser only accept second line.
|
||||
However you nned to write a custom argument or constraint to holding this value.
|
||||
|
||||
\subsection arg_parser__limitation__switch_dependency Switch Dependency
|
||||
|
||||
\code{.sh}
|
||||
exec --action-a --action-b
|
||||
\endcode
|
||||
|
||||
For command line written above,
|
||||
if you hope \c --action-a and \c --action-b is exclusive,
|
||||
or \c --action-b only be valid if \c --action-a specified,
|
||||
you should manually implement this.
|
||||
Parser don't have such features to process this switch dependency.
|
||||
|
||||
The thing you need to do is set these switches are \b not optional.
|
||||
And after parser do a success parsing,
|
||||
manually calling AbstractArgument::IsCaptured to fetch whether corresponding switches are captured,
|
||||
then do your personal dependency check.
|
||||
|
||||
*/
|
||||
}
|
||||
208
doc/src/carton/binstore.dox
Normal file
208
doc/src/carton/binstore.dox
Normal file
@@ -0,0 +1,208 @@
|
||||
namespace yycc::carton::binstore {
|
||||
/**
|
||||
|
||||
\page binstore Binary Settings Storage (Binstore)
|
||||
|
||||
The binstore module provides a binary settings storage system that allows
|
||||
applications to persistently store and retrieve configuration settings in
|
||||
a binary format. It includes functionality for type-safe serialization and deserialization,
|
||||
setting management with unique tokens for access control, version control with migration strategies,
|
||||
and comprehensive error handling.
|
||||
|
||||
\section binstore__overview Overview
|
||||
|
||||
The binstore module consists of several key components:
|
||||
|
||||
\li types: Basic types and error handling for the module
|
||||
\li serdes: Serialization/deserialization functionality for different data types
|
||||
\li setting: Management of settings with name-based lookup and token-based access
|
||||
\li configuration: Version and settings collection management
|
||||
\li storage: Main storage class for loading/saving settings to/from files or streams
|
||||
|
||||
\section binstore__example Example Usage
|
||||
|
||||
Here is a complete example showing how to use the binstore module:
|
||||
|
||||
\code{.cpp}
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/binstore.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace yycc::carton::binstore;
|
||||
using namespace yycc::patch::stream;
|
||||
|
||||
enum class LogLevel : uint8_t { Debug, Info, Warning, Error };
|
||||
|
||||
int main() {
|
||||
// Create settings collection
|
||||
auto settings = setting::SettingCollection();
|
||||
auto int_setting_token = settings.add_setting(setting::Setting(u8"max_connections"));
|
||||
auto float_setting_token = settings.add_setting(setting::Setting(u8"timeout"));
|
||||
auto string_setting_token = settings.add_setting(setting::Setting(u8"server_address"));
|
||||
auto bool_setting_token = settings.add_setting(setting::Setting(u8"enable_logging"));
|
||||
auto enum_setting_token = settings.add_setting(setting::Setting(u8"log_level"));
|
||||
|
||||
// Create configuration with version 1
|
||||
auto config = configuration::Configuration(1, std::move(settings));
|
||||
|
||||
// Create storage with the configuration
|
||||
auto storage = storage::Storage(std::move(config));
|
||||
|
||||
// Using appropriate SerDes types for different data types
|
||||
using IntSerDes = serdes::IntegralSerDes<int32_t>;
|
||||
using FloatSerDes = serdes::FloatingPointSerDes<float>;
|
||||
using StringSerDes = serdes::StringSerDes;
|
||||
using BoolSerDes = serdes::BoolSerDes<true>; // true as default value
|
||||
using EnumSerDes = serdes::EnumSerDes<LogLevel, LogLevel::Info>;
|
||||
|
||||
// Set values
|
||||
storage.set_value<IntSerDes>(int_setting_token, 100);
|
||||
storage.set_value<FloatSerDes>(float_setting_token, 2.5f);
|
||||
storage.set_value<StringSerDes>(string_setting_token, u8"localhost");
|
||||
storage.set_value<BoolSerDes>(bool_setting_token, true);
|
||||
storage.set_value<EnumSerDes>(enum_setting_token, LogLevel::Debug);
|
||||
|
||||
// Save to file
|
||||
if (auto result = storage.save_into_file("config.bin"); result.has_value()) {
|
||||
std::cout << "Configuration saved successfully" << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to save configuration" << std::endl;
|
||||
}
|
||||
|
||||
// Load from file
|
||||
auto new_config = configuration::Configuration(1, setting::SettingCollection());
|
||||
auto new_storage = storage::Storage(std::move(new_config));
|
||||
|
||||
if (auto result = new_storage.load_from_file("config.bin", storage::LoadStrategy::MigrateOld); result.has_value()) {
|
||||
std::cout << "Configuration loaded successfully" << std::endl;
|
||||
|
||||
// Get values
|
||||
int32_t max_conn = new_storage.get_value<IntSerDes>(int_setting_token);
|
||||
float timeout = new_storage.get_value<FloatSerDes>(float_setting_token);
|
||||
std::u8string addr = new_storage.get_value<StringSerDes>(string_setting_token);
|
||||
bool logging = new_storage.get_value<BoolSerDes>(bool_setting_token);
|
||||
LogLevel level = new_storage.get_value<EnumSerDes>(enum_setting_token);
|
||||
|
||||
std::cout << "Max connections: " << max_conn << std::endl;
|
||||
std::cout << "Timeout: " << timeout << std::endl;
|
||||
std::cout << "Server address: " << addr << std::endl;
|
||||
std::cout << "Logging enabled: " << (logging ? "yes" : "no") << std::endl;
|
||||
std::cout << "Log level: " << static_cast<int>(level) << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to load configuration" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section binstore__components Components
|
||||
|
||||
\subsection binstore__settings Settings Management
|
||||
|
||||
Settings are identified by unique names and accessed via tokens. The [SettingCollection](\ref setting::SettingCollection)
|
||||
manages a collection of settings and ensures no duplicates.
|
||||
|
||||
\subsection binstore__configuration Configuration
|
||||
|
||||
The [Configuration](\ref configuration::Configuration) class holds the version identifier and the collection of settings.
|
||||
Version control is crucial for handling configuration migration between application versions.
|
||||
|
||||
\subsection binstore__storage Storage
|
||||
|
||||
The [Storage](\ref storage::Storage) class is the main interface for setting/getting values and loading/saving configurations.
|
||||
It provides methods for both file-based and stream-based operations.
|
||||
|
||||
\subsection binstore__serdes Serialization/Deserialization
|
||||
|
||||
SerDes (Serializer/Deserializer) classes handle type-safe conversion between values and their binary representation.
|
||||
Built-in SerDes types include:
|
||||
|
||||
\li Integral types ([IntegralSerDes](\ref serdes::IntegralSerDes))
|
||||
\li Floating-point types ([FloatingPointSerDes](\ref serdes::FloatingPointSerDes))
|
||||
\li String types ([StringSerDes](\ref serdes::StringSerDes))
|
||||
\li Boolean types ([BoolSerDes](\ref serdes::BoolSerDes))
|
||||
\li Enum types ([EnumSerDes](\ref serdes::EnumSerDes))
|
||||
|
||||
For some of them, you can specify value range and default value via template parameters.
|
||||
|
||||
\section binstore__load_strategies Load Strategies
|
||||
|
||||
The binstore module provides different strategies for handling version mismatches:
|
||||
|
||||
\li [OnlyCurrent](\ref storage::LoadStrategy::OnlyCurrent): Only accept configurations with matching version
|
||||
\li [MigrateOld](\ref storage::LoadStrategy::MigrateOld): Accept matching and older versions, reject newer versions
|
||||
\li [AcceptAll](\ref storage::LoadStrategy::AcceptAll): Accept all versions (not recommended for production)
|
||||
|
||||
\section binstore__custom_serdes Custom SerDes
|
||||
|
||||
Custom SerDes (Serializer/Deserializer) can be created by implementing the \c SerDes concept.
|
||||
A valid SerDes must satisfy the following requirements:
|
||||
|
||||
\li Have a type alias called \c ValueType indicating the corresponding setting type
|
||||
\li Have a member function called \c serialize that accepts a const reference of the setting data and returns \c ByteArray
|
||||
or \c std::nullopt if serialization fails.
|
||||
\li Have a member function called \c deserialize that converts \c ByteArray to the desired type
|
||||
or returns \c std::nullopt if deserialization fails.
|
||||
\li Have a member function called \c reset that returns a default \c ByteArray value.
|
||||
|
||||
Here is an example of a custom SerDes for storing IPv4 addresses:
|
||||
|
||||
\code{.cpp}
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
struct IPv4Address {
|
||||
std::uint8_t octets[4];
|
||||
|
||||
IPv4Address() : octets{0, 0, 0, 0} {}
|
||||
IPv4Address(std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) {
|
||||
octets[0] = a; octets[1] = b; octets[2] = c; octets[3] = d;
|
||||
}
|
||||
};
|
||||
|
||||
struct IPv4SerDes {
|
||||
using ValueType = IPv4Address;
|
||||
static constexpr size_t VALUE_SIZE = sizeof(IPv4Address); // 4 octets
|
||||
|
||||
std::optional<types::ByteArray> serialize(const ValueType& value) const {
|
||||
types::ByteArray ba;
|
||||
ba.resize_data(VALUE_SIZE);
|
||||
std::memcpy(ba.get_data_ptr(), value.octets, VALUE_SIZE);
|
||||
return ba;
|
||||
}
|
||||
|
||||
std::optional<ValueType> deserialize(const types::ByteArray& ba) const {
|
||||
if (ba.get_data_size() != VALUE_SIZE) return std::nullopt;
|
||||
|
||||
ValueType value;
|
||||
std::memcpy(value.octets, ba.get_data_ptr(), VALUE_SIZE);
|
||||
return value;
|
||||
}
|
||||
|
||||
types::ByteArray reset() const {
|
||||
// Reset to local address
|
||||
ValueType default_value(127, 0, 0, 1);
|
||||
return this->serialize(default_value).value();
|
||||
}
|
||||
};
|
||||
\endcode
|
||||
|
||||
To use the custom SerDes:
|
||||
|
||||
\code{.cpp}
|
||||
// Add setting to collection
|
||||
auto ip_setting_token = settings.add_setting(setting::Setting(u8"server_ip"));
|
||||
|
||||
// Use custom SerDes
|
||||
IPv4SerDes ip_serdes;
|
||||
storage.set_value<IPv4SerDes>(ip_setting_token, IPv4Address(192, 168, 1, 1));
|
||||
|
||||
// Retrieve value
|
||||
IPv4Address ip_addr = storage.get_value<IPv4SerDes>(ip_setting_token);
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
187
doc/src/carton/clap.dox
Normal file
187
doc/src/carton/clap.dox
Normal file
@@ -0,0 +1,187 @@
|
||||
namespace yycc::carton::clap {
|
||||
/**
|
||||
|
||||
\page clap Command Line Argument Parser (CLAP)
|
||||
|
||||
Command Line Argument Parser (CLAP) module for handling command line arguments and environment variables.
|
||||
This module provides a comprehensive system for defining, parsing, and validating command line
|
||||
arguments and environment variables. It includes components for defining application metadata,
|
||||
command line options, variables, and utilities for parsing and validation.
|
||||
|
||||
\section clap__overview Overview
|
||||
|
||||
The CLAP module consists of several key components:
|
||||
|
||||
\li Types: Error types and result types used throughout the module
|
||||
\li Validator: Type-safe validation for command line argument values
|
||||
\li Option: Command line options with short and long names
|
||||
\li Variable: Environment variables that can be captured
|
||||
\li Summary: Application metadata (name, version, author, description)
|
||||
\li Application: Complete application definition with options and variables
|
||||
\li Manual: Help and version information generation
|
||||
\li Parser: Command line argument parsing functionality
|
||||
\li Resolver: Environment variable resolution functionality
|
||||
|
||||
\section clap__example Example Usage
|
||||
|
||||
Here is a complete example showing how to use the CLAP module:
|
||||
|
||||
\code{.cpp}
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/clap.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace yycc::carton::clap;
|
||||
using namespace yycc::patch::stream;
|
||||
|
||||
// Define an application with options and variables
|
||||
int main(int argc, char* argv[]) {
|
||||
// Create application summary
|
||||
auto summary = summary::Summary(u8"MyApp", u8"author", u8"1.0.0", u8"A sample application");
|
||||
|
||||
// Create options collection
|
||||
auto options = option::OptionCollection();
|
||||
auto int_opt = options.add_option(option::Option(u8"i", u8"int", u8"NUM", u8"integral argument"));
|
||||
auto float_opt = options.add_option(option::Option(u8"f", std::nullopt, u8"NUM", u8"floating point argument"));
|
||||
auto string_opt = options.add_option(option::Option(std::nullopt, u8"string", u8"STR", u8"string argument"));
|
||||
auto flag_opt = options.add_option(option::Option(u8"v", std::nullopt, std::nullopt, u8"verbose mode"));
|
||||
|
||||
// Create variables collection
|
||||
auto variables = variable::VariableCollection();
|
||||
auto env_var = variables.add_variable(variable::Variable(u8"ENV_VAR", u8"Environment variable description", true));
|
||||
|
||||
// Create the application and manual
|
||||
auto app = application::Application(std::move(summary), std::move(options), std::move(variables));
|
||||
auto manual = manual::Manual(app);
|
||||
|
||||
// Parse command line arguments
|
||||
auto result = parser::Parser::from_system(app);
|
||||
if (result.has_value()) {
|
||||
auto parser = std::move(result.value());
|
||||
|
||||
// Get values using validators
|
||||
using IntValidator = validator::IntegralValidator<int>;
|
||||
using FloatValidator = validator::FloatingPointValidator<float>;
|
||||
using StringValidator = validator::StringValidator;
|
||||
|
||||
// Check and get integer option
|
||||
if (auto int_val = parser.get_value_option<IntValidator>(int_opt); int_val.has_value()) {
|
||||
std::cout << "Integer value: " << int_val.value() << std::endl;
|
||||
}
|
||||
|
||||
// Check and get float option
|
||||
if (auto float_val = parser.get_value_option<FloatValidator>(float_opt); float_val.has_value()) {
|
||||
std::cout << "Float value: " << float_val.value() << std::endl;
|
||||
}
|
||||
|
||||
// Check and get string option
|
||||
if (auto str_val = parser.get_value_option<StringValidator>(string_opt); str_val.has_value()) {
|
||||
std::cout << "String value: " << str_val.value() << std::endl;
|
||||
}
|
||||
|
||||
// Check flag option
|
||||
if (auto flag_val = parser.get_flag_option(flag_opt); flag_val.has_value() && flag_val.value()) {
|
||||
std::cout << "Verbose mode enabled" << std::endl;
|
||||
}
|
||||
} else {
|
||||
// Print help if parsing failed
|
||||
manual.print_help(std::cout);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
This code handles command lines like:
|
||||
\code{.sh}
|
||||
./myapp -i 123 -f 2.5 --string "hello world" -v
|
||||
\endcode
|
||||
|
||||
\section clap__components Components
|
||||
|
||||
\subsection clap__application Application Definition
|
||||
|
||||
The [Application](\ref application::Application) class represents a complete command line application with its summary, options, and environment variables.
|
||||
It combines the application metadata, command line options, and environment variables into a single unit.
|
||||
|
||||
\subsection clap__options Options
|
||||
|
||||
[Option](\ref option::Option) is command line arguments that can accept values or act as flags.
|
||||
They can have both short names (single character)
|
||||
and long names (full text). The [OptionCollection](\ref option::OptionCollection) manages a collection of options and ensures no duplicates.
|
||||
|
||||
\subsection clap__variables Variables
|
||||
|
||||
[Variable](\ref variable::Variable) represent environment variables that can be captured and validated. The [VariableCollection](\ref variable::VariableCollection)
|
||||
manages a collection of environment variables and ensures no duplicates.
|
||||
|
||||
\subsection clap__parsing Parsing
|
||||
|
||||
The [Parser](\ref parser::Parser) class handles command line argument parsing. It can be created from user-provided arguments
|
||||
or from system arguments (argc/argv). Values are retrieved using type-safe validators.
|
||||
|
||||
\subsection clap__validation Validation
|
||||
|
||||
Validators ensure type-safe validation of command line argument values.
|
||||
The module provides built-in validators for:
|
||||
|
||||
\li Integral types ([IntegralValidator](\ref validator::IntegralValidator))
|
||||
\li Floating-point types ([FloatingPointValidator](\ref validator::FloatingPointValidator))
|
||||
\li String types ([StringValidator](\ref validator::StringValidator))
|
||||
|
||||
For some of them, you also can specify value range via template arguments.
|
||||
|
||||
\section clap__custom_validators Custom Validator
|
||||
|
||||
Custom validators can be created by implementing the \c Validator concept.
|
||||
A valid validator must satisfy the following requirements:
|
||||
|
||||
\li Have a type alias called \c ReturnType indicating the return value type
|
||||
\li Have a member function called \c validate that receives <TT>const std::u8string_view&</TT> as its only argument
|
||||
and returns validated \c ReturnType or \c std::nullopt if validation fails
|
||||
|
||||
Here is an example of a custom validator that validates email addresses:
|
||||
|
||||
\code{.cpp}
|
||||
#include <yycc/string/reinterpret.hpp>
|
||||
#include <regex>
|
||||
|
||||
struct EmailValidator {
|
||||
using ReturnType = std::u8string;
|
||||
|
||||
std::optional<ReturnType> validate(const std::u8string_view& sv) const {
|
||||
// Simple email validation using regex
|
||||
static const std::regex email_regex(
|
||||
R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
|
||||
|
||||
auto email_str = yycc::string::reinterpret::as_ordinary_view(sv);
|
||||
if (std::regex_match(email_str, email_regex)) {
|
||||
return sv;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
\endcode
|
||||
|
||||
To use the custom validator:
|
||||
|
||||
\code{.cpp}
|
||||
// Add option to application
|
||||
auto email_opt = options.add_option(option::Option(std::nullopt, u8"email", u8"EMAIL", u8"Email address"));
|
||||
|
||||
// Use custom validator
|
||||
if (auto email_val = parser.get_value_option<EmailValidator>(email_opt); email_val.has_value()) {
|
||||
std::cout << yycc::patch::format(u8"Valid email: {}", email_val); << std::endl;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section clap__limitations Limitations
|
||||
|
||||
Due to the limitations of implementation,
|
||||
CLAP now only allow only zero or one associated value for single option.
|
||||
More than one assocciated value for single option is not supported.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
namespace YYCC::ConfigManager {
|
||||
/**
|
||||
|
||||
\page config_manager Universal Config Manager
|
||||
|
||||
YYCC::ConfigManager give programmer an universal way to manage its program settings.
|
||||
There is an example about how to use universal config manager.
|
||||
In following content, we will describe it in detail.
|
||||
|
||||
\code
|
||||
class TestConfigManager {
|
||||
public:
|
||||
enum class TestEnum : int8_t {
|
||||
Test1, Test2, Test3
|
||||
};
|
||||
|
||||
TestConfigManager() :
|
||||
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
|
||||
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
|
||||
m_FloatSetting(YYCC_U8("float-setting"), 0.0f, YYCC::Constraints::GetNumberRangeConstraint<float>(-1.0f, 1.0f)),
|
||||
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
|
||||
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
||||
&m_IntSetting, &m_StringSetting, &m_FloatSetting, &m_EnumSetting
|
||||
})
|
||||
{}
|
||||
~TestConfigManager() {}
|
||||
|
||||
YYCC::ConfigManager::NumberSetting<int32_t> m_IntSetting;
|
||||
YYCC::ConfigManager::StringSetting m_StringSetting;
|
||||
YYCC::ConfigManager::NumberSetting<float> m_FloatSetting;
|
||||
YYCC::ConfigManager::NumberSetting<TestEnum> m_EnumSetting;
|
||||
|
||||
YYCC::ConfigManager::CoreManager m_CoreManager;
|
||||
};
|
||||
|
||||
// Initialize config manager
|
||||
TestConfigManager test;
|
||||
// Load settings.
|
||||
test.m_CoreManager.Load()
|
||||
// Get string setting value.
|
||||
auto val = test.m_StringSetting.Get();
|
||||
\endcode
|
||||
|
||||
\section config_manager__setting Setting
|
||||
|
||||
Setting can be seen as the leaf of the config tree.
|
||||
Each setting describe a single configuration entry.
|
||||
|
||||
\subsection config_manager__setting__presets Setting Presets
|
||||
|
||||
We currently provide 2 setting preset classes which you can directly use.
|
||||
|
||||
\li NumberSetting: The setting storing a number inside.
|
||||
It is a template class. Support all arithmetic and enum types (integral, floating point, bool, enum).
|
||||
\li StringSetting: The setting storing a string inside.
|
||||
|
||||
When constructing these settings,
|
||||
you need to provide its unique name which will be used when saving to file or reading from file.
|
||||
Also you need to provide a default value for it.
|
||||
It will be used when fail to read file or initializing itself.
|
||||
|
||||
Optionally, you also can provide a constraint to setting.
|
||||
Constraint is the struct instructing library to limit value in specified range.
|
||||
It usually is used for making sure the setting stored value is valid.
|
||||
See \ref constraints chapters to know how we provide constraints.
|
||||
|
||||
\subsection config_manager__setting__custom Custom Setting
|
||||
|
||||
In most cases, the combination use of setting presets and constraints is enough.
|
||||
However, if you still are urge to create your personal setting,
|
||||
please inherit AbstractSetting and implement essential class functions.
|
||||
For the class functions you need to implement,
|
||||
please refer to our setting presets, NumberSetting and StringSetting.
|
||||
|
||||
\section config_manager__core_manager Core Manager
|
||||
|
||||
CoreManager manage a collection of settings.
|
||||
And have responsibility to reading and writing config file.
|
||||
|
||||
We highly suggest that you create a personal config manager class like example does.
|
||||
Then put essential settings and core manager inside it.
|
||||
Please note you must place core manager after all settings.
|
||||
Because the order of C++ initializing its class member is the order you declared them.
|
||||
The constructor of core manager need the pointer to all it managed settings,
|
||||
so it must be initialized after initializing all settings.
|
||||
|
||||
When initializing core manager, you need assign config file path first.
|
||||
Then you need specify a version number.
|
||||
Version number is important.
|
||||
It will be used when reading config file and only can be increased if needed (version can not downgrade).
|
||||
The last argument is an initializer list which contain the \b pointer to all settings this manager managed.
|
||||
|
||||
When executing YYCC::ConfigManager::CoreManager::Load to load configs, it will perform following steps one by one:
|
||||
|
||||
<UL>
|
||||
<LI>
|
||||
Open given config file.
|
||||
<UL>
|
||||
<LI>
|
||||
If given file is not existing, loading function will simply return and all configs will be reset to its default value.
|
||||
</LI>
|
||||
<LI>
|
||||
Success to open file, go to next step.
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
|
||||
<LI>
|
||||
Fetch version number from file.
|
||||
<UL>
|
||||
<LI>
|
||||
If fail to read version number from file, loading function will simply return and all configs will be reset to its default value.
|
||||
</LI>
|
||||
<LI>
|
||||
If the version of config file is higher than your specified version number when constructing this class,
|
||||
core manager will assume you are trying to read a config file created by a higher version program,
|
||||
and will reject reading and use default value for all settings.
|
||||
</LI>
|
||||
<LI>
|
||||
If the version of config file is lower than your specified version number,
|
||||
core manager will try to read config file and do proper migration (set default value for configs which do not existing) if possible.
|
||||
</LI>
|
||||
<LI>
|
||||
If the version of config file is equal than your specified version number,
|
||||
core manager will read config file normally.
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
|
||||
<LI>
|
||||
Read config file body.
|
||||
<UL>
|
||||
<LI>
|
||||
If any IO error occurs when reading, loading function will simply return.
|
||||
All read config will keep their read value and all configs which has not been read will keep their default value.
|
||||
</LI>
|
||||
<LI>
|
||||
If some config can not parse binary data to its type,
|
||||
this config will be skipped and core manager will process next config.
|
||||
This config will keep its default value.
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
All of these scenarios can be found by the return value of loading function.
|
||||
The return type of loading function, ConfigLoadResult is a flag enum.
|
||||
You can find whether loading process happend specified issue by using bitwise operation on it.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,30 +1,30 @@
|
||||
namespace YYCC::ExceptionHelper {
|
||||
namespace yycc::carton::ironpad {
|
||||
/**
|
||||
|
||||
\page exception_helper Unhandled Exception Handler
|
||||
\page ironpad Unhandled Exception Handler
|
||||
|
||||
Most Linux users are familiar with core dump.
|
||||
However core dump is a tough work on Windows especially most Windows users are naive for getting core dump.
|
||||
So it is essential to make an easy-to-visit core dump Feature for Windows program.
|
||||
YYCC provides this feature in YYCC::ExceptionHelper.
|
||||
Most Linux users are familiar with using core dump to find bugs.
|
||||
However finding bugs is a tough work on Windows especially most Windows users are naive for getting core dump.
|
||||
So it is essential to make an easy-to-visit core dump feature for Windows program.
|
||||
This is the reason why I create this module, yycc::carton::ironpad.
|
||||
|
||||
You may know Google also has a similar and universal project called Crashpad used by Google Chrome.
|
||||
That's right. But it is too heavy.
|
||||
I just want to implement a tiny but worked core dump feature on Windows.
|
||||
|
||||
This module is Windows specific.
|
||||
It will be invisible on other platforms.
|
||||
It still be available on other operating systems but all of its functions are do nothing.
|
||||
|
||||
\section exception_helper__usage Usage
|
||||
\section ironpad__usage Usage
|
||||
|
||||
\subsection exception_helper__usage__code Register Code
|
||||
\subsection ironpad__usage__code Register Code
|
||||
|
||||
In most scenarios, programmer only need call #Register when program started or module loaded.
|
||||
And call #Unregister when program exited or module unloaded.
|
||||
In most scenarios, programmer only need call #startup when program started or module loaded.
|
||||
And call #shutdown when program exited or module unloaded.
|
||||
All details are hidden by these 2 feature.
|
||||
Programmer do not need worried about the implementation of unhandled exception handler.
|
||||
|
||||
Optionally, you can provide a function pointer during calling #Register as a callback.
|
||||
Optionally, you can provide a function pointer during calling #startup as a callback.
|
||||
The prototype of this function pointer is #ExceptionCallback.
|
||||
This callback will be called if any unhandled exception happened.
|
||||
It provides 2 pathes to log file and core dump file respectively.
|
||||
@@ -35,21 +35,21 @@ However, please note the pathes provided by callback may be empty.
|
||||
In this case, it means that handler fail to create corresponding log files.
|
||||
Also, if you trying to register unhandled exception handler on the same process in different module with different callback,
|
||||
only the callback provided in first success registering will be called when unhandled exception happened,
|
||||
due to \ref exception_helper__notes__singleton design.
|
||||
due to \ref ironpad__notes__singleton design.
|
||||
|
||||
\subsection exception_helper__usage__location Location
|
||||
\subsection ironpad__usage__location Location
|
||||
|
||||
When unhandled exception occurs,
|
||||
unhandled exception handler will try to record error log and core dump in following path:
|
||||
|
||||
\li Error Log: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.log</TT>
|
||||
\li Core Dump: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
|
||||
\li Error Log: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.log</TT>
|
||||
\li Core Dump: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
|
||||
|
||||
The italic characters <I>program.exe</I> and <I>pid</I> will be replaced by program name and process ID respectively at runtime.
|
||||
Directory <TT>\%LOCALAPPDATA\%\\CrashDumps</TT> also is Windows used crash dump directory.
|
||||
So you may see some other core dumps done by Windows in it.
|
||||
Directory <TT>\%LOCALAPPDATA\%\\IronPad</TT> is the dedicated directory for this module.
|
||||
So you may see the generated logs and dumps in it.
|
||||
|
||||
\subsection exception_helper__usage__last_remedy Last Remedy
|
||||
\subsection ironpad__usage__last_remedy Last Remedy
|
||||
|
||||
If unhandled exception handler occurs error, these stuff may not be generated correctly.
|
||||
The end user may not find them and send them to you.
|
||||
@@ -65,40 +65,40 @@ Also please note the last remedy may still have a little bit possibility to occu
|
||||
especially the error occurs in back trace function.
|
||||
There is no guaranteen that unhandled exception handler must generate error log and core dump.
|
||||
|
||||
\section exception_helper__notes Notes
|
||||
\section ironpad__notes Notes
|
||||
|
||||
\subsection exception_helper__notes__thread_safe Thread Safe
|
||||
\subsection ironpad__notes__thread_safe Thread Safe
|
||||
|
||||
All exposed functions in YYCC::ExceptionHelper are thread safe.
|
||||
The implementation uses \c std:mutex to ensure this.
|
||||
All exposed functions in this namespace are thread safe.
|
||||
The implementation uses \c std::mutex to ensure this.
|
||||
|
||||
\subsection exception_helper__notes__singleton Singleton Handler
|
||||
\subsection ironpad__notes__singleton Singleton Handler
|
||||
|
||||
YYCC::ExceptionHelper also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
|
||||
This namespace also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
|
||||
For example, you have an executable program A.exe, and 2 dynamic libraries B.dll and C.dll.
|
||||
A.exe and B.dll use YYCC unhandled exception handler feature but C.dll not.
|
||||
A.exe will load B.dll and C.dll at runtime.
|
||||
Although both A.exe and B.dll call #Register,
|
||||
Although both A.exe and B.dll call #startup,
|
||||
when unhandled exception occurs, there is only one error report output,
|
||||
which may be generated by A.exe or B.dll accoridng to their order of loading.
|
||||
|
||||
The core purpose of this is making sure the program will not output too many error report for the same unhandled exception,
|
||||
no matter how many modules calling #Register are loaded.
|
||||
no matter how many modules calling #startup are loaded.
|
||||
Only one error report is enough.
|
||||
|
||||
More precisely, we use \c CreateMutexW to create an unique mutex in Windows global scope,
|
||||
to make sure #Register only run once in the same process.
|
||||
to make sure #startup only run once in the same process.
|
||||
It is very like the implementation of singleton application.
|
||||
|
||||
\subsection exception_helper__notes__recursive_calling Recursive Calling
|
||||
\subsection ironpad__notes__recursive_calling Recursive Calling
|
||||
|
||||
The implementation of unhandled exception handler may also will throw exception.
|
||||
This will cause infinite recursive calling.
|
||||
YYCC::ExceptionHelper has internal mechanism to prevent this bad case.
|
||||
This namespace has internal mechanism to prevent this bad case.
|
||||
If this really happened, the handler will quit silent and will not cause any issue.
|
||||
Programmer don't need to worry about this.
|
||||
|
||||
\subsection exception_helper__notes__user_callback The Timing of User Callback
|
||||
\subsection ironpad__notes__user_callback The Timing of User Callback
|
||||
|
||||
The timing of calling user callback is the tail of unhandled exception handler.
|
||||
It means that all log and coredump have been written if possible before calling callback.
|
||||
@@ -64,19 +64,11 @@
|
||||
|
||||
\li \subpage csconsole
|
||||
|
||||
<!--
|
||||
\li \subpage ironpad
|
||||
|
||||
\li \subpage constraints
|
||||
\li \subpage clap
|
||||
|
||||
\li \subpage encoding_helper
|
||||
|
||||
\li \subpage config_manager
|
||||
|
||||
\li \subpage arg_parser
|
||||
|
||||
\li \subpage exception_helper
|
||||
|
||||
-->
|
||||
\li \subpage binstore
|
||||
|
||||
<B>Windows Specific Features</B>
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ std::vector<std::u8string> split_owned(const std::u8string_view& strl, const std
|
||||
All these overloads take a string view as the first argument representing the string need to be split.
|
||||
The second argument is a string view representing the delimiter for splitting.
|
||||
|
||||
The first function #lazy_split returns a #LazySplit object that can be used in range-based for loops.
|
||||
The first function #lazy_split returns a LazySplit object that can be used in range-based for loops.
|
||||
This is lazy-computed and memory-efficient for large datasets.
|
||||
The second function #split returns a vector of string views, which is memory-efficient
|
||||
but the views are only valid as long as the original string remains valid.
|
||||
|
||||
@@ -133,10 +133,10 @@ auto result4 = open_folder(params);
|
||||
|
||||
There are 4 file dialogs you can choose:
|
||||
|
||||
\li #open_file: Open single file
|
||||
\li #open_files: Open multiple files
|
||||
\li #save_file: Save single file
|
||||
\li #open_folder: Open single directory
|
||||
\li open_file(): Open single file
|
||||
\li open_files(): Open multiple files
|
||||
\li save_file(): Save single file
|
||||
\li open_folder(): Open single directory
|
||||
|
||||
\subsection windows__dialog__result__arguments Arguments
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* \li https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
||||
* \li https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
||||
*
|
||||
* For how to utilize this functions provided by this namespace, please view \ref console_helper.
|
||||
* For how to utilize this functions provided by this namespace, please view \ref csconsole.
|
||||
*
|
||||
* @warning
|
||||
* All functions provided by this namespace are too aggressive.
|
||||
|
||||
@@ -248,7 +248,7 @@ namespace yycc::carton::fft {
|
||||
* @param[out] freq_scope The length of this data must be N / 2.
|
||||
* The first data is 0Hz and the frequency of last data is decided by sample rate which can be computed by get_max_freq() function in this class.
|
||||
* @param[in] window The window instance applied to data.
|
||||
* @warnings
|
||||
* @warning
|
||||
* This function is \b NOT thread-safe.
|
||||
* Please do NOT call this function in different thread for one instance.
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @details
|
||||
* This namespace is currently works on Windows.
|
||||
* On other platforms, this namespace provided functions do nothing.
|
||||
* For how to utilize this namespace, please see \ref exception_helper.
|
||||
* For how to utilize this namespace, please see \ref ironpad.
|
||||
*
|
||||
* This feature is originate from my created Virtools plugin.
|
||||
* Because its user frequently trigger some weird behaviors but I have no idea about the detail of them.
|
||||
|
||||
Reference in New Issue
Block a user