前段时间仿照虚幻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
|