MSDNライブラリを見ていたら「qsort_s」という関数を発見。
クイックソート関数qsortは昔からあったが、知らないうちにqsort_sができていた。これら2つは何が違うかと言うと...比較用関数に独自パラメータ(context)を渡せるかどうか。qsortはパラメータを渡せないため、単純なソートには使えるものの実際問題として使い物にならない関数だった。
そのため今まではクイックソートが必要な時はqsortのソースコードを元にパラメータが利用できる関数を作成しなければいけなかった。qsort_sのおかげでその手間が省けるようになった。ここではこのqsort_sを利用してCStringArrayの文字列をソートする。
#include "atlstr.h" #include "atlcoll.h" class CDnpSortString { //比較関数 static int SortStringArrayCompare(void* pContext,const void* pData1,const void* pData2) { if(pContext == NULL) return 0; return ((CDnpSortString*)pContext)->SortStringArrayCompare_(pData1,pData2); } //昇順か降順か bool _bABC; //ソート対象 CAtlArray<CAtlString>* _pastrText; public: CDnpSortString() { _bABC = true; _pastrText = NULL; } //比較関数の実体 //外部からは呼ばないこと! int SortStringArrayCompare_(const void* pData1,const void* pData2) { size_t nSize; if(_pastrText == NULL) return 0; nSize = _pastrText->GetCount(); if(*(size_t*)pData1 >= nSize || *(size_t*)pData2 >= nSize) return 0; if(_bABC) return ::_tcscmp((*_pastrText)[*(size_t*)pData1],(*_pastrText)[*(size_t*)pData2]); else return ::_tcscmp((*_pastrText)[*(size_t*)pData2],(*_pastrText)[*(size_t*)pData1]); } // // CStringArrayのソート // bool SortStringArray(CAtlArray<CAtlString>& astrText,bool bABC) { size_t i; size_t nSize; size_t* pnIndex; CAtlArray<CAtlString> astrTmp; nSize = astrText.GetCount(); if(nSize == 0) return false; pnIndex = new size_t[nSize]; if(pnIndex == NULL) return false; astrTmp.SetCount(nSize); for(i = 0; i < nSize; i++) { pnIndex[i] = i; astrTmp[i] = astrText[i]; } _bABC = bABC; _pastrText = &astrText; ::qsort_s(pnIndex,nSize,sizeof(size_t),SortStringArrayCompare,this); _pastrText = NULL; for(i = 0; i < nSize; i++) astrText[i] = astrTmp[pnIndex[i]]; delete pnIndex; return true; } }; void Test(void) { /////////////////// //ダミーデータ用意 // CAtlArray<CAtlString> astrFiles; astrFiles.Add(_T("abc")); astrFiles.Add(_T("aac")); astrFiles.Add(_T("aec")); astrFiles.Add(_T("bbc")); /////////////////// //ソート // CDnpSortString cSort; cSort.SortStringArray(astrFiles,false); /////////////////// //結果表示 // size_t i; size_t nSize; CAtlString strMessage; nSize = astrFiles.GetCount(); for(i = 0; i < nSize; i++) { strMessage += astrFiles[i]; strMessage += _T("\n"); } ::MessageBox(NULL,strMessage,_T(""),MB_OK); }