From e6c24b8b61edc0bdf320b6817ccc554fb54566e1 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 4 Aug 2024 11:57:56 +0800 Subject: [PATCH] feat: add helper macro and new Win32 function. - add IsValidCodePage in WinFctHelper to check whether code page number is valid. - add 6 macros to batchly (add / set default) (move / copy) (constructor / assign operator). - add default or delete (copy / move) (constructor / assign operator) for some classes. --- doc/src/index.dox | 2 +- doc/src/library_macros.dox | 80 ++++++++++++++++++++++++++++++++++++ doc/src/platform_checker.dox | 37 ----------------- doc/src/win_fct_helper.dox | 1 + src/ArgParser.cpp | 20 ++++----- src/ArgParser.hpp | 50 +++++++++++----------- src/ConfigManager.hpp | 4 ++ src/DialogHelper.hpp | 4 ++ src/WinFctHelper.cpp | 5 +++ src/WinFctHelper.hpp | 7 ++++ src/YYCCInternal.hpp | 46 +++++++++++++++++++++ testbench/main.cpp | 3 ++ 12 files changed, 185 insertions(+), 74 deletions(-) create mode 100644 doc/src/library_macros.dox delete mode 100644 doc/src/platform_checker.dox diff --git a/doc/src/index.dox b/doc/src/index.dox index 91fabc7..ce11db4 100644 --- a/doc/src/index.dox +++ b/doc/src/index.dox @@ -29,7 +29,7 @@ \li \subpage intro - \li \subpage platform_checker + \li \subpage library_macros \li \subpage library_encoding diff --git a/doc/src/library_macros.dox b/doc/src/library_macros.dox new file mode 100644 index 0000000..e4bdee8 --- /dev/null +++ b/doc/src/library_macros.dox @@ -0,0 +1,80 @@ +namespace YYCC { +/** + +\page library_macros Library Macros + +In this page we will introduce the macros defined by this library +which can not be grouped in other topic. + +\section library_macros__platform_checker Platform Checker + +In many cross platform applications, +programmer usually write code adapted to different platforms in one source file +and enable them respectively by macros representing the target platform. +As a cross platform library, +YYCC also has this feature and you can utilize it if you don't have other ways to so the same things. + +\subsection library_macros__platform_checker__values Values + +YYCC always define a macro called \c YYCC_OS to indicate the system of target platform. +In implementation, it will check following list from top to bottom to set matched value for it. + +\li \c YYCC_OS_WINDOWS: Windows environment. It is done by checking whether environment define \c _WIN32 macro. +\li \c YYCC_OS_LINUX: In current implementation, this means target platform is \b NOT Windows. + +\subsection library_macros__platform_checker__usage Usage + +Now you know any possible value of \c YYCC_OS. +The next step is how to use it to enable specified code in specific target platform. +We take Windows platform for example. +Assume \c blabla() function is Windows specific. +We have following example code: + +\code +#if YYCC_OS == YYCC_OS_WINDOWS +blabla(); +#endif +\endcode + +It's enough and simple that use \c \#if to bracket the Windows specified code. + +\section library_macros__batch_class_copy_move Batch Class Copy / Move Functions + +YYCC provides 6 macros to batchly remove class copy constructor and move constructor, +or set default class copy constructor and move constructor. + + + +*/ +} \ No newline at end of file diff --git a/doc/src/platform_checker.dox b/doc/src/platform_checker.dox deleted file mode 100644 index 185d29b..0000000 --- a/doc/src/platform_checker.dox +++ /dev/null @@ -1,37 +0,0 @@ -namespace YYCC { -/** - -\page platform_checker Platform Checker - -In many cross platform applications, -programmer usually write code adapted to different platforms in one source file -and enable them respectively by macros representing the target platform. -As a cross platform library, -YYCC also has this feature and you can utilize it if you don't have other ways to so the same things. - -\section platform_checker__values Values - -YYCC always define a macro called \c YYCC_OS to indicate the system of target platform. -In implementation, it will check following list from top to bottom to set matched value for it. - -\li \c YYCC_OS_WINDOWS: Windows environment. It is done by checking whether environment define \c _WIN32 macro. -\li \c YYCC_OS_LINUX: In current implementation, this means target platform is \b NOT Windows. - -\section platform_checker__usage Usage - -Now you know any possible value of \c YYCC_OS. -The next step is how to use it to enable specified code in specific target platform. -We take Windows platform for example. -Assume \c blabla() function is Windows specific. -We have following example code: - -\code -#if YYCC_OS == YYCC_OS_WINDOWS -blabla(); -#endif -\endcode - -It's enough and simple that use \c \#if to bracket the Windows specified code. - -*/ -} \ No newline at end of file diff --git a/doc/src/win_fct_helper.dox b/doc/src/win_fct_helper.dox index 4a56efb..6b17eb0 100644 --- a/doc/src/win_fct_helper.dox +++ b/doc/src/win_fct_helper.dox @@ -14,6 +14,7 @@ Currently this namespace has following functions: \li #GetTempDirectory: Get temporary directory in Windows. \li #GetModuleFileName: Get the path to module in file system by given handle. \li #GetLocalAppData: Get the path inside \%LOCALAPPDATA\% +\li #IsValidCodePage: Check whether given code page number is valid. */ } \ No newline at end of file diff --git a/src/ArgParser.cpp b/src/ArgParser.cpp index 998a6da..04eacf1 100644 --- a/src/ArgParser.cpp +++ b/src/ArgParser.cpp @@ -51,22 +51,22 @@ namespace YYCC::ArgParser { #endif ArgumentList::ArgumentList(std::vector&& arguments) : - m_Arguments(arguments), m_ArgumentsIterator(m_Arguments.begin()) {} + m_Arguments(arguments), m_ArgumentsCursor(0u) {} void ArgumentList::Prev() { - if (m_ArgumentsIterator == m_Arguments.begin()) + if (m_ArgumentsCursor == 0u) throw std::runtime_error("attempt to move on the head of iterator."); - --m_ArgumentsIterator; + --m_ArgumentsCursor; } void ArgumentList::Next() { if (IsEOF()) throw std::runtime_error("attempt to move on the tail of iterator."); - ++m_ArgumentsIterator; + ++m_ArgumentsCursor; } const yycc_u8string& ArgumentList::Argument() const { if (IsEOF()) throw std::runtime_error("attempt to get data on the tail of iterator."); - return *m_ArgumentsIterator; + return m_Arguments[m_ArgumentsCursor]; } bool ArgumentList::IsSwitch(bool* is_long_name, yycc_u8string* long_name, yycc_char8_t* short_name) const { @@ -87,7 +87,7 @@ namespace YYCC::ArgParser { bool ArgumentList::IsLongNameSwitch(yycc_u8string* name_part) const { // fetch current parameter if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator."); - const yycc_u8string& param = *m_ArgumentsIterator; + const yycc_u8string& param = m_Arguments[m_ArgumentsCursor]; // find double slash if (param.find(AbstractArgument::DOUBLE_DASH) != 0u) return false; // check gotten long name @@ -101,7 +101,7 @@ namespace YYCC::ArgParser { bool ArgumentList::IsShortNameSwitch(yycc_char8_t* name_part) const { // fetch current parameter if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator."); - const yycc_u8string& param = *m_ArgumentsIterator; + const yycc_u8string& param = m_Arguments[m_ArgumentsCursor]; // if the length is not exactly equal to 2, // or it not starts with dash, // it is impossible a short name @@ -118,13 +118,13 @@ namespace YYCC::ArgParser { bool ArgumentList::IsValue(yycc_u8string* val) const { bool is_value = !IsSwitch(); if (is_value && val != nullptr) - *val = *m_ArgumentsIterator; + *val = m_Arguments[m_ArgumentsCursor]; return is_value; } - bool ArgumentList::IsEOF() const { return m_ArgumentsIterator == m_Arguments.end(); } + bool ArgumentList::IsEOF() const { return m_ArgumentsCursor >= m_Arguments.size(); } - void ArgumentList::Reset() { m_ArgumentsIterator = m_Arguments.begin(); } + void ArgumentList::Reset() { m_ArgumentsCursor = 0u; } #pragma endregion diff --git a/src/ArgParser.hpp b/src/ArgParser.hpp index d2b300c..46228f7 100644 --- a/src/ArgParser.hpp +++ b/src/ArgParser.hpp @@ -55,20 +55,13 @@ namespace YYCC::ArgParser { private: /** * @brief Constructor of ArgumentList used internally. - * @param[in] arguments + * @param[in] arguments * Underlying argument list. * This argument list should remove first executable name before passing it to there. */ ArgumentList(std::vector&& arguments); public: - /// @brief Default copy constructor - ArgumentList(const ArgumentList&) = default; - /// @brief Default copy assigner - ArgumentList& operator=(const ArgumentList&) = default; - /// @brief Default move constructor - ArgumentList(ArgumentList&&) = default; - /// @brief Default move assigner - ArgumentList& operator=(ArgumentList&&) = default; + YYCC_DEF_CLS_COPY_MOVE(ArgumentList); public: /** @@ -89,17 +82,17 @@ namespace YYCC::ArgParser { const yycc_u8string& Argument() const; /** * @brief Check whether current argument is a option / switch. - * @param[out] is_long_name + * @param[out] is_long_name * It will be set true if this argument is long name, otherwise short name. * nullptr if you don't want to receive this infomation. - * @param[out] long_name + * @param[out] long_name * The container holding matched long name if it is (double dash stripped). * nullptr if you don't want to receive this infomation. - * @param[out] short_name + * @param[out] short_name * The variable holding matched short name if it is (dash stripped). * nullptr if you don't want to receive this infomation. * @exception std::runtime_error Try fetching data at the tail of argument list. - * @return + * @return * True if it is, otherwise false. * If this function return false, all given parameters are in undefined status. */ @@ -109,7 +102,7 @@ namespace YYCC::ArgParser { yycc_char8_t* short_name = nullptr) const; /** * @brief Check whether current argument is a value. - * @param[out] val + * @param[out] val * The variable holding value if it is. * nullptr if you don't want to receive this infomation. * @exception std::runtime_error Try fetching data at the tail of argument list. @@ -133,7 +126,7 @@ namespace YYCC::ArgParser { /** * @brief Check whether current argument is long name option / switch. * @details This function is used by IsSwitch() internally. - * @param[out] name_part + * @param[out] name_part * The container holding matched long name if it is (double dash stripped). * nullptr if you don't want to receive this infomation. * @return True if it is, otherwise false. @@ -142,7 +135,7 @@ namespace YYCC::ArgParser { /** * @brief Check whether current argument is short name option / switch. * @details This function is used by IsSwitch() internally. - * @param[out] name_part + * @param[out] name_part * The variable holding matched short name if it is (dash stripped). * nullptr if you don't want to receive this infomation. * @return True if it is, otherwise false. @@ -151,7 +144,7 @@ namespace YYCC::ArgParser { private: std::vector m_Arguments; - std::vector::const_iterator m_ArgumentsIterator; + size_t m_ArgumentsCursor; }; /** @@ -171,7 +164,7 @@ namespace YYCC::ArgParser { /** * @brief Check whether given short name is valid. * @details - * An ASCII code of valid short name + * An ASCII code of valid short name * should not lower than #MIN_SHORT_NAME or higher than #MAX_SHORT_NAME. * It also can not be #DASH. * @param[in] short_name Short name for checking. @@ -181,7 +174,7 @@ namespace YYCC::ArgParser { /** * @brief Check whether given long name is valid. * @details - * An ASCII code of every item in valid long name + * An ASCII code of every item in valid long name * should not lower than #MIN_SHORT_NAME or higher than #MAX_SHORT_NAME. * However it can be #DASH. This is different with short name. * @param[in] long_name Long name for checking. @@ -209,6 +202,7 @@ namespace YYCC::ArgParser { const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr, bool is_optional = false); virtual ~AbstractArgument(); + YYCC_DEL_CLS_COPY_MOVE(AbstractArgument); // ===== User Implementation ===== protected: @@ -226,12 +220,12 @@ namespace YYCC::ArgParser { virtual bool Parse(ArgumentList& al) = 0; /** * @brief User implemented custom reset function - * @remarks + * @remarks * In this function, user should claer its stored value if is has. * You don't need clar capture state. That is done by library self. */ virtual void Reset() = 0; - + // ===== Basic Infos ===== public: /// @brief Check whether this argument specify long name. @@ -267,7 +261,7 @@ namespace YYCC::ArgParser { yycc_u8string m_Description; yycc_u8string m_ArgumentExample; bool m_IsOptional; - + // ===== Capture State ===== public: /// @brief Check whether this argument has been captured. @@ -296,12 +290,13 @@ namespace YYCC::ArgParser { const yycc_char8_t* summary, const yycc_char8_t* description, std::initializer_list arguments); ~OptionContext(); + YYCC_DEL_CLS_COPY_MOVE(OptionContext); public: /** * @brief Start a parse. * @param[in] al The reference to ArgumentList for parsing. - * @return + * @return * True if success, otherwise false. * If this function return false, you should not visit any arguments it managed. */ @@ -325,7 +320,7 @@ namespace YYCC::ArgParser { }; #pragma region Argument Presets - + /** * @brief Arithmetic (integral, floating point. except bool) type argument * @tparam _Ty The internal stored type belongs to arithmetic type. @@ -350,6 +345,7 @@ namespace YYCC::ArgParser { Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) : AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {} virtual ~NumberArgument() {} + YYCC_DEL_CLS_COPY_MOVE(NumberArgument); public: /// @brief Get stored data in argument. @@ -401,14 +397,15 @@ namespace YYCC::ArgParser { const yycc_char8_t* description = nullptr) : // bool switch must be optional, because it is false if no given switch. // bool switch doesn't have argument, so it doesn't have example property. - AbstractArgument(long_name, short_name, description, nullptr, true) {} + AbstractArgument(long_name, short_name, description, nullptr, true) {} virtual ~SwitchArgument() {} + YYCC_DEL_CLS_COPY_MOVE(SwitchArgument); protected: virtual bool Parse(ArgumentList& al) override { return true; } // simply return true because no value to store. virtual void Reset() override {} // nothing need to be reset. }; - + /// @brief String type argument class StringArgument : public AbstractArgument { public: @@ -429,6 +426,7 @@ namespace YYCC::ArgParser { Constraints::Constraint constraint = Constraints::Constraint {}) : AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {} virtual ~StringArgument() {} + YYCC_DEL_CLS_COPY_MOVE(StringArgument); public: /// @brief Get stored data in argument. diff --git a/src/ConfigManager.hpp b/src/ConfigManager.hpp index b5664a9..0f453d2 100644 --- a/src/ConfigManager.hpp +++ b/src/ConfigManager.hpp @@ -30,6 +30,7 @@ namespace YYCC::ConfigManager { */ AbstractSetting(const yycc_u8string_view& name); virtual ~AbstractSetting(); + YYCC_DEL_CLS_COPY_MOVE(AbstractSetting); // Name interface public: @@ -89,6 +90,7 @@ namespace YYCC::ConfigManager { uint64_t version_identifier, std::initializer_list settings); ~CoreManager() {} + YYCC_DEL_CLS_COPY_MOVE(CoreManager); // Core functions public: @@ -129,6 +131,7 @@ namespace YYCC::ConfigManager { Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) : AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constraint(constraint) {} virtual ~NumberSetting() {} + YYCC_DEL_CLS_COPY_MOVE(NumberSetting); /// @brief Get stored data in setting. _Ty Get() const { return m_Data; } @@ -189,6 +192,7 @@ namespace YYCC::ConfigManager { m_DefaultData = default_value; } virtual ~StringSetting() {} + YYCC_DEL_CLS_COPY_MOVE(StringSetting); /// @brief Get reference to stored string. const yycc_u8string& Get() const { return m_Data; } diff --git a/src/DialogHelper.hpp b/src/DialogHelper.hpp index c5986a3..66c0749 100644 --- a/src/DialogHelper.hpp +++ b/src/DialogHelper.hpp @@ -31,6 +31,7 @@ namespace YYCC::DialogHelper { friend class WinFileDialog; public: WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {} + YYCC_DEL_CLS_COPY_MOVE(WinFileFilters); /// @brief Get the count of available file filters UINT GetFilterCount() const { @@ -67,6 +68,7 @@ namespace YYCC::DialogHelper { class FileFilters { public: FileFilters() : m_Filters() {} + YYCC_DEL_CLS_COPY_MOVE(FileFilters); /** * @brief Add a filter pair in file types list. @@ -123,6 +125,7 @@ namespace YYCC::DialogHelper { m_WinFileTypes(), m_WinDefaultFileTypeIndex(0u), m_HasTitle(false), m_HasInitFileName(false), m_WinTitle(), m_WinInitFileName(), m_WinInitDirectory(nullptr) {} + YYCC_DEL_CLS_COPY_MOVE(WinFileDialog); /// @brief Get whether this dialog has owner. bool HasOwner() const { return m_WinOwner != NULL; } @@ -189,6 +192,7 @@ namespace YYCC::DialogHelper { m_DefaultFileTypeIndex(0u), m_Title(), m_InitFileName(), m_InitDirectory(), m_HasTitle(false), m_HasInitFileName(false), m_HasInitDirectory(false) {} + YYCC_DEL_CLS_COPY_MOVE(FileDialog); /** * @brief Set the owner of dialog. diff --git a/src/WinFctHelper.cpp b/src/WinFctHelper.cpp index 130662d..bb6ee55 100644 --- a/src/WinFctHelper.cpp +++ b/src/WinFctHelper.cpp @@ -85,6 +85,11 @@ namespace YYCC::WinFctHelper { return YYCC::EncodingHelper::WcharToUTF8(known_path.get(), ret); } + bool IsValidCodePage(UINT code_page) { + CPINFOEXW cpinfo; + return GetCPInfoExW(code_page, 0, &cpinfo); + } + } #endif diff --git a/src/WinFctHelper.hpp b/src/WinFctHelper.hpp index 50a5ffd..03ac29a 100644 --- a/src/WinFctHelper.hpp +++ b/src/WinFctHelper.hpp @@ -57,6 +57,13 @@ namespace YYCC::WinFctHelper { */ bool GetLocalAppData(yycc_u8string& ret); + /** + * @brief Check whether given code page number is a valid one. + * @param[in] code_page The code page number. + * @return True if it is valid, otherwise false. + */ + bool IsValidCodePage(UINT code_page); + } #endif diff --git a/src/YYCCInternal.hpp b/src/YYCCInternal.hpp index 7614b49..0b901d3 100644 --- a/src/YYCCInternal.hpp +++ b/src/YYCCInternal.hpp @@ -1,5 +1,7 @@ #pragma once +#pragma region Operating System Identifier Macros + // Define operating system macros #define YYCC_OS_WINDOWS 2 #define YYCC_OS_LINUX 3 @@ -10,6 +12,10 @@ #define YYCC_OS YYCC_OS_LINUX #endif +#pragma endregion + +#pragma region Windows Shitty Behavior Disable Macros + // If we are in Windows, // we need add 2 macros to disable Windows shitty warnings and errors of // depracted functions and not secure functions. @@ -24,6 +30,10 @@ #endif +#pragma endregion + +#pragma region YYCC UTF8 Types + // Define the UTF8 char type we used. // And do a polyfill if no embedded char8_t type. @@ -67,4 +77,40 @@ namespace YYCC { It is equal to \c std::u8string_view if your current C++ standard support it. */ +#pragma endregion + +#pragma region Batch Class Move / Copy Function Macros + + /// @brief Explicitly remove copy (\c constructor and \c operator\=) for given class. +#define YYCC_DEL_CLS_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_DEL_CLS_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_DEL_CLS_COPY_MOVE(CLSNAME) \ + YYCC_DEL_CLS_COPY(CLSNAME) \ + YYCC_DEL_CLS_MOVE(CLSNAME) + + /// @brief Explicitly set default copy (\c constructor and \c operator\=) for given class. +#define YYCC_DEF_CLS_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_DEF_CLS_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_DEF_CLS_COPY_MOVE(CLSNAME) \ + YYCC_DEF_CLS_COPY(CLSNAME) \ + YYCC_DEF_CLS_MOVE(CLSNAME) + +#pragma endregion + diff --git a/testbench/main.cpp b/testbench/main.cpp index c5439c9..96cbab0 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -388,6 +388,9 @@ namespace YYCCTestbench { 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)) == true, YYCC_U8("YYCC::WinFctHelper::IsValidCodePage")); + Assert(YYCC::WinFctHelper::IsValidCodePage(static_cast(114514)) == false, YYCC_U8("YYCC::WinFctHelper::IsValidCodePage")); + #endif }