前段時間仿照虛幻3寫ConfigFile,有兩個重要的需求,其一是快速的搜索配置文件中的Key_Value_pair;其二是可以還原到讀入時的狀態,或者友好串行化。很明顯,要做到快速的搜索需要用二叉樹或者散列表來幫忙,但是如果直接用map或者hash_map都不行,會破壞輸入順序,以下面的例子為例: key1=1 key0=0 key2=2 那么實際上在map中的順序是0 1 2,因此破壞了順序,如果要達到還原的效果,則需要自己使用vector來保存輸入的順序,那么這會造成很大的負擔,特別是在出現刪除的時候。 不過還好,強大的boost提供了multi_index庫,我們可以使用它來對同一個容器提供兩個不同的視圖(View),一個有序一個只是個普通序列。 再考慮到字符編碼、大小寫敏感和是否支持重復Key_value_pair,可以得到這樣的一個聲明: template < typename TCHARTYPE, bool bCaseSensitive, bool bMulti > class config_info; 對于bCaseSensitive,我們需要對算法進行分派,使得總是能夠調用到正確的算法比如strcmp和stricmp,這需要通過特化來做到。 是否支持重復鍵,可以用map和multimap來區分。 這些都可以用特化來做,不過如果是這樣的話,那么對于頂級類config_info來說,規模會是指數膨脹,這時候需要Traits的幫助,這樣我們把不同的部分都放到Traits類中,將相同的部分放到config_info當中。 因此,我們可以得到大概這樣一個類,以下是代碼片段: template < typename TCHARTYPE, bool bCaseSensitive, bool bMulti > class config_info { protected: typedef boost::multi_index::multi_index_container< section_info< TCHARTYPE, bCaseSensitive, false >, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::identity< section_info< TCHARTYPE, bCaseSensitive, false > > >, boost::multi_index::sequenced<> > > single_container_type;
typedef boost::multi_index::multi_index_container< section_info< TCHARTYPE, bCaseSensitive, true >, boost::multi_index::indexed_by< boost::multi_index::ordered_non_unique< boost::multi_index::identity< section_info< TCHARTYPE, bCaseSensitive, true > > >, boost::multi_index::sequenced<> > > multi_container_type;
public: typedef typename boost::mpl::if_c< bMulti, multi_container_type, single_container_type >::type container_type;
typedef typename container_type::nth_index<0>::type first_view_type; typedef typename container_type::nth_index<1>::type second_view_type; typedef typename first_view_type::const_iterator ordered_const_iterator; typedef typename second_view_type::const_iterator non_ordered_const_iterator; typedef std::pair< ordered_const_iterator, ordered_const_iterator > ordered_const_iterator_range;
typedef detail::config_info_provider_traits<TCHARTYPE> provider_traits; typedef detail::config_info_string_algorithm<bCaseSensitive> string_algorithm;
typedef std::vector< TCHARTYPE > buffer_type; typedef section_info< TCHARTYPE, bCaseSensitive, bMulti > section_type;
// 這樣可以更好的利用IDE的Intellisense typedef std::basic_string<TCHARTYPE> string_type; typedef typename section_type::value_type value_type; typedef typename section_type::key_type key_type; typedef typename section_type::key_value_type key_value_type;
這里我直接使用了boost.mpl來實現靜態分離(不好意思,我不知道怎么描述,這個詞匯是我自己想出來的,不知道準確不準確,請原諒我大言不慚的為它定義,即:通過某種方法,在編譯時選擇正確的類。如果您知道怎么正確描述這種行為,請告訴我,謝謝。 )。 如果你覺得上面typedef太多、嵌套層次太深的話,請看下面的微縮版代碼: #include <iostream> #include <set> using namespace std;
namespace mpl { template < bool bCondition, typename T1, typename T2 > struct _if { typedef void type; };
template < typename T1, typename T2 > struct _if< true, T1, T2 > { typedef T1 type; };
template < typename T1, typename T2 > struct _if< false, T1, T2 > { typedef T2 type; }; }
#pragma pack(1) // 不對齊
template < typename T, bool bMulti > class TestContainer { // msstl下面它們大小都是28 (對于int來說) typedef std::set< T > single_type; typedef std::multiset<T> multi_type;
// dummy只用來演示類大小的不同 typedef char single_dummy; // 不對齊的時候字節數是1 typedef double multi_dummy; // 不對齊的時候字節數是8 typedef typename mpl::_if< bMulti, multi_dummy, single_dummy >::type dummy_type; public: typedef typename mpl::_if< bMulti, multi_type, single_type >::type set_type;
public: void insert( const T& t ) { m_set.insert( t ); }
void print() const { for( set_type::const_iterator it = m_set.begin(); it != m_set.end(); ++it ) { cout<< *it <<" "; } cout<< endl; } private: set_type m_set;
dummy_type m_dummy; };
int main() { { cout<< "TestContainer<int, true> : " << sizeof(TestContainer<int, true>) << endl;
TestContainer<int, true> t; t.insert( 100 ); t.insert( 100 ); t.insert( 200 ); t.insert( 200 ); t.insert( 200 ); t.print(); }
{ cout<< "TestContainer<int, false> : " << sizeof(TestContainer<int, false>) << endl;
TestContainer<int, false> t; t.insert( 100 ); t.insert( 100 ); t.insert( 200 ); t.insert( 200 ); t.insert( 200 ); t.print(); }
return 0; }
輸出結果是: TestContainer<int, true> : 36 100 100 200 200 200 TestContainer<int, false> : 29 100 200 請按任意鍵繼續. . . 根據是否支持重復,bMulti是否為真,通過一點模板元的代碼可以讓編譯器自動選擇正確的容器。當然前提是bMulti必須是在編譯時就能確定的值。如果是變量,要通過變量來選擇正確的類的話,則是動態的,如果你愿意,可以稱為動態分離(⊙﹏⊙b汗,不要倒,你要淡定。 )。
boost.mpl提供了大量的、強大的和令人欽佩的模板元編程庫。說到底,這都依賴于編譯器,依賴于模板特化,包括使用模板元計算斐波那契數列亦是如此,讓人不得不佩服C++的偉大。 以上面的簡單例子為例,那么我們可以知道,當bMulti的值不同的時候,編譯器會自動得到不同的type。 回到config_info,兩行綠色的代碼做的事情就是把變化提取出來,然后這樣的好處在于我在config_info里面總是能夠用同樣的接口做不同的事情。以序列化為例: inline void serialize( buffer_type& buf ) const { // 序列化所有的注釋 comment_container::const_iterator itRight = m_comments.end();
for( comment_container::const_iterator it = m_comments.begin(); it != m_comments.end(); ++it ) { if( it->m_ePos == comment_type::ECP_RIGHT ) { itRight = it; continue; } else { buf.push_back( provider_traits::standard_comment_char() ); buf.push_back( provider_traits::standard_space_char() ); buf.insert( buf.end(), it->m_strInfo.begin(), it->m_strInfo.end() ); buf.push_back( provider_traits::return_char() ); } }
// 序列化section buf.push_back( TCHARTYPE('[') ); buf.insert( buf.end(), m_strSectionName.begin(), m_strSectionName.end() ); buf.push_back( TCHARTYPE(']') ); // 不要對這個感到意外,因為我太懶了,還沒調整,應該改到Traits中去的,當然對于char和wchar_t來說,這會工作正常。
if( itRight != m_comments.end() ) { buf.push_back( provider_traits::standard_space_char() ); buf.push_back( provider_traits::standard_comment_char() ); buf.insert( buf.end(), itRight->m_strInfo.begin(), itRight->m_strInfo.end() ); }
buf.push_back( provider_traits::return_char() );
for( second_view_type::const_iterator it = m_Con.get<1>().begin(); it != m_Con.get<1>().end(); ++it ) { it->serialize( buf ); } } 再看看這個provider_traits的定義你就明白了: /** * @brief 提供用於提供不同字符串編碼的標準信息 */ template < typename TCHARTYPE > class config_info_provider_traits { public: typedef std::basic_string<TCHARTYPE> string_type; public: static const TCHARTYPE* comments_string(); static const TCHARTYPE return_char(); static const TCHARTYPE standard_comment_char(); static const TCHARTYPE standard_space_char(); static const TCHARTYPE* null_string(); static const TCHARTYPE equals_sign(); };
template <> class config_info_provider_traits<char> { public: typedef std::basic_string<char> string_type; public: static const char* comments_string() { return ";#"; }
static const char return_char() { return '\n'; }
static const char standard_comment_char() { return ';'; }
static const char standard_space_char() { return ' '; }
static const char* null_string() { return ""; }
static const char equals_sign() { return '='; } };
template <> class config_info_provider_traits<wchar_t> { public: typedef std::basic_string<wchar_t> string_type; public: static const wchar_t* comments_string() { return L";#"; }
static const wchar_t return_char() { return L'\n'; }
static const wchar_t standard_comment_char() { return L';'; }
static const wchar_t standard_space_char() { return L' '; }
static const wchar_t* null_string() { return L""; }
static const wchar_t equals_sign() { return L'='; } }; 字符串算法部分: /** * @brief 用于分發算法 使用C庫函數優化字符串比較操作 */ template < typename TCHARTYPE > class std_basic_string_algorithm { public: inline static bool equals( const std::basic_string<TCHARTYPE>& Input, const std::basic_string<TCHARTYPE>& Test ) { return 0 == std::char_traits<TCHARTYPE>::compare( Input.c_str(), Test.c_str() ); }
inline static bool iequals( const std::basic_string<TCHARTYPE>& Input, const std::basic_string<TCHARTYPE>& Test );
inline static bool lexicographical_compare( const std::basic_string<TCHARTYPE>& Input, const std::basic_string<TCHARTYPE>& Test ) { return std::char_traits<TCHARTYPE>::compare( Input.c_str(), Test.c_str() ) < 0; }
inline static bool ilexicographical_compare( const std::basic_string<TCHARTYPE>& Input, const std::basic_string<TCHARTYPE>& Test ); }; 我使用strcmp、stricmp、wcscmp、wcsicmp在char和wchar_t的特化類中優化這些算法。 我使用下面這個類來分離大小寫是否敏感: /** * @brief 用于分離字符串在是否大小寫敏感的狀態下的算法 */ template < bool bCaseSensitive > class config_info_string_algorithm { public: template<typename Range1T, typename Range2T> inline static bool equals( const Range1T& Input, const Range2T& Test);
template<typename Range1T, typename Range2T> inline static bool lexicographical_compare( const Range1T& Arg1, const Range2T& Arg2 ); }; 在特化類中它們分別會調用equals或者iequals等。
如果你要問我為什么要用模板來寫,我說這樣很方便,而且效率也不會低: typedef config_file<char, false, false > char_config_file; typedef config_file<wchar_t, false, false > wchar_config_file; typedef config_file<char, false, true > char_data_provider; typedef config_file<wchar_t, false, true > wchar_data_provider; 我只需要4個typedef就可以得到4個完全不同的類,甚至通過更多的組合生成更多的類,何樂而不為呢?
好了,朋友們有感興趣的可以去看Boost的文檔看mpl相關的東西。
轉自: http://hi.baidu.com/_%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/c399dc94029dbe12d31b7076.html
|