refactor: add document for some namespaces
This commit is contained in:
parent
adc99274f4
commit
a6382d6a22
@ -6,36 +6,37 @@
|
||||
#include <format>
|
||||
#endif
|
||||
|
||||
/// @brief
|
||||
/**
|
||||
* @brief Provides Rust-style panic functionality for immediate program termination on unrecoverable errors.
|
||||
* @details
|
||||
* This namespace provides macros and functions to handle unrecoverable errors in C++ code.
|
||||
* It imitate Rust's \c panic! macro behavior, allowing the program to immediately exit with error information and stack traces.
|
||||
*
|
||||
* After writing programs in Rust, I deeply realized the necessity of handling errors immediately.
|
||||
* When encountering unrecoverable errors, the program should exit immediately and report the error, which ensures program robustness.
|
||||
* Therefore, I introduced this namespace and implemented macros and functions equivalent to Rust's \c panic! macro.
|
||||
*
|
||||
* Unfortunately, I cannot change the exception mechanism in the standard library.
|
||||
* The standard library will still throw exceptions where it does, and I cannot prevent that.
|
||||
* Therefore, I suggest a good practice that any C++ exception should be immediately treated as an error and cause the program to crash and exit.
|
||||
* For this reason, registering any unhandled error callbacks which may resume the execution of program is prohibited to prevent unexpected continuation of execution.
|
||||
* For code we write ourselves that we can control, we should use the macros provided in this file instead of throwing exceptions.
|
||||
* In this way, unexpected behavior in our code will cause the program to exit immediately, outputting error information and stack traces.
|
||||
* Standard library exceptions will also cause the program to exit, but without stack information.
|
||||
*/
|
||||
namespace yycc::rust::panic {
|
||||
|
||||
// TODO: Move these comments into Doxygen comments of this namespace.
|
||||
|
||||
// YYC MARK:
|
||||
// 在使用Rust编写程序后,我深刻地认识到,将错误立即进行处理的必要性。
|
||||
// 而在陷入不可恢复错误后,也应当立即退出程序并报告错误,这是对程序健壮性的保证。
|
||||
// 因此我引入了这个命名空间,并带来了与Rust中panic!宏等效的宏和函数。
|
||||
|
||||
// YYC MARK:
|
||||
// 遗憾的是,我无法改变标准库中的异常机制。
|
||||
// 标准库中会抛出异常的内容仍然会抛出异常,我不能阻止它们。
|
||||
// 因此我在这里规定,在Stardust项目中,任何C++异常都应立即被视为错误,并导致程序崩溃退出。
|
||||
// 也正因为于此,规定不允许注册任何未处理错误的回调,以防止意料之外的继续运行。
|
||||
// 而对于我们自己编写的,能控制的代码,则应用本文件中提供的宏来代替异常抛出。
|
||||
// 这样一来,我们代码中的非预期行为会使得程序立即退出,并输出错误信息和堆栈。
|
||||
// 而标准库中的异常,也会使得程序退出,只不过没有堆栈信息罢了。
|
||||
|
||||
/**
|
||||
* @brief 像Rust的panic!宏一样立即使整个程序崩溃。
|
||||
* @details 宏参数为需要附加的提示信息。
|
||||
* @brief Immediately crashes the entire program like Rust's \c panic! macro.
|
||||
* @details The macro parameter is the additional message to display.
|
||||
*/
|
||||
#define RS_PANIC(msg) ::yycc::rust::panic::panic(__FILE__, __LINE__, (msg))
|
||||
|
||||
/**
|
||||
* @brief 像Rust的panic!宏一样立即使整个程序崩溃。
|
||||
* @brief Immediately crashes the entire program like Rust's \c panic! macro.
|
||||
* @details
|
||||
* 宏参数为需要格式化的信息和参数,格式和参数请参考std::format。
|
||||
* 因为本质上这个宏就是调用std::format来实现的。
|
||||
* The macro parameters are the message to format and its arguments, following \c std::format syntax.
|
||||
* This macro essentially calls \c std::format internally.
|
||||
*/
|
||||
#if defined(YYCC_CPPFEAT_FORMAT)
|
||||
#if defined(YYCC_CPPFEAT_VA_OPT)
|
||||
@ -46,13 +47,13 @@ namespace yycc::rust::panic {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 像Rust的panic!宏一样立即使整个程序崩溃。
|
||||
* @brief Immediately crashes the entire program like Rust's \c panic! macro.
|
||||
* @details
|
||||
* 此函数是宏所调用的真正的崩溃输出函数。
|
||||
* 崩溃信息将被写入到stderr中,信息中还包含堆栈信息。
|
||||
* @param[in] file Panic时的源代码文件。该参数一般由宏代为填写。
|
||||
* @param[in] line Panic时的源代码文件中的行号。该参数一般由宏代为填写。
|
||||
* @param[in] msg Panic时需要显示的提示信息。
|
||||
* This is the actual crash output function called by the macros.
|
||||
* The crash information will be written to \c stderr, including stack traces if your C++ runtime support it.
|
||||
* @param[in] file Source file name where panic occurred. Usually filled by macros.
|
||||
* @param[in] line Line number in source file where panic occurred. Usually filled by macros.
|
||||
* @param[in] msg Message to display during panic.
|
||||
*/
|
||||
[[noreturn]] void panic(const char* file, int line, const std::string_view& msg);
|
||||
|
||||
|
@ -5,34 +5,56 @@
|
||||
#include <expected>
|
||||
#endif
|
||||
|
||||
/// @brief The reproduction of Rust Option type.
|
||||
/// @details
|
||||
/// After writing a program in Rust, I deeply realized the advantages of Rust and its indispensable infrastructure, \c Result.
|
||||
/// Therefore, it is essential to introduce Result into C++ to strengthen the security of the code.
|
||||
/// I simulated Result in C++ as best I could, and its members Ok and Err (which are actually done by DeepSeek).
|
||||
///
|
||||
/// Why not write it the C++ way? Because the way C++ uses Result is too ugly and not explicit enough.
|
||||
/// In the way C++ is written, the expected value is gotten by returning directly,
|
||||
/// and if you encounter void specialization, you must write a pair of curly braces, which is very unclear.
|
||||
/// For unexpected value, you need to manually build it by calling \c std::unexpected, which is more of a pain.
|
||||
/// If you need to construct an unexpected value in place, you even need to put a \c std::in_place as the first argument to the constructor,
|
||||
/// Otherwise, the std::unexpected constructor will not forward the given arguments to the underlying constructor.
|
||||
///
|
||||
///
|
||||
/// @remarks
|
||||
/// This namespace only work with environment supporting `std::expected` (i.e. C++ 23).
|
||||
/**
|
||||
* @brief The reproduction of Rust Option type.
|
||||
* @details
|
||||
* After writing programs in Rust, I deeply recognized the advantages of Rust and its indispensable infrastructure Result.
|
||||
* Therefore, introducing Result into C++ to enhance coding safety is essential.
|
||||
* I've done my best to simulate Rust's \c Result and its members \c Ok and \c Err (actually, I had DeepSeek simulate them).
|
||||
*
|
||||
* Why not write it in C++ style? Because C++'s way of using \c Result is too ugly and not explicit enough.
|
||||
* In C++'s approach, the expected value is returned directly,
|
||||
* and when encountering void specialization, you must write a pair of curly braces, which is very unclear.
|
||||
* For unexpected values, you need to manually construct \c std::unexpected, which is even more painful.
|
||||
* If you need in-place construction of unexpected values, you even need to put \c std::in_place as the first parameter of the constructor,
|
||||
* otherwise \c std::unexpected 's constructor won't forward the subsequent parameters to the unexpected value's constructor.
|
||||
*
|
||||
* In the \c Result type, type \c E can be any value according to your needs.
|
||||
* In Rust, an unexpected value type \c Ea can be converted to another unexpected value type \c Eb.
|
||||
* This feature is implemented through the \c From trait, allowing you to safely wrap one type of unexpected value into another in a function.
|
||||
* But in C++, we have C++ ways to do the same thing.
|
||||
* Assuming for each type \c E, we define a separate struct to describe them,
|
||||
* then we just need to add some extra constructors to the struct to convert them from one type to another.
|
||||
*
|
||||
* For example, type \c Ea is a struct named \c IoError.
|
||||
* In this struct, there is a member of type \c IoErrorKind indicating the category of this IO error.
|
||||
* At the same time, it has a constructor with its own type as the only parameter, used to construct (copy or move) itself.
|
||||
* Now in a function, we want to convert it to another type \c Eb named \c SystemError .
|
||||
* All you need to do is create a new struct named \c SystemError, then write all necessary constructors and other functions for it.
|
||||
* Then, the key point is to add a constructor with parameter type <TT>const IoError&</TT>.
|
||||
* This way, we can simply convert type \c Ea to type \c Eb through calls like: <TT>Err<Result<T, E>>(result.error());</TT>.
|
||||
*
|
||||
* In Rust, if you want to get human-readable descriptions of unexpected values, you must implement the \c Display trait.
|
||||
* But you don't need to do this in C++, you must write your own conversion functions to adapt to various output requirements.
|
||||
* For example, when using \c std::format, you need to write suitable formatting adapters for \c std::format.
|
||||
* Similarly, when using \c std::cerr 's \c operator<< overload, you also need to write suitable adapters.
|
||||
* @remarks This namespace only work with environment supporting `std::expected` (i.e. C++ 23).
|
||||
*/
|
||||
namespace yycc::rust::result {
|
||||
|
||||
#if defined(YYCC_CPPFEAT_EXPECTED)
|
||||
|
||||
/**
|
||||
* @brief Equivalent Rust \c Result in C++
|
||||
* @tparam T The type of the expected value.
|
||||
* @tparam E The type of the unexpected value.
|
||||
*/
|
||||
template<typename T, typename E>
|
||||
using Result = std::expected<T, E>;
|
||||
|
||||
/**
|
||||
* @brief Equvialent Rust \c Result::Ok in C++
|
||||
* @brief Equvialent Rust \c Result::Ok in C++.
|
||||
* @tparam ResultType The type of the Result instance.
|
||||
* @param[in] args The arguments for building expected value.
|
||||
* @return An built Result instance with expected value.
|
||||
*/
|
||||
@ -48,7 +70,8 @@ namespace yycc::rust::result {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equvialent Rust \c Result::Err in C++
|
||||
* @brief Equvialent Rust \c Result::Err in C++.
|
||||
* @tparam ResultType The type of the Result instance.
|
||||
* @param[in] args The arguments for building unexpected value.
|
||||
* @return An built Result instance with unexpected value.
|
||||
*/
|
||||
@ -57,30 +80,6 @@ namespace yycc::rust::result {
|
||||
return ResultType(std::unexpect, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// TODO: Move these comments into Doxygen comments of this namespace.
|
||||
|
||||
// YYC MARK:
|
||||
// Result类型中,类型E可以是根据你需求的任意值。
|
||||
// 在Rust中,一个非期望值类型Ea可以被转换为另一个非期望值类型Eb。
|
||||
// 这一特性通过From trait来实现。这样你就可以在一个函数中安全地将一种非期望值包装成另一种非期望值。
|
||||
// 但在C++中,我们有C++的方式来做同样的事。
|
||||
// 假设对于每一个类型E,我们都分别定义一个struct来描述它们,
|
||||
// 那么我们只需要为struct添加一些额外的构造函数,就可以将它们从一个类型转换到另一个。
|
||||
// ---
|
||||
// 例如,类型Ea是一个名为IoError的struct。
|
||||
// 在这个struct中,有一个类型为IoErrorKind的成员,指示了该IO错误的类别。
|
||||
// 与此同时,它还有一个以自己类型为唯一参数的构造函数,用于构建(复制或移动)它自己。
|
||||
// 现在在一个函数中。我们希望将它转换为另一个名为SystemError的类型Eb。
|
||||
// 你需要做的仅仅是新建一个名为SystemError的struct,然后为它编写所有必要的构造函数和其它函数。
|
||||
// 然后,重点是,为它添加一个形参类型为`const IoError&`的构造函数。
|
||||
// 这样一来,我们就可以通过类似这样的调用:`Err<Result<T, E>>(result.error());`,简单地将类型Ea转换为类型Eb。
|
||||
|
||||
// YYC MARK:
|
||||
// 在Rust中,如果你想获得非预期值的人类可读说明,你就必须要实现名为Display的trait。
|
||||
// 而你不需要在C++中这样做,你必须编写自己的转换函数,以适应各种输出需求。
|
||||
// 例如,在使用std::format时,你需要编写适合std::format的格式化适配器。
|
||||
// 又如,你在使用std::cerr的operator<<重载时,你也需要编写合适的适配器。
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -11,11 +11,15 @@
|
||||
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
||||
#define NS_YYCC_STRING_OP ::yycc::string::op
|
||||
|
||||
/**
|
||||
* @brief Provides string parsing utilities for converting strings to numeric and boolean values.
|
||||
* @details
|
||||
* This namespace contains functions for parsing strings into various numeric types (integer, floating point)
|
||||
* and boolean values. It uses \c std::from_chars internally for efficient parsing.
|
||||
* @remarks See https://zh.cppreference.com/w/cpp/utility/from_chars for underlying called functions.
|
||||
*/
|
||||
namespace yycc::string::parse {
|
||||
|
||||
// Developer Notes:
|
||||
// Reference: https://zh.cppreference.com/w/cpp/utility/from_chars
|
||||
|
||||
/// @private
|
||||
/// @brief The error kind when parsing string into number.
|
||||
enum class ParseError {
|
||||
@ -31,10 +35,11 @@ namespace yycc::string::parse {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief priv_parse
|
||||
* @param strl
|
||||
* @param fmt
|
||||
* @return
|
||||
* @brief Internal parsing function for floating point types
|
||||
* @tparam T Floating point type (float, double, etc)
|
||||
* @param strl The UTF-8 string view to parse
|
||||
* @param fmt The floating point format to use
|
||||
* @return ParseResult<T> containing either the parsed value or a ParseError
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl, std::chars_format fmt) {
|
||||
@ -64,10 +69,11 @@ namespace yycc::string::parse {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief priv_parse
|
||||
* @param strl
|
||||
* @param base
|
||||
* @return
|
||||
* @brief Internal parsing function for integral types (except bool)
|
||||
* @tparam T Integral type (int, long, etc)
|
||||
* @param strl The UTF-8 string view to parse
|
||||
* @param base Numeric base (2-36)
|
||||
* @return ParseResult<T> containing either the parsed value or a ParseError
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl, int base) {
|
||||
@ -97,9 +103,10 @@ namespace yycc::string::parse {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief priv_parse
|
||||
* @param strl
|
||||
* @return
|
||||
* @brief Internal parsing function for boolean type
|
||||
* @tparam T Must be bool type
|
||||
* @param strl The UTF-8 string view to parse ("true" or "false", case insensitive)
|
||||
* @return ParseResult<bool> containing either the parsed value or a ParseError
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
|
||||
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl) {
|
||||
|
@ -3,20 +3,67 @@
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
|
||||
/**
|
||||
* @brief Provides utilities for reinterpretation between UTF-8 and ordinary string types.
|
||||
* @details
|
||||
* Please note that there is no encoding convertion happended in this namespace provided functions.
|
||||
* They just simply reinterpret one string to another string.
|
||||
* The validation of UTF8 string is guaranteed by user self.
|
||||
*/
|
||||
namespace yycc::string::reinterpret {
|
||||
|
||||
#define _YYCC_U8(strl) u8 ## strl ///< The assistant macro for YYCC_U8.
|
||||
#define YYCC_U8(strl) (reinterpret_cast<const ::yycc::string::u8char*>(_YYCC_U8(strl))) ///< The macro for creating UTF8 string literal. See \ref library_encoding.
|
||||
#define YYCC_U8_CHAR(chr) (static_cast<::yycc::string::u8char>(chr)) ///< The macro for casting ordinary char type into YYCC UTF8 char type.
|
||||
|
||||
/**
|
||||
* @brief Reinterpret ordinary C-string to UTF-8 string (const version).
|
||||
* @param src Source ordinary string
|
||||
* @return Pointer to UTF-8 encoded string
|
||||
*/
|
||||
const NS_YYCC_STRING::u8char* as_utf8(const char* src);
|
||||
/**
|
||||
* @brief Reinterpret ordinary C-string as an UTF-8 string (non-const version).
|
||||
* @param src Source ordinary string
|
||||
* @return Pointer to UTF-8 encoded string
|
||||
*/
|
||||
NS_YYCC_STRING::u8char* as_utf8(char* src);
|
||||
/**
|
||||
* @brief Reinterpret ordinary string view to copied UTF-8 string.
|
||||
* @param src Source ordinary string view
|
||||
* @return UTF-8 encoded string
|
||||
*/
|
||||
NS_YYCC_STRING::u8string as_utf8(const std::string_view& src);
|
||||
/**
|
||||
* @brief Reinterpret ordinary string view to UTF-8 string view.
|
||||
* @param src Source ordinary string view
|
||||
* @return UTF-8 encoded string view
|
||||
*/
|
||||
NS_YYCC_STRING::u8string_view as_utf8_view(const std::string_view& src);
|
||||
|
||||
/**
|
||||
* @brief Reinterpret UTF-8 C-string to ordinary string (const version).
|
||||
* @param src Source UTF-8 string
|
||||
* @return Pointer to ordinary string
|
||||
*/
|
||||
const char* as_ordinary(const NS_YYCC_STRING::u8char* src);
|
||||
/**
|
||||
* @brief Reinterpret UTF-8 C-string to ordinary string (non-const version).
|
||||
* @param src Source UTF-8 string
|
||||
* @return Pointer to ordinary string
|
||||
*/
|
||||
char* as_ordinary(NS_YYCC_STRING::u8char* src);
|
||||
/**
|
||||
* @brief Reinterpret UTF-8 string view to ordinary string.
|
||||
* @param src Source UTF-8 string view
|
||||
* @return Ordinary string
|
||||
*/
|
||||
std::string as_ordinary(const NS_YYCC_STRING::u8string_view& src);
|
||||
/**
|
||||
* @brief Reinterpret UTF-8 string view to ordinary string view
|
||||
* @param src Source UTF-8 string view
|
||||
* @return Ordinary string view
|
||||
*/
|
||||
std::string_view as_ordinary_view(const NS_YYCC_STRING::u8string_view& src);
|
||||
|
||||
}
|
||||
|
@ -9,15 +9,22 @@
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
||||
|
||||
/**
|
||||
* @brief Provides stringify utilities for converting numeric and boolean values to strings.
|
||||
* @details
|
||||
* This namespace contains functions for stringifying various numeric types (integer, floating point)
|
||||
* and boolean values into string. It uses \c std::to_chars internally for efficient stringify.
|
||||
* @remarks
|
||||
* See https://en.cppreference.com/w/cpp/utility/to_chars for underlying called functions.
|
||||
* Default float precision = 6 is gotten from: https://en.cppreference.com/w/c/io/fprintf
|
||||
*/
|
||||
namespace yycc::string::stringify {
|
||||
|
||||
// Developer Notes:
|
||||
// Reference: https://en.cppreference.com/w/cpp/utility/to_chars
|
||||
// Default float precision = 6 is gotten from: https://en.cppreference.com/w/c/io/fprintf
|
||||
|
||||
/// @private
|
||||
/// @brief Size of the internal buffer used for string conversion.
|
||||
inline constexpr size_t STRINGIFY_BUFFER_SIZE = 64u;
|
||||
/// @private
|
||||
/// @brief Type alias for the buffer used in string conversion.
|
||||
using StringifyBuffer = std::array<NS_YYCC_STRING::u8char, STRINGIFY_BUFFER_SIZE>;
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user