带Format和GetBufferSetLength功能的std::[w]string类。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/passFuHao/article/details/52926264


/*
	stdex.h
		c++标准库一些扩展函数或类。


	注意:
		1、在此文件中添加代码时不要产生c/c++标准库以外的依赖。
		2、此文件中所有未注明异常安全等级的函数默认异常安全级别为“不抛出异常”或“提供基本的异常安全”保证。

	< .fuhao >
	< .2016年10月13日15时07分 >
	< [email protected] >
	< Copyright (C.) 2009-2016 fuhao software team., all rights reserved >
*/

#pragma once

// 屏蔽deprecated警告
__pragma (warning( push ));
__pragma(warning( disable:4996 ));
__pragma(warning( disable:4995 ));

// HttpTaskPool.h将FastFormat符号定义为宏
__pragma(push_macro("FastFormat"))
#undef FastFormat

#include <vector>
#include <assert.h>
#include <string.h>
#include <xstddef>
#include <xutility>
#include <memory>
#include <functional>

#include <Shlwapi.h>

__pragma(comment(lib, "Shlwapi.lib"))

#if defined( _AFX )
#include <afxstr.h>
#endif

#if defined( _ATL )
#include <atlstr.h>
#endif

#ifndef _FormatMessage_format_string_
#define _FormatMessage_format_string_
#endif

#if !defined( _STDEX )
	#define _STDEX
#endif

#if !defined( _LOOPINDEX )
	#define _LOOPINDEX
#endif

#if !defined( _STDEXSTRING )
	#define _STDEXSTRING 
#endif

namespace stdex
{

#if defined( WIN32 )
	/*
		ReadProcessString
			ReadProcessMemory的读取字符串版。用来在无法得知字符串长度的情况下读取目标进程中的字符串。

		模板参数:
			_Cty,char或wchar_t或unsigned short类型。hProcess所描述的进程中lpBaseAddress指向的字符串类型。

		参数:
			hProcess,进程句柄。见ReadProcessMemory。
			lpBaseAddress,字符串基址。见ReadProcessMemory函数lpBaseAddress参数。
			lpNumberOfCharRead,读取的字符串长度。(不包括零结尾)。

		返回值:
			成功返回读取到当前进程中的字符串缓冲区,该缓冲区由VirtualAlloc 分配,用户在不需要使用后应调用VirtualFree
		将其释放。
			失败返回NULL,调用GetLastError可获得失败原因。
			
		< .fuhao >
		< .2017年5月6日18时29分 >
		< [email protected] >
		< Copyright (C.) 2009-2017 fuhao software team., all rights reserved >
	*/
	template< typename _Cty > _Cty* ReadProcessString( __in HANDLE hProcess, __in LPCVOID lpBaseAddress, __out SIZE_T *lpNumberOfCharRead )
	{
		// 不要退化 _Cty
		static_assert(std::is_same< _Cty, CHAR >::value ||
			std::is_same< _Cty, WCHAR >::value || std::is_same< _Cty, unsigned short >::value, "ReadProcessString只能读取由char[]或wchar_t[]组成的字符串");

		struct /* 当前函数中无法确定该调用哪个strlen[A/W]来计算串长度,必须利用编译器自动匹配。 */
		{
			inline const size_t operator()( __in LPCSTR p )const 
			{ return (size_t )lstrlenA( p ); }
			inline const size_t operator()( __in LPCWSTR p ) const 
			{ return (size_t)lstrlenW( p ); }
			inline const size_t operator()( __in unsigned short *p ) const
			{ return (size_t)lstrlenW( (LPCWSTR)p ); }
		}CountString;

		struct _CtyDx /* _Cty* 删除器 */
		{ void operator()( const _Cty* p ){ VirtualFree( (LPVOID)p, IGNORE, MEM_RELEASE ); } };
		typedef std::unique_ptr< _Cty, _CtyDx > _CtyPtr;
		typedef std::list< std::pair< _CtyPtr, SIZE_T > > BufferChainType;

		SYSTEM_INFO si;
		BOOL bSucceeded = FALSE;
		BufferChainType lstBufferChain;
		_Cty *lpBuffer = NULL, *lpBasePtr = (_Cty*)lpBaseAddress;
		SIZE_T cchNumberOfCharRead = 0, cchCountOfCharRead = 0;
		LPVOID lpPageBaseAddress = NULL;

		auto GetPageBaseAddress = []( LPCVOID lpAddress, DWORD dwPageSize )->LPVOID
		{ return (LPVOID)(((DWORD)lpAddress) - (((DWORD)lpAddress) % dwPageSize)); };

		*lpNumberOfCharRead = 0;

		GetSystemInfo( &si );
		lpPageBaseAddress = GetPageBaseAddress( lpBaseAddress, si.dwPageSize );
		assert( lpBaseAddress >= lpPageBaseAddress );

		/** 页面粒度一定是_Cty类型所占长度的整倍数,否则可能跨页。 lpBaseAddress
			指向的地址也必须是经过对齐的,对齐粒度一定是_Cty类型长度,否则也可能跨页。*/
		if( (DWORD)lpBaseAddress % sizeof( _Cty ) || si.dwPageSize % sizeof( _Cty ) ){
			SetLastError( ERROR_MAPPED_ALIGNMENT );
			return NULL;
		}

		/** 第一次读第一页剩余长度,往后每次读一页,但第一次分配内存时仍然分配一整页
			内存,为了避免无法容纳零结尾符而产生额外的内存分配开销。*/
		for( SIZE_T cbReadSize = si.dwPageSize - ((LPBYTE)lpBaseAddress - (LPBYTE)lpPageBaseAddress);; cbReadSize = si.dwPageSize )
		{
			lpBuffer = (_Cty*)VirtualAlloc( NULL, si.dwPageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
			if( lpBuffer == NULL )
			{
				break;
			}

			/** 把lpBuffer的最后一个字符设置为零,读完后检查最后一个字符是否还是零,如果仍然是
				零则需要从头统计字符串长度,否则表示读取的字符串完全占满了缓冲区,不必再统计长度
				直接进行下一次读取即可。*/
			lpBuffer[cbReadSize / sizeof( _Cty ) - 1] = 0;

			if( ReadProcessMemory( hProcess, (LPCVOID)lpBasePtr, lpBuffer, cbReadSize, NULL ) )
			{
				if( lpBuffer[cbReadSize / sizeof( _Cty ) - 1] == 0 ){
					cchNumberOfCharRead = CountString( lpBuffer );
				}
				else {
					cchNumberOfCharRead = cbReadSize / sizeof( _Cty );
				}

				// 如果push_back内抛出异常,此处的_CtyPtr(lpBuffer)会负责将lpBuffer删除。
				lstBufferChain.push_back( std::make_pair( _CtyPtr( lpBuffer ), cchNumberOfCharRead ) );
				lpBasePtr += cchNumberOfCharRead;
				cchCountOfCharRead += cchNumberOfCharRead;

				if( lpBuffer[cbReadSize / sizeof( _Cty ) - 1] == 0 )
				{
					bSucceeded = TRUE;
					break;
				}
			}
			else
			{
				VirtualFree( lpBuffer, 0, MEM_RELEASE );
				break;
			}
		}

		BufferChainType::iterator it( lstBufferChain.begin() ), ed( lstBufferChain.end() );

		/** 一次也没读成功,或者内存分配失败。 */
		if( !bSucceeded )
		{
			return NULL;
		}
		assert( lstBufferChain.size() != 0 );

		if( lstBufferChain.size() == 1 /* 只读了一次 */ )
		{
			/** 
				it->first指向的字符串没有零结尾,但内存长度一定是si.dwPageSize所以,
				也许剩余的内存应该能装下零结尾符。如果剩余的内存不足以容纳零结尾符,
				则继续向下执行重新分配内存并拷贝……
			*/
			if( it->second < si.dwPageSize / sizeof( _Cty ) )
			{
				*lpNumberOfCharRead = cchCountOfCharRead;
				lpBuffer = it->first.release();
				lpBuffer[it->second] = 0;
				return lpBuffer;
			}
		}

		_Cty *pResult = (_Cty*)VirtualAlloc( NULL, cchCountOfCharRead * sizeof( _Cty ) + sizeof( _Cty ), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
		if( pResult == NULL )
		{
			return NULL;
		}

		for( SIZE_T cchNumberOfWrited = 0; it != ed; it++ )
		{
			CopyMemory( &pResult[cchNumberOfWrited], it->first.get(), it->second * sizeof( _Cty ) );
			cchNumberOfWrited += it->second;
		}

		*lpNumberOfCharRead = cchCountOfCharRead;
		pResult[cchCountOfCharRead] = 0;

		return pResult;
	}
#endif // if defined( WIN32 )

	/** std::[w]string不提供任何方法操作其内部对象,调API用std::[w]string极其不爽,还不支持Format。
		CString没迭代器……用标准算法简直自讨苦吃还要MFC或ATL当累赘,以下字符串类既方便调API又
		方便用标准算法,但阉割了部分CString功能。比如不支持BSTR……,以后再加吧。
		以下配置信息写入 autoexp.dat 文件的Visualizer节并关闭VS调试选项中“在变量窗口中显示
		对象原始结构”以支持调试时预览字符串;
		[Visualizer]
		stdex::StringT<char,*>{
			preview		( #if (($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,s] ) #else ( [$e._Bx._Ptr,s] ))
		}
		stdex::StringT<unsigned short,*>|stdex::StringT<wchar_t,*>{
			preview		( #if (($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,su] ) #else ( [$e._Bx._Ptr,su] ))
		}

		注意:
			std::basic_string类没有虚析构函数,不要给 StringT 类增加任何成员变量和虚函数。

		< .fuhao >
		< .2016年10月25日14时56分 >
		< [email protected] >
		< Copyright (C.) 2009-2016 北京顺福群聚科技有限公司, all rights reserved >
	*/
#if defined( _AFX ) || defined( _ATL )
	template< class _StringType, class _MFCStringType, class StringTraits >
#else
	template< class _StringType, class StringTraits >
#endif
	class StringT : public std::basic_string < _StringType, std::char_traits<_StringType>, std::allocator<_StringType> >
	{
	public:
		typedef std::basic_string < _StringType, std::char_traits<_StringType>, std::allocator<_StringType> > _BaseType;
#if defined( _AFX ) || defined( _ATL )
		typedef StringT< _StringType, _MFCStringType, StringTraits > _MyType;
#else
		typedef StringT< _StringType, StringTraits > _MyType;
#endif
		/** 不继承构造函数;
			using basic_string::basic_string; */
		StringT()	 : _BaseType() 
		{ // 构造空字符串
		};
		StringT( const _MyType& _Right )
			: _BaseType( _Right )
		{	// 拷贝构造
		}

		StringT( const _BaseType& _Right )
			: _BaseType( _Right )
		{	// _Right参数是 std::[w]string 类型
		}

		StringT( const _BaseType& _Right, size_type _Roff,
			size_type _Count = npos )
			: _BaseType( _Right, _Roff, _Count )
		{	// _Right参数是 std::[w]string 类型
		}

		StringT( const _StringType *_Ptr, size_type _Count )
			: _BaseType( _Ptr, _Count )
		{	// 用字符指针和长度构造,能接受空指针
		}
		
		StringT( const _StringType *_Ptr )
			: _BaseType( _Ptr )
		{	// 用字符指针构造,能接受空指针
		}

		StringT( size_type _Count, _StringType _Ch )
			: _BaseType( _Count, _Ch )
		{	// 串长度为 _Count,内容为 _Ch
		}
#if defined( _AFX ) || defined( _ATL )
		StringT( const _MFCStringType &_Right )
			: StringT( (const _StringType*)_Right )
		{	// 接受CString或CAtlString类型的构造函数;
			// 接受CString或CAtlString类型的拷贝赋值也是此函数;
		}
#endif
		template<class _Iter >
			StringT( _Iter _First, _Iter _Last )
			: _BaseType( _First, _Last )
		{	// 通过两个指针或迭代器构造
		}

		StringT( _MyType&& _Right ) _NOEXCEPT
			: _BaseType( _Right )
		{	// 移动构造
		}
		StringT( _BaseType&& _Right ) _NOEXCEPT
			: _BaseType( _Right )
		{	// 移动构造
		}

#if defined( WIN32 )
		/** 此函数有设计缺陷,当ReadProcessString失败时会抛出std::runtime_error异常,
			而导致ReadProcessString失败的原因可能由目标进程退出或hProcess无效(或失效)
			引起。VirtualAlloc失败也会导致ReadProcessString失败。又因构造函数无法向调用
			者传递失败原因,所以只能抛出异常…… 
				
				该函数暂被声名为弃用。
		*/
		__declspec(deprecated("String类中带HANDLE参数的构造函数有设计缺陷已被弃用,但尚无代替函数。请直接调用ReadProcessString从目标进程读取字符串"))
		StringT( HANDLE hProcess, const _StringType *_Ptr )
			: _BaseType()
		{	// 从hProcess进程中读取_Ptr指向的字符串来构造
			assert( hProcess && _Ptr );

			SIZE_T cchStringSize = 0;
			_StringType *lpString = ReadProcessString<_StringType>( hProcess, (LPCVOID)_Ptr, &cchStringSize );

			if( !lpString ){
				std::_Xruntime_error( "ReadProcessString没能正确读取目标进程中的字符串" );
			}

			assign( lpString, (typename size_type)cchStringSize );
			if( !VirtualFree( lpString, 0, MEM_RELEASE ) ){
				std::_Xruntime_error( "无法调用VirtualFree释放由ReadProcessString分配的内存。" );
			}
		}
#endif

		_MyType &operator =( const _MyType & ) = default;

		operator _StringType *() const
		{
			return GetBuffer();
		}
		operator const _StringType *() const
		{
			return GetBuffer();
		}

		inline void __cdecl FormatV( _In_z_ _Printf_format_string_ const _StringType *pszFormat, _In_ va_list args )
		{
			assert( pszFormat != NULL );
			if( pszFormat == NULL ){
				std::_Xinvalid_argument( __FUNCTION__"函数的pszFormat参数不能为NULL" );
			}

			const int nLength = StringTraits::GetFormattedLength( pszFormat, args );
			if( nLength < 0 ){
				std::_Xinvalid_argument( __FUNCTION__"函数的args参数有误" );
			}

			std::shared_ptr< _StringType > 
				pszBuffer( new _StringType[nLength + 1], []( const _StringType *p ){ delete[] p; } );
			StringTraits::Format( pszBuffer.get(), nLength + 1, pszFormat, args );

			// 赋值,不是 append
			assign( pszBuffer.get() );
		}

		void __cdecl AppendFormatV(
			_In_z_ _Printf_format_string_ const _StringType *pszFormat,
			_In_ va_list args )
		{
			assert( pszFormat != NULL );
			if( pszFormat == NULL ){
				std::_Xinvalid_argument( __FUNCTION__"函数的pszFormat参数不能为NULL" );
			}

			const int nLength = StringTraits::GetFormattedLength( pszFormat, args );
			if( nLength < 0 ){
				std::_Xinvalid_argument( __FUNCTION__"函数的args参数有误" );
			}

			std::shared_ptr< _StringType > 
				pszBuffer( new _StringType[nLength + 1], []( const _StringType *p ){ delete[] p; } );
			StringTraits::Format( pszBuffer.get(), nLength + 1, pszFormat, args );

			// append,不是赋值
			append( pszBuffer.get() );
		}

		inline void __cdecl Format( _In_z_ _FormatMessage_format_string_ const _StringType *pszFormat, ... )
		{
			va_list argList;
			va_start( argList, pszFormat );
			FormatV( pszFormat, argList );
			va_end( argList );
		}

		inline void __cdecl AppendFormat(
			_In_z_ _Printf_format_string_  const _StringType *pszFormat,
			... )
		{
			assert( pszFormat != NULL );

			va_list argList;
			va_start( argList, pszFormat );
			AppendFormatV( pszFormat, argList );
			va_end( argList );
		}

		static _MyType __cdecl FastFormat( _In_z_ _Printf_format_string_ const _StringType *pszFormat,
		... )
		{
			assert( pszFormat != NULL );
			_MyType s;

			va_list argList;
			va_start( argList, pszFormat );
			s.FormatV( pszFormat, argList );
			va_end( argList );

			return s;
		}

		static _MyType FormatMessage( _In_z_ _FormatMessage_format_string_  DWORD dwMessageId )
		{
			_MyType _Result;
			_StringType * lpMessage = StringTraits::FormatMessage( dwMessageId );
			if( lpMessage == NULL ){
				return _Result;
			}

			_Result = lpMessage;
			if( LocalFree( (HLOCAL)lpMessage ) != NULL ){
				std::_Xruntime_error( __FUNCTION__"无法调用LocalFree释放占用的空间" );
			}
			return _Result;
		}

		_StringType *GetBuffer() const
		{
			return const_cast< _StringType*>( data() );
		}

		_Ret_cap_( nLength + 1 ) _StringType *GetBufferReserveLength( __in int nLength )
		{
			reserve( nLength );		// reserve后,对象可以容纳nLength个字符和一个零结尾符
			return GetBuffer();
		}

		_Ret_cap_( nLength + 1 ) _StringType *GetBufferSetLength( _In_ int nLength )
		{
			SetLength( nLength );
			return GetBuffer();
		}

		void ReleaseBuffer( _In_ int nNewLength = -1 )
		{
			if( nNewLength < 0 ) {
				nNewLength = StringTraits::StringLengthN( GetBuffer(), capacity() );
			}
			/** 
					如果nNewLength >= size(),而且capacity() >= nNewLength则直接
				修改内部变量_Mysize的值为nNewLength。避免resize内部调用append
				把尾部覆盖成0,可能还会略微提升效率。

				另外,如果不这么做,以下示例代码将会导致错误:
					
					String s;
					lstrcpyn( s.GetBufferReserveLength( 10 ), _T("1234567890") );
					s.ReleaseBuffer();

					以上代码中,s.ReleaseBuffer内部调用SetLength,若SetLength直接调用
				resize,resize内部检查到size() < nLength则调用append(nLength-_Mysize,0)
				向后增加nLength-_Mysize个0符号导致lstrcpyn向对象内写入的内容丢失。

				< .fuhao >
				< .2017年2月19日14时53分 >
				< [email protected] >
				< Copyright (C.) 2009-2017 fuhao software team., all rights reserved >
			*/
			if( capacity() >= (size_type)nNewLength && (size_type)nNewLength >= size() ){
				/** c++ 标准并未规定 string 必须有_Mysize。_Mysize是VS2003里string的实现,
					所以,修改 string::_Mysize 的代码实际通过 append函数完成的。当然,是个不
					寻常的 append 。*/
				size_type Excess = (size_type)nNewLength - size();
				append( data() + size(), Excess );
			}
			else{
				SetLength( nNewLength );
			}
		}

		void SetLength( _In_ int nLength )
		{
			assert( nLength >= 0 );
			resize( (size_type)nLength, 0 );
		}

		int GetLength() const _NOEXCEPT
		{
			return (int)size();
		}

		bool IsEmpty() const _NOEXCEPT
		{
			return(GetLength() == 0);
		}

		/** 此函数效率极低,若需高效替换,请自行实现替换算法。该函数仅为提供方便,对效率不敏感之处使用。*/
		void Replace( _In_z_ const _StringType *pszOld, _In_z_ const _StringType *pszNew )
		{
			const size_type cchOld = StringTraits::StringLengthN( pszOld, (size_type)~0 ),
				cchNew = StringTraits::StringLengthN( pszNew, (size_type)~0 );

			for( size_type pos = 0; (pos = find( pszOld, pos )) != npos; pos += cchNew ){
				replace( pos, cchOld, pszNew, cchNew );
			}
		}

		void Replace( _In_ const _StringType chOld, _In_ const _StringType chNew )
		{
			std::replace( begin(), end(), chOld, chNew );
		}

		bool Trim()	/* 剪除前后的空格、制表、回车、换行符 */
		{
			if( StringTraits::Trim( GetBuffer() ) )
			{
				ReleaseBuffer();
				return true;
			}

			return false;
		}

		bool Trim( _In_ const _StringType * pszTrimChar )
		{
			if( StringTraits::Trim( GetBuffer(), pszTrimChar ) )
			{
				ReleaseBuffer();
				return true;
			}

			return false;
		}

		int CompareNumber( _In_ const _StringType *lpStr2, int nChar )
		{
			return StringTraits::CompareNumber( data(), lpStr2, nChar );
		}

		int CompareIgnore( _In_ const _StringType *lpStr2 )
		{
			return StringTraits::CompareIgnore( data(), lpStr2 );
		}

		int CompareNumberIgnore( _In_ const _StringType *lpStr2, int nChar )
		{
			return StringTraits::CompareNumberIgnore( data(), lpStr2, nChar );
		}

		// 从pszSource中移除未包含在pszCharSet中的串。(该函数仅支持宽串)
		void RemoveSpanExcluding( _In_z_ const wchar_t *pszCharSet )
		{
			StringTraits::RemoveSpanExcluding( GetBuffer(), GetLength(), pszCharSet );
			ReleaseBuffer();
		}

		// 从pszSource中移除包含在pszCharSet中的串。(该函数仅支持宽串)
		void RemoveSpanIncluding( _In_z_ const wchar_t *pszCharSet )
		{
			StringTraits::RemoveSpanIncluding( GetBuffer(), GetLength(), pszCharSet );
			ReleaseBuffer();
		}

		// 以 _Ch 为分隔符拆分字符串为一组字符串。返回拆分后的一组字符串。用户可通过模板参数
		// _Rty 指定接收返回的字符串组的容器,通常是 vector< String > 或 list< String > 和 [d]queue< String > 等。
		template< typename _Rty > _Rty Split( const _StringType _Ch ) const
		{
			return StringTraits::SplitString< _Rty >( GetBuffer(), _Ch );
		}
	};

	template< class _Cty > class StringTraitsBase
	{
	public:
		static_assert(std::is_same< _Cty, wchar_t >::value || std::is_same< _Cty, char >::value || std::is_same< _Cty, unsigned short >::value,
			"StringTraitsT只接受 wchar_t、char和unsigned short类型的模板参数");

		typedef _Cty* _CtyPtr;

		/*
			RemoveIf
				从字符串中移除满足 _Pred 条件的字符。

			参数:
				pszSource,零结尾字符串,移除操作在该字符串上进行。
				cchSizeOfSource,pszSource指向的字符串长度。(不包括零结尾符在内的长度,既,如果有字符串 "123",其长度为3)
				_Pred,谓词,用以表达是否应当移除某个字符。

			返回值:
				无意义。

			注意:
				cchSizeOfSource的值通常由strlen或其它统计字符串长度的函数获得,而非 _countof 或 sizeof 获得。

			警告:
				RemoveIf<char>版本将不保证对中文作正确处理。
		*/
		template< typename _Pr > static inline void __cdecl RemoveIf(
			_Inout_cap_( cchSizeOfSource ) _CtyPtr pszSource, _In_ size_t cchSizeOfSource, _Pr _Pred )
		{
			_CtyPtr pszBegin = pszSource, pszPtr = pszSource,
				pszEnd = pszSource + cchSizeOfSource;

			for( ; pszBegin != pszEnd && *pszBegin; pszBegin++ )
			{
				if( _Pred( *pszBegin ) )
				{
					*pszPtr++ = *pszBegin;
				}
			}

			*pszPtr = 0;
		}
	};

	class StringWTraits : public StringTraitsBase< wchar_t >
	{
	public:
		static bool __cdecl Trim( __inout LPWSTR psz, __in LPCWSTR pszTrimChars = L" \t\r\n" )
		{
			return !!StrTrimW( psz, pszTrimChars );
		}

		static int __cdecl CompareNumber( __in LPCWSTR lpStr1, __in LPCWSTR lpStr2, int nChar )
		{
			return StrCmpNW( lpStr1, lpStr2, nChar );
		}
		static int __cdecl CompareIgnore( __in LPCWSTR lpStr1, __in LPCWSTR lpStr2 )
		{
			return StrCmpIW( lpStr1, lpStr2 );
		}
		static int __cdecl CompareNumberIgnore( __in LPCWSTR lpStr1, __in LPCWSTR lpStr2, int nChar )
		{
			return StrCmpNIW( lpStr1, lpStr2, nChar );
		}

		static int __cdecl GetFormattedLength(
			_In_z_ _Printf_format_string_ LPCWSTR pszFormat, va_list args ) throw()
		{
			return _vscwprintf( pszFormat, args );
		}
		static int __cdecl Format(
			_Out_writes_( nLength ) LPWSTR pszBuffer,
			_In_ size_t nLength,
			_In_ _Printf_format_string_ LPCWSTR pszFormat, va_list args ) throw()
		{
			return vswprintf_s( pszBuffer, nLength, pszFormat, args );
		}
		static int __cdecl StringLengthN(
			_In_reads_opt_z_( sizeInXChar ) const wchar_t* psz,
			_In_ size_t sizeInXChar ) throw()
		{
			if( psz == NULL )
			{
				return(0);
			}
			return(int( wcsnlen( psz, sizeInXChar ) ));
		}
#if defined( WIN32 )
		static __success( return != NULL ) LPWSTR FormatMessage( _In_z_ _FormatMessage_format_string_ DWORD dwMessageId )
		{
			LPWSTR lpResult = NULL;
			if( !::FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL, dwMessageId, GetThreadLocale(),
				(LPWSTR)&lpResult, 0, (va_list*)NULL ) ){
				return NULL;
			}

			// LocalFree( (HLOCAL)lpResult );
			return lpResult;
		}
#endif

		// 从pszSource中移除未包含在pszCharSet中的串。
		static void __cdecl RemoveSpanExcluding(
			_Inout_cap_( cchSizeOfSource ) wchar_t *pszSource, _In_ size_t cchSizeOfSource, _In_z_ const wchar_t *pszCharSet )
		{
			return RemoveIf( pszSource, cchSizeOfSource, [pszCharSet]( wchar_t ch ){ return wcschr( pszCharSet, ch ); } );
		}

		// 从pszSource中移除包含在pszCharSet中的串。
		static void __cdecl RemoveSpanIncluding(
			_Inout_cap_( cchSizeOfSource ) wchar_t *pszSource, _In_ size_t cchSizeOfSource, _In_z_ const wchar_t *pszCharSet )
		{
			return RemoveIf( pszSource, cchSizeOfSource, [pszCharSet]( wchar_t ch ){ return !wcschr( pszCharSet, ch ); } );
		}

		static const wchar_t *_StrChr( __in const wchar_t *Src, __in const wchar_t _Ch )
		{
			if( !Src || !*Src )
				return NULL;

			const wchar_t *Ptr( Src );
			for( ; *Ptr && *Ptr != _Ch; ++Ptr );
			return Ptr;
		}

		template< typename _Rty > static _Rty SplitString( __in const wchar_t *Src, __in const wchar_t _Ch )
		{
			_Rty rs;
			const wchar_t *Ptr( Src ), *Next( NULL ); 
			while( Next = _StrChr( Ptr, _Ch ) )
			{
				rs.push_back( { Ptr, (size_t)(Next - Ptr) } );
				Ptr = Next + !!(*Next);
			}

			if( Ptr && !*Ptr && *(Ptr - 1) == _Ch )
				rs.push_back( { (const wchar_t*)nullptr, 0 } );

			return std::move( rs );
		}
	};

	class StringATraits : public StringTraitsBase< char >
	{
	public:
		static bool __cdecl Trim( __inout LPSTR psz, __in LPCSTR pszTrimChars = " \t\r\n" )
		{
			return !!StrTrimA( psz, pszTrimChars );
		}

		static int __cdecl CompareNumber( __in LPCSTR lpStr1, __in LPCSTR lpStr2, int nChar )
		{
			return StrCmpNA( lpStr1, lpStr2, nChar );
		}
		static int __cdecl CompareIgnore( __in LPCSTR lpStr1, __in LPCSTR lpStr2)
		{
			return StrCmpIA( lpStr1, lpStr2 );
		}
		static int __cdecl CompareNumberIgnore( __in LPCSTR lpStr1, __in LPCSTR lpStr2, int nChar )
		{
			return StrCmpNIA( lpStr1, lpStr2, nChar );
		}

		static int __cdecl GetFormattedLength(
			_In_z_ _Printf_format_string_ LPCSTR pszFormat, va_list args ) throw()
		{
			return _vscprintf( pszFormat, args );
		}
		static int __cdecl Format(
			_Out_writes_to_( nlength, return ) LPSTR pszBuffer,
			_In_ size_t nlength,
			_In_z_ _Printf_format_string_ LPCSTR pszFormat, va_list args ) throw()
		{
			return vsprintf_s( pszBuffer, nlength, pszFormat, args );
		}
		static int __cdecl StringLengthN(
			_In_reads_opt_z_( sizeInXChar ) const char* psz,
			_In_ size_t sizeInXChar ) throw()
		{
			if( psz == NULL )
			{
				return(0);
			}
			return(int( strnlen( psz, sizeInXChar ) ));
		}
#if defined( WIN32 )
		static __success( return != NULL ) LPSTR FormatMessage( _In_z_ _FormatMessage_format_string_ DWORD dwMessageId )
		{
			LPSTR lpResult = NULL;
			if( !::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL, dwMessageId, GetThreadLocale(),
				(LPSTR)&lpResult, 0, NULL ) ){
				return NULL;
			}

			// LocalFree( (HLOCAL)lpResult );
			return lpResult;
		}
#endif

		// 从pszSource中移除未包含在pszCharSet中的串。
		static void __cdecl RemoveSpanExcluding(
			_Inout_cap_( cchSizeOfSource ) char *pszSource, _In_ size_t cchSizeOfSource, _In_z_ const char *pszCharSet )
		{
			return RemoveIf( pszSource, cchSizeOfSource, [pszCharSet]( char ch ){ return strchr( pszCharSet, ch ); } );
		}

		// 从pszSource中移除包含在pszCharSet中的串。
		static void __cdecl RemoveSpanIncluding(
			_Inout_cap_( cchSizeOfSource ) char *pszSource, _In_ size_t cchSizeOfSource, _In_z_ const char *pszCharSet )
		{
			return RemoveIf( pszSource, cchSizeOfSource, [pszCharSet]( char ch ){ return !strchr( pszCharSet, ch ); } );
		}

		static const char *_StrChr( __in const char *Src, __in const char _Ch )
		{
			if( !Src || !*Src )
				return NULL;

			const char *Ptr( Src );
			for( ; *Ptr && *Ptr != _Ch; ++Ptr );
			return Ptr;
		}

		template< typename _Rty > static _Rty SplitString( __in const char *Src, __in const char _Ch )
		{
			_Rty rs;
			const char *Ptr( Src ), *Next( NULL );
			while( Next = _StrChr( Ptr, _Ch ) )
			{
				rs.push_back( { Ptr, (size_t)(Next - Ptr) } );
				Ptr = Next + !!(*Next);
			}

			if( Ptr && !*Ptr && *(Ptr - 1) == _Ch )
				rs.push_back( { (const char*)nullptr, 0 } );

			return std::move( rs );
		}
	};

#if defined( _UNICODE )
	typedef StringWTraits StringTTraits;
#else
	typedef StringATraits StringTTraits;
#endif		// end if defined( _UNICODE )

#if defined( _ATL )
	typedef StringT < wchar_t, ATL::CAtlStringW, StringWTraits > AtlStringW;
	typedef StringT< char, ATL::CAtlStringA, StringATraits > AtlStringA;
	typedef StringT< TCHAR, ATL::CAtlString, StringTTraits > AtlString;
	#if !defined( _AFX )		// 只有ATL没有MFC
		typedef AtlStringW StringW;
		typedef AtlStringA StringA;
		typedef AtlString String;
	#endif		// endif !defined( _AFX )
#endif		// endif defiend( _ATL )

#if defined( _AFX )			// 如果有MFC
	typedef StringT < wchar_t, CStringW, StringWTraits > StringW;
	typedef StringT< char, CStringA, StringATraits > StringA;
	typedef StringT< TCHAR, CString, StringTTraits > String;
#endif

#if !defined( _AFX ) && !defined( _ATL )		// 如果既没MFC也没ATL
	typedef StringT < wchar_t, StringWTraits > StringW;
	typedef StringT< char, StringATraits > StringA;
	#if defined( _UNICODE )
		typedef StringW String;
	#else
		typedef StringA String;
	#endif		// end if defined( _UNICODE )
#endif		// end #if defined( _AFX ) || defined( _ATL )
}

/* 特化两个hash函数以支持标准库容器。

	注意:
		不要去掉以下代码中std::hash和stdex::StringW以及stdex::StringA前的std和stdex
	作用域限定符。该限定符用于强调此hash函数针对stdex作用域下的StringW或StringA
	类型,而非用户代码环境中当前可见的StringW或StringA类型。
*/
template<>
	struct std::hash < stdex::StringW >
		: public std::hash< std::wstring >{
	};

template<>
	struct std::hash < stdex::StringA > 
		: public std::hash< std::string > {
	};

__pragma(pop_macro( "FastFormat" ));

// assert里写了 warning pop 了。
__pragma(warning( disable:4193 ))
__pragma(warning( pop ));



注意:

不要在不同模块(DLL或静态库模块)间传递String类的对象,如果必须,请保证当前模块与被调模块有相同的MFC、ATL和C++环境并保证两模块间使用的MFC、ATL以及C++版本相同。


2017年6月27日11时11分更新:

在String类中增加RemoveSpanExcluding和RemoveSpanIncluding函数,用以从String对象中移除包含(或不含)在某个集合内的字符。

在stdex空间中增加ReadProcessString函数,用以从其它进程读取字符串。


修改FormatV和AppendFormatV函数,使符合异常安全之最高标准。

猜你喜欢

转载自blog.csdn.net/passFuHao/article/details/52926264