兔子的技术博客

兔子

   :: 首页 :: 联系 :: 聚合  :: 管理
  202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

留言簿(10)

最新评论

阅读排行榜

评论排行榜

       前段时间仿照虚幻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
posted on 2011-08-04 15:24 会飞的兔子 阅读(818) 评论(0)  编辑 收藏 引用 所属分类: C++及开发环境

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理