diff --git a/build-gnu.bat b/build-gnu.bat index 3c4aa390ca..818a501048 100644 --- a/build-gnu.bat +++ b/build-gnu.bat @@ -87,12 +87,6 @@ if errorlevel 1 ( popd exit /b 1 ) -mingw32-make -f "%~dp0tests\unittests\resources\en-US\Makefile" -C "%~dp0tests\unittests\resources\en-US" OUTDIR="%TESTS1_BUILD_DIR%" -if errorlevel 1 ( - echo error 2 errorlevel %errorlevel% - popd - exit /b 1 -) mingw32-make -f "%TESTS1_MAKEFILE%" MYDEFINES="%MYDEFINES%" SAKURA_CORE=../sakura_core OUTDIR=%OUTDIR% -j4 if errorlevel 1 ( echo error 2 errorlevel %errorlevel% diff --git a/sakura/sakura.natvis b/sakura/sakura.natvis index e55cf42bd9..2d760ef5fa 100644 --- a/sakura/sakura.natvis +++ b/sakura/sakura.natvis @@ -1,6 +1,6 @@ - + empty {m_pRawData,su} @@ -9,4 +9,27 @@ m_nDataBufSize / 2 + + empty + {m_pRawData,s} + + m_pRawData,s + m_nRawLen + m_nDataBufSize + + + + empty + {m_szData._Elems,su} + + m_szData._Elems,su + + + + empty + {m_szData._Elems,s} + + m_szData._Elems,s + + \ No newline at end of file diff --git a/sakura_core/CDataProfile.cpp b/sakura_core/CDataProfile.cpp index da53a8e116..4df4a57d5e 100644 --- a/sakura_core/CDataProfile.cpp +++ b/sakura_core/CDataProfile.cpp @@ -7,34 +7,6 @@ #include "StdAfx.h" #include "CDataProfile.h" -#include - -/*! - コンストラクタ - */ -StringBufferW::StringBufferW(WCHAR* pData, size_t maxCount) - : pszData_(pData) - , cchDataSize_(maxCount) -{ - if (pszData_ == nullptr) { - throw std::invalid_argument("data can't be nullptr"); - } - - if (cchDataSize_ == 0) { - throw std::invalid_argument("count can't be zero"); - } -} - -/*! - 代入演算子 - */ -StringBufferW& StringBufferW::operator = (std::wstring_view rhs) -{ - ::wcsncpy_s(pszData_, cchDataSize_, rhs.data(), _TRUNCATE); - - return *this; -} - namespace profile_data { //! 設定値を文字列に変換する diff --git a/sakura_core/CDataProfile.h b/sakura_core/CDataProfile.h index 25af7e8d31..d9857fe566 100644 --- a/sakura_core/CDataProfile.h +++ b/sakura_core/CDataProfile.h @@ -19,22 +19,7 @@ * * 読み書き可能なWCHARバッファとサイズを指定して構築する */ -class StringBufferW { - WCHAR* pszData_; - size_t cchDataSize_; - -public: - explicit StringBufferW(WCHAR* pData, size_t maxCount); - - template - explicit StringBufferW(WCHAR (&buffer)[N]) - : StringBufferW(buffer, N) {} - - [[nodiscard]] const WCHAR* c_str() const noexcept { return pszData_; } - [[nodiscard]] size_t capacity() const noexcept { return cchDataSize_; } - - StringBufferW& operator = (std::wstring_view rhs); -}; +using StringBufferW = basis::TCharBuffer; /*! * プロファイル用データ変換 @@ -220,7 +205,7 @@ namespace profile_data { template<> [[nodiscard]] inline bool TryParse(std::wstring_view profile, StringBufferW& value) noexcept { - if (profile.length() < value.capacity()) { + if (profile.length() < std::size(value)) { value = profile; return true; } diff --git a/sakura_core/StdAfx.h b/sakura_core/StdAfx.h index 6e3d5c5ded..224ac994d8 100644 --- a/sakura_core/StdAfx.h +++ b/sakura_core/StdAfx.h @@ -118,5 +118,11 @@ // プリコンパイルの有無がビルドパフォーマンスに大きく影響するため。 #include "env/DLLSHAREDATA.h" +// 文字列リテラルのサッフィクス L""s を有効にします +using namespace std::literals::string_literals; + +// 文字列参照リテラルのサッフィクス L""sv を有効にします +using namespace std::literals::string_view_literals; + //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。 diff --git a/sakura_core/macro/CPPA.cpp b/sakura_core/macro/CPPA.cpp index b7e35c7e09..12999c7bc1 100644 --- a/sakura_core/macro/CPPA.cpp +++ b/sakura_core/macro/CPPA.cpp @@ -247,6 +247,7 @@ char* CPPA::GetDeclarations( const MacroFuncInfo& cMacroFuncInfo, char* szBuffer strcpy( szArguments[i], "u0: Unknown" ); } } + std::filesystem::path funcName(cMacroFuncInfo.m_pszFuncName); if ( i > 0 ){ // 引数があったとき int j; char szArgument[8*20]; @@ -256,18 +257,18 @@ char* CPPA::GetDeclarations( const MacroFuncInfo& cMacroFuncInfo, char* szBuffer strcat( szArgument, "; " ); strcat( szArgument, szArguments[j] ); } - auto_sprintf( szBuffer, "%hs S_%ls(%hs)%hs; index %d;", + auto_sprintf( szBuffer, "%hs S_%hs(%hs)%hs; index %d;", szType, - cMacroFuncInfo.m_pszFuncName, + funcName.string().c_str(), szArgument, szReturn, cMacroFuncInfo.m_nFuncID ); } else { - auto_sprintf( szBuffer, "%hs S_%ls%hs; index %d;", + auto_sprintf( szBuffer, "%hs S_%hs%hs; index %d;", szType, - cMacroFuncInfo.m_pszFuncName, + funcName.string().c_str(), szReturn, cMacroFuncInfo.m_nFuncID ); diff --git a/sakura_core/mem/CMemory.h b/sakura_core/mem/CMemory.h index a31afddd41..dbae51b38a 100644 --- a/sakura_core/mem/CMemory.h +++ b/sakura_core/mem/CMemory.h @@ -25,11 +25,20 @@ #define CheckKanjiCode_MAXREADLENGTH 16384 /*! - メモリバッファクラス - - ヒープメモリにバッファ領域を確保する - 内部バッファのサイズは8の倍数に切り上げて確保される。 - NUL文字(\0)を含むバイナリシーケンスを格納することができる。 + * メモリバッファクラス + * + * ヒープメモリにバッファ領域を確保する + * 内部バッファのサイズは8の倍数に切り上げて確保される。 + * NUL文字(\0)を含むバイナリシーケンスを格納することができる。 + * + * * 元々は文字列バッファの変換機能を多く含むゴッドオブジェクトだった。 + * * UNICODE対応で文字列変換機能の一部が分離された。 + * * 残タスク _AppendSzメソッドの分離。 + * * 残タスク SwapHLByteメソッドの分離。 + * + * @author Norio Nakatani + * @date 1998/03/06 新規作成 + * @date 2007/11/06 kobake Unicode対応 */ class CMemory { @@ -39,6 +48,7 @@ class CMemory CMemory( const void* pData, size_t nDataLen ); CMemory( const CMemory& rhs ); CMemory( CMemory&& other ) noexcept; + // デストラクタを仮想にすると仮想関数テーブルへのポインタを持つ為にインスタンスの容量が増えてしまうので仮想にしない // 仮想デストラクタでは無いので派生クラスでメンバー変数を追加しない事 ~CMemory() noexcept; @@ -53,9 +63,16 @@ class CMemory void AppendRawData( const void* pData, size_t nDataLen ); //!< バッファの最後にデータを追加する void Reset( void ) noexcept; //!< バッファをリセットする - [[nodiscard]] const std::byte* GetRawPtr() const noexcept { return m_pRawData; } //!< データへのポインタを返す - std::byte* GetRawPtr() noexcept { return m_pRawData; } //!< データへのポインタを返す - [[nodiscard]] int GetRawLength() const noexcept { return static_cast(m_nRawLen); } //!<データ長を返す。バイト単位。 + template auto get() noexcept { return (T*)m_pRawData; } //!< データへのポインタを返す + template auto get() const noexcept { return (const T*)m_pRawData; } //!< データへのポインタを返す + + auto GetRawPtr() noexcept { return m_pRawData; } //!< データへのポインタを返す + auto GetRawPtr() const noexcept { return m_pRawData; } //!< データへのポインタを返す + + int GetRawLength() const noexcept { return Length(); } //!<データ長を返す。バイト単位。 + + template auto length() const noexcept { return m_nRawLen / sizeof(T); } + template int Length() const noexcept { return int(length()); } // 演算子 CMemory& operator = ( const CMemory& rhs ); @@ -72,7 +89,7 @@ class CMemory void _SetRawLength( size_t nLength ); void swap( CMemory& left ) noexcept; //! メモリ再確保を行わずに格納できる最大バイト数を求める - [[nodiscard]] int capacity() const noexcept { return 8 <= m_nDataBufSize ? m_nDataBufSize - 2: 0; } + int capacity() const noexcept { return 8 <= m_nDataBufSize ? m_nDataBufSize - 2: 0; } private: // 2002/2/10 aroka アクセス権変更 /* diff --git a/sakura_core/mem/CNative.cpp b/sakura_core/mem/CNative.cpp index fa4dbe763a..39e6b1fba9 100644 --- a/sakura_core/mem/CNative.cpp +++ b/sakura_core/mem/CNative.cpp @@ -7,8 +7,3 @@ #include "StdAfx.h" #include "CNative.h" -//! 空っぽにする -void CNative::Clear() -{ - this->_GetMemory()->_SetRawLength(0); -} diff --git a/sakura_core/mem/CNative.h b/sakura_core/mem/CNative.h index dc71c3ef23..afcaf9fd50 100644 --- a/sakura_core/mem/CNative.h +++ b/sakura_core/mem/CNative.h @@ -11,19 +11,205 @@ #include "mem/CMemory.h" -//※CMemoryをprotect継承することにより、あまり自由にCMemoryを使えないようにしておく -class CNative : protected CMemory{ -public: - //CMemory*ポインタを得る - CMemory* _GetMemory(){ return static_cast(this); } - const CMemory* _GetMemory() const{ return static_cast(this); } +#include "util/string_ex.h" + +/*! + * 文字列バッファ管理クラステンプレート + * + * ヒープメモリにバッファ領域を確保する + * ※CMemoryをprotect継承することにより、あまり自由にCMemoryを使えないようにしておく + * + * @author kobake + * @date 2007/11/06 kobake 新規作成 + */ +template +class CNative : protected CMemory { +private: + using Me = CNative; + using Base = CMemory; public: - //汎用 - void Clear(); //!< 空っぽにする -}; + using char_type = C; + using string_type = std::basic_string; + using string_view_type = std::basic_string_view; + + /*! + * デフォルトコンストラクタ + * + * メモリを確保せずに構築。 + */ + CNative() noexcept = default; + + /*! + * 文字列ポインタとサイズを指定して構築。 + */ + CNative( + _In_reads_(cchData) + const char_type* pData, //!< [in] 文字列ポインタ + size_t cchData //!< [in] 文字数 + ) + { + // CMemory派生クラスにはメンバー追加禁止 + static_assert(sizeof(Me) == sizeof(CMemory)); + + SetString(pData, cchData); + } + + //! 文字列参照を指定して構築。 + explicit CNative(string_view_type rhs) { SetString(rhs.data(), rhs.length()); } + + /*! + * 文字列ポインタを指定して構築。 + * + * 以下のような記述をできるようにexplicitは付けない。 + * CNativeW buf = L"value"; + */ + /* implicit */ CNative(_In_opt_z_ const char_type* pszData) { SetString(pszData); } + + /*! + * 文字列を指定して構築。 + * + * 以下のような記述をできるようにexplicitは付けない。 + * CNativeW buf = L"value"s; + * + * if (std::wsmatch m; std::regex_match(buf, m, std::wregex(LR"(^(val).*$)"))) { + * CNativeW matched = m[1]; + * } + */ + /* implicit */ CNative(const string_type& rhs) { SetString(rhs.data(), rhs.length()); } + + CMemory* _GetMemory() { return static_cast(this); } //(this); } //(); } + const char_type* data() const noexcept { return get(); } + + bool empty() const noexcept { return length() == 0; } //!< 空っぽかどうか + + char_type* GetStringPtr() { return data(); } + const char_type* GetStringPtr() const { return data(); } + + int GetStringLength() const { return Length(); } + + size_t length() const noexcept { return Base::length(); } + int Length() const noexcept { return int(length()); } + + //! バッファの内容を置き換える。 + void SetString( + _In_reads_(cchData) + const char_type* pData, //!< [in] 文字列ポインタ + size_t cchData //!< [in] 文字数 + ) + { + Base::SetRawData(pData, cchData * sizeof(char_type)); + } + + //! バッファの内容を置き換える。 + void SetString(_In_opt_z_ const char_type* pszData) { + if (!pszData) { + Base::Reset(); + } else { + SetString(pszData, auto_strlen(pszData)); + } + } + + //! メモリ再確保を行わずに格納できる最大文字数を求める + size_t size() const noexcept { return Base::capacity() / sizeof(char_type); } + + Me& operator = (string_view_type rhs) { SetString(rhs.data(), rhs.length()); return *this; } + Me& operator = (_In_opt_z_ const char_type* rhs) { SetString(rhs); return *this; } + Me& operator = (const string_type& rhs) { SetString(rhs.data(), rhs.length()); return *this; } + Me& operator = (const char_type ch) { SetString(&ch, 1); return *this; } + + Me& operator += (string_view_type rhs) { AppendString(rhs.data(), rhs.length()); return *this; } + Me& operator += (_In_z_ const char_type* rhs) { AppendString(rhs); return *this; } + Me& operator += (const string_type& rhs) { AppendString(rhs.data(), rhs.length()); return *this; } + Me& operator += (const char_type ch) { AppendString(&ch, 1); return *this; } + + explicit operator string_view_type() const noexcept { return string_view_type(data(), length()); } + explicit operator string_type() const noexcept { return string_type(data(), length()); } +}; #endif /* SAKURA_CNATIVE_D712C69D_7FA3_49CE_B63A_49273441B558_H_ */ diff --git a/sakura_core/mem/CNativeA.cpp b/sakura_core/mem/CNativeA.cpp index e5d005538b..88407fddee 100644 --- a/sakura_core/mem/CNativeA.cpp +++ b/sakura_core/mem/CNativeA.cpp @@ -6,121 +6,4 @@ */ #include "StdAfx.h" #include "CNativeA.h" -#include "debug/Debug1.h" -CNativeA::CNativeA( const char* szData, size_t cchData ) -{ - SetString(szData, cchData); -} - -CNativeA::CNativeA(const char* szData) -{ - SetString(szData); -} - -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // -// ネイティブ設定インターフェース // -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // - -// バッファの内容を置き換える -void CNativeA::SetString( const char* pszData ) -{ - if( pszData != nullptr ){ - std::string_view data( pszData ); - SetString( data.data(), data.length() ); - }else{ - Reset(); - } -} - -// バッファの内容を置き換える。nLenは文字単位。 -void CNativeA::SetString( const char* pData, size_t nDataLen ) -{ - SetRawData( pData, nDataLen ); -} - -// バッファの内容を置き換える -void CNativeA::SetNativeData( const CNativeA& cNative ) -{ - SetRawData( cNative.GetRawPtr(), cNative.GetRawLength() ); -} - -// バッファの最後にデータを追加する -void CNativeA::AppendString( const char* pszData ) -{ - AppendString(pszData, strlen(pszData)); -} - -//! バッファの最後にデータを追加する。nLengthは文字単位。 -void CNativeA::AppendString( const char* pszData, size_t nLength ) -{ - AppendRawData( pszData, nLength ); -} - -//! バッファの最後にデータを追加する (フォーマット機能付き) -void CNativeA::AppendStringF(const char* pszData, ...) -{ - char buf[2048]; - - // 整形 - va_list v; - va_start(v, pszData); - int len = _vsnprintf_s(buf, _countof(buf), _TRUNCATE, pszData, v); - int e = errno; - va_end(v); - - if (len == -1) { - DEBUG_TRACE(L"AppendStringF error. errno = %d", e); - throw std::exception(); - } - - // 追加 - AppendString( buf, len ); -} - -const CNativeA& CNativeA::operator = ( char cChar ) -{ - char pszChar[2]; - pszChar[0] = cChar; - pszChar[1] = '\0'; - SetRawData( pszChar, 1 ); - return *this; -} - -//! バッファの最後にデータを追加する -void CNativeA::AppendNativeData( const CNativeA& cNative ) -{ - AppendRawData( cNative.GetRawPtr(), cNative.GetRawLength() ); -} - -//! (重要:nDataLenは文字単位) バッファサイズの調整。必要に応じて拡大する。 -void CNativeA::AllocStringBuffer( size_t nDataLen ) -{ - AllocBuffer( nDataLen ); -} - -const CNativeA& CNativeA::operator += ( char ch ) -{ - char szChar[2]={ch,'\0'}; - AppendString(szChar); - return *this; -} - -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // -// ネイティブ取得インターフェース // -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // - -int CNativeA::GetStringLength() const -{ - return GetRawLength(); -} - -// 任意位置の文字取得。nIndexは文字単位。 -char CNativeA::operator[]( size_t nIndex ) const -{ - if( nIndex < static_cast(GetStringLength()) ){ - return GetStringPtr()[nIndex]; - }else{ - return 0; - } -} diff --git a/sakura_core/mem/CNativeA.h b/sakura_core/mem/CNativeA.h index 055fb0ac4b..d7f898a763 100644 --- a/sakura_core/mem/CNativeA.h +++ b/sakura_core/mem/CNativeA.h @@ -11,37 +11,13 @@ #include "CNative.h" -class CNativeA final : public CNative{ -public: - CNativeA() noexcept = default; - CNativeA( const char* szData, size_t cchData ); - CNativeA( const char* szData ); - - //ネイティブ設定 - void SetString( const char* pszData ); //!< バッファの内容を置き換える - void SetString( const char* pData, size_t nDataLen ); //!< バッファの内容を置き換える。nDataLenは文字単位。 - void SetNativeData( const CNativeA& cNative ); //!< バッファの内容を置き換える - void AppendString( const char* pszData ); //!< バッファの最後にデータを追加する - void AppendString( const char* pszData, size_t nLength ); //!< バッファの最後にデータを追加する。nLengthは文字単位。 - void AppendStringF(const char* pszData, ...); //!< バッファの最後にデータを追加する (フォーマット機能付き) - void AppendNativeData( const CNativeA& cNative ); //!< バッファの最後にデータを追加する - void AllocStringBuffer( size_t nDataLen ); //!< (重要:nDataLenは文字単位) バッファサイズの調整。必要に応じて拡大する。 - - //ネイティブ取得 - int GetStringLength() const; - char operator[]( size_t nIndex ) const; //!< 任意位置の文字取得。nIndexは文字単位。 - const char* GetStringPtr() const - { - return reinterpret_cast(GetRawPtr()); - } - char* GetStringPtr() - { - return reinterpret_cast(GetRawPtr()); - } - - //演算子 - const CNativeA& operator=( char ); - const CNativeA& operator+=( char ); -}; +/*! + * マルチバイト文字列管理クラス + * + * @author kobake + * @date 2007/11/06 kobake 新規作成 + * @deprecated use std::string instead + */ +using CNativeA = CNative; #endif /* SAKURA_CNATIVEA_03C02187_A42C_4403_9D24_8B4CA20EEA81_H_ */ diff --git a/sakura_core/mem/CNativeW.cpp b/sakura_core/mem/CNativeW.cpp index a3f7e83e29..203f650d71 100644 --- a/sakura_core/mem/CNativeW.cpp +++ b/sakura_core/mem/CNativeW.cpp @@ -50,41 +50,10 @@ CStringRef::CStringRef( const CNativeW& cmem ) noexcept return 0; } -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // -// コンストラクタ・デストラクタ // -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // -//! nDataLenは文字単位。 -CNativeW::CNativeW( const wchar_t* pData, size_t nDataLen ) -{ - SetString( pData, nDataLen ); -} - -CNativeW::CNativeW( const wchar_t* pData ) -{ - SetString(pData); -} - // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // // ネイティブ設定インターフェース // // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // -// バッファの内容を置き換える -void CNativeW::SetString( const wchar_t* pData, size_t nDataLen ) -{ - SetRawData( pData,nDataLen * sizeof(wchar_t) ); -} - -// バッファの内容を置き換える -void CNativeW::SetString( const wchar_t* pszData ) -{ - if( pszData != nullptr ){ - std::wstring_view data(pszData); - SetString( data.data(), data.length() ); - }else{ - Reset(); - } -} - void CNativeW::SetStringHoldBuffer( const wchar_t* pData, size_t nDataLen ) { SetRawDataHoldBuffer( pData, nDataLen * sizeof(wchar_t) ); @@ -96,67 +65,6 @@ void CNativeW::SetNativeData( const CNativeW& cNative ) SetRawData( cNative ); } -//! (重要:nDataLenは文字単位) バッファサイズの調整。必要に応じて拡大する。 -void CNativeW::AllocStringBuffer( size_t nDataLen ) -{ - AllocBuffer( nDataLen * sizeof(wchar_t) ); -} - -//! バッファの最後にデータを追加する。nLengthは文字単位。 -void CNativeW::AppendString( const wchar_t* pszData, size_t nDataLen ) -{ - AppendRawData( pszData, nDataLen * sizeof(wchar_t) ); -} - -//! バッファの最後にデータを追加する -void CNativeW::AppendString( std::wstring_view data ) -{ - AppendString( data.data(), data.length() ); -} - -/*! - * バッファの最後にデータを追加する (フォーマット機能付き) - * - * @param format フォーマット書式文字列 - * @param va_args C-style の可変長引数 - * @throws std::invalid_argument formatが無効値 - * @throws std::bad_alloc メモリ確保に失敗 - * @remark 不正なフォーマットを指定すると無効なパラメータ例外で即死します。 - */ -void CNativeW::AppendStringF( std::wstring_view format, ... ) -{ - // _vscwprintf に NULL を渡してはならないので除外する - if( format.empty() ){ - throw std::invalid_argument( "format can't be empty" ); - } - - // 可変長引数のポインタを取得 - va_list v; - va_start( v, format ); - - // 整形によって追加される文字数をカウント - const int additional = ::_vscwprintf( format.data(), v ); - - // 現在の文字列長を取得 - const auto currentLength = GetStringLength(); - - // 現在の文字数 + 追加文字数が収まるようにバッファを拡張する - const auto newCapacity = currentLength + additional; - AllocStringBuffer( newCapacity ); - - int added = 0; - if( additional > 0 ){ - // 追加処理の実体はCRTに委譲。この関数は無効な書式を与えると即死する。 - added = ::_vsnwprintf_s( &GetStringPtr()[currentLength], static_cast(additional) + 1, _TRUNCATE, format.data(), v ); - } - - // 可変長引数のポインタを解放 - va_end( v ); - - // 文字列終端を再設定する - _SetStringLength( currentLength + added ); -} - //! バッファの最後にデータを追加する void CNativeW::AppendNativeData( const CNativeW& cmemData ) { @@ -189,21 +97,8 @@ CNativeW operator + (const CNativeW& lhs, const wchar_t* rhs) noexcept(false) CNativeW operator + (const wchar_t* lhs, const CNativeW& rhs) noexcept(false) { CNativeW tmp(lhs); - return tmp + rhs; -} - -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // -// ネイティブ取得インターフェース // -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // - -// GetAt()と同機能 -[[nodiscard]] wchar_t CNativeW::operator[]( size_t nIndex ) const -{ - if( nIndex < static_cast(GetStringLength()) ){ - return GetStringPtr()[nIndex]; - }else{ - return 0; - } + tmp += rhs; + return tmp; } /*! diff --git a/sakura_core/mem/CNativeW.h b/sakura_core/mem/CNativeW.h index e80636e94e..fa4766144d 100644 --- a/sakura_core/mem/CNativeW.h +++ b/sakura_core/mem/CNativeW.h @@ -16,7 +16,13 @@ class CNativeW; -//! 文字列への参照を保持するクラス +/*! + * 文字列への参照を保持するクラス + * + * @author kobake + * @date 2007/11/06 kobake 新規作成 + * @deprecated use std::wstring_view instead + */ class CStringRef final{ public: CStringRef() noexcept = default; @@ -42,74 +48,63 @@ bool operator != (const wchar_t* lhs, const CNativeW& rhs) noexcept; CNativeW operator + (const CNativeW& lhs, const wchar_t* rhs) noexcept(false); CNativeW operator + (const wchar_t* lhs, const CNativeW& rhs) noexcept(false); -//! UNICODE文字列管理クラス -class CNativeW final : public CNative{ - friend bool operator == (const CNativeW& lhs, const wchar_t* rhs) noexcept; +/*! + * ワイド文字列管理クラス + * + * @author kobake + * @date 2007/11/06 kobake 新規作成 + */ +class CNativeW : public CNative { +private: + using Me = CNativeW; + using Base = CNative; public: //コンストラクタ・デストラクタ - CNativeW() noexcept = default; - CNativeW( const wchar_t* pData, size_t nDataLen ); //!< nDataLenは文字単位。 - CNativeW( const wchar_t* pData ); + using Base::Base; + + using Base::operator=; + using Base::operator+=; /*! メモリ確保済みかどうか */ [[nodiscard]] bool IsValid() const noexcept { return GetStringPtr() != nullptr; } - //管理 - void AllocStringBuffer( size_t nDataLen ); //!< (重要:nDataLenは文字単位) バッファサイズの調整。必要に応じて拡大する。 - - //WCHAR - void SetString( const wchar_t* pData, size_t nDataLen ); //!< バッファの内容を置き換える。nDataLenは文字単位。 - void SetString( const wchar_t* pszData ); //!< バッファの内容を置き換える。 void SetStringHoldBuffer( const wchar_t* pData, size_t nDataLen ); - void AppendString( const wchar_t* pszData, size_t nDataLen ); //!< バッファの最後にデータを追加する。nLengthは文字単位。成功すればtrue。メモリ確保に失敗したらfalseを返す。 - void AppendString( std::wstring_view data ); //!< バッファの最後にデータを追加する - void AppendStringF( std::wstring_view format, ... ); //!< バッファの最後にデータを追加する (フォーマット機能付き) //CNativeW void SetNativeData( const CNativeW& cNative ); //!< バッファの内容を置き換える void AppendNativeData( const CNativeW& cNative ); //!< バッファの最後にデータを追加する //演算子 - CNativeW operator + (const CNativeW& rhs) const { return (CNativeW(*this) += rhs); } - CNativeW& operator += (const CNativeW& rhs) { AppendNativeData(rhs); return *this; } - CNativeW& operator += (wchar_t ch) { return (*this += CNativeW(&ch, 1)); } - bool operator == (const CNativeW& rhs) const noexcept { return 0 == Compare(rhs); } - bool operator != (const CNativeW& rhs) const noexcept { return !(*this == rhs); } - - //ネイティブ取得インターフェース - [[nodiscard]] wchar_t operator[]( size_t nIndex ) const; //!< 任意位置の文字取得。nIndexは文字単位。 - CLogicInt GetStringLength() const //!< 文字列長を返す。文字単位。 - { - return CLogicInt(CNative::GetRawLength() / sizeof(wchar_t)); - } - const wchar_t* GetStringPtr() const - { - return reinterpret_cast(GetRawPtr()); - } - wchar_t* GetStringPtr() - { - return reinterpret_cast(GetRawPtr()); - } + Me& operator += (const Me& rhs) { AppendNativeData(rhs); return *this; } - //特殊 - void _SetStringLength( size_t nLength ) + friend bool operator == (const Me& lhs, const Me& rhs) noexcept { return 0 == lhs.Compare(rhs); } + friend bool operator != (const Me& lhs, const Me& rhs) noexcept { return !(lhs == rhs); } + + //! 任意位置の文字取得。nIndexは文字単位。 + char_type operator[]( size_t nIndex ) const //!< 任意位置の文字取得。nIndexは文字単位。 { - _SetRawLength( nLength * sizeof(wchar_t) ); + if (nIndex < length()) { + return data()[nIndex]; + } else { + return 0; + } } + + CLogicInt GetStringLength() const { return CLogicInt(Base::GetStringLength()); } + //末尾を1文字削る - void Chop() - { - int n = GetStringLength(); - n-=1; - if(n>=0){ - _SetStringLength(n); + void Chop() { + if (auto n = Base::GetStringLength(); 0 < n){ + _SetStringLength(n - 1); } } + //! メモリバッファを入れ替える void swap( CNativeW& left ){ CMemory::swap( left ); } + //! メモリ再確保を行わずに格納できる最大文字数を求める [[nodiscard]] int capacity() const noexcept { return CMemory::capacity() / sizeof(wchar_t); @@ -134,7 +129,6 @@ class CNativeW final : public CNative{ void Replace( std::wstring_view strFrom, std::wstring_view strTo ); //!< 文字列置換 void Replace( const wchar_t* pszFrom, size_t nFromLen, const wchar_t* pszTo, size_t nToLen ); //!< 文字列置換 -public: // -- -- staticインターフェース -- -- // //! 指定した位置の文字がwchar_t何個分かを返す static CLogicInt GetSizeOfChar( const wchar_t* pData, int nDataLen, int nIdx ); @@ -157,7 +151,4 @@ class CNativeW final : public CNative{ { return GetHabaOfChar(cStr.GetPtr(), cStr.GetLength(), nIdx, bEnableExtEol); } }; -// 派生クラスでメンバー追加禁止 -static_assert(sizeof(CNativeW) == sizeof(CNative), "size check"); - #endif /* SAKURA_CNATIVEW_3B48F63E_5B62_4FAB_9718_0D80114E20C1_H_ */ diff --git a/sakura_core/util/StaticType.h b/sakura_core/util/StaticType.h index 5776d7a007..c90365409d 100644 --- a/sakura_core/util/StaticType.h +++ b/sakura_core/util/StaticType.h @@ -14,6 +14,428 @@ #include "util/string_ex.h" #include "debug/Debug2.h" +namespace basis { + +template +class SString; + +/*! + * バッファ参照型クラステンプレート + * + * 共有メモリ入出力 CShareData_IO で使われていたStringBufferWを拡張したもの。 + * + * 確保済み生ポインタをC++で扱うためのもので stdcpp20のstd::span と似ている。 + * ポインター型への暗黙変換ができる。 + * 代入と加算代入の演算子が使える。(入りきらない場合は切り捨て。) + */ +template +class TCharBuffer { +private: + using Me = TCharBuffer; + + C* m_Data; //!< 文字列バッファ。バッファは常にNUL終端されているとは限らない。 + size_t m_Size; //!< バッファサイズ(NUL終端の分を含む)。 + +public: + using char_type = C; + using string_type = std::basic_string; + using string_view_type = std::basic_string_view; + + /*! + * コンストラクタ + * + * 読み書き可能な文字バッファとサイズを指定して構築する + */ + TCharBuffer( + _In_reads_(count) + char_type* buffer, //!< [in] 文字列バッファ(NUL終端不要)。 + _In_range_(1, UINT_MAX) + size_t count //!< [in] バッファサイズ(NUL終端の分を含む)。 + ) + : m_Data(buffer) + , m_Size(count) + { + if (m_Data == nullptr) { + throw std::invalid_argument("data can't be null"); + } + + if (m_Size == 0) { + throw std::invalid_argument("count can't be zero"); + } + } + + /*! + * 暗黙変換用コンストラクタ + * + * 配列拡張型からの暗黙変換を許容する。 + */ + template + explicit TCharBuffer(SString& buffer) noexcept + : m_Data(buffer) + , m_Size(std::size(buffer)) + { + } + + /*! + * 配列ラップ用コンストラクタ + */ + template + explicit TCharBuffer(char_type (&buffer)[N]) noexcept + : m_Data(buffer) + , m_Size(std::size(buffer)) + { + } + + //! 文字列ポインタと文字数を指定して内容を拡張する。 + errno_t append( + _In_reads_(count) + const char_type* text, //!< [in] 文字列(NUL終端不要) + _In_range_(1, UINT_MAX - 1) + size_t count //!< [in] 文字列長(NUL終端を含まない) + ) noexcept + { + if (count < 1) return EINVAL; + const auto currentLength = length(); + return Me(data() + currentLength, size() - currentLength).assign(text, count); + } + + //! 文字列ポインタを指定して内容を拡張する。 + errno_t append(_In_z_ const char_type* rhs) { + if (!rhs) return EINVAL; + return append(rhs, auto_strlen(rhs)); + } + + //! 文字列を指定して内容を拡張する。 + errno_t append(const string_type& rhs) noexcept { + return append(rhs.data(), rhs.length()); + } + + //! 文字列参照を指定して内容を拡張する。 + errno_t append(string_view_type rhs) noexcept { + return append(rhs.data(), rhs.length()); + } + + //! 文字列ポインタと文字数を指定して内容を置き換える。 + errno_t assign( + _In_reads_opt_(count) + const char_type* text, //!< [in] 文字列(NUL終端不要、途中にNUL文字があってもOK) + _In_range_(0, UINT_MAX - 1) + size_t count //!< [in] 文字列長(NUL終端を含まない) + ) noexcept + { + // 現在の文字数を算出する + const auto currentLength = length(); + + // 設定する文字数を算出する + const auto setLength = std::min(count, size() - 1); + + // コピーできる文字が存在する場合のみ、コピーを行う + if (text && setLength && data() != text) { + auto_strncpy_s(data(), size(), text, setLength); + } + // NUL終端する + else if (data()[setLength]) { + auto_memset(data() + setLength, '\0', std::max(1, currentLength - setLength)); + } + + // 戻り値を返却 + return setLength < count ? STRUNCATE : 0; + } + + //! 文字列ポインタを指定して内容を置き換える。 + errno_t assign(_In_opt_z_ const char_type* rhs) { + if (!rhs) { + return assign(nullptr, 0); + } else { + return assign(string_view_type(rhs)); + } + } + + //! 文字列を指定して内容を置き換える。 + errno_t assign(const string_type& rhs) noexcept { + return assign(rhs.data(), rhs.length()); + } + + //! 文字列参照を指定して内容を置き換える。 + errno_t assign(string_view_type rhs) noexcept { + return assign(rhs.data(), rhs.length()); + } + + //! ファイルパスを指定して内容を置き換える。 + errno_t assign(const std::filesystem::path& rhs) { + if constexpr (std::is_same_v) { + return assign(rhs.wstring()); + } else { +#ifdef __MINGW32__ + // MinGW-w64不具合対応(現在のコードページに依らずUTF-8にエンコードしてしまう) + *data() = 0; + size_t converted = 0; + return wcstombs_s(&converted, data(), size(), rhs.c_str(), _TRUNCATE); +#else + return assign(rhs.string()); +#endif + } + + } + + const char_type* c_str() const noexcept { return data(); } + char_type* data() noexcept { return m_Data; } + const char_type* data() const noexcept { return m_Data; } + + //! 文字列が空かどうかを判定する + bool empty() const noexcept { return 0 == length(); } + + /*! + * 文字列の長さを取得する + * + * Cランタイム関数や添え字アクセスで全域にゴミを詰めた場合、0を返す。 + */ + size_t length() const noexcept { + if (0 == data()[0]) { + // バッファの先頭がNULなら長さはゼロ + return 0; + } else { + // バッファの先頭がNUL以外ならstrnlenで先頭からNULを検索する + const auto len = auto_strnlen(data(), size()); + return len < size() ? len : 0; + } + } + + size_t size() const noexcept { return m_Size; } + + Me& operator = (string_view_type rhs) { assign(rhs); return *this; } + Me& operator = (_In_opt_z_ const char_type* rhs) { assign(rhs); return *this; } + Me& operator = (const string_type& rhs) { assign(rhs); return *this; } + Me& operator = (const char_type ch) { assign(&ch, 1); return *this; } + Me& operator = (const std::filesystem::path& rhs) { assign(rhs); return *this; } + + template + Me& operator = (const SString& rhs ) { assign(rhs, rhs.length()); return *this; } + + Me& operator += (string_view_type rhs) { append(rhs); return *this; } + Me& operator += (_In_z_ const char_type* rhs) { append(rhs); return *this; } + Me& operator += (const string_type& rhs) { append(rhs); return *this; } + Me& operator += (const char_type ch) { append(&ch, 1); return *this; } + + template + Me& operator += (const SString& rhs ) { append(rhs, rhs.length()); return *this; } + + explicit operator string_view_type() const noexcept { return string_view_type(data(), length()); } + explicit operator std::filesystem::path() const noexcept { return std::filesystem::path(string_view_type(*this)); } + + /*! + * 文字列ポインタに変換する。 + * + * Windows API関数に渡すときキャスト不要になるよう暗黙キャストを許容する。 + */ + /* implicit */ operator char_type*() noexcept { return data(); } + + /*! + * 文字列ポインタに変換する。 + * + * Windows API関数に渡すときキャスト不要になるよう暗黙キャストを許容する。 + */ + /* implicit */ operator const char_type*() const noexcept { return c_str(); } +}; + +/*! + * 文字配列拡張型クラステンプレート + * + * StaticString(ヒープを用いない文字列クラス)を拡張したもの。 + * 代入演算子と加算演算子を追加している。 + * std::wstringやstd::filesystem::pathへの対応も後付け。 + * + * Cスタイル生配列の代わりに使える。 + * ポインター型への暗黙変換ができる。 + * 代入と加算代入の演算子が使える。(入りきらない場合は切り捨て。) + */ +template +class SString { +private: + using Me = SString; + std::array m_szData{}; + +public: + using char_type = C; + using string_type = std::basic_string; + using string_view_type = std::basic_string_view; + using buffer_type = TCharBuffer; + + static constexpr size_t size() noexcept { return N; } + + /*! + * デフォルトコンストラクタ + * + * コンテンツを指定せずに構築。 + */ + SString() = default; + + //! 文字列参照を指定して構築。 + explicit SString(string_view_type rhs) noexcept { assign(rhs); } + + /*! + * 文字列ポインタを指定して構築。 + * + * 以下のような記述をできるようにexplicitは付けない。 + * SString<_MAX_PATH> buf = L"value"; + */ + /* implicit */ SString(_In_opt_z_ const char_type* rhs) noexcept { assign(rhs); } + + /*! + * 文字列を指定して構築。 + * + * 以下のような記述をできるようにexplicitは付けない。 + * SString<_MAX_PATH> buf = L"value"s; + * + * if (std::wsmatch m; std::regex_match(buf, m, std::wregex(LR"(^(val).*$)"))) { + * CNativeW matched = m[1]; + * } + */ + /* implicit */ SString(const string_type& rhs) noexcept { assign(rhs); } + + /*! + * ファイルパスを指定して構築。 + * + * 以下のような記述をできるようにexplicitは付けない。 + * SString<_MAX_PATH> buf = std::filesystem::path(L"sakura.ini"); + */ + /* implicit */ SString(const std::filesystem::path& rhs) noexcept { assign(rhs); } + + //! 文字列ポインタと文字数を指定して内容を拡張する。 + errno_t append( + _In_reads_(count) + const char_type* text, + _In_range_(1, N - 1) + size_t count + ) + { + const auto currentLength = length(); + return buffer_type(data() + currentLength, size() - currentLength).assign(text, count); + } + + //! 文字列ポインタを指定して内容を拡張する。 + errno_t append(_In_z_ const char_type* rhs) { + if (!rhs) return EINVAL; + const auto currentLength = length(); + return buffer_type(data() + currentLength, size() - currentLength).assign(rhs); + } + + //! 文字列を指定して内容を拡張する。 + errno_t append(const string_type& rhs) { + if (rhs.empty()) return EINVAL; + const auto currentLength = length(); + return buffer_type(data() + currentLength, size() - currentLength).assign(rhs); + } + + //! 文字列参照を指定して内容を拡張する。 + errno_t append(string_view_type rhs) { + if (rhs.empty()) return EINVAL; + const auto currentLength = length(); + return buffer_type(data() + currentLength, size() - currentLength).assign(rhs); + } + + //! ファイルパスを指定して内容を拡張する。 + errno_t append(const std::filesystem::path& rhs) { + if (rhs.empty()) return EINVAL; + const auto currentLength = length(); + return buffer_type(data() + currentLength, size() - currentLength).assign(rhs); + } + + //! 文字列ポインタと文字数を指定して内容を置き換える。 + errno_t assign( + _In_reads_opt_(count) + const char_type* text, + _In_range_(0, N - 1) + size_t count + ) + { + return buffer_type(data(), size()).assign(text, count); + } + + //! 文字列ポインタを指定して内容を置き換える。 + errno_t assign(_In_opt_z_ const char_type* rhs) { + return buffer_type(data(), size()).assign(rhs); + } + + //! 文字列を指定して内容を置き換える。 + errno_t assign(const string_type& rhs) { + return buffer_type(data(), size()).assign(rhs); + } + + //! 文字列参照を指定して内容を置き換える。 + errno_t assign(string_view_type rhs) { + return buffer_type(data(), size()).assign(rhs); + } + + //! ファイルパスを指定して内容を置き換える。 + errno_t assign(const std::filesystem::path& rhs) { + return buffer_type(data(), size()).assign(rhs); + } + + const char_type* c_str() const noexcept { return data(); } + char_type* data() noexcept { return m_szData.data(); } + const char_type* data() const noexcept { return m_szData.data(); } + + //! 文字列が空かどうかを判定する + bool empty() const noexcept { return 0 == length(); } + + /*! + * 文字列の長さを取得する + * + * Cランタイム関数や添え字アクセスで全域にゴミを詰めた場合、0を返す。 + */ + size_t length() const noexcept { + if (0 == data()[0]) { + // バッファの先頭がNULなら長さはゼロ + return 0; + } else { + // バッファの先頭がNUL以外ならstrnlenで先頭からNULを検索する + const auto len = auto_strnlen(data(), size()); + return len < size() ? len : 0; + } + } + + /*! + * 文字列の長さを取得する + * + * 旧コードとの互換性のため戻り値をintにキャストするバージョンを残す。 + */ + int Length() const noexcept { return int(length()); } + + Me& operator = (string_view_type rhs) { assign(rhs); return *this; } + Me& operator = (_In_opt_z_ const char_type* rhs) { assign(rhs); return *this; } + Me& operator = (const string_type& rhs) { assign(rhs); return *this; } + Me& operator = (const char_type ch) { assign(&ch, 1); return *this; } + Me& operator = (const std::filesystem::path& rhs) { assign(rhs); return *this; } + + Me& operator += (string_view_type rhs) { append(rhs); return *this; } + Me& operator += (_In_z_ const char_type* rhs) { append(rhs); return *this; } + Me& operator += (const string_type& rhs) { append(rhs); return *this; } + Me& operator += (const char_type ch) { append(&ch, 1); return *this; } + + explicit operator string_view_type() const noexcept { return string_view_type(data(), length()); } + explicit operator std::filesystem::path() const noexcept { return std::filesystem::path(string_view_type(*this)); } + + /*! + * 文字列ポインタに変換する。 + * + * Windows API関数に渡すときキャスト不要になるよう暗黙キャストを許容する。 + */ + /* implicit */ operator char_type*() noexcept { return data(); } + + /*! + * 文字列ポインタに変換する。 + * + * Windows API関数に渡すときキャスト不要になるよう暗黙キャストを許容する。 + */ + /* implicit */ operator const char_type*() const noexcept { return c_str(); } +}; + +} // namespace basis + +template +using SString = basis::SString; + //! ヒープを用いないvector //2007.09.23 kobake 作成。 template @@ -76,42 +498,86 @@ class StaticVector{ ElementType m_aElements[MAX_SIZE]; }; -//! ヒープを用いない文字列クラス -//2007.09.23 kobake 作成。 -template -class StaticString{ +/*! + * ヒープを用いない文字列クラス + * + * ヒープとはnewで確保するメモリ領域のこと。 + * std::wstringは文字列バッファを動的に確保するが、 + * StaticStringはスタックに固定サイズのバッファを確保する。 + * + * @date 2007/09/23 kobake 作成。 + */ +template +class StaticString : public SString { private: - using Me = StaticString; -public: - static const int BUFFER_COUNT = N_BUFFER_COUNT; + using Me = StaticString; + using Base = SString; + public: - //コンストラクタ・デストラクタ - StaticString(){ m_szData[0]=0; } - StaticString(const WCHAR* rhs){ if(!rhs) m_szData[0]=0; else wcscpy(m_szData,rhs); } + using char_type = typename Base::char_type; - //クラス属性 - size_t GetBufferCount() const{ return N_BUFFER_COUNT; } + static constexpr int BUFFER_COUNT = N_BUFFER_COUNT; - //データアクセス - WCHAR* GetBufferPointer() { return m_szData; } - const WCHAR* GetBufferPointer() const{ return m_szData; } - const WCHAR* c_str() const{ return m_szData; } //std::string風 + /*! + * バッファの個数を取得 + * + * 旧コードとの互換性のため 長い名前のバージョンを残しておく + */ + static size_t GetBufferCount() noexcept { return BUFFER_COUNT; } - //簡易データアクセス - operator WCHAR*() { return m_szData; } - operator const WCHAR*() const{ return m_szData; } - WCHAR At(int nIndex) const{ return m_szData[nIndex]; } + /* + * コンストラクタは基底クラスのものを流用する。 + */ + using Base::Base; - //簡易コピー - void Assign(const WCHAR* src){ if(!src) m_szData[0]=0; else wcscpy_s(m_szData,_countof(m_szData),src); } - Me& operator = (const WCHAR* src){ Assign(src); return *this; } + /* + * 基底クラスの代入演算子を流用する。 + */ + using Base::operator =; - //各種メソッド - int Length() const { return static_cast(auto_strnlen(m_szData, BUFFER_COUNT)); } + /*! + * データアクセス + * + * 旧コードとの互換性のため 長い名前のバージョンを残しておく + */ + char_type* GetBufferPointer() noexcept { return Base::data(); } -private: - WCHAR m_szData[N_BUFFER_COUNT]; + /*! + * データアクセス + * + * 旧コードとの互換性のため 長い名前のバージョンを残しておく + */ + const char_type* GetBufferPointer() const noexcept { return Base::data(); } + + /*! + * 指定位置の文字を取得 + * + * 旧コードとの互換性のため stdcpp と振る舞いの異なるバージョンを残しておく + */ + char_type At(int nIndex) const noexcept + { + if (nIndex < Base::Length()) { + return Base::data()[nIndex]; + } else { + return 0; + } + } + + /*! + * 簡易コピー + * + * 旧コードとの互換性のため std::wstring と異なる名前のバージョンを残しておく + */ + void Assign(_In_opt_z_ const char_type* src) { Base::assign(src); } }; +/*! + * バッファサイズ取得マクロ + * + * 旧コードとの互換性のため残しておく。 + * + * 代わりにstd::size() を使うことを推奨。 + */ #define _countof2(s) s.BUFFER_COUNT + #endif /* SAKURA_STATICTYPE_54CC2BD5_4C7C_4584_B515_EF8C533B90EA_H_ */ diff --git a/sakura_core/util/string_ex.h b/sakura_core/util/string_ex.h index f6bce59122..b327478bc9 100644 --- a/sakura_core/util/string_ex.h +++ b/sakura_core/util/string_ex.h @@ -126,6 +126,16 @@ inline ACHAR* auto_strcpy(ACHAR* dst, const ACHAR* src){ return strcpy(dst,src); inline WCHAR* auto_strcpy(WCHAR* dst, const WCHAR* src){ return wcscpy(dst,src); } inline errno_t auto_strcpy_s(ACHAR* dst, size_t nDstCount, const ACHAR* src){ return strcpy_s(dst,nDstCount,src); } inline errno_t auto_strcpy_s(WCHAR* dst, size_t nDstCount, const WCHAR* src){ return wcscpy_s(dst,nDstCount,src); } + +template +errno_t auto_strncpy_s(_Out_writes_(nDstCount) value_type* dst, size_t nDstCount, _In_reads_(count) const value_type* src, size_t count) { + if constexpr (std::is_same_v) { + return wcsncpy_s(dst, nDstCount, src, count); + } else { + return strncpy_s(dst, nDstCount, src, count); + } +} + inline ACHAR* auto_strncpy(ACHAR* dst,const ACHAR* src,size_t count){ return strncpy(dst,src,count); } inline WCHAR* auto_strncpy(WCHAR* dst,const WCHAR* src,size_t count){ return wcsncpy(dst,src,count); } inline ACHAR* auto_memset(ACHAR* dest, ACHAR c, size_t count){ memset (dest,c,count); return dest; } @@ -178,6 +188,8 @@ WCHAR* strtotcs( WCHAR* dest, const ACHAR* src, size_t count ); WCHAR* strtotcs( WCHAR* dest, const WCHAR* src, size_t count ); //印字系 +inline int auto_vscprintf(const ACHAR* format, va_list& v) { return ::_vscprintf (format, v); } +inline int auto_vscprintf(const WCHAR* format, va_list& v) { return ::_vscwprintf(format, v); } inline int auto_vsprintf(ACHAR* buf, const ACHAR* format, va_list& v) { return ::vsprintf(buf, format, v); } inline int auto_vsprintf(WCHAR* buf, const WCHAR* format, va_list& v) { return ::_vswprintf(buf, format, v); } inline int auto_sprintf(ACHAR* buf, const ACHAR* format, ...) { va_list args; va_start(args, format); const int n = auto_vsprintf(buf, format, args); va_end(args); return n; } diff --git a/tests/unittests/Makefile b/tests/unittests/Makefile index 500d8d6bb5..58f027443b 100644 --- a/tests/unittests/Makefile +++ b/tests/unittests/Makefile @@ -148,7 +148,7 @@ $(exe): $(OBJS) $(SAKURA_OBJS) $(OBJS): -tests1_rc.o: tests1_rc.rc.utf8 $(sakura_rc) resources.ja-JP.zip resources.en-US.zip +tests1_rc.o: tests1_rc.rc.utf8 $(sakura_rc) resources.ja-JP.zip $(RC) -c utf-8 -I. -I$(SRCDIR) $(DEFINES) $< -o $@ tests1_rc.rc.utf8: tests1_rc.rc diff --git a/tests/unittests/code-main.cpp b/tests/unittests/code-main.cpp index e47e7a0361..237f78cd24 100644 --- a/tests/unittests/code-main.cpp +++ b/tests/unittests/code-main.cpp @@ -6,20 +6,6 @@ */ #include "pch.h" -#ifndef NOMINMAX -#define NOMINMAX -#endif /* #ifndef NOMINMAX */ - -#include -#include - -#include -#include -#include -#include -#include -#include - #include "basis/primitive.h" #include "util/string_ex.h" #include "StartEditorProcessForTest.h" @@ -85,9 +71,22 @@ static void InvokeWinMainIfNeeded(std::wstring_view commandLine) * テストモジュールのエントリポイント */ int wmain(int argc, wchar_t **argv) { + + // テスト実行時のロケールは日本語に固定する + const auto langId = MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT); + const auto lcid = MAKELCID(langId, SORT_DEFAULT); + SetThreadUILanguage(lcid); // スレッドのUI言語を変更 + // コマンドラインに -PROF 指定がある場合、wWinMainを起動して終了する。 InvokeWinMainIfNeeded(::GetCommandLineW()); + // LCIDからロケール名を取得("ja-JP"が取れる) + StaticString szLocaleName; + LCIDToLocaleName(lcid, szLocaleName, szLocaleName.Length(), 0); + + // Cロケールも変更 + _wsetlocale(LC_ALL, szLocaleName); + // WinMainを起動しない場合、標準のgmock_main同様の処理を実行する。 // InitGoogleMock は Google Test の初期化も行うため、InitGoogleTest を別に呼ぶ必要はない。 printf("Running main() from %s\n", __FILE__); diff --git a/tests/unittests/resources/en-US/Makefile b/tests/unittests/resources/en-US/Makefile deleted file mode 100644 index 74fd6ef65e..0000000000 --- a/tests/unittests/resources/en-US/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# Makefile for MinGW32/MinGW-W64 - -SRCDIR = $(patsubst %/,%,$(subst \,/,$(dir $(firstword $(MAKEFILE_LIST))))) - -# If SRCDIR is different from the current directory, set it to VPATH. -# (If SRCDIR ends with a backslash, remove it before set to VPATH.) -ifneq ($(SRCDIR),.) -VPATH = $(patsubst %\,%,$(SRCDIR)) -endif - -# The directory where the .exe files will be output. -# If empty, they will be output to the default directories. -OUTDIR = - -ifeq ($(SHELL),sh.exe) -# If cmd.exe is used as a shell. -MKDIR = md -RM = del -DIRSEP = $(strip \ ) -DEVNULL = NUL -P7Z = 7z -else -# If unix-like shell is used. -MKDIR = mkdir -p -RM = rm -f -DIRSEP = / -DEVNULL = /dev/null -P7Z = 7z -endif - -ifndef PREFIX -PREFIX= -RCPREFIX= -else ifeq ($(PREFIX),x86_64-w64-mingw32-) -RCPREFIX=$(PREFIX) -else ifeq ($(PREFIX),i686-w64-mingw32-) -ifeq ($(OS),Windows_NT) -RCPREFIX= -else -RCPREFIX=$(PREFIX) -endif -endif - -RC= $(RCPREFIX)windres - -TARGET = $(or $(OUTDIR),.)/resources.en-US.zip - -SRC_DIRS = test-plugin - -all: $(TARGET) - -$(TARGET): - $(P7Z) u $@ -tzip -mcu=on -r $(SRC_DIRS) - -clean: - -$(RM) $(TARGET) - -.PHONY: all clean diff --git a/tests/unittests/resources/en-US/test-plugin/plugin.def b/tests/unittests/resources/en-US/test-plugin/plugin.def deleted file mode 100644 index 0930e38b46..0000000000 --- a/tests/unittests/resources/en-US/test-plugin/plugin.def +++ /dev/null @@ -1 +0,0 @@ -plugin.def diff --git a/tests/unittests/test-cdecode.cpp b/tests/unittests/test-cdecode.cpp index 8a5893470f..898081c292 100644 --- a/tests/unittests/test-cdecode.cpp +++ b/tests/unittests/test-cdecode.cpp @@ -112,6 +112,10 @@ TEST(CDecode, uuencode) s.SetString(L"begin 666 test"); EXPECT_FALSE(CDecode_UuDecode().DoDecode(s, &m)); + // ボディに空行が含まれる場合 + s.SetString(L"begin 666 test\r\n\r\n"); + EXPECT_FALSE(CDecode_UuDecode().DoDecode(s, &m)); + // 文字列が end で終わっていない場合 s.SetString(L"begin 666 test\r\n!

szAlt = L"設定値"; + + // ACT + szText = szAlt; + + // ASSERT + EXPECT_THAT(szText, StrEq(std::wstring(szAlt))); + EXPECT_THAT(szText.length(), szAlt.length()); +} + +/*! + * @brief StringBufferWのテスト + * + * nullptrを代入する + */ +TEST(StringBufferW, assign101) +{ + // ARRANGE + LOGFONT lf{}; + wcscpy_s(lf.lfFaceName, L"初期値"); + auto szText = StringBufferW(lf.lfFaceName); + + // ACT + szText = nullptr; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"")); + EXPECT_THAT(szText.length(), 0); +} + +/*! + * @brief StringBufferWのテスト + * + * 文字列ポインタを代入する(値がnullptr) + */ +TEST(StringBufferW, assign102) +{ + // ARRANGE + LOGFONT lf{}; + wcscpy_s(lf.lfFaceName, L"初期値"); + auto szText = StringBufferW(lf.lfFaceName); + LPCWSTR pszNull = nullptr; + + // ACT + szText = pszNull; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"")); + EXPECT_THAT(szText.length(), 0); +} + +/*! + * @brief StringBufferWのテスト + * + * 文字列参照を代入する(サイズオーバー) + */ +TEST(StringBufferW, assign104) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + + // ACT + szText = L"設定値1"sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"設定値")); + EXPECT_THAT(szText.length(), 3); + + // ACT2 + EXPECT_EQ(STRUNCATE, szText.assign(L"設定値1"sv)); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"設定値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列ポインタを追加する + */ +TEST(StringBufferW, append002) +{ + // ARRANGE + LOGFONT lf{}; + wcscpy_s(lf.lfFaceName, L"初期値"); + auto szText = StringBufferW(lf.lfFaceName); + + // ACT + szText += L"よん♪"; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値よん♪")); + EXPECT_THAT(szText.length(), 6); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列を追加する + */ +TEST(StringBufferW, append003) +{ + // ARRANGE + LOGFONT lf{}; + wcscpy_s(lf.lfFaceName, L"初期値"); + auto szText = StringBufferW(lf.lfFaceName); + + // ACT + szText += L"よん♪"s; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値よん♪")); + EXPECT_THAT(szText.length(), 6); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列参照タを追加する + */ +TEST(StringBufferW, append004) +{ + // ARRANGE + LOGFONT lf{}; + wcscpy_s(lf.lfFaceName, L"初期値"); + auto szText = StringBufferW(lf.lfFaceName); + + // ACT + szText += L"よん♪"sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値よん♪")); + EXPECT_THAT(szText.length(), 6); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に1文字を追加する + */ +TEST(StringBufferW, append005) +{ + // ARRANGE + LOGFONT lf{}; + wcscpy_s(lf.lfFaceName, L"初期値"); + auto szText = StringBufferW(lf.lfFaceName); + + // ACT + szText += L't'; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値t")); + EXPECT_THAT(szText.length(), 4); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾にnullptrを追加する + */ +TEST(StringBufferW, append101) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + + // ACT + szText += nullptr; // SALによりコンパイラ警告が発生する + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); + + // ACT & ASSERT + EXPECT_THAT(szText.append(nullptr), EINVAL); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列ポインタを追加する(値がnullptr) + */ +TEST(StringBufferW, append102) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + LPCWSTR pszNull = nullptr; + + // ACT + szText += pszNull; // SALによりコンパイラ警告が発生する + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); + + // ACT & ASSERT + EXPECT_THAT(szText.append(pszNull), EINVAL); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列を追加する(値が空文字) + */ +TEST(StringBufferW, append103) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + + // ACT + szText += L""s; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); + + // ACT & ASSERT + EXPECT_THAT(szText.append(L""s), EINVAL); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列参照を追加する(サイズオーバー) + */ +TEST(StringBufferW, append203) +{ + // ARRANGE + SString<4> szBase = L"ザクⅡ"; + auto szText = StringBufferW(szBase); + + // ACT & ASSERT + EXPECT_THAT(szText.append(L"Ⅱ MS-06F"s), STRUNCATE); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ザクⅡ")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列ポインタを追加する(途中にNULを含む) + */ +TEST(StringBufferW, append202) +{ + // ARRANGE + SString<4> szBase = L"t"; + auto szText = StringBufferW(szBase); + + // ACT + szText += L"e\0t"; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"te")); + EXPECT_THAT(szText.length(), 2); +} + +/*! + * @brief StringBufferWのテスト + * + * 末尾に文字列参照を追加する(途中にNULを含む) + */ +TEST(StringBufferW, append204) +{ + // ARRANGE + SString<4> szBase = L"t"; + auto szText = StringBufferW(szBase); + + // ACT + szText += L"e\0t"sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"te")); + EXPECT_THAT(szText.length(), 2); +} + +/*! + * @brief StringBufferWのテスト + * + * 文字列長を計算する + */ +TEST(StringBufferW, length) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + const wchar_t dummy = 0x2025; + + // ASSERT + EXPECT_EQ(szText.data()[3], 0); + EXPECT_EQ(szText.length(), 3); + + // ACT(領域全体にゴミ投入) + auto_memset(szText.data(), dummy, int(std::size(szText))); + + // ASSERT + EXPECT_EQ(szText.data()[3], dummy); + EXPECT_EQ(szText.length(), 0); + + // ACT(NUL終端する) + szText[2] = 0; + + // ASSERT + EXPECT_EQ(szText.length(), 2); + EXPECT_EQ(szText.data()[3], dummy); + + // ACT + szText = dummy; + + // ASSERT + EXPECT_EQ(szText.length(), 1); +} + +/*! + * @brief StringBufferWのテスト + * + * 文字列が空かどうか判定する + */ +TEST(StringBufferW, empty) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + + // ASSERT + EXPECT_FALSE(szText.empty()); + + // ACT + szText = nullptr; + + // ASSERT + EXPECT_TRUE(szText.empty()); +} + +/*! + * @brief StringBufferWのテスト + * + * 文字列参照型に変換する + */ +TEST(StringBufferW, toStringView) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + + // ASSERT + EXPECT_THAT(std::wstring_view(szText), StrEq(L"初期値")); +} + +/*! + * @brief StringBufferWのテスト + * + * ファイルパス型に変換する + */ +TEST(StringBufferW, toFilePath) +{ + // ARRANGE + SString<4> szBase = L"初期値"; + auto szText = StringBufferW(szBase); + + // ACT + auto path = std::filesystem::path(szText); + + // ASSERT + EXPECT_FALSE(path.empty()); + + // ACT + szText = nullptr; + path = std::filesystem::path(szText); + + // ASSERT + EXPECT_TRUE(path.empty()); +} + +} // namespace mystring diff --git a/tests/unittests/test-file.cpp b/tests/unittests/test-file.cpp index b8ba52b386..12533fa0d1 100644 --- a/tests/unittests/test-file.cpp +++ b/tests/unittests/test-file.cpp @@ -579,6 +579,8 @@ TEST(file, FileMatchScoreSepExt) ASSERT_EQ(_countof(LR"(testXX)") - 1 + _countof(LR"(.t)") - 1, result); } +namespace mystring { + /*! GetExtのテスト */ @@ -608,6 +610,8 @@ TEST(CFilePath, GetExt) ASSERT_EQ(path.c_str() + path.Length(), path.GetExt()); } +} // namespace mystring + /*! CFileNameManager::GetFilePathFormatのテスト */ diff --git a/tests/unittests/test-statictype.cpp b/tests/unittests/test-statictype.cpp index db3aff4064..db90fb79c1 100644 --- a/tests/unittests/test-statictype.cpp +++ b/tests/unittests/test-statictype.cpp @@ -55,3 +55,647 @@ TEST(StaticVector, push_back) // 追加できないので、サイズをカウントアップしてはいけない EXPECT_EQ(1, vec.size()); } + +namespace mystring { + +/*! + * @brief StaticStringのテスト + * + * 引数なしで初期化する + */ +TEST(StaticString, init001) +{ + // ACT + StaticString<4> buf; + + // ASSERT + EXPECT_THAT(buf, StrEq(L"")); + EXPECT_THAT(buf.GetBufferPointer(), StrEq(L"")); + EXPECT_THAT(buf.GetBufferCount(), 4); + EXPECT_THAT(LPWSTR(buf), StrEq(L"")); + EXPECT_THAT(LPCWSTR(buf), StrEq(L"")); + EXPECT_THAT(buf.empty(), IsTrue()); + EXPECT_THAT(buf.length(), 0); + EXPECT_THAT(buf.Length(), 0); + EXPECT_THAT(std::size(buf), 4); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列ポインタで初期化する + */ +TEST(StaticString, init002) +{ + // ARRANGE + constexpr auto& initialValue = L"初期値"; + + // ACT + StaticString<4> buf(initialValue); + + // ASSERT + EXPECT_THAT(buf, StrEq(L"初期値")); + EXPECT_THAT(buf.GetBufferPointer(), StrEq(L"初期値")); + EXPECT_THAT(buf.GetBufferCount(), 4); + EXPECT_THAT(LPWSTR(buf), StrEq(L"初期値")); + EXPECT_THAT(LPCWSTR(buf), StrEq(L"初期値")); + EXPECT_THAT(buf.empty(), IsFalse()); + EXPECT_THAT(buf.length(), 3); + EXPECT_THAT(buf.Length(), 3); + EXPECT_THAT(std::size(buf), 4); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列で初期化する + */ +TEST(StaticString, init003) +{ + // ACT + StaticString<4> szText(L"初期値"s); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列参照で初期化する + */ +TEST(StaticString, init004) +{ + // ACT + StaticString<4> szText(L"初期値"sv); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * ファイルパスで初期化する + */ +TEST(StaticString, init006) +{ + // ARRANGE + std::filesystem::path path = L"初期値"; + + // ACT + StaticString<4> szText = path; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列ポインタで初期化する(値がnullptr) + */ +TEST(StaticString, init101) +{ + // ACT + StaticString<4> szText = nullptr; // SALによりコンパイラ警告が発生する + + // ASSERT + EXPECT_THAT(szText, StrEq(L"")); + EXPECT_THAT(szText.length(), 0); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列ポインタで初期化する + */ +TEST(StaticString, init102) +{ + // ACT + StaticString<4> buf(nullptr); + + // ASSERT + EXPECT_THAT(buf, StrEq(L"")); + EXPECT_THAT(buf.length(), 0); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列参照で初期化する(サイズオーバー) + */ +TEST(StaticString, init104) +{ + // ACT + StaticString<4> szText = L"長過ぎる"; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"長過ぎ")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + */ +TEST(StaticString, assign001) +{ + // ARRANGE + constexpr auto& initialValue = L"初期値"; + StaticString<4> buf(initialValue); + + // ACT + buf = L"設定値"; + + // ASSERT + EXPECT_THAT(buf, StrEq(L"設定値")); + EXPECT_THAT(buf.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列ポインタを代入する + */ +TEST(StaticString, assign002) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT + szText = L"設定値"; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"設定値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列を代入する + */ +TEST(StaticString, assign003) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT + szText = L"設定値"s; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"設定値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列参照を代入する + */ +TEST(StaticString, assign004) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT + szText = L"設定値"sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"設定値")); + EXPECT_THAT(szText.length(), 3); + + // ACT + szText = std::wstring_view(szText, 1); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"設")); + EXPECT_THAT(szText.length(), 1); +} + +/*! + * @brief StaticStringのテスト + * + * 1文字を代入する + */ +TEST(StaticString, assign005) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT + szText = L'a'; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"a")); + EXPECT_THAT(szText.length(), 1); + EXPECT_EQ(szText[0], L'a'); + EXPECT_EQ(szText[1], L'\0'); +} + +/*! + * @brief StaticStringのテスト + * + * ファイルパスを代入する + */ +TEST(StaticString, assign006) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + std::filesystem::path path = L"設定値"; + + // ACT + szText = path; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"設定値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * nullptrを代入する + */ +TEST(StaticString, assign101) +{ + // ARRANGE + constexpr auto& initialValue = L"初期値"; + StaticString<4> buf(initialValue); + + // ACT + buf = nullptr; + + // ASSERT + EXPECT_THAT(buf, StrEq(L"")); + EXPECT_THAT(buf.length(), 0); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列ポインタを代入する(値がnullptr) + */ +TEST(StaticString, assign102) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + LPCWSTR pszNull = nullptr; + + // ACT + szText = pszNull; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"")); + EXPECT_THAT(szText.length(), 0); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列参照を代入する(サイズオーバー) + */ +TEST(StaticString, assign104) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT + szText = L"長過ぎる"sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"長過ぎ")); + EXPECT_THAT(szText.length(), 3); + + // ACT2 + EXPECT_EQ(STRUNCATE, szText.assign(L"overflow"sv)); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ove")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列ポインタを追加する + */ +TEST(StaticString, append002) +{ + // ARRANGE + StaticString<4> szText = L"ザク"; + + // ACT + szText += L"Ⅱ"; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ザクⅡ")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列を追加する + */ +TEST(StaticString, append003) +{ + // ARRANGE + StaticString<4> szText = L"ザク"; + + // ACT + szText += L"Ⅱ"s; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ザクⅡ")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列参照タを追加する + */ +TEST(StaticString, append004) +{ + // ARRANGE + StaticString<4> szText = L"ザク"; + + // ACT + szText += L"Ⅱ"sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ザクⅡ")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に1文字を追加する + */ +TEST(StaticString, append005) +{ + // ARRANGE + StaticString<4> szText = L"ザク"; + + // ACT + szText += L'Ⅱ'; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ザクⅡ")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾にnullptrを追加する + */ +TEST(StaticString, append101) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT + szText += nullptr; // SALによりコンパイラ警告が発生する + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); + + // ACT & ASSERT + EXPECT_THAT(szText.append(nullptr), EINVAL); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列ポインタを追加する(値がnullptr) + */ +TEST(StaticString, append102) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + LPCWSTR pszNull = nullptr; + + // ACT + szText += pszNull; // SALによりコンパイラ警告が発生する + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); + + // ACT & ASSERT + EXPECT_THAT(szText.append(pszNull), EINVAL); + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列を追加する(値が空文字列) + */ +TEST(StaticString, append103) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT & ASSERT + EXPECT_THAT(szText.append(L""s), EINVAL); + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); + + // ACT + szText += L""s; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列参照を追加する(値が空文字列) + */ +TEST(StaticString, append104) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT & ASSERT + EXPECT_THAT(szText.append(L""sv), EINVAL); + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); + + // ACT + szText += L""sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"初期値")); + EXPECT_THAT(szText.length(), 3); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列ポインタを追加する(途中にNULを含む) + */ +TEST(StaticString, append202) +{ + // ARRANGE + StaticString<4> szText = L"ザ"; + + // ACT + szText += L"ク\0Ⅱ"; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ザク")); + EXPECT_THAT(szText.length(), 2); +} + +/*! + * @brief StaticStringのテスト + * + * 末尾に文字列参照を追加する(途中にNULを含む) + */ +TEST(StaticString, append204) +{ + StaticString<4> szText = L"ザ"; + + // ACT + szText += L"ク\0Ⅱ"sv; + + // ASSERT + EXPECT_THAT(szText, StrEq(L"ザク")); + EXPECT_THAT(szText.length(), 2); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列長を計算する + */ +TEST(StaticString, length) +{ + // ARRANGE + StaticString<4> szText = L"ザクⅡ"; + const wchar_t dummy = 0x2025; + + // ASSERT + EXPECT_EQ(szText.length(), 3); + EXPECT_EQ(szText.data()[3], 0); + + // ACT(領域全体にゴミ投入) + auto_memset(szText.data(), dummy, int(std::size(szText))); + + // ASSERT + EXPECT_EQ(szText.length(), 0); + EXPECT_EQ(szText.data()[3], dummy); + + // ACT(NUL終端する) + szText[2] = 0; + + // ASSERT + EXPECT_EQ(szText.length(), 2); + EXPECT_EQ(szText.data()[3], dummy); + + // ACT + szText = dummy; + + // ASSERT + EXPECT_EQ(szText.length(), 1); +} + +/*! + * @brief StaticStringのテスト + * + * 文字列が空かどうか判定する + */ +TEST(StaticString, empty) +{ + // ARRANGE + StaticString<4> szText = L"ザクⅡ"; + + // ASSERT + EXPECT_FALSE(szText.empty()); + + // ACT + szText = nullptr; + + // ASSERT + EXPECT_TRUE(szText.empty()); +} + +/*! + * @brief StaticStringのテスト + */ +TEST(StaticString, constAt) +{ + // ARRANGE + const StaticString<4> szText = L"初期値"; + + // ASSERT + EXPECT_THAT(szText.At(0), L'初'); + EXPECT_THAT(szText.At(1), L'期'); + EXPECT_THAT(szText.At(2), L'値'); + EXPECT_THAT(szText.At(3), '\0'); + EXPECT_THAT(szText.At(4), '\0'); + EXPECT_THAT(szText.At(5), '\0'); +} + +/*! + * @brief StaticStringのテスト + * + * ファイルパス型に変換する + */ +TEST(StaticString, toFilePath) +{ + // ARRANGE + StaticString<4> szText = L"初期値"; + + // ACT + auto path = std::filesystem::path(szText); + + // ASSERT + EXPECT_FALSE(path.empty()); + + // ACT + szText = nullptr; + path = std::filesystem::path(szText); + + // ASSERT + EXPECT_TRUE(path.empty()); +} + +/*! + * @brief SStringのテスト + * + * ファイルパスで初期化する + */ +TEST(SStringA, init006) +{ + // ARRANGE + std::string_view file(__FILE__); + std::filesystem::path path(file); + + // ACT + SString<_MAX_PATH, CHAR> szText = path; + + // ASSERT + EXPECT_THAT(szText, StrEq(file)); + EXPECT_THAT(szText.length(), file.length()); +} + +} // namespace mystring diff --git a/tests/unittests/tests1.vcxproj b/tests/unittests/tests1.vcxproj index 62dd362b0f..bb1d6c96f1 100644 --- a/tests/unittests/tests1.vcxproj +++ b/tests/unittests/tests1.vcxproj @@ -199,14 +199,10 @@ - - - - \ No newline at end of file diff --git a/tests/unittests/tests1_rc.rc b/tests/unittests/tests1_rc.rc index ed7dace791..ca4999b1c7 100644 --- a/tests/unittests/tests1_rc.rc +++ b/tests/unittests/tests1_rc.rc @@ -53,49 +53,6 @@ IDR_ZIPRES1 101 "resources.ja-JP.zip" #endif // 日本語 (日本) resources ///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// 英語 (米国) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "tests1_rc.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// 101 -// -IDR_ZIPRES1 101 "resources.en-US.zip" - -#endif // 英語 (米国) resources -///////////////////////////////////////////////////////////////////////////// - - - #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// //