diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0cbe1b2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,314 @@ +# yaml-language-server: $schema=https://json.schemastore.org/clang-format.json +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: DontAlign +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AttributeMacros: + - __capability +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: All +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: false +BreakTemplateDeclarations: Yes +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - forever + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^:YYCC.natvis> + yycc/string/reinterpret.cpp + yycc/string/op.cpp + # YYCC/COMHelper.cpp + # YYCC/ArgParser.cpp + # YYCC/ConfigManager.cpp + # YYCC/ConsoleHelper.cpp + # YYCC/DialogHelper.cpp + # YYCC/EncodingHelper.cpp + # YYCC/ExceptionHelper.cpp + # YYCC/StdPatch.cpp + # YYCC/IOHelper.cpp + # YYCC/StringHelper.cpp + # YYCC/WinFctHelper.cpp + # # Natvis (only for MSVC) + # $<$:YYCC.natvis> ) target_sources(YYCCommonplace PUBLIC FILE_SET HEADERS FILES # Headers - # Common headers - YYCC/Constraints.hpp - YYCC/COMHelper.hpp - YYCC/ArgParser.hpp - YYCC/ConfigManager.hpp - YYCC/ConsoleHelper.hpp - YYCC/DialogHelper.hpp - YYCC/EncodingHelper.hpp - YYCC/EnumHelper.hpp - YYCC/ExceptionHelper.hpp - YYCC/StdPatch.hpp - YYCC/IOHelper.hpp - YYCC/ParserHelper.hpp - YYCC/StringHelper.hpp - YYCC/WinFctHelper.hpp - # Windows including guard pair - YYCC/WinImportPrefix.hpp - YYCC/WinImportSuffix.hpp - # Internal - YYCC/YYCCVersion.hpp - YYCC/YYCCInternal.hpp - # Exposed - YYCCommonplace.hpp + yycc.hpp + yycc/version.hpp + yycc/prelude/core.hpp + yycc/prelude/rust.hpp + yycc/macro/version_cmp.hpp + yycc/macro/os_detector.hpp + yycc/macro/class_copy_move.hpp + yycc/string.hpp + yycc/string/reinterpret.hpp + yycc/string/op.hpp + yycc/rust/primitive.hpp + yycc/windows/unsafe_suppressor.hpp + yycc/windows/import_guard_head.hpp + yycc/windows/import_guard_tail.hpp + yycc/constraint.hpp + yycc/constraint/builder.hpp + + # # Headers + # # Common headers + # YYCC/Constraints.hpp + # YYCC/COMHelper.hpp + # YYCC/ArgParser.hpp + # YYCC/ConfigManager.hpp + # YYCC/ConsoleHelper.hpp + # YYCC/DialogHelper.hpp + # YYCC/EncodingHelper.hpp + # YYCC/EnumHelper.hpp + # YYCC/ExceptionHelper.hpp + # YYCC/StdPatch.hpp + # YYCC/IOHelper.hpp + # YYCC/ParserHelper.hpp + # YYCC/StringHelper.hpp + # YYCC/WinFctHelper.hpp + # # Windows including guard pair + # YYCC/WinImportPrefix.hpp + # YYCC/WinImportSuffix.hpp + # # Internal + # YYCC/YYCCVersion.hpp + # YYCC/YYCCInternal.hpp + # # Exposed + # YYCCommonplace.hpp ) # Setup header infomations target_include_directories(YYCCommonplace diff --git a/src/YYCC/ArgParser.cpp b/src/YYCCLegacy/ArgParser.cpp similarity index 100% rename from src/YYCC/ArgParser.cpp rename to src/YYCCLegacy/ArgParser.cpp diff --git a/src/YYCC/ArgParser.hpp b/src/YYCCLegacy/ArgParser.hpp similarity index 100% rename from src/YYCC/ArgParser.hpp rename to src/YYCCLegacy/ArgParser.hpp diff --git a/src/YYCC/COMHelper.cpp b/src/YYCCLegacy/COMHelper.cpp similarity index 100% rename from src/YYCC/COMHelper.cpp rename to src/YYCCLegacy/COMHelper.cpp diff --git a/src/YYCC/COMHelper.hpp b/src/YYCCLegacy/COMHelper.hpp similarity index 100% rename from src/YYCC/COMHelper.hpp rename to src/YYCCLegacy/COMHelper.hpp diff --git a/src/YYCC/ConfigManager.cpp b/src/YYCCLegacy/ConfigManager.cpp similarity index 100% rename from src/YYCC/ConfigManager.cpp rename to src/YYCCLegacy/ConfigManager.cpp diff --git a/src/YYCC/ConfigManager.hpp b/src/YYCCLegacy/ConfigManager.hpp similarity index 100% rename from src/YYCC/ConfigManager.hpp rename to src/YYCCLegacy/ConfigManager.hpp diff --git a/src/YYCC/ConsoleHelper.cpp b/src/YYCCLegacy/ConsoleHelper.cpp similarity index 100% rename from src/YYCC/ConsoleHelper.cpp rename to src/YYCCLegacy/ConsoleHelper.cpp diff --git a/src/YYCC/ConsoleHelper.hpp b/src/YYCCLegacy/ConsoleHelper.hpp similarity index 100% rename from src/YYCC/ConsoleHelper.hpp rename to src/YYCCLegacy/ConsoleHelper.hpp diff --git a/src/YYCC/Constraints.hpp b/src/YYCCLegacy/Constraints.hpp similarity index 100% rename from src/YYCC/Constraints.hpp rename to src/YYCCLegacy/Constraints.hpp diff --git a/src/YYCC/DialogHelper.cpp b/src/YYCCLegacy/DialogHelper.cpp similarity index 100% rename from src/YYCC/DialogHelper.cpp rename to src/YYCCLegacy/DialogHelper.cpp diff --git a/src/YYCC/DialogHelper.hpp b/src/YYCCLegacy/DialogHelper.hpp similarity index 100% rename from src/YYCC/DialogHelper.hpp rename to src/YYCCLegacy/DialogHelper.hpp diff --git a/src/YYCC/EncodingHelper.cpp b/src/YYCCLegacy/EncodingHelper.cpp similarity index 100% rename from src/YYCC/EncodingHelper.cpp rename to src/YYCCLegacy/EncodingHelper.cpp diff --git a/src/YYCC/EncodingHelper.hpp b/src/YYCCLegacy/EncodingHelper.hpp similarity index 100% rename from src/YYCC/EncodingHelper.hpp rename to src/YYCCLegacy/EncodingHelper.hpp diff --git a/src/YYCC/EnumHelper.hpp b/src/YYCCLegacy/EnumHelper.hpp similarity index 100% rename from src/YYCC/EnumHelper.hpp rename to src/YYCCLegacy/EnumHelper.hpp diff --git a/src/YYCC/ExceptionHelper.cpp b/src/YYCCLegacy/ExceptionHelper.cpp similarity index 100% rename from src/YYCC/ExceptionHelper.cpp rename to src/YYCCLegacy/ExceptionHelper.cpp diff --git a/src/YYCC/ExceptionHelper.hpp b/src/YYCCLegacy/ExceptionHelper.hpp similarity index 100% rename from src/YYCC/ExceptionHelper.hpp rename to src/YYCCLegacy/ExceptionHelper.hpp diff --git a/src/YYCC/IOHelper.cpp b/src/YYCCLegacy/IOHelper.cpp similarity index 100% rename from src/YYCC/IOHelper.cpp rename to src/YYCCLegacy/IOHelper.cpp diff --git a/src/YYCC/IOHelper.hpp b/src/YYCCLegacy/IOHelper.hpp similarity index 100% rename from src/YYCC/IOHelper.hpp rename to src/YYCCLegacy/IOHelper.hpp diff --git a/src/YYCC/ParserHelper.hpp b/src/YYCCLegacy/ParserHelper.hpp similarity index 100% rename from src/YYCC/ParserHelper.hpp rename to src/YYCCLegacy/ParserHelper.hpp diff --git a/src/YYCC/StdPatch.cpp b/src/YYCCLegacy/StdPatch.cpp similarity index 100% rename from src/YYCC/StdPatch.cpp rename to src/YYCCLegacy/StdPatch.cpp diff --git a/src/YYCC/StdPatch.hpp b/src/YYCCLegacy/StdPatch.hpp similarity index 100% rename from src/YYCC/StdPatch.hpp rename to src/YYCCLegacy/StdPatch.hpp diff --git a/src/YYCC/StringHelper.cpp b/src/YYCCLegacy/StringHelper.cpp similarity index 100% rename from src/YYCC/StringHelper.cpp rename to src/YYCCLegacy/StringHelper.cpp diff --git a/src/YYCC/StringHelper.hpp b/src/YYCCLegacy/StringHelper.hpp similarity index 100% rename from src/YYCC/StringHelper.hpp rename to src/YYCCLegacy/StringHelper.hpp diff --git a/src/YYCC/WinFctHelper.cpp b/src/YYCCLegacy/WinFctHelper.cpp similarity index 100% rename from src/YYCC/WinFctHelper.cpp rename to src/YYCCLegacy/WinFctHelper.cpp diff --git a/src/YYCC/WinFctHelper.hpp b/src/YYCCLegacy/WinFctHelper.hpp similarity index 100% rename from src/YYCC/WinFctHelper.hpp rename to src/YYCCLegacy/WinFctHelper.hpp diff --git a/src/YYCC/WinImportPrefix.hpp b/src/YYCCLegacy/WinImportPrefix.hpp similarity index 100% rename from src/YYCC/WinImportPrefix.hpp rename to src/YYCCLegacy/WinImportPrefix.hpp diff --git a/src/YYCC/WinImportSuffix.hpp b/src/YYCCLegacy/WinImportSuffix.hpp similarity index 100% rename from src/YYCC/WinImportSuffix.hpp rename to src/YYCCLegacy/WinImportSuffix.hpp diff --git a/src/YYCC/YYCCInternal.hpp b/src/YYCCLegacy/YYCCInternal.hpp similarity index 100% rename from src/YYCC/YYCCInternal.hpp rename to src/YYCCLegacy/YYCCInternal.hpp diff --git a/src/YYCCommonplace.hpp b/src/YYCCommonplaceLegacy.hpp similarity index 100% rename from src/YYCCommonplace.hpp rename to src/YYCCommonplaceLegacy.hpp diff --git a/src/yycc.hpp b/src/yycc.hpp new file mode 100644 index 0000000..07adb9e --- /dev/null +++ b/src/yycc.hpp @@ -0,0 +1,16 @@ +#pragma once + +// Library Version and Comparison Macros +#include "yycc/version.hpp" +#include "yycc/macro/version_cmp.hpp" + +// Operating System Identifier Macros +#include "yycc/macro/os_detector.hpp" + +// Windows Shitty Behavior Disable Macros +#include "yycc/windows/unsafe_suppressor.hpp" + +// Batch Class Move / Copy Function Macros +#include "yycc/macro/class_copy_move.hpp" + +namespace yycc {} diff --git a/src/yycc/binstore/kernel.hpp b/src/yycc/binstore/kernel.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/clap/kernel.hpp b/src/yycc/clap/kernel.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/constraint.hpp b/src/yycc/constraint.hpp new file mode 100644 index 0000000..4f16d1e --- /dev/null +++ b/src/yycc/constraint.hpp @@ -0,0 +1,78 @@ +#pragma once +#include "macro/class_copy_move.hpp" +#include +#include +#include + +/// @brief The namespace containing generic constraint concept used varied in other modules. +namespace yycc::constraint { + + /// @brief Function prototype used in Constraint for checking whether given value is valid. + /// @details Analyze given value, and return true if value is legal, otherwise false. + template + using FnCheck = std::function; + /// @brief Function prototype used in Constraint for clamping given value into a valid value. + /// @details Analyze given value, return clamped value. + template + using FnClamp = std::function; + + /** + * @brief The constraint applied to settings to limit its stored value. + * @tparam T The data type this constraint need to be processed with. + * @details + * Constraint class contains various features: + * \li Check: Check whether given value is in range. + * \li Clamp: Clamp given value into valid value. + * Every instances of Constraint can have some, or none of these features. + * So it is essential to check whether instance has corresponding features before using it. + */ + template + class Constraint { + public: + Constraint(FnCheck&& fn_check, FnClamp&& fn_clamp) : + fn_check(std::move(fn_check)), fn_clamp(std::move(fn_clamp)) {} + YYCC_DELETE_COPY(Constraint) + YYCC_DEFAULT_MOVE(Constraint) + + /** + * @brief Perform Check feature. + * @param[in] value The valid for checking. + * @return True if valid is okey, otherwise false. + * @exception std::logic_error Raised if this feature is not supported. + */ + bool check(const T& value) const { + if (!support_check()) { + throw std::logic_error("this Constraint do not support check operation"); + } else { + return fn_check(value); + } + } + /** + * @brief Perform Clamp feature. + * @param[in] value The valid for clamping. + * @return The result after clamping. + * @exception std::logic_error Raised if this feature is not supported. + */ + T clamp(const T& value) const { + if (!support_clamp()) { + throw std::logic_error("this Constraint do not support clamp operation"); + } else { + return fn_clamp(value); + } + } + + /// @brief Check whether this Constraint support Check feature. + /// @return True if it support, otherwise false. + bool support_check() const noexcept { return this->fn_check != nullptr; } + /// @brief Check whether this Constraint support Clamp feature. + /// @return True if it support, otherwise false. + bool support_clamp() const noexcept { return this->fn_clamp != nullptr; } + + private: + /// @brief Pointer to Check feature function. + FnCheck fn_check; + /// @brief Pointer to Clamp feature function. + FnClamp fn_clamp; + }; + +} // namespace yycc::core::constraint diff --git a/src/yycc/constraint/builder.hpp b/src/yycc/constraint/builder.hpp new file mode 100644 index 0000000..8ff6801 --- /dev/null +++ b/src/yycc/constraint/builder.hpp @@ -0,0 +1,67 @@ +#pragma once +#include "../constraint.hpp" +#include "../string.hpp" +#include + +#define NS_YYCC_STRING ::yycc::string + +/// @brief The namespace containing convenient function building common used Constraint instance. +namespace yycc::constraint::builder { + + /** + * @brief Build Constraint for arithmetic values by minimum and maximum value range. + * @tparam T 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 && !std::is_same_v, int> = 0> + Constraint min_max_constraint(T min_value, T max_value) { + if (min_value > max_value) + throw std::invalid_argument("the max value must be equal or greater than min value"); + + auto fn_check = [min_value, max_value](const T& val) -> bool { + return (val <= max_value) && (val >= min_value); + }; + auto fn_clamp = [min_value, max_value](const T& val) -> T { + return std::clamp(val, min_value, max_value); + }; + return Constraint(std::move(fn_check), std::move(fn_clamp)); + } + + /** + * @brief Get constraint for enum values by enumerating all possible values. + * @tparam T 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, int> = 0> + Constraint enum_constraint(const std::initializer_list& il) { + std::set data(il); + + auto fn_check = [data](const T& val) -> bool { return data.find(val) != data.end(); }; + return Constraint(std::move(fn_check), nullptr); + } + + /** + * @brief Get constraint for string values by enumerating 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. + */ + inline Constraint GetStringEnumerationConstraint( + const std::initializer_list& il) { + std::set data(il); + + auto fn_check = [data](const NS_YYCC_STRING::u8string& val) -> bool { + return data.find(NS_YYCC_STRING::u8string_view(val)) != data.end(); + }; + return Constraint(std::move(fn_check), nullptr); + } + +} // namespace yycc::constraint::builder + +#undef NS_YYCC_STRING diff --git a/src/yycc/macro/class_copy_move.hpp b/src/yycc/macro/class_copy_move.hpp new file mode 100644 index 0000000..3e2b56f --- /dev/null +++ b/src/yycc/macro/class_copy_move.hpp @@ -0,0 +1,31 @@ +#pragma once + +/// @brief Explicitly remove copy (\c constructor and \c operator\=) for given class. +#define YYCC_DELETE_COPY(CLSNAME) \ + CLSNAME(const CLSNAME&) = delete; \ + CLSNAME& operator=(const CLSNAME&) = delete; + +/// @brief Explicitly remove move (\c constructor and \c operator\=) for given class. +#define YYCC_DELETE_MOVE(CLSNAME) \ + CLSNAME(CLSNAME&&) = delete; \ + CLSNAME& operator=(CLSNAME&&) = delete; + +/// @brief Explicitly remove (copy and move) (\c constructor and \c operator\=) for given class. +#define YYCC_DELETE_COPY_MOVE(CLSNAME) \ + YYCC_DELETE_COPY(CLSNAME) \ + YYCC_DELETE_MOVE(CLSNAME) + +/// @brief Explicitly set default copy (\c constructor and \c operator\=) for given class. +#define YYCC_DEFAULT_COPY(CLSNAME) \ + CLSNAME(const CLSNAME&) = default; \ + CLSNAME& operator=(const CLSNAME&) = default; + +/// @brief Explicitly set default move (\c constructor and \c operator\=) for given class. +#define YYCC_DEFAULT_MOVE(CLSNAME) \ + CLSNAME(CLSNAME&&) = default; \ + CLSNAME& operator=(CLSNAME&&) = default; + +/// @brief Explicitly set default (copy and move) (\c constructor and \c operator\=) for given class. +#define YYCC_DEFAULT_COPY_MOVE(CLSNAME) \ + YYCC_DEFAULT_COPY(CLSNAME) \ + YYCC_DEFAULT_MOVE(CLSNAME) diff --git a/src/yycc/macro/os_detector.hpp b/src/yycc/macro/os_detector.hpp new file mode 100644 index 0000000..9d8e270 --- /dev/null +++ b/src/yycc/macro/os_detector.hpp @@ -0,0 +1,11 @@ +#pragma once + +// Define operating system macros +#define YYCC_OS_WINDOWS 2 +#define YYCC_OS_LINUX 3 +// Check current operating system. +#if defined(_WIN32) +#define YYCC_OS YYCC_OS_WINDOWS +#else +#define YYCC_OS YYCC_OS_LINUX +#endif diff --git a/src/yycc/macro/version_cmp.hpp b/src/yycc/macro/version_cmp.hpp new file mode 100644 index 0000000..cb40550 --- /dev/null +++ b/src/yycc/macro/version_cmp.hpp @@ -0,0 +1,26 @@ +#pragma once + +/// @brief Return true if left version number is equal to right version number, otherwise false. +#define YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3) ((av1) == (bv1) && (av2) == (bv2) && (av3) == (bv3)) +/// @brief Return true if left version number is not equal to right version number, otherwise false. +#define YYCC_VERCMP_NE(av1, av2, av3, bv1, bv2, bv3) (!YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3)) +/// @brief Return true if left version number is greater than right version number, otherwise false. +#define YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) ( \ + ((av1) > (bv1)) || \ + ((av1) == (bv1) && (av2) > (bv2)) || \ + ((av1) == (bv1) && (av2) == (bv2) && (av3) > (bv3)) \ +) +/// @brief Return true if left version number is greater than or equal to right version number, otherwise false. +#define YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3)) +/// @brief Return true if left version number is not lower than right version number, otherwise false. +#define YYCC_VERCMP_NL(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3) +/// @brief Return true if left version number is lower than right version number, otherwise false. +#define YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) ( \ + ((av1) < (bv1)) || \ + ((av1) == (bv1) && (av2) < (bv2)) || \ + ((av1) == (bv1) && (av2) == (bv2) && (av3) < (bv3)) \ +) +/// @brief Return true if left version number is lower than or equal to right version number, otherwise false. +#define YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3)) +/// @brief Return true if left version number is not greater than right version number, otherwise false. +#define YYCC_VERCMP_NG(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3) diff --git a/src/yycc/patch/container.hpp b/src/yycc/patch/container.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/patch/path.hpp b/src/yycc/patch/path.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/patch/string.hpp b/src/yycc/patch/string.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/prelude/core.hpp b/src/yycc/prelude/core.hpp new file mode 100644 index 0000000..9e3f39b --- /dev/null +++ b/src/yycc/prelude/core.hpp @@ -0,0 +1,18 @@ +#pragma once + +// Prelude section +#include "../string.hpp" +namespace yycc::prelude { + +#define NS_YYCC_STRING ::yycc::string + + using u8char = NS_YYCC_STRING::u8char; + using u8string = NS_YYCC_STRING::u8string; + using u8string_view = NS_YYCC_STRING::u8string_view; + +#undef NS_YYCC_STRING + +} // namespace yycc::prelude + +// Expose all members +using namespace yycc::prelude; diff --git a/src/yycc/prelude/rust.hpp b/src/yycc/prelude/rust.hpp new file mode 100644 index 0000000..b7ddcd6 --- /dev/null +++ b/src/yycc/prelude/rust.hpp @@ -0,0 +1,41 @@ +#pragma once + +// Include YYCC prelude first +#include "core.hpp" + +// Rust prelude section +#include "../rust/primitive.hpp" +#include + +namespace yycc::prelude::rust { + // Include primitive types + +#define NS_RUST_PRIMITIVE ::yycc::rust::primitive + + using i8 = NS_RUST_PRIMITIVE::i8; + using i16 = NS_RUST_PRIMITIVE::i16; + using i32 = NS_RUST_PRIMITIVE::i32; + using i64 = NS_RUST_PRIMITIVE::i64; + using u8 = NS_RUST_PRIMITIVE::u8; + using u16 = NS_RUST_PRIMITIVE::u16; + using u32 = NS_RUST_PRIMITIVE::u32; + using u64 = NS_RUST_PRIMITIVE::u64; + + using isize = NS_RUST_PRIMITIVE::isize; + using usize = NS_RUST_PRIMITIVE::usize; + + using f32 = NS_RUST_PRIMITIVE::f32; + using f64 = NS_RUST_PRIMITIVE::f64; + + using str = NS_RUST_PRIMITIVE::str; + +#undef NS_RUST_PRIMITIVE + + // Other types + using String = ::yycc::string::u8string; + template + using Vec = std::vector; +} // namespace yycc::prelude::rust + +// Expose all members +using namespace yycc::prelude::rust; diff --git a/src/yycc/rust/parse.hpp b/src/yycc/rust/parse.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/rust/primitive.hpp b/src/yycc/rust/primitive.hpp new file mode 100644 index 0000000..7a4d3cf --- /dev/null +++ b/src/yycc/rust/primitive.hpp @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include "../string.hpp" + +namespace yycc::rust::primitive { + + // `bool` is keyword so should not declare it anymore. + // `char` is keyword so should not declare it anymore. + + using i8 = std::int8_t; + using i16 = std::int16_t; + using i32 = std::int32_t; + using i64 = std::int64_t; + using u8 = std::uint8_t; + using u16 = std::uint16_t; + using u32 = std::uint32_t; + using u64 = std::uint64_t; + + using isize = std::ptrdiff_t; + using usize = std::size_t; + + using f32 = float; + using f64 = double; + + using str = ::yycc::string::u8string_view; +} + diff --git a/src/yycc/string.hpp b/src/yycc/string.hpp new file mode 100644 index 0000000..6118f07 --- /dev/null +++ b/src/yycc/string.hpp @@ -0,0 +1,41 @@ +#pragma once + +// Define the UTF8 char type we used. +// And do a polyfill if no embedded char8_t type. + +#include +#include + +namespace yycc::string { + /** + \typedef u8char_t + \brief YYCC UTF8 char type. + \details + This char type is an alias to \c char8_t if your current C++ standard support it. + Otherwise it is defined as unsigned char as C++ 20 stdandard does. + */ + /** + \typedef u8string + \brief YYCC UTF8 string container type. + \details + This type is defined as \c std::basic_string. + It is equal to \c std::u8string if your current C++ standard support it. + */ + /** + \typedef u8string_view + \brief YYCC UTF8 string view type. + \details + This type is defined as \c std::basic_string_view. + It is equal to \c std::u8string_view if your current C++ standard support it. + */ + +#if defined(__cpp_char8_t) + using u8char = char8_t; + using u8string = std::u8string; + using u8string_view = std::u8string_view; +#else + using u8char = unsigned char; + using u8string = std::basic_string; + using u8string_view = std::basic_string_view; +#endif +} // namespace yycc::string diff --git a/src/yycc/string/op.cpp b/src/yycc/string/op.cpp new file mode 100644 index 0000000..f5920ba --- /dev/null +++ b/src/yycc/string/op.cpp @@ -0,0 +1,226 @@ +#include "op.hpp" + +#include +#include "reinterpret.hpp" + +#define NS_YYCC_STRING ::yycc::string +#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret + +namespace yycc::string::op { + +#pragma region Printf VPrintf + + bool printf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, ...) { + va_list argptr; + va_start(argptr, format); + bool ret = vprintf(strl, format, argptr); + va_end(argptr); + return ret; + } + + bool vprintf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, va_list argptr) { + va_list args1; + va_copy(args1, argptr); + va_list args2; + va_copy(args2, argptr); + + // the return value is desired char count without NULL terminal. + // minus number means error + int count = std::vsnprintf( + nullptr, + 0, + NS_YYCC_STRING_REINTERPRET::as_ordinary(format), + args1 + ); + if (count < 0) { + // invalid length returned by vsnprintf. + return false; + } + va_end(args1); + + // resize std::string to desired count. + // and pass its length + 1 to std::vsnprintf, + // because std::vsnprintf only can write "buf_size - 1" chars with a trailing NULL. + // however std::vsnprintf already have a trailing NULL, so we plus 1 for it. + strl.resize(count); + int write_result = std::vsnprintf( + NS_YYCC_STRING_REINTERPRET::as_ordinary(strl.data()), + strl.size() + 1, + NS_YYCC_STRING_REINTERPRET::as_ordinary(format), + args2 + ); + va_end(args2); + + if (write_result < 0 || write_result > count) { + // invalid write result in vsnprintf. + return false; + } + + return true; + } + + NS_YYCC_STRING::u8string printf(const NS_YYCC_STRING::u8char* format, ...) { + NS_YYCC_STRING::u8string ret; + + va_list argptr; + va_start(argptr, format); + vprintf(ret, format, argptr); + va_end(argptr); + + return ret; + } + + NS_YYCC_STRING::u8string vprintf(const NS_YYCC_STRING::u8char* format, va_list argptr) { + NS_YYCC_STRING::u8string ret; + + va_list argcpy; + va_copy(argcpy, argptr); + vprintf(ret, format, argcpy); + va_end(argcpy); + + return ret; + } + +#pragma endregion + +#pragma region Replace + + void replace(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl) { + // Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string + + // check requirements + // from string should not be empty + NS_YYCC_STRING::u8string from_strl(_from_strl); + NS_YYCC_STRING::u8string to_strl(_to_strl); + if (from_strl.empty()) return; + + // start replace one by one + size_t start_pos = 0; + while ((start_pos = strl.find(from_strl, start_pos)) != NS_YYCC_STRING::u8string::npos) { + strl.replace(start_pos, from_strl.size(), to_strl); + start_pos += to_strl.size(); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } + } + + NS_YYCC_STRING::u8string replace(const NS_YYCC_STRING::u8string_view& _strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl) { + // prepare result + NS_YYCC_STRING::u8string strl(_strl); + replace(strl, _from_strl, _to_strl); + // return value + return strl; + } + +#pragma endregion + +#pragma region Join + + NS_YYCC_STRING::u8string join(JoinDataProvider fct_data, const NS_YYCC_STRING::u8string_view& decilmer) { + NS_YYCC_STRING::u8string ret; + bool is_first = true; + NS_YYCC_STRING::u8string_view element; + + // fetch element + while (fct_data(element)) { + // insert decilmer + if (is_first) is_first = false; + else { + // append decilmer. + ret.append(decilmer); + } + + // insert element if it is not empty + if (!element.empty()) + ret.append(element); + } + + return ret; + } + +#pragma endregion + +#pragma region Upper Lower + + template + static void generic_lower_upper(NS_YYCC_STRING::u8string& strl) { + // References: + // https://en.cppreference.com/w/cpp/algorithm/transform + // https://en.cppreference.com/w/cpp/string/byte/tolower + std::transform( + strl.cbegin(), strl.cend(), strl.begin(), + [](unsigned char c) -> char { + if constexpr (BIsToLower) return std::tolower(c); + else return std::toupper(c); + } + ); + } + + void lower(NS_YYCC_STRING::u8string& strl) { + generic_lower_upper(strl); + } + + NS_YYCC_STRING::u8string lower(const NS_YYCC_STRING::u8string_view& strl) { + NS_YYCC_STRING::u8string ret(strl); + lower(ret); + return ret; + } + + void upper(NS_YYCC_STRING::u8string& strl) { + generic_lower_upper(strl); + } + + NS_YYCC_STRING::u8string upper(const NS_YYCC_STRING::u8string_view& strl) { + // same as Lower, just replace char transform function. + NS_YYCC_STRING::u8string ret(strl); + upper(ret); + return ret; + } + +#pragma endregion + +#pragma region Split + + std::vector split(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer) { + // call split view + auto view_result = split_view(strl, _decilmer); + + // copy string view result to string + std::vector elems; + elems.reserve(view_result.size()); + for (const auto& strl_view : view_result) { + elems.emplace_back(NS_YYCC_STRING::u8string(strl_view)); + } + // return copied result + return elems; + } + + std::vector split_view(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer) { + // Reference: + // https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c + + // prepare return value + std::vector elems; + + // if string need to be splitted is empty, return original string (empty string). + // if decilmer is empty, return original string. + NS_YYCC_STRING::u8string decilmer(_decilmer); + if (strl.empty() || decilmer.empty()) { + elems.emplace_back(strl); + return elems; + } + + // start spliting + std::size_t previous = 0, current; + while ((current = strl.find(decilmer.c_str(), previous)) != NS_YYCC_STRING::u8string::npos) { + elems.emplace_back(strl.substr(previous, current - previous)); + previous = current + decilmer.size(); + } + // try insert last part but prevent possible out of range exception + if (previous <= strl.size()) { + elems.emplace_back(strl.substr(previous)); + } + return elems; + } + +#pragma endregion + +} diff --git a/src/yycc/string/op.hpp b/src/yycc/string/op.hpp new file mode 100644 index 0000000..52e5a9f --- /dev/null +++ b/src/yycc/string/op.hpp @@ -0,0 +1,156 @@ +#pragma once +#include +#include +#include +#include "../string.hpp" + +#define NS_YYCC_STRING ::yycc::string + +namespace yycc::string::op { + + /** + * @brief Perform a string formatting operation. + * @param[out] strl + * The string container receiving the result. + * There is no guarantee that the content is not modified when function failed. + * @param[in] format The format string. + * @param[in] ... Argument list of format string. + * @return True if success, otherwise false. + */ + bool printf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, ...); + /** + * @brief Perform a string formatting operation. + * @param[out] strl + * The string container receiving the result. + * There is no guarantee that the content is not modified when function failed. + * @param[in] format The format string. + * @param[in] argptr Argument list of format string. + * @return True if success, otherwise false. + */ + bool vprintf(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8char* format, va_list argptr); + /** + * @brief Perform a string formatting operation. + * @param[in] format The format string. + * @param[in] ... Argument list of format string. + * @return The formatting result. Empty string if error happened. + */ + NS_YYCC_STRING::u8string printf(const NS_YYCC_STRING::u8char* format, ...); + /** + * @brief Perform a string formatting operation. + * @param[in] format The format string. + * @param[in] argptr Argument list of format string. + * @return The formatting result. Empty string if error happened. + */ + NS_YYCC_STRING::u8string vprintf(const NS_YYCC_STRING::u8char* format, va_list argptr); + + /** + * @brief Modify given string with all occurrences of substring \e old replaced by \e new. + * @param[in,out] strl The string for replacing + * @param[in] _from_strl The \e old string. + * @param[in] _to_strl The \e new string. + */ + void replace(NS_YYCC_STRING::u8string& strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl); + /** + * @brief Return a copy with all occurrences of substring \e old replaced by \e new. + * @param[in] _strl The string for replacing + * @param[in] _from_strl The \e old string. + * @param[in] _to_strl The \e new string. + * @return The result of replacement. + */ + NS_YYCC_STRING::u8string replace(const NS_YYCC_STRING::u8string_view& _strl, const NS_YYCC_STRING::u8string_view& _from_strl, const NS_YYCC_STRING::u8string_view& _to_strl); + + /** + * @brief The data provider of general join function. + * @details + * For programmer using lambda to implement this function pointer: + * \li During calling, implementation should assign the reference of string view passed in argument + * to the string which need to be joined. + * \li Function return true to continue joining. otherwise return false to stop joining. + * The argument content assigned in the calling returning false is not included in join process. + */ + using JoinDataProvider = std::function; + /** + * @brief Universal join function. + * @details + * This function use function pointer as a general data provider interface, + * so this function suit for all types container. + * You can use this universal join function for any custom container by + * using C++ lambda syntax to create a code block adapted to this function pointer. + * @param[in] fct_data The function pointer in JoinDataProvider type prividing the data to be joined. + * @param[in] decilmer The decilmer used for joining. + * @return The result string of joining. + */ + NS_YYCC_STRING::u8string join(JoinDataProvider fct_data, const NS_YYCC_STRING::u8string_view& decilmer); + /** + * @brief Specialized join function for standard library container. + * @tparam InputIt + * Must meet the requirements of LegacyInputIterator. + * It also can be dereferenced and then implicitly converted to NS_YYCC_STRING::u8string_view. + * @param[in] first The beginning of the range of elements to join. + * @param[in] last The terminal of the range of elements to join (exclusive). + * @param[in] decilmer The decilmer used for joining. + * @return The result string of joining. + */ + template + NS_YYCC_STRING::u8string join(InputIt first, InputIt last, const NS_YYCC_STRING::u8string_view& decilmer) { + return join([&first, &last](NS_YYCC_STRING::u8string_view& view) -> bool { + // if we reach tail, return false to stop join process + if (first == last) return false; + // otherwise fetch data, inc iterator and return. + view = *first; + ++first; + return true; + }, decilmer); + } + + /** + * @brief Convert given string to lowercase. + * @param[in,out] strl The string to be lowercase. + */ + void lower(NS_YYCC_STRING::u8string& strl); + /** + * @brief Return a copy of the string converted to lowercase. + * @param[in] strl The string to be lowercase. + * @return The copy of the string converted to lowercase. + */ + NS_YYCC_STRING::u8string lower(const NS_YYCC_STRING::u8string_view& strl); + /** + * @brief Convert given string to uppercase. + * @param[in,out] strl The string to be uppercase. + */ + void upper(NS_YYCC_STRING::u8string& strl); + /** + * @brief Return a copy of the string converted to uppercase. + * @param[in] strl The string to be uppercase. + * @return The copy of the string converted to uppercase. + */ + NS_YYCC_STRING::u8string upper(const NS_YYCC_STRING::u8string_view& strl); + + /** + * @brief Split given string with specified decilmer. + * @param[in] strl The string need to be splitting. + * @param[in] _decilmer The decilmer for splitting. + * @return + * The split result. + * \par + * If given string or decilmer are empty, + * the result container will only contain 1 entry which is equal to given string. + */ + std::vector split(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer); + /** + * @brief Split given string with specified decilmer as string view. + * @param[in] strl The string need to be splitting. + * @param[in] _decilmer The decilmer for splitting. + * @return + * The split result with string view format. + * This will not produce any copy of original string. + * \par + * If given string or decilmer are empty, + * the result container will only contain 1 entry which is equal to given string. + * @see Split(const NS_YYCC_STRING::u8string_view&, const NS_YYCC_STRING::u8char*) + */ + std::vector split_view(const NS_YYCC_STRING::u8string_view& strl, const NS_YYCC_STRING::u8string_view& _decilmer); + +} + +#undef NS_YYCC_STRING diff --git a/src/yycc/string/reinterpret.cpp b/src/yycc/string/reinterpret.cpp new file mode 100644 index 0000000..a7dd798 --- /dev/null +++ b/src/yycc/string/reinterpret.cpp @@ -0,0 +1,33 @@ +#include "reinterpret.hpp" + +#define NS_YYCC_STRING ::yycc::string + +namespace yycc::string::reinterpret { + + const NS_YYCC_STRING::u8char* as_utf8(const char* src) { + return reinterpret_cast(src); + } + NS_YYCC_STRING::u8char* as_utf8(char* src) { + return reinterpret_cast(src); + } + NS_YYCC_STRING::u8string as_utf8(const std::string_view& src) { + return NS_YYCC_STRING::u8string(reinterpret_cast(src.data()), src.size()); + } + NS_YYCC_STRING::u8string_view as_utf8_view(const std::string_view& src) { + return NS_YYCC_STRING::u8string_view(reinterpret_cast(src.data()), src.size()); + } + + const char* as_ordinary(const NS_YYCC_STRING::u8char* src) { + return reinterpret_cast(src); + } + char* as_ordinary(NS_YYCC_STRING::u8char* src) { + return reinterpret_cast(src); + } + std::string as_ordinary(const NS_YYCC_STRING::u8string_view& src) { + return std::string(reinterpret_cast(src.data()), src.size()); + } + std::string_view as_ordinary_view(const NS_YYCC_STRING::u8string_view& src) { + return std::string_view(reinterpret_cast(src.data()), src.size()); + } + +} diff --git a/src/yycc/string/reinterpret.hpp b/src/yycc/string/reinterpret.hpp new file mode 100644 index 0000000..7062e5d --- /dev/null +++ b/src/yycc/string/reinterpret.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "../string.hpp" + +#define NS_YYCC_STRING ::yycc::string + +namespace yycc::string::reinterpret { + +#define _YYCC_U8(strl) u8 ## strl ///< The assistant macro for YYCC_U8. +#define YYCC_U8(strl) (reinterpret_cast(_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. + + const NS_YYCC_STRING::u8char* as_utf8(const char* src); + NS_YYCC_STRING::u8char* as_utf8(char* src); + NS_YYCC_STRING::u8string as_utf8(const std::string_view& src); + NS_YYCC_STRING::u8string_view as_utf8_view(const std::string_view& src); + + const char* as_ordinary(const NS_YYCC_STRING::u8char* src); + char* as_ordinary(NS_YYCC_STRING::u8char* src); + std::string as_ordinary(const NS_YYCC_STRING::u8string_view& src); + std::string_view as_ordinary_view(const NS_YYCC_STRING::u8string_view& src); + +} + +#undef NS_YYCC_STRING diff --git a/src/yycc/version.hpp b/src/yycc/version.hpp new file mode 100644 index 0000000..bffc7b4 --- /dev/null +++ b/src/yycc/version.hpp @@ -0,0 +1,5 @@ +#pragma once + +#define YYCC_VER_MAJOR 2 +#define YYCC_VER_MINOR 0 +#define YYCC_VER_PATCH 0 diff --git a/cmake/YYCCVersion.hpp.in b/src/yycc/version.hpp.in similarity index 100% rename from cmake/YYCCVersion.hpp.in rename to src/yycc/version.hpp.in diff --git a/src/yycc/windows/com.cpp b/src/yycc/windows/com.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/windows/com.hpp b/src/yycc/windows/com.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/windows/dialog.cpp b/src/yycc/windows/dialog.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/windows/dialog.hpp b/src/yycc/windows/dialog.hpp new file mode 100644 index 0000000..e69de29 diff --git a/src/yycc/windows/import_guard_head.hpp b/src/yycc/windows/import_guard_head.hpp new file mode 100644 index 0000000..d6a331a --- /dev/null +++ b/src/yycc/windows/import_guard_head.hpp @@ -0,0 +1,19 @@ +// It is by design that no pragma once or #if to prevent deplicated including. +// Because this header is the part of wrapper, not a real header. +// #pragma once + +#include "../macro/os_detector.hpp" + +#if YYCC_OS == YYCC_OS_WINDOWS + +// Define 2 macros to disallow Windows generate MIN and MAX macros +// which cause std::min and std::max can not function as normal. +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#if !defined(NOMINMAX) +#define NOMINMAX +#endif + +#endif diff --git a/src/yycc/windows/import_guard_tail.hpp b/src/yycc/windows/import_guard_tail.hpp new file mode 100644 index 0000000..4b1e71f --- /dev/null +++ b/src/yycc/windows/import_guard_tail.hpp @@ -0,0 +1,23 @@ +// It is by design that no pragma once or #if to prevent deplicated including. +// Because this header is the part of wrapper, not a real header. +// #pragma once + +#include "../macro/os_detector.hpp" + +#if YYCC_OS == YYCC_OS_WINDOWS + +// Windows also will generate following macros +// which may cause the function sign is different in Windows and other platforms. +// So we simply remove them. +// Because #undef will not throw error if there are no matched macro, +// so we simply #undef them directly. +#undef GetObject +#undef GetClassName +#undef LoadImage +#undef GetTempPath +#undef GetModuleFileName +#undef CopyFile +#undef MoveFile +#undef DeleteFile + +#endif diff --git a/src/yycc/windows/unsafe_suppressor.hpp b/src/yycc/windows/unsafe_suppressor.hpp new file mode 100644 index 0000000..7e88447 --- /dev/null +++ b/src/yycc/windows/unsafe_suppressor.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "../macro/os_detector.hpp" + +// If we are in Windows, +// we need add 2 macros to disable Windows shitty warnings and errors of +// depracted functions and not secure functions. +#if YYCC_OS == YYCC_OS_WINDOWS + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif +#if !defined(_CRT_NONSTDC_NO_DEPRECATE) +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#endif diff --git a/testbench/CMakeLists.txt b/testbench/CMakeLists.txt index 0a19192..c5ee56d 100644 --- a/testbench/CMakeLists.txt +++ b/testbench/CMakeLists.txt @@ -4,15 +4,22 @@ add_executable(YYCCTestbench "") target_sources(YYCCTestbench PRIVATE main.cpp + yycc/constraint.cpp + yycc/constraint/builder.cpp + yycc/string.cpp + yycc/string/op.cpp + yycc/string/reinterpret.cpp ) -# Add YYCC as its library +# Setup headers target_include_directories(YYCCTestbench -PRIVATE - YYCCommonplace +PUBLIC + "${CMAKE_CURRENT_LIST_DIR}" ) +# Setup libraries target_link_libraries(YYCCTestbench PRIVATE YYCCommonplace + GTest::gtest_main ) # Setup C++ standard target_compile_features(YYCCTestbench PUBLIC cxx_std_17) @@ -29,8 +36,6 @@ PRIVATE $<$:/utf-8> ) -# Install testbench only on Release mode -install(TARGETS YYCCTestbench - CONFIGURATIONS Release RelWithDebInfo MinSizeRel - RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH} -) +# Discover all test +include(GoogleTest) +gtest_discover_tests(YYCCTestbench) diff --git a/testbench/main.cpp b/testbench/main.cpp index 3041158..54793b7 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -1,735 +1,6 @@ -#include -#include -#include -#include - -namespace Console = YYCC::ConsoleHelper; - -namespace YYCCTestbench { - -#pragma region Unicode Test Data - - // UNICODE Test Strings - // Ref: https://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8 -#define TEST_UNICODE_STR_JAPAN "\u30E6\u30FC\u30B6\u30FC\u5225\u30B5\u30A4\u30C8" -#define TEST_UNICODE_STR_CHINA "\u7B80\u4F53\u4E2D\u6587" -#define TEST_UNICODE_STR_KOREA "\uD06C\uB85C\uC2A4 \uD50C\uB7AB\uD3FC\uC73C\uB85C" -#define TEST_UNICODE_STR_ISRAEL "\u05DE\u05D3\u05D5\u05E8\u05D9\u05DD \u05DE\u05D1\u05D5\u05E7\u05E9\u05D9\u05DD" -#define TEST_UNICODE_STR_EGYPT "\u0623\u0641\u0636\u0644 \u0627\u0644\u0628\u062D\u0648\u062B" -#define TEST_UNICODE_STR_GREECE "\u03A3\u1F72 \u03B3\u03BD\u03C9\u03C1\u03AF\u03B6\u03C9 \u1F00\u03C0\u1F78" -#define TEST_UNICODE_STR_RUSSIA "\u0414\u0435\u0441\u044F\u0442\u0443\u044E \u041C\u0435\u0436\u0434\u0443\u043D\u0430\u0440\u043E\u0434\u043D\u0443\u044E" -#define TEST_UNICODE_STR_THAILAND "\u0E41\u0E1C\u0E48\u0E19\u0E14\u0E34\u0E19\u0E2E\u0E31\u0E48\u0E19\u0E40\u0E2A\u0E37\u0E48\u0E2D\u0E21\u0E42\u0E17\u0E23\u0E21\u0E41\u0E2A\u0E19\u0E2A\u0E31\u0E07\u0E40\u0E27\u0E0A" -#define TEST_UNICODE_STR_FRANCE "fran\u00E7ais langue \u00E9trang\u00E8re" -#define TEST_UNICODE_STR_SPAIN "ma\u00F1ana ol\u00E9" -#define TEST_UNICODE_STR_MATHMATICS "\u222E E\u22C5da = Q, n \u2192 \u221E, \u2211 f(i) = \u220F g(i)" -#define TEST_UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug - -#define CONCAT(prefix, strl) prefix ## strl -#define CPP_U8_LITERAL(strl) YYCC_U8(strl) -#define CPP_U16_LITERAL(strl) CONCAT(u, strl) -#define CPP_U32_LITERAL(strl) CONCAT(U, strl) -#define CPP_WSTR_LITERAL(strl) CONCAT(L, strl) - - static std::vector c_UTF8TestStrTable { - CPP_U8_LITERAL(TEST_UNICODE_STR_JAPAN), - CPP_U8_LITERAL(TEST_UNICODE_STR_CHINA), - CPP_U8_LITERAL(TEST_UNICODE_STR_KOREA), - CPP_U8_LITERAL(TEST_UNICODE_STR_ISRAEL), - CPP_U8_LITERAL(TEST_UNICODE_STR_EGYPT), - CPP_U8_LITERAL(TEST_UNICODE_STR_GREECE), - CPP_U8_LITERAL(TEST_UNICODE_STR_RUSSIA), - CPP_U8_LITERAL(TEST_UNICODE_STR_THAILAND), - CPP_U8_LITERAL(TEST_UNICODE_STR_FRANCE), - CPP_U8_LITERAL(TEST_UNICODE_STR_SPAIN), - CPP_U8_LITERAL(TEST_UNICODE_STR_MATHMATICS), - CPP_U8_LITERAL(TEST_UNICODE_STR_EMOJI), - }; - static std::vector c_WStrTestStrTable { - CPP_WSTR_LITERAL(TEST_UNICODE_STR_JAPAN), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_CHINA), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_KOREA), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_ISRAEL), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_EGYPT), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_GREECE), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_RUSSIA), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_THAILAND), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_FRANCE), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_SPAIN), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_MATHMATICS), - CPP_WSTR_LITERAL(TEST_UNICODE_STR_EMOJI), - }; - static std::vector c_UTF16TestStrTable { - CPP_U16_LITERAL(TEST_UNICODE_STR_JAPAN), - CPP_U16_LITERAL(TEST_UNICODE_STR_CHINA), - CPP_U16_LITERAL(TEST_UNICODE_STR_KOREA), - CPP_U16_LITERAL(TEST_UNICODE_STR_ISRAEL), - CPP_U16_LITERAL(TEST_UNICODE_STR_EGYPT), - CPP_U16_LITERAL(TEST_UNICODE_STR_GREECE), - CPP_U16_LITERAL(TEST_UNICODE_STR_RUSSIA), - CPP_U16_LITERAL(TEST_UNICODE_STR_THAILAND), - CPP_U16_LITERAL(TEST_UNICODE_STR_FRANCE), - CPP_U16_LITERAL(TEST_UNICODE_STR_SPAIN), - CPP_U16_LITERAL(TEST_UNICODE_STR_MATHMATICS), - CPP_U16_LITERAL(TEST_UNICODE_STR_EMOJI), - }; - static std::vector c_UTF32TestStrTable { - CPP_U32_LITERAL(TEST_UNICODE_STR_JAPAN), - CPP_U32_LITERAL(TEST_UNICODE_STR_CHINA), - CPP_U32_LITERAL(TEST_UNICODE_STR_KOREA), - CPP_U32_LITERAL(TEST_UNICODE_STR_ISRAEL), - CPP_U32_LITERAL(TEST_UNICODE_STR_EGYPT), - CPP_U32_LITERAL(TEST_UNICODE_STR_GREECE), - CPP_U32_LITERAL(TEST_UNICODE_STR_RUSSIA), - CPP_U32_LITERAL(TEST_UNICODE_STR_THAILAND), - CPP_U32_LITERAL(TEST_UNICODE_STR_FRANCE), - CPP_U32_LITERAL(TEST_UNICODE_STR_SPAIN), - CPP_U32_LITERAL(TEST_UNICODE_STR_MATHMATICS), - CPP_U32_LITERAL(TEST_UNICODE_STR_EMOJI), - }; - -#undef CPP_WSTR_LITERAL -#undef CPP_U32_LITERAL -#undef CPP_U16_LITERAL -#undef CPP_U8_LITERAL -#undef CONCAT - -#pragma endregion - - static void Assert(bool condition, const YYCC::yycc_char8_t* description) { - if (condition) { - Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("OK: %s")), description); - } else { - Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Failed: %s\n")), description); - std::abort(); - } - } - - static void ConsoleTestbench() { - // Color Test - Console::EnableColorfulConsole(); - Console::WriteLine(YYCC_U8("Color Test:")); - -#define TEST_MACRO(col) Console::WriteLine(YYCC_U8("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col )); - // U+2588 is full block - - TEST_MACRO(BLACK); - TEST_MACRO(RED); - TEST_MACRO(GREEN); - TEST_MACRO(YELLOW); - TEST_MACRO(BLUE); - TEST_MACRO(MAGENTA); - TEST_MACRO(CYAN); - TEST_MACRO(WHITE); - -#undef TEST_MACRO - - // UTF8 Output Test - Console::WriteLine(YYCC_U8("UTF8 Output Test:")); - for (const auto& strl : c_UTF8TestStrTable) { - Console::FormatLine(YYCC_U8("\t%s"), strl.c_str()); - } - - // UTF8 Input Test - Console::WriteLine(YYCC_U8("UTF8 Input Test:")); - for (const auto& strl : c_UTF8TestStrTable) { - Console::FormatLine(YYCC_U8("\tPlease type: %s"), strl.c_str()); - Console::Write(YYCC_U8("\t> ")); - - YYCC::yycc_u8string gotten(Console::ReadLine()); - if (gotten == strl) Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("\tMatched! Got: %s")), gotten.c_str()); - else Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("\tNOT Matched! Got: %s")), gotten.c_str()); - } - - } - - static void EncodingTestbench() { - // get test tuple size - size_t count = c_UTF8TestStrTable.size(); - - // check the convertion between given string - for (size_t i = 0u; i < count; ++i) { - // get item - const auto& u8str = c_UTF8TestStrTable[i]; - const auto& u16str = c_UTF16TestStrTable[i]; - const auto& u32str = c_UTF32TestStrTable[i]; - - // create cache variables - YYCC::yycc_u8string u8cache; - std::u16string u16cache; - std::u32string u32cache; - - // do convertion check - Assert(YYCC::EncodingHelper::UTF8ToUTF16(u8str, u16cache) && u16cache == u16str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF16")); - Assert(YYCC::EncodingHelper::UTF8ToUTF32(u8str, u32cache) && u32cache == u32str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF32")); - - Assert(YYCC::EncodingHelper::UTF16ToUTF8(u16str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF16ToUTF8")); - Assert(YYCC::EncodingHelper::UTF32ToUTF8(u32str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF32ToUTF8")); - } - - // check wstring convertion on windows -#if YYCC_OS == YYCC_OS_WINDOWS - for (size_t i = 0u; i < count; ++i) { - // get item - const auto& u8str = c_UTF8TestStrTable[i]; - const auto& wstr = c_WStrTestStrTable[i]; - - // create cache variables - YYCC::yycc_u8string u8cache; - std::wstring wcache; - - // do convertion check - Assert(YYCC::EncodingHelper::UTF8ToWchar(u8str.c_str(), wcache) && wcache == wstr, YYCC_U8("YYCC::EncodingHelper::UTF8ToWchar")); - Assert(YYCC::EncodingHelper::WcharToUTF8(wstr.c_str(), u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::WcharToUTF8")); - } -#endif - - } - - static void StringTestbench() { - // Test Printf - auto test_printf = YYCC::StringHelper::Printf(YYCC_U8("%s == %s"), YYCC_U8("Hello World"), YYCC_U8("Hello, world")); - Assert(test_printf == YYCC_U8("Hello World == Hello, world"), YYCC_U8("YYCC::StringHelper::Printf")); - - // Test Replace - auto test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("bb"), YYCC_U8("dd")); // normal case - Assert(test_replace == YYCC_U8("aaddcc"), YYCC_U8("YYCC::StringHelper::Replace")); - test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("zz"), YYCC_U8("yy")); // no replace - Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace")); - test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC::yycc_u8string_view(), YYCC_U8("zz")); // empty finding - Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace")); - test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaaabbaa"), YYCC_U8("aa"), YYCC_U8("")); // no replaced string - Assert(test_replace == YYCC_U8("bb"), YYCC_U8("YYCC::StringHelper::Replace")); - test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaxcc"), YYCC_U8("x"), YYCC_U8("yx")); // nested replacing - Assert(test_replace == YYCC_U8("aayxcc"), YYCC_U8("YYCC::StringHelper::Replace")); - test_replace = YYCC::StringHelper::Replace(YYCC::yycc_u8string_view(), YYCC_U8(""), YYCC_U8("xy")); // empty source string - Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace")); - - // Test Upper / Lower - auto test_lower = YYCC::StringHelper::Lower(YYCC_U8("LOWER")); - Assert(test_lower == YYCC_U8("lower"), YYCC_U8("YYCC::StringHelper::Lower")); - auto test_upper = YYCC::StringHelper::Upper(YYCC_U8("upper")); - Assert(test_upper == YYCC_U8("UPPER"), YYCC_U8("YYCC::StringHelper::Upper")); - - // Test Join - std::vector test_join_container { - YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("") - }; - auto test_join = YYCC::StringHelper::Join(test_join_container.begin(), test_join_container.end(), YYCC_U8(", ")); - Assert(test_join == YYCC_U8(", 1, 2, "), YYCC_U8("YYCC::StringHelper::Join")); - - // Test Split - auto test_split = YYCC::StringHelper::Split(YYCC_U8(", 1, 2, "), YYCC_U8(", ")); // normal - Assert(test_split.size() == 4u, YYCC_U8("YYCC::StringHelper::Split")); - Assert(test_split[0] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split")); - Assert(test_split[1] == YYCC_U8("1"), YYCC_U8("YYCC::StringHelper::Split")); - Assert(test_split[2] == YYCC_U8("2"), YYCC_U8("YYCC::StringHelper::Split")); - Assert(test_split[3] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split")); - test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("-")); // no matched decilmer - Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); - Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split")); - test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC::yycc_u8string_view()); // empty decilmer - Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); - Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split")); - test_split = YYCC::StringHelper::Split(YYCC::yycc_u8string_view(), YYCC_U8("")); // empty source string - Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); - Assert(test_split[0].empty(), YYCC_U8("YYCC::StringHelper::Split")); - - } - - static void ParserTestbench() { - - // Test success TryParse -#define TEST_MACRO(type_t, value, string_value, ...) { \ - YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \ - type_t cache; \ - Assert(YYCC::ParserHelper::TryParse(cache_string, cache, ##__VA_ARGS__) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \ -} - - TEST_MACRO(int8_t, INT8_C(-61), "-61"); - TEST_MACRO(uint8_t, UINT8_C(200), "200"); - TEST_MACRO(int16_t, INT16_C(6161), "6161"); - TEST_MACRO(uint16_t, UINT16_C(32800), "32800"); - TEST_MACRO(int32_t, INT32_C(61616161), "61616161"); - TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293"); - TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161"); - TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807"); - TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16); - TEST_MACRO(float, 1.0f, "1.0"); - TEST_MACRO(double, 1.0, "1.0"); - TEST_MACRO(bool, true, "true"); - -#undef TEST_MACRO - - // Test failed TryParse -#define TEST_MACRO(type_t, string_value, ...) { \ - YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \ - type_t cache; \ - Assert(!YYCC::ParserHelper::TryParse(cache_string, cache, ##__VA_ARGS__), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \ -} - - TEST_MACRO(int8_t, "6161"); - TEST_MACRO(uint8_t, "32800"); - TEST_MACRO(int16_t, "61616161"); - TEST_MACRO(uint16_t, "4294967293"); - TEST_MACRO(int32_t, "616161616161"); - TEST_MACRO(uint32_t, "9223372036854775807"); - TEST_MACRO(int64_t, "616161616161616161616161"); - TEST_MACRO(uint64_t, "92233720368547758079223372036854775807"); - TEST_MACRO(float, "1e40"); - TEST_MACRO(double, "1e114514"); - TEST_MACRO(bool, "hello, world!"); - -#undef TEST_MACRO - - // Test ToString -#define TEST_MACRO(type_t, value, string_value, ...) { \ - type_t cache = value; \ - YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString(cache, ##__VA_ARGS__)); \ - Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \ -} - - TEST_MACRO(int8_t, INT8_C(-61), "-61"); - TEST_MACRO(uint8_t, UINT8_C(200), "200"); - TEST_MACRO(int16_t, INT16_C(6161), "6161"); - TEST_MACRO(uint16_t, UINT16_C(32800), "32800"); - TEST_MACRO(int32_t, INT32_C(61616161), "61616161"); - TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293"); - TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161"); - TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807"); - TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16); - TEST_MACRO(float, 1.0f, "1.0", std::chars_format::fixed, 1); - TEST_MACRO(double, 1.0, "1.0", std::chars_format::fixed, 1); - TEST_MACRO(bool, true, "true"); - -#undef TEST_MACRO - - } - - static void DialogTestbench() { -#if YYCC_OS == YYCC_OS_WINDOWS - - YYCC::yycc_u8string ret; - std::vector rets; - - YYCC::DialogHelper::FileDialog params; - auto& filters = params.ConfigreFileTypes(); - filters.Add(YYCC_U8("Microsoft Word (*.docx; *.doc)"), { YYCC_U8("*.docx"), YYCC_U8("*.doc") }); - filters.Add(YYCC_U8("Microsoft Excel (*.xlsx; *.xls)"), { YYCC_U8("*.xlsx"), YYCC_U8("*.xls") }); - filters.Add(YYCC_U8("Microsoft PowerPoint (*.pptx; *.ppt)"), { YYCC_U8("*.pptx"), YYCC_U8("*.ppt") }); - filters.Add(YYCC_U8("Text File (*.txt)"), { YYCC_U8("*.txt") }); - filters.Add(YYCC_U8("All Files (*.*)"), { YYCC_U8("*.*") }); - params.SetDefaultFileTypeIndex(0u); - if (YYCC::DialogHelper::OpenFileDialog(params, ret)) { - Console::FormatLine(YYCC_U8("Open File: %s"), ret.c_str()); - } - if (YYCC::DialogHelper::OpenMultipleFileDialog(params, rets)) { - Console::WriteLine(YYCC_U8("Open Multiple Files:")); - for (const auto& item : rets) { - Console::FormatLine(YYCC_U8("\t%s"), item.c_str()); - } - } - if (YYCC::DialogHelper::SaveFileDialog(params, ret)) { - Console::FormatLine(YYCC_U8("Save File: %s"), ret.c_str()); - } - params.Clear(); - if (YYCC::DialogHelper::OpenFolderDialog(params, ret)) { - Console::FormatLine(YYCC_U8("Open Folder: %s"), ret.c_str()); - } - -#endif - } - - static void ExceptionTestbench() { -#if YYCC_OS == YYCC_OS_WINDOWS - - YYCC::ExceptionHelper::Register([](const YYCC::yycc_u8string& log_path, const YYCC::yycc_u8string& coredump_path) -> void { - MessageBoxW( - NULL, - YYCC::EncodingHelper::UTF8ToWchar( - YYCC::StringHelper::Printf(YYCC_U8("Log generated:\nLog path: %s\nCore dump path: %s"), log_path.c_str(), coredump_path.c_str()) - ).c_str(), - L"Fatal Error", MB_OK + MB_ICONERROR - ); - } - ); - - // Perform a div zero exception. -#if defined (YYCC_DEBUG_UE_FILTER) - // Reference: https://stackoverflow.com/questions/20981982/is-it-possible-to-debug-unhandledexceptionfilters-with-a-debugger - __try { - // all of code normally inside of main or WinMain here... - int i = 1, j = 0; - int k = i / j; - } __except (YYCC::ExceptionHelper::DebugCallUExceptionImpl(GetExceptionInformation())) { - OutputDebugStringW(L"executed filter function\n"); - } -#else - int i = 1, j = 0; - int k = i / j; -#endif - - YYCC::ExceptionHelper::Unregister(); - -#endif - } - - static void WinFctTestbench() { -#if YYCC_OS == YYCC_OS_WINDOWS - - HMODULE test_current_module; - Assert((test_current_module = YYCC::WinFctHelper::GetCurrentModule()) != nullptr, YYCC_U8("YYCC::WinFctHelper::GetCurrentModule")); - Console::FormatLine(YYCC_U8("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR), test_current_module); - - YYCC::yycc_u8string test_temp; - Assert(YYCC::WinFctHelper::GetTempDirectory(test_temp), YYCC_U8("YYCC::WinFctHelper::GetTempDirectory")); - Console::FormatLine(YYCC_U8("Temp Directory: %s"), test_temp.c_str()); - - YYCC::yycc_u8string test_module_name; - Assert(YYCC::WinFctHelper::GetModuleFileName(YYCC::WinFctHelper::GetCurrentModule(), test_module_name), YYCC_U8("YYCC::WinFctHelper::GetModuleFileName")); - Console::FormatLine(YYCC_U8("Current Module File Name: %s"), test_module_name.c_str()); - - YYCC::yycc_u8string test_localappdata_path; - Assert(YYCC::WinFctHelper::GetLocalAppData(test_localappdata_path), YYCC_U8("YYCC::WinFctHelper::GetLocalAppData")); - Console::FormatLine(YYCC_U8("Local AppData: %s"), test_localappdata_path.c_str()); - - Assert(YYCC::WinFctHelper::IsValidCodePage(static_cast(1252)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage")); - Assert(!YYCC::WinFctHelper::IsValidCodePage(static_cast(114514)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage")); - - // MARK: There is no testbench for MoveFile, CopyFile DeleteFile. - // Because they can operate file system files. - // And may cause test environment entering unstable status. - -#endif - } - - static void StdPatchTestbench() { - - // Std Path - - std::filesystem::path test_path; - for (const auto& strl : c_UTF8TestStrTable) { - test_path /= YYCC::StdPatch::ToStdPath(strl); - } - YYCC::yycc_u8string test_slashed_path(YYCC::StdPatch::ToUTF8Path(test_path)); - -#if YYCC_OS == YYCC_OS_WINDOWS - std::wstring wdecilmer(1u, std::filesystem::path::preferred_separator); - YYCC::yycc_u8string decilmer(YYCC::EncodingHelper::WcharToUTF8(wdecilmer)); -#else - YYCC::yycc_u8string decilmer(1u, std::filesystem::path::preferred_separator); -#endif - YYCC::yycc_u8string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable.begin(), c_UTF8TestStrTable.end(), decilmer)); - - Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::StdPatch::ToStdPath, YYCC::StdPatch::ToUTF8Path")); - - // StartsWith, EndsWith - YYCC::yycc_u8string test_starts_ends_with(YYCC_U8("aaabbbccc")); - Assert(YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::StartsWith")); - Assert(!YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::StartsWith")); - Assert(!YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::EndsWith")); - Assert(YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::EndsWith")); - - // Contains - std::set test_set { 1, 2, 3, 4, 6, 7 }; - Assert(YYCC::StdPatch::Contains(test_set, static_cast(1)), YYCC_U8("YYCC::StdPatch::Contains")); - Assert(!YYCC::StdPatch::Contains(test_set, static_cast(5)), YYCC_U8("YYCC::StdPatch::Contains")); - std::map test_map { { 1, 1.0f }, { 4, 4.0f } }; - Assert(YYCC::StdPatch::Contains(test_map, static_cast(1)), YYCC_U8("YYCC::StdPatch::Contains")); - Assert(!YYCC::StdPatch::Contains(test_map, static_cast(5)), YYCC_U8("YYCC::StdPatch::Contains")); - - } - - enum class TestFlagEnum : uint8_t { - Test1 = 0b00000000, - Test2 = 0b00000001, - Test3 = 0b00000010, - Test4 = 0b00000100, - Test5 = 0b00001000, - Test6 = 0b00010000, - Test7 = 0b00100000, - Test8 = 0b01000000, - Test9 = 0b10000000, - Inverted = 0b01111111, - Merged = Test3 + Test5, - }; - - static void EnumHelperTestbench() { - TestFlagEnum val; - - Assert(YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5) == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Merge")); - - Assert(YYCC::EnumHelper::Invert(TestFlagEnum::Test9) == TestFlagEnum::Inverted, YYCC_U8("YYCC::EnumHelper::Invert")); - - val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5); - YYCC::EnumHelper::Mask(val, TestFlagEnum::Test3); - Assert(YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask")); - val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5); - YYCC::EnumHelper::Mask(val, TestFlagEnum::Test4); - Assert(!YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask")); - - val = TestFlagEnum::Test3; - YYCC::EnumHelper::Add(val, TestFlagEnum::Test5); - Assert(val == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Add")); - - val = TestFlagEnum::Merged; - YYCC::EnumHelper::Remove(val, TestFlagEnum::Test5); - Assert(val == TestFlagEnum::Test3, YYCC_U8("YYCC::EnumHelper::Remove")); - - val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5); - Assert(YYCC::EnumHelper::Has(val, TestFlagEnum::Test3), YYCC_U8("YYCC::EnumHelper::Has")); - Assert(!YYCC::EnumHelper::Has(val, TestFlagEnum::Test4), YYCC_U8("YYCC::EnumHelper::Has")); - - Assert(!YYCC::EnumHelper::Bool(TestFlagEnum::Test1), YYCC_U8("YYCC::EnumHelper::Bool")); - Assert(YYCC::EnumHelper::Bool(TestFlagEnum::Test2), YYCC_U8("YYCC::EnumHelper::Bool")); - - } - - static void VersionMacroTestbench() { - Assert(YYCC_VERCMP_E(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_E")); - Assert(!YYCC_VERCMP_NE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NE")); - Assert(YYCC_VERCMP_G(1, 2, 3, 0, 2, 5), YYCC_U8("YYCC_VERCMP_G")); - Assert(YYCC_VERCMP_GE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_GE")); - Assert(YYCC_VERCMP_NL(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NL")); - Assert(YYCC_VERCMP_L(0, 2, 5, 1, 2, 3), YYCC_U8("YYCC_VERCMP_L")); - Assert(YYCC_VERCMP_LE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_LE")); - Assert(YYCC_VERCMP_NG(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NG")); - } - - enum class TestEnum : int8_t { - Test1, Test2, Test3 - }; - - class TestConfigManager { - public: - TestConfigManager() : - m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)), - m_FloatSetting(YYCC_U8("float-setting"), 0.0f), - m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")), - m_BoolSetting(YYCC_U8("bool-setting"), false), - m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::Constraints::GetMinMaxRangeConstraint(-1.0f, 1.0f)), - m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1), - m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), { - &m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting - }) {} - ~TestConfigManager() {} - - void PrintSettings() { - Console::WriteLine(YYCC_U8("Config Manager Settings:")); - - Console::FormatLine(YYCC_U8("\tint-setting: %" PRIi32), m_IntSetting.Get()); - Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_FloatSetting.Get()); - Console::FormatLine(YYCC_U8("\tstring-setting: %s"), m_StringSetting.Get().c_str()); - - Console::FormatLine(YYCC_U8("\tbool-setting: %s"), m_BoolSetting.Get() ? YYCC_U8("true") : YYCC_U8("false")); - Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_ClampedFloatSetting.Get()); - Console::FormatLine(YYCC_U8("\tenum-setting: %" PRIi8), static_cast>(m_EnumSetting.Get())); - } - - YYCC::ConfigManager::NumberSetting m_IntSetting; - YYCC::ConfigManager::NumberSetting m_FloatSetting; - YYCC::ConfigManager::StringSetting m_StringSetting; - - YYCC::ConfigManager::NumberSetting m_BoolSetting; - YYCC::ConfigManager::NumberSetting m_ClampedFloatSetting; - YYCC::ConfigManager::NumberSetting m_EnumSetting; - - YYCC::ConfigManager::CoreManager m_CoreManager; - }; - - static void ConfigManagerTestbench() { - // init cfg manager - TestConfigManager test; - - // test constraint works - Assert(!test.m_ClampedFloatSetting.Set(2.0f), YYCC_U8("YYCC::Constraints::Constraint")); - Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::Constraints::Constraint")); - - // test modify settings -#define TEST_MACRO(member_name, set_val) { \ - Assert(test.member_name.Set(set_val), YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \ - Assert(test.member_name.Get() == set_val, YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \ -} - - TEST_MACRO(m_IntSetting, INT32_C(114)); - TEST_MACRO(m_FloatSetting, 2.0f); - TEST_MACRO(m_StringSetting, YYCC_U8("fuck")); - TEST_MACRO(m_BoolSetting, true); - TEST_MACRO(m_ClampedFloatSetting, 0.5f); - TEST_MACRO(m_EnumSetting, TestEnum::Test2); - -#undef TEST_MACRO - - // test save - test.PrintSettings(); - Assert(test.m_CoreManager.Save(), YYCC_U8("YYCC::ConfigManager::CoreManager::Save")); - - // test reset - test.m_CoreManager.Reset(); - test.PrintSettings(); - Assert(test.m_IntSetting.Get() == INT32_C(0), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); - Assert(test.m_FloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); - Assert(test.m_StringSetting.Get() == YYCC_U8(""), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); - Assert(test.m_BoolSetting.Get() == false, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); - Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); - Assert(test.m_EnumSetting.Get() == TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); - - // test load - YYCC::ConfigManager::ConfigLoadResult wrong_result = YYCC::EnumHelper::Merge( - YYCC::ConfigManager::ConfigLoadResult::ItemError, - YYCC::ConfigManager::ConfigLoadResult::BrokenFile - ); - Assert(!YYCC::EnumHelper::Has(test.m_CoreManager.Load(), wrong_result), YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); - test.PrintSettings(); - Assert(test.m_IntSetting.Get() == INT32_C(114), YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); - Assert(test.m_FloatSetting.Get() == 2.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); - Assert(test.m_StringSetting.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); - Assert(test.m_BoolSetting.Get() == true, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); - Assert(test.m_ClampedFloatSetting.Get() == 0.5f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); - Assert(test.m_EnumSetting.Get() == TestEnum::Test2, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); - - } - - 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(-1.0f, 1.0f)), - m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), { - &m_IntArgument, &m_FloatArgument, &m_StringArgument, - &m_BoolArgument, &m_ClampedFloatArgument - }) {} - ~TestArgParser() {} - - YYCC::ArgParser::NumberArgument m_IntArgument; - YYCC::ArgParser::NumberArgument m_FloatArgument; - YYCC::ArgParser::StringArgument m_StringArgument; - - YYCC::ArgParser::SwitchArgument m_BoolArgument; - YYCC::ArgParser::NumberArgument m_ClampedFloatArgument; - - YYCC::ArgParser::OptionContext m_OptionContext; - }; - - static void ArgParserTestbench(int argc, char* argv[]) { - // test command line getter - { - YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromStd")); - auto result = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv); - for (result.Reset(); !result.IsEOF(); result.Next()) { - YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str()); - } - } -#if YYCC_OS == YYCC_OS_WINDOWS - { - YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromWin32")); - auto result = YYCC::ArgParser::ArgumentList::CreateFromWin32(); - for (result.Reset(); !result.IsEOF(); result.Next()) { - YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str()); - } - } -#endif - - // test option context - // init option context - TestArgParser test; - -#define PREPARE_DATA(...) const char* test_argv[] = { __VA_ARGS__ }; \ -auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeof(char*), const_cast(test_argv)); - - // normal test - { - PREPARE_DATA("exec", "-i", "114514"); - Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(!test.m_FloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(!test.m_StringArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(!test.m_ClampedFloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - // no argument - { - PREPARE_DATA("exec"); - Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - // error argument - { - PREPARE_DATA("exec", "-?", "114514"); - Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - // lost argument - { - PREPARE_DATA("exec", "-i"); - Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - // dplicated assign - { - PREPARE_DATA("exec", "-i", "114514" "--int", "114514"); - Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - // extra useless argument - { - PREPARE_DATA("exec", "-i", "114514" "1919810"); - Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - // invalid clamp argument - { - PREPARE_DATA("exec", "-i", "114514", "--clamped-float", "114.0"); - Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - // full argument - { - PREPARE_DATA("exec", "-i", "114514", "-f", "2.0", "--string", "fuck", "-b", "--clamped-float", "0.5"); - Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(test.m_FloatArgument.IsCaptured() && test.m_FloatArgument.Get() == 2.0f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(test.m_StringArgument.IsCaptured() && test.m_StringArgument.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - Assert(test.m_ClampedFloatArgument.IsCaptured() && test.m_ClampedFloatArgument.Get() == 0.5f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); - test.m_OptionContext.Reset(); - } - - // Help text - test.m_OptionContext.Help(); - -#undef PREPARE_DATA - - } - -} +#include int main(int argc, char* argv[]) { - -#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0) -#error "The YYCC library used when compiling is not match code expected, this may cause build error." -#error "If you trust it, please annotate these preprocessor statement, otherwise please contact developer." -#endif - - // common testbench - // normal - YYCCTestbench::EncodingTestbench(); - YYCCTestbench::StringTestbench(); - YYCCTestbench::ParserTestbench(); - YYCCTestbench::WinFctTestbench(); - YYCCTestbench::StdPatchTestbench(); - YYCCTestbench::EnumHelperTestbench(); - YYCCTestbench::VersionMacroTestbench(); - // advanced - YYCCTestbench::ConfigManagerTestbench(); - YYCCTestbench::ArgParserTestbench(argc, argv); - - // testbench which may terminal app or ordering input - YYCCTestbench::ConsoleTestbench(); - YYCCTestbench::DialogTestbench(); - YYCCTestbench::ExceptionTestbench(); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/testbench/main_legacy.cpp b/testbench/main_legacy.cpp new file mode 100644 index 0000000..3041158 --- /dev/null +++ b/testbench/main_legacy.cpp @@ -0,0 +1,735 @@ +#include +#include +#include +#include + +namespace Console = YYCC::ConsoleHelper; + +namespace YYCCTestbench { + +#pragma region Unicode Test Data + + // UNICODE Test Strings + // Ref: https://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8 +#define TEST_UNICODE_STR_JAPAN "\u30E6\u30FC\u30B6\u30FC\u5225\u30B5\u30A4\u30C8" +#define TEST_UNICODE_STR_CHINA "\u7B80\u4F53\u4E2D\u6587" +#define TEST_UNICODE_STR_KOREA "\uD06C\uB85C\uC2A4 \uD50C\uB7AB\uD3FC\uC73C\uB85C" +#define TEST_UNICODE_STR_ISRAEL "\u05DE\u05D3\u05D5\u05E8\u05D9\u05DD \u05DE\u05D1\u05D5\u05E7\u05E9\u05D9\u05DD" +#define TEST_UNICODE_STR_EGYPT "\u0623\u0641\u0636\u0644 \u0627\u0644\u0628\u062D\u0648\u062B" +#define TEST_UNICODE_STR_GREECE "\u03A3\u1F72 \u03B3\u03BD\u03C9\u03C1\u03AF\u03B6\u03C9 \u1F00\u03C0\u1F78" +#define TEST_UNICODE_STR_RUSSIA "\u0414\u0435\u0441\u044F\u0442\u0443\u044E \u041C\u0435\u0436\u0434\u0443\u043D\u0430\u0440\u043E\u0434\u043D\u0443\u044E" +#define TEST_UNICODE_STR_THAILAND "\u0E41\u0E1C\u0E48\u0E19\u0E14\u0E34\u0E19\u0E2E\u0E31\u0E48\u0E19\u0E40\u0E2A\u0E37\u0E48\u0E2D\u0E21\u0E42\u0E17\u0E23\u0E21\u0E41\u0E2A\u0E19\u0E2A\u0E31\u0E07\u0E40\u0E27\u0E0A" +#define TEST_UNICODE_STR_FRANCE "fran\u00E7ais langue \u00E9trang\u00E8re" +#define TEST_UNICODE_STR_SPAIN "ma\u00F1ana ol\u00E9" +#define TEST_UNICODE_STR_MATHMATICS "\u222E E\u22C5da = Q, n \u2192 \u221E, \u2211 f(i) = \u220F g(i)" +#define TEST_UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug + +#define CONCAT(prefix, strl) prefix ## strl +#define CPP_U8_LITERAL(strl) YYCC_U8(strl) +#define CPP_U16_LITERAL(strl) CONCAT(u, strl) +#define CPP_U32_LITERAL(strl) CONCAT(U, strl) +#define CPP_WSTR_LITERAL(strl) CONCAT(L, strl) + + static std::vector c_UTF8TestStrTable { + CPP_U8_LITERAL(TEST_UNICODE_STR_JAPAN), + CPP_U8_LITERAL(TEST_UNICODE_STR_CHINA), + CPP_U8_LITERAL(TEST_UNICODE_STR_KOREA), + CPP_U8_LITERAL(TEST_UNICODE_STR_ISRAEL), + CPP_U8_LITERAL(TEST_UNICODE_STR_EGYPT), + CPP_U8_LITERAL(TEST_UNICODE_STR_GREECE), + CPP_U8_LITERAL(TEST_UNICODE_STR_RUSSIA), + CPP_U8_LITERAL(TEST_UNICODE_STR_THAILAND), + CPP_U8_LITERAL(TEST_UNICODE_STR_FRANCE), + CPP_U8_LITERAL(TEST_UNICODE_STR_SPAIN), + CPP_U8_LITERAL(TEST_UNICODE_STR_MATHMATICS), + CPP_U8_LITERAL(TEST_UNICODE_STR_EMOJI), + }; + static std::vector c_WStrTestStrTable { + CPP_WSTR_LITERAL(TEST_UNICODE_STR_JAPAN), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_CHINA), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_KOREA), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_ISRAEL), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_EGYPT), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_GREECE), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_RUSSIA), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_THAILAND), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_FRANCE), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_SPAIN), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_MATHMATICS), + CPP_WSTR_LITERAL(TEST_UNICODE_STR_EMOJI), + }; + static std::vector c_UTF16TestStrTable { + CPP_U16_LITERAL(TEST_UNICODE_STR_JAPAN), + CPP_U16_LITERAL(TEST_UNICODE_STR_CHINA), + CPP_U16_LITERAL(TEST_UNICODE_STR_KOREA), + CPP_U16_LITERAL(TEST_UNICODE_STR_ISRAEL), + CPP_U16_LITERAL(TEST_UNICODE_STR_EGYPT), + CPP_U16_LITERAL(TEST_UNICODE_STR_GREECE), + CPP_U16_LITERAL(TEST_UNICODE_STR_RUSSIA), + CPP_U16_LITERAL(TEST_UNICODE_STR_THAILAND), + CPP_U16_LITERAL(TEST_UNICODE_STR_FRANCE), + CPP_U16_LITERAL(TEST_UNICODE_STR_SPAIN), + CPP_U16_LITERAL(TEST_UNICODE_STR_MATHMATICS), + CPP_U16_LITERAL(TEST_UNICODE_STR_EMOJI), + }; + static std::vector c_UTF32TestStrTable { + CPP_U32_LITERAL(TEST_UNICODE_STR_JAPAN), + CPP_U32_LITERAL(TEST_UNICODE_STR_CHINA), + CPP_U32_LITERAL(TEST_UNICODE_STR_KOREA), + CPP_U32_LITERAL(TEST_UNICODE_STR_ISRAEL), + CPP_U32_LITERAL(TEST_UNICODE_STR_EGYPT), + CPP_U32_LITERAL(TEST_UNICODE_STR_GREECE), + CPP_U32_LITERAL(TEST_UNICODE_STR_RUSSIA), + CPP_U32_LITERAL(TEST_UNICODE_STR_THAILAND), + CPP_U32_LITERAL(TEST_UNICODE_STR_FRANCE), + CPP_U32_LITERAL(TEST_UNICODE_STR_SPAIN), + CPP_U32_LITERAL(TEST_UNICODE_STR_MATHMATICS), + CPP_U32_LITERAL(TEST_UNICODE_STR_EMOJI), + }; + +#undef CPP_WSTR_LITERAL +#undef CPP_U32_LITERAL +#undef CPP_U16_LITERAL +#undef CPP_U8_LITERAL +#undef CONCAT + +#pragma endregion + + static void Assert(bool condition, const YYCC::yycc_char8_t* description) { + if (condition) { + Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("OK: %s")), description); + } else { + Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Failed: %s\n")), description); + std::abort(); + } + } + + static void ConsoleTestbench() { + // Color Test + Console::EnableColorfulConsole(); + Console::WriteLine(YYCC_U8("Color Test:")); + +#define TEST_MACRO(col) Console::WriteLine(YYCC_U8("\t" YYCC_COLOR_ ## col ("\u2588\u2588") YYCC_COLOR_LIGHT_ ## col("\u2588\u2588") " " #col " / LIGHT " #col )); + // U+2588 is full block + + TEST_MACRO(BLACK); + TEST_MACRO(RED); + TEST_MACRO(GREEN); + TEST_MACRO(YELLOW); + TEST_MACRO(BLUE); + TEST_MACRO(MAGENTA); + TEST_MACRO(CYAN); + TEST_MACRO(WHITE); + +#undef TEST_MACRO + + // UTF8 Output Test + Console::WriteLine(YYCC_U8("UTF8 Output Test:")); + for (const auto& strl : c_UTF8TestStrTable) { + Console::FormatLine(YYCC_U8("\t%s"), strl.c_str()); + } + + // UTF8 Input Test + Console::WriteLine(YYCC_U8("UTF8 Input Test:")); + for (const auto& strl : c_UTF8TestStrTable) { + Console::FormatLine(YYCC_U8("\tPlease type: %s"), strl.c_str()); + Console::Write(YYCC_U8("\t> ")); + + YYCC::yycc_u8string gotten(Console::ReadLine()); + if (gotten == strl) Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_GREEN("\tMatched! Got: %s")), gotten.c_str()); + else Console::FormatLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("\tNOT Matched! Got: %s")), gotten.c_str()); + } + + } + + static void EncodingTestbench() { + // get test tuple size + size_t count = c_UTF8TestStrTable.size(); + + // check the convertion between given string + for (size_t i = 0u; i < count; ++i) { + // get item + const auto& u8str = c_UTF8TestStrTable[i]; + const auto& u16str = c_UTF16TestStrTable[i]; + const auto& u32str = c_UTF32TestStrTable[i]; + + // create cache variables + YYCC::yycc_u8string u8cache; + std::u16string u16cache; + std::u32string u32cache; + + // do convertion check + Assert(YYCC::EncodingHelper::UTF8ToUTF16(u8str, u16cache) && u16cache == u16str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF16")); + Assert(YYCC::EncodingHelper::UTF8ToUTF32(u8str, u32cache) && u32cache == u32str, YYCC_U8("YYCC::EncodingHelper::UTF8ToUTF32")); + + Assert(YYCC::EncodingHelper::UTF16ToUTF8(u16str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF16ToUTF8")); + Assert(YYCC::EncodingHelper::UTF32ToUTF8(u32str, u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::UTF32ToUTF8")); + } + + // check wstring convertion on windows +#if YYCC_OS == YYCC_OS_WINDOWS + for (size_t i = 0u; i < count; ++i) { + // get item + const auto& u8str = c_UTF8TestStrTable[i]; + const auto& wstr = c_WStrTestStrTable[i]; + + // create cache variables + YYCC::yycc_u8string u8cache; + std::wstring wcache; + + // do convertion check + Assert(YYCC::EncodingHelper::UTF8ToWchar(u8str.c_str(), wcache) && wcache == wstr, YYCC_U8("YYCC::EncodingHelper::UTF8ToWchar")); + Assert(YYCC::EncodingHelper::WcharToUTF8(wstr.c_str(), u8cache) && u8cache == u8str, YYCC_U8("YYCC::EncodingHelper::WcharToUTF8")); + } +#endif + + } + + static void StringTestbench() { + // Test Printf + auto test_printf = YYCC::StringHelper::Printf(YYCC_U8("%s == %s"), YYCC_U8("Hello World"), YYCC_U8("Hello, world")); + Assert(test_printf == YYCC_U8("Hello World == Hello, world"), YYCC_U8("YYCC::StringHelper::Printf")); + + // Test Replace + auto test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("bb"), YYCC_U8("dd")); // normal case + Assert(test_replace == YYCC_U8("aaddcc"), YYCC_U8("YYCC::StringHelper::Replace")); + test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC_U8("zz"), YYCC_U8("yy")); // no replace + Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace")); + test_replace = YYCC::StringHelper::Replace(YYCC_U8("aabbcc"), YYCC::yycc_u8string_view(), YYCC_U8("zz")); // empty finding + Assert(test_replace == YYCC_U8("aabbcc"), YYCC_U8("YYCC::StringHelper::Replace")); + test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaaabbaa"), YYCC_U8("aa"), YYCC_U8("")); // no replaced string + Assert(test_replace == YYCC_U8("bb"), YYCC_U8("YYCC::StringHelper::Replace")); + test_replace = YYCC::StringHelper::Replace(YYCC_U8("aaxcc"), YYCC_U8("x"), YYCC_U8("yx")); // nested replacing + Assert(test_replace == YYCC_U8("aayxcc"), YYCC_U8("YYCC::StringHelper::Replace")); + test_replace = YYCC::StringHelper::Replace(YYCC::yycc_u8string_view(), YYCC_U8(""), YYCC_U8("xy")); // empty source string + Assert(test_replace == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Replace")); + + // Test Upper / Lower + auto test_lower = YYCC::StringHelper::Lower(YYCC_U8("LOWER")); + Assert(test_lower == YYCC_U8("lower"), YYCC_U8("YYCC::StringHelper::Lower")); + auto test_upper = YYCC::StringHelper::Upper(YYCC_U8("upper")); + Assert(test_upper == YYCC_U8("UPPER"), YYCC_U8("YYCC::StringHelper::Upper")); + + // Test Join + std::vector test_join_container { + YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("") + }; + auto test_join = YYCC::StringHelper::Join(test_join_container.begin(), test_join_container.end(), YYCC_U8(", ")); + Assert(test_join == YYCC_U8(", 1, 2, "), YYCC_U8("YYCC::StringHelper::Join")); + + // Test Split + auto test_split = YYCC::StringHelper::Split(YYCC_U8(", 1, 2, "), YYCC_U8(", ")); // normal + Assert(test_split.size() == 4u, YYCC_U8("YYCC::StringHelper::Split")); + Assert(test_split[0] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split")); + Assert(test_split[1] == YYCC_U8("1"), YYCC_U8("YYCC::StringHelper::Split")); + Assert(test_split[2] == YYCC_U8("2"), YYCC_U8("YYCC::StringHelper::Split")); + Assert(test_split[3] == YYCC_U8(""), YYCC_U8("YYCC::StringHelper::Split")); + test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC_U8("-")); // no matched decilmer + Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); + Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split")); + test_split = YYCC::StringHelper::Split(YYCC_U8("test"), YYCC::yycc_u8string_view()); // empty decilmer + Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); + Assert(test_split[0] == YYCC_U8("test"), YYCC_U8("YYCC::StringHelper::Split")); + test_split = YYCC::StringHelper::Split(YYCC::yycc_u8string_view(), YYCC_U8("")); // empty source string + Assert(test_split.size() == 1u, YYCC_U8("YYCC::StringHelper::Split")); + Assert(test_split[0].empty(), YYCC_U8("YYCC::StringHelper::Split")); + + } + + static void ParserTestbench() { + + // Test success TryParse +#define TEST_MACRO(type_t, value, string_value, ...) { \ + YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \ + type_t cache; \ + Assert(YYCC::ParserHelper::TryParse(cache_string, cache, ##__VA_ARGS__) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \ +} + + TEST_MACRO(int8_t, INT8_C(-61), "-61"); + TEST_MACRO(uint8_t, UINT8_C(200), "200"); + TEST_MACRO(int16_t, INT16_C(6161), "6161"); + TEST_MACRO(uint16_t, UINT16_C(32800), "32800"); + TEST_MACRO(int32_t, INT32_C(61616161), "61616161"); + TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293"); + TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161"); + TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807"); + TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16); + TEST_MACRO(float, 1.0f, "1.0"); + TEST_MACRO(double, 1.0, "1.0"); + TEST_MACRO(bool, true, "true"); + +#undef TEST_MACRO + + // Test failed TryParse +#define TEST_MACRO(type_t, string_value, ...) { \ + YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \ + type_t cache; \ + Assert(!YYCC::ParserHelper::TryParse(cache_string, cache, ##__VA_ARGS__), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \ +} + + TEST_MACRO(int8_t, "6161"); + TEST_MACRO(uint8_t, "32800"); + TEST_MACRO(int16_t, "61616161"); + TEST_MACRO(uint16_t, "4294967293"); + TEST_MACRO(int32_t, "616161616161"); + TEST_MACRO(uint32_t, "9223372036854775807"); + TEST_MACRO(int64_t, "616161616161616161616161"); + TEST_MACRO(uint64_t, "92233720368547758079223372036854775807"); + TEST_MACRO(float, "1e40"); + TEST_MACRO(double, "1e114514"); + TEST_MACRO(bool, "hello, world!"); + +#undef TEST_MACRO + + // Test ToString +#define TEST_MACRO(type_t, value, string_value, ...) { \ + type_t cache = value; \ + YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString(cache, ##__VA_ARGS__)); \ + Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \ +} + + TEST_MACRO(int8_t, INT8_C(-61), "-61"); + TEST_MACRO(uint8_t, UINT8_C(200), "200"); + TEST_MACRO(int16_t, INT16_C(6161), "6161"); + TEST_MACRO(uint16_t, UINT16_C(32800), "32800"); + TEST_MACRO(int32_t, INT32_C(61616161), "61616161"); + TEST_MACRO(uint32_t, UINT32_C(4294967293), "4294967293"); + TEST_MACRO(int64_t, INT64_C(616161616161), "616161616161"); + TEST_MACRO(uint64_t, UINT64_C(9223372036854775807), "9223372036854775807"); + TEST_MACRO(uint32_t, UINT32_C(0xffff), "ffff", 16); + TEST_MACRO(float, 1.0f, "1.0", std::chars_format::fixed, 1); + TEST_MACRO(double, 1.0, "1.0", std::chars_format::fixed, 1); + TEST_MACRO(bool, true, "true"); + +#undef TEST_MACRO + + } + + static void DialogTestbench() { +#if YYCC_OS == YYCC_OS_WINDOWS + + YYCC::yycc_u8string ret; + std::vector rets; + + YYCC::DialogHelper::FileDialog params; + auto& filters = params.ConfigreFileTypes(); + filters.Add(YYCC_U8("Microsoft Word (*.docx; *.doc)"), { YYCC_U8("*.docx"), YYCC_U8("*.doc") }); + filters.Add(YYCC_U8("Microsoft Excel (*.xlsx; *.xls)"), { YYCC_U8("*.xlsx"), YYCC_U8("*.xls") }); + filters.Add(YYCC_U8("Microsoft PowerPoint (*.pptx; *.ppt)"), { YYCC_U8("*.pptx"), YYCC_U8("*.ppt") }); + filters.Add(YYCC_U8("Text File (*.txt)"), { YYCC_U8("*.txt") }); + filters.Add(YYCC_U8("All Files (*.*)"), { YYCC_U8("*.*") }); + params.SetDefaultFileTypeIndex(0u); + if (YYCC::DialogHelper::OpenFileDialog(params, ret)) { + Console::FormatLine(YYCC_U8("Open File: %s"), ret.c_str()); + } + if (YYCC::DialogHelper::OpenMultipleFileDialog(params, rets)) { + Console::WriteLine(YYCC_U8("Open Multiple Files:")); + for (const auto& item : rets) { + Console::FormatLine(YYCC_U8("\t%s"), item.c_str()); + } + } + if (YYCC::DialogHelper::SaveFileDialog(params, ret)) { + Console::FormatLine(YYCC_U8("Save File: %s"), ret.c_str()); + } + params.Clear(); + if (YYCC::DialogHelper::OpenFolderDialog(params, ret)) { + Console::FormatLine(YYCC_U8("Open Folder: %s"), ret.c_str()); + } + +#endif + } + + static void ExceptionTestbench() { +#if YYCC_OS == YYCC_OS_WINDOWS + + YYCC::ExceptionHelper::Register([](const YYCC::yycc_u8string& log_path, const YYCC::yycc_u8string& coredump_path) -> void { + MessageBoxW( + NULL, + YYCC::EncodingHelper::UTF8ToWchar( + YYCC::StringHelper::Printf(YYCC_U8("Log generated:\nLog path: %s\nCore dump path: %s"), log_path.c_str(), coredump_path.c_str()) + ).c_str(), + L"Fatal Error", MB_OK + MB_ICONERROR + ); + } + ); + + // Perform a div zero exception. +#if defined (YYCC_DEBUG_UE_FILTER) + // Reference: https://stackoverflow.com/questions/20981982/is-it-possible-to-debug-unhandledexceptionfilters-with-a-debugger + __try { + // all of code normally inside of main or WinMain here... + int i = 1, j = 0; + int k = i / j; + } __except (YYCC::ExceptionHelper::DebugCallUExceptionImpl(GetExceptionInformation())) { + OutputDebugStringW(L"executed filter function\n"); + } +#else + int i = 1, j = 0; + int k = i / j; +#endif + + YYCC::ExceptionHelper::Unregister(); + +#endif + } + + static void WinFctTestbench() { +#if YYCC_OS == YYCC_OS_WINDOWS + + HMODULE test_current_module; + Assert((test_current_module = YYCC::WinFctHelper::GetCurrentModule()) != nullptr, YYCC_U8("YYCC::WinFctHelper::GetCurrentModule")); + Console::FormatLine(YYCC_U8("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR), test_current_module); + + YYCC::yycc_u8string test_temp; + Assert(YYCC::WinFctHelper::GetTempDirectory(test_temp), YYCC_U8("YYCC::WinFctHelper::GetTempDirectory")); + Console::FormatLine(YYCC_U8("Temp Directory: %s"), test_temp.c_str()); + + YYCC::yycc_u8string test_module_name; + Assert(YYCC::WinFctHelper::GetModuleFileName(YYCC::WinFctHelper::GetCurrentModule(), test_module_name), YYCC_U8("YYCC::WinFctHelper::GetModuleFileName")); + Console::FormatLine(YYCC_U8("Current Module File Name: %s"), test_module_name.c_str()); + + YYCC::yycc_u8string test_localappdata_path; + Assert(YYCC::WinFctHelper::GetLocalAppData(test_localappdata_path), YYCC_U8("YYCC::WinFctHelper::GetLocalAppData")); + Console::FormatLine(YYCC_U8("Local AppData: %s"), test_localappdata_path.c_str()); + + Assert(YYCC::WinFctHelper::IsValidCodePage(static_cast(1252)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage")); + Assert(!YYCC::WinFctHelper::IsValidCodePage(static_cast(114514)), YYCC_U8("YYCC::WinFctHelper::IsValidCodePage")); + + // MARK: There is no testbench for MoveFile, CopyFile DeleteFile. + // Because they can operate file system files. + // And may cause test environment entering unstable status. + +#endif + } + + static void StdPatchTestbench() { + + // Std Path + + std::filesystem::path test_path; + for (const auto& strl : c_UTF8TestStrTable) { + test_path /= YYCC::StdPatch::ToStdPath(strl); + } + YYCC::yycc_u8string test_slashed_path(YYCC::StdPatch::ToUTF8Path(test_path)); + +#if YYCC_OS == YYCC_OS_WINDOWS + std::wstring wdecilmer(1u, std::filesystem::path::preferred_separator); + YYCC::yycc_u8string decilmer(YYCC::EncodingHelper::WcharToUTF8(wdecilmer)); +#else + YYCC::yycc_u8string decilmer(1u, std::filesystem::path::preferred_separator); +#endif + YYCC::yycc_u8string test_joined_path(YYCC::StringHelper::Join(c_UTF8TestStrTable.begin(), c_UTF8TestStrTable.end(), decilmer)); + + Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::StdPatch::ToStdPath, YYCC::StdPatch::ToUTF8Path")); + + // StartsWith, EndsWith + YYCC::yycc_u8string test_starts_ends_with(YYCC_U8("aaabbbccc")); + Assert(YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::StartsWith")); + Assert(!YYCC::StdPatch::StartsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::StartsWith")); + Assert(!YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("aaa")), YYCC_U8("YYCC::StdPatch::EndsWith")); + Assert(YYCC::StdPatch::EndsWith(test_starts_ends_with, YYCC_U8("ccc")), YYCC_U8("YYCC::StdPatch::EndsWith")); + + // Contains + std::set test_set { 1, 2, 3, 4, 6, 7 }; + Assert(YYCC::StdPatch::Contains(test_set, static_cast(1)), YYCC_U8("YYCC::StdPatch::Contains")); + Assert(!YYCC::StdPatch::Contains(test_set, static_cast(5)), YYCC_U8("YYCC::StdPatch::Contains")); + std::map test_map { { 1, 1.0f }, { 4, 4.0f } }; + Assert(YYCC::StdPatch::Contains(test_map, static_cast(1)), YYCC_U8("YYCC::StdPatch::Contains")); + Assert(!YYCC::StdPatch::Contains(test_map, static_cast(5)), YYCC_U8("YYCC::StdPatch::Contains")); + + } + + enum class TestFlagEnum : uint8_t { + Test1 = 0b00000000, + Test2 = 0b00000001, + Test3 = 0b00000010, + Test4 = 0b00000100, + Test5 = 0b00001000, + Test6 = 0b00010000, + Test7 = 0b00100000, + Test8 = 0b01000000, + Test9 = 0b10000000, + Inverted = 0b01111111, + Merged = Test3 + Test5, + }; + + static void EnumHelperTestbench() { + TestFlagEnum val; + + Assert(YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5) == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Merge")); + + Assert(YYCC::EnumHelper::Invert(TestFlagEnum::Test9) == TestFlagEnum::Inverted, YYCC_U8("YYCC::EnumHelper::Invert")); + + val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5); + YYCC::EnumHelper::Mask(val, TestFlagEnum::Test3); + Assert(YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask")); + val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5); + YYCC::EnumHelper::Mask(val, TestFlagEnum::Test4); + Assert(!YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask")); + + val = TestFlagEnum::Test3; + YYCC::EnumHelper::Add(val, TestFlagEnum::Test5); + Assert(val == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Add")); + + val = TestFlagEnum::Merged; + YYCC::EnumHelper::Remove(val, TestFlagEnum::Test5); + Assert(val == TestFlagEnum::Test3, YYCC_U8("YYCC::EnumHelper::Remove")); + + val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5); + Assert(YYCC::EnumHelper::Has(val, TestFlagEnum::Test3), YYCC_U8("YYCC::EnumHelper::Has")); + Assert(!YYCC::EnumHelper::Has(val, TestFlagEnum::Test4), YYCC_U8("YYCC::EnumHelper::Has")); + + Assert(!YYCC::EnumHelper::Bool(TestFlagEnum::Test1), YYCC_U8("YYCC::EnumHelper::Bool")); + Assert(YYCC::EnumHelper::Bool(TestFlagEnum::Test2), YYCC_U8("YYCC::EnumHelper::Bool")); + + } + + static void VersionMacroTestbench() { + Assert(YYCC_VERCMP_E(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_E")); + Assert(!YYCC_VERCMP_NE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NE")); + Assert(YYCC_VERCMP_G(1, 2, 3, 0, 2, 5), YYCC_U8("YYCC_VERCMP_G")); + Assert(YYCC_VERCMP_GE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_GE")); + Assert(YYCC_VERCMP_NL(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NL")); + Assert(YYCC_VERCMP_L(0, 2, 5, 1, 2, 3), YYCC_U8("YYCC_VERCMP_L")); + Assert(YYCC_VERCMP_LE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_LE")); + Assert(YYCC_VERCMP_NG(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NG")); + } + + enum class TestEnum : int8_t { + Test1, Test2, Test3 + }; + + class TestConfigManager { + public: + TestConfigManager() : + m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)), + m_FloatSetting(YYCC_U8("float-setting"), 0.0f), + m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")), + m_BoolSetting(YYCC_U8("bool-setting"), false), + m_ClampedFloatSetting(YYCC_U8("clamped-float-setting"), 0.0f, YYCC::Constraints::GetMinMaxRangeConstraint(-1.0f, 1.0f)), + m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1), + m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), { + &m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting + }) {} + ~TestConfigManager() {} + + void PrintSettings() { + Console::WriteLine(YYCC_U8("Config Manager Settings:")); + + Console::FormatLine(YYCC_U8("\tint-setting: %" PRIi32), m_IntSetting.Get()); + Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_FloatSetting.Get()); + Console::FormatLine(YYCC_U8("\tstring-setting: %s"), m_StringSetting.Get().c_str()); + + Console::FormatLine(YYCC_U8("\tbool-setting: %s"), m_BoolSetting.Get() ? YYCC_U8("true") : YYCC_U8("false")); + Console::FormatLine(YYCC_U8("\tfloat-setting: %f"), m_ClampedFloatSetting.Get()); + Console::FormatLine(YYCC_U8("\tenum-setting: %" PRIi8), static_cast>(m_EnumSetting.Get())); + } + + YYCC::ConfigManager::NumberSetting m_IntSetting; + YYCC::ConfigManager::NumberSetting m_FloatSetting; + YYCC::ConfigManager::StringSetting m_StringSetting; + + YYCC::ConfigManager::NumberSetting m_BoolSetting; + YYCC::ConfigManager::NumberSetting m_ClampedFloatSetting; + YYCC::ConfigManager::NumberSetting m_EnumSetting; + + YYCC::ConfigManager::CoreManager m_CoreManager; + }; + + static void ConfigManagerTestbench() { + // init cfg manager + TestConfigManager test; + + // test constraint works + Assert(!test.m_ClampedFloatSetting.Set(2.0f), YYCC_U8("YYCC::Constraints::Constraint")); + Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::Constraints::Constraint")); + + // test modify settings +#define TEST_MACRO(member_name, set_val) { \ + Assert(test.member_name.Set(set_val), YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \ + Assert(test.member_name.Get() == set_val, YYCC_U8("YYCC::ConfigManager::AbstractSetting::Set")); \ +} + + TEST_MACRO(m_IntSetting, INT32_C(114)); + TEST_MACRO(m_FloatSetting, 2.0f); + TEST_MACRO(m_StringSetting, YYCC_U8("fuck")); + TEST_MACRO(m_BoolSetting, true); + TEST_MACRO(m_ClampedFloatSetting, 0.5f); + TEST_MACRO(m_EnumSetting, TestEnum::Test2); + +#undef TEST_MACRO + + // test save + test.PrintSettings(); + Assert(test.m_CoreManager.Save(), YYCC_U8("YYCC::ConfigManager::CoreManager::Save")); + + // test reset + test.m_CoreManager.Reset(); + test.PrintSettings(); + Assert(test.m_IntSetting.Get() == INT32_C(0), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); + Assert(test.m_FloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); + Assert(test.m_StringSetting.Get() == YYCC_U8(""), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); + Assert(test.m_BoolSetting.Get() == false, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); + Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); + Assert(test.m_EnumSetting.Get() == TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset")); + + // test load + YYCC::ConfigManager::ConfigLoadResult wrong_result = YYCC::EnumHelper::Merge( + YYCC::ConfigManager::ConfigLoadResult::ItemError, + YYCC::ConfigManager::ConfigLoadResult::BrokenFile + ); + Assert(!YYCC::EnumHelper::Has(test.m_CoreManager.Load(), wrong_result), YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); + test.PrintSettings(); + Assert(test.m_IntSetting.Get() == INT32_C(114), YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); + Assert(test.m_FloatSetting.Get() == 2.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); + Assert(test.m_StringSetting.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); + Assert(test.m_BoolSetting.Get() == true, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); + Assert(test.m_ClampedFloatSetting.Get() == 0.5f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); + Assert(test.m_EnumSetting.Get() == TestEnum::Test2, YYCC_U8("YYCC::ConfigManager::CoreManager::Load")); + + } + + 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(-1.0f, 1.0f)), + m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), { + &m_IntArgument, &m_FloatArgument, &m_StringArgument, + &m_BoolArgument, &m_ClampedFloatArgument + }) {} + ~TestArgParser() {} + + YYCC::ArgParser::NumberArgument m_IntArgument; + YYCC::ArgParser::NumberArgument m_FloatArgument; + YYCC::ArgParser::StringArgument m_StringArgument; + + YYCC::ArgParser::SwitchArgument m_BoolArgument; + YYCC::ArgParser::NumberArgument m_ClampedFloatArgument; + + YYCC::ArgParser::OptionContext m_OptionContext; + }; + + static void ArgParserTestbench(int argc, char* argv[]) { + // test command line getter + { + YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromStd")); + auto result = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv); + for (result.Reset(); !result.IsEOF(); result.Next()) { + YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str()); + } + } +#if YYCC_OS == YYCC_OS_WINDOWS + { + YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromWin32")); + auto result = YYCC::ArgParser::ArgumentList::CreateFromWin32(); + for (result.Reset(); !result.IsEOF(); result.Next()) { + YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str()); + } + } +#endif + + // test option context + // init option context + TestArgParser test; + +#define PREPARE_DATA(...) const char* test_argv[] = { __VA_ARGS__ }; \ +auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeof(char*), const_cast(test_argv)); + + // normal test + { + PREPARE_DATA("exec", "-i", "114514"); + Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(!test.m_FloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(!test.m_StringArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(!test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(!test.m_ClampedFloatArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + // no argument + { + PREPARE_DATA("exec"); + Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + // error argument + { + PREPARE_DATA("exec", "-?", "114514"); + Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + // lost argument + { + PREPARE_DATA("exec", "-i"); + Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + // dplicated assign + { + PREPARE_DATA("exec", "-i", "114514" "--int", "114514"); + Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + // extra useless argument + { + PREPARE_DATA("exec", "-i", "114514" "1919810"); + Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + // invalid clamp argument + { + PREPARE_DATA("exec", "-i", "114514", "--clamped-float", "114.0"); + Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + // full argument + { + PREPARE_DATA("exec", "-i", "114514", "-f", "2.0", "--string", "fuck", "-b", "--clamped-float", "0.5"); + Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(test.m_FloatArgument.IsCaptured() && test.m_FloatArgument.Get() == 2.0f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(test.m_StringArgument.IsCaptured() && test.m_StringArgument.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(test.m_BoolArgument.IsCaptured(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + Assert(test.m_ClampedFloatArgument.IsCaptured() && test.m_ClampedFloatArgument.Get() == 0.5f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse")); + test.m_OptionContext.Reset(); + } + + // Help text + test.m_OptionContext.Help(); + +#undef PREPARE_DATA + + } + +} + +int main(int argc, char* argv[]) { + +#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0) +#error "The YYCC library used when compiling is not match code expected, this may cause build error." +#error "If you trust it, please annotate these preprocessor statement, otherwise please contact developer." +#endif + + // common testbench + // normal + YYCCTestbench::EncodingTestbench(); + YYCCTestbench::StringTestbench(); + YYCCTestbench::ParserTestbench(); + YYCCTestbench::WinFctTestbench(); + YYCCTestbench::StdPatchTestbench(); + YYCCTestbench::EnumHelperTestbench(); + YYCCTestbench::VersionMacroTestbench(); + // advanced + YYCCTestbench::ConfigManagerTestbench(); + YYCCTestbench::ArgParserTestbench(argc, argv); + + // testbench which may terminal app or ordering input + YYCCTestbench::ConsoleTestbench(); + YYCCTestbench::DialogTestbench(); + YYCCTestbench::ExceptionTestbench(); +} diff --git a/testbench/yycc/constraint.cpp b/testbench/yycc/constraint.cpp new file mode 100644 index 0000000..66509e4 --- /dev/null +++ b/testbench/yycc/constraint.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +#define CONSTRAINT ::yycc::constraint::Constraint + +namespace yycctest::constraint { + + template + bool check(const T& value) { + return false; + } + + template + T clamp(const T& value) { + return value; + } + + TEST(Constraint, Normal) { + CONSTRAINT instance(check, clamp); + EXPECT_TRUE(instance.support_check()); + EXPECT_TRUE(instance.support_clamp()); + EXPECT_FALSE(instance.check(0)); + EXPECT_EQ(instance.clamp(0), 0); + } + + TEST(Constraint, SomeNone) { + { + CONSTRAINT instance(check, nullptr); + EXPECT_TRUE(instance.support_check()); + EXPECT_FALSE(instance.support_clamp()); + EXPECT_FALSE(instance.check(0)); + } + { + CONSTRAINT instance(nullptr, clamp); + EXPECT_FALSE(instance.support_check()); + EXPECT_TRUE(instance.support_clamp()); + EXPECT_EQ(instance.clamp(0), 0); + } + } + + TEST(Constraint, AllNone) { + CONSTRAINT instance(nullptr, nullptr); + EXPECT_FALSE(instance.support_check()); + EXPECT_FALSE(instance.support_clamp()); + } + +} diff --git a/testbench/yycc/constraint/builder.cpp b/testbench/yycc/constraint/builder.cpp new file mode 100644 index 0000000..24cf2d4 --- /dev/null +++ b/testbench/yycc/constraint/builder.cpp @@ -0,0 +1,5 @@ +#include + +namespace yycctest::constraint::builder { + +} diff --git a/testbench/yycc/string.cpp b/testbench/yycc/string.cpp new file mode 100644 index 0000000..257ceaf --- /dev/null +++ b/testbench/yycc/string.cpp @@ -0,0 +1,2 @@ + +namespace yycctest::string {} diff --git a/testbench/yycc/string/op.cpp b/testbench/yycc/string/op.cpp new file mode 100644 index 0000000..54656ed --- /dev/null +++ b/testbench/yycc/string/op.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#define OP ::yycc::string::op + +namespace yycctest::string::op { + + TEST(StringOp, Printf) { + auto rv = OP::printf(YYCC_U8("%s == %s"), YYCC_U8("Hello World"), YYCC_U8("Hello, world")); + EXPECT_EQ(rv, YYCC_U8("Hello World == Hello, world")); + } + + TEST(StringOp, Replace) { + + } + + TEST(StringOp, Lower) { + auto rv = OP::lower(YYCC_U8("LOWER")); + EXPECT_EQ(rv, YYCC_U8("lower")); + } + + TEST(StringOp, Upper) { + auto rv = OP::upper(YYCC_U8("upper")); + EXPECT_EQ(rv, YYCC_U8("UPPER")); + } + + TEST(StringOp, Join) { + + } + + TEST(StringOp, Split) { + + } + +} diff --git a/testbench/yycc/string/reinterpret.cpp b/testbench/yycc/string/reinterpret.cpp new file mode 100644 index 0000000..a4570e6 --- /dev/null +++ b/testbench/yycc/string/reinterpret.cpp @@ -0,0 +1,6 @@ +#include +#include + +namespace yycctest::string::reinterpret { + +}