IoBind,一个序列化代码工厂。






4.89/5 (9投票s)
2003年6月4日
7分钟阅读

80760

765
IoBind 提出了一种新的对象序列化方法。
引言
IoBind
是一个高度灵活的库,用于将对象序列化为 string
或从 string
反序列化。它利用元编程和策略在 **编译时** 为复杂对象创建读取器和写入器。
换句话说,IoBind
是一个代码工厂,可以生成定制的读取器和写入器:您指定要执行的操作和要处理的对象,它将生成相应的代码。
IoBind 的最新版本可在 SourceForge 上找到。
Outline
- 快速示例
- 将字符串转换为
string
- XML 转义字符串
- 组合策略
- 读回数据
- 将字符串转换为
- 编译与安装
- encode 方法
- 策略
- operator +
- 字符串操作
- Base64
- Zip
- XML 转义,Latin1 转义
- 序列容器
- pair 结构
- 关联容器
- 历史
- 参考文献
快速示例
让我们从一些快速示例开始,看看 IoBind 是关于什么的。
将字符串转换为...字符串
看第一个代码片段
string s("<xml/>");
// we convert s to a string
s = encode( s, to_string_p );
cerr<<s<<endl;
-- output
<xml/>
这似乎没有用,因为我正在将字符串转换为字符串。无论如何,让我们看看第二行是什么
encode
是一个模板函数,它将一个转换策略(见下文)应用于s
,to_string_p
是一个 **转换策略**,它接受一个值并将其转换为字符串(使用boost::format
库)。
XML 转义字符串
现在,假设您需要将此字符串存储在 XML 文档中。您需要将转义字符(<
、>
等)替换为 <
、>
等。
s="escape me:&\"'<>"
s=encode(s, escape_to_xml_p );
cerr<<s<<endl;
-- output
escape me:&"'<>
这里发生的是:字符串已使用 escape_to_xml_p
转换策略转换为转义字符串。
因此,使用预定义的策略,您可以轻松地转换字符串
- base64,
- 加密,
- 压缩,
- 等等...
组合策略
拥有一组基本策略是好的,但通过组合它们来创建新策略才是真正有趣的地方(这是元编程发生的地方)。
假设您有一个字符串向量,您想将其转换为字符串,然后将该字符串转义为 XML。
vector<string> v_string;
// filling v_int
v_string.push_back("a string");
v_string.push_back("<string/>");
// transforming to escaped string:
s = encode(
v_string.begin(),
v_string.end(),
escape_to_xml_p * sequence_to_string_p
);
cerr<<s<<endl;
-- output
a string,<string/>
在此调用中,sequence_to_string_p
是一个将迭代器范围转换为字符串的策略。运算符 +
组合了两个策略(类似于函数组合)并 **创建了一个新的转换策略**。
请注意,您还可以指定一个策略应用于容器的每个值。这与 pair 策略结合使用,可以序列化关联容器。
读回数据
所有“to”策略都有它们的“from”对应项来读取数据。例如,我们可以轻松地从字符串读取 vector<int>
:
vector<int> v_int;
string str("0,1,2,3,4");
// reading vector of ints
v_int = encode(str,
sequence_from_string_p
% v_int
<< from_string()
);
其中sequence_from_string_p
是一个通用的转换策略,用于读取字符串序列。此转换策略是一个模板类,依赖于两个模板参数Container
,要填充的容器类型,Policy
,应用于每个元素的转换策略,
- 运算符
%
构建一个新的sequence_from_string
转换策略,该策略将值添加到与v_int
类型相同的容器中。具体来说,此运算符将Container
模板参数替换为v_int
的类型, - 运算符
<<
类似于%
,但它作用于策略。
上面的内容有些令人困惑,让我们深入了解一下策略是如何一步步构建的。
sequence_from_string_p
% v_int
<< from_string()
sequence_from_string_p
= A:这是一个预定义的序列转换策略:它读取由逗号分隔的字符串序列。- Container =
vector<string>
- Policy =
from_string<string>
- Container =
A % v_int
= B:容器已被v_int
类型替换。- Container =
vector<int>
- Policy =
from_string<string>
- Container =
B << from_string
:应用于元素的转换策略从字符串中读取一个() int
。- Container =
vector<int>
- Policy =
from_string<int>
- Container =
因此,通过这个小声明,我们构建了一个新的转换策略,它是一个 A 和 B 的复杂混合。
编译与安装
您需要 Boost(参见 [1])和一个好的编译器(不要指望 VC6 能编译)。IoBind
大量使用 Boost:它使用 type_traits
、mpl
、call_traits
、spirit
、(可选的 regex
)和 format
库。
可以通过以下方式包含头文件
#include <iobind/iobind.hpp>
另请注意,所有 IoBind 类都位于 iobind
命名空间中。
encode
方法
encode
是一个模板方法,它将一个转换策略应用于一个对象。它的返回类型取决于策略的返回类型。它提供了两个重载
template<
typename Value,
typename Policy
>
typename Policy::return_type
encode(
Value const& value_,
Policy const& policy_
);
// range version
template<
typename Iterator,
typename Policy
>
typename Policy::return_type
encode(
Iterator begin_,
Iterator end_,
Policy const& policy_
);
这是 IoBind
的面向用户的接口。在此之前的部分中给出了该方法的使用示例。
转换策略
个体转向行为 [...] 是一个更大结构中的组成部分,就像旋律中的音符或故事中的文字一样。 Craig Reynolds,《自主角色转向行为》,GDC99。
转换策略是 IoBind
的构成部分。您可以组合它们来创建复杂的序列化器。
operator *
接受两个策略并组合它们。如果 a
和 b
是两个策略,那么
a*b( value) = a( b( value ) )
.
字符串操作
这是一个基本字符串转换策略,单独使用时不是很有用,但与其他策略结合使用时会非常方便。
struct to_string;
使用boost::format
转换值。如果值不支持此操作,编译器将失败。template<typename Value> struct from_string;
使用ostringstream
将流转换为 Value,
示例
int i;
string str=encode(i, to_string() );
i=encode(str, from_string<std::string>() );
请注意,几乎所有策略都有预定义的实例:to_string
策略被实例化为 to_string_p
。
Base64
将流转换为 base64 方案。
struct to_base64;
将流转换为 base64 格式,struct from_base64;
将流从 base64 格式转换回来,
示例
string str="test";
str=encode( str, to_base64_p);
str=encode( str, from_base64_p);
这些策略基于 Konstant Pilipchuk 的 base64 iostream 转换器。
// base64.hpp // Autor Konstantin Pilipchuk // mailto:lostd@ukr.net
XML 和 Latin1 转义
此策略负责将字符串转换为符合 XML 或 Latin1 的字符串。它将保留字符 <,>,... 替换为 <, > 等。
struct escape_to_xml; struct escape_to_latin1;
将字符串转义为 XML(< 到 <)或 Latin1,struct unescape_from_xml; struct unescape_from_latin1;
将字符串从 XML(< 到 <)或 Latin1 取消转义,
用法非常直接,类似于 to_base64
、from_base64
。
序列容器
此策略处理序列容器,例如 vector
、list
等(正如您稍后将看到的,它也可用于关联容器)。
template< typename Policy > struct sequence_to_string;
将序列转换为string
。此策略具有以下构造函数sequence_to_string( policy_const_reference item_policy_, string_param_type begin_ = "", string_param_type delimiter_ = ",", string_param_type end_ = "" )
其中item_<CODE>policy_
是应用于序列元素的策略,其他参数用于分隔数据。实际上,写入器的核心是output <<m_begin .. <<m_policy.encode(value)<<m_delimiter, // this is done multiple times .. <<m_end;
template< typename Container, typename Policy > struct sequence_from_string;
从字符串读取序列并填充容器。此策略具有以下构造函数sequence_from_string( policy_const_reference item_policy_, string_param_type begin_ = "", string_param_type delimiter_ = ",", string_param_type end_ = "" )
其中参数的行为与上面类似。item_policy_
用于在将字符串添加到容器之前对其进行转换。
这些策略支持用于处理策略或容器更改的其他运算符
<<
,更改策略,%
,更改容器类型(仅适用于pair_from_string
)。
示例:将 vector<float>
的元素转换为 base64 再转换回来
vector<float> v_f;
for (i=1;i<5;++i)
v_f.push_back( 1/static_cast<FLOAT>(i) );
str=encode(
v_f.begin(),
v_f.end(),
sequence_to_string_p << to_base64()
);
cerr<<"\tv (to_string, base64): "<<str<<endl;
cerr<<"\tv is cleared..."<<endl;
v_f.clear();
v_f=encode(
str,
sequence_from_string_b( v_f, from_string<float>() * from_base64() )
);
cerr<<"\tv (from_string from base64): "<<encode(
v_f.begin(),
v_f.end(),
sequence_to_string_p
)
<<endl;
-- output
v (to_string, base64): MQA=,MC41AA==,MC4zMzMzMzMA,MC4yNQA=
v is cleared...
v (from_string from base64): 1,0.5,0.333333,0.25
Pair
此策略处理著名的 std::pair
结构。
template< typename FirstPolicy, typename SecondPolicy > class pair_to_string;
将pair
转换为字符串。此类具有以下构造函数pair_to_string( first_policy_const_reference first_policy_, second_policy_const_reference second_policy_, string_param_type begin_ = "(", string_param_type delimiter_ = ":", string_param_type end_ = ")" )
其中first/second_policy_
是分别应用于pair
的first
和second
成员的策略,其他参数用于分隔数据。实际上,写入器的核心是output <<m_begin <<m_first_policy.encode(value.first) <<m_delimiter, <<m_second_policy.encode(value.second), <<m_end;
template< typename Pair, typename FirstPolicy, typename SecondPolicy > class pair_from_string;
从字符串读取pair
。此类具有以下构造函数pair_from_string( first_policy_const_reference first_policy_, second_policy_const_reference second_policy_, string_param_type begin_ = "(", string_param_type delimiter_ = ":", string_param_type end_ = ")" )
其中first/second_policy_
是分别应用于pair
的first
和second
成员的策略,其他参数用于分隔数据。实际上,写入器的核心是pair.first=m_frist_policy.encode(first_string); pair.second=m_second_policy.encode(second_string);
这些策略支持用于处理策略、pair 类型更改的新运算符
<<
,更改第一个策略,>>
,更改第二个策略,%
,更改 pair 类型(仅适用于pair_from_string
)。
示例
pair<int,string> p_fs(1,"second"); str=encode( p_fs, pair_to_string_p); cerr<<"\tpair (1,second): "<<str<<endl; cerr<<"\treseting pair"<<endl; p_fs.first=0; p_fs.second=""; p_fs=encode( str, pair_from_string_p % p_fs << from_string<int>() >> from_string<std::string>() ); cerr<<"\tpair (from string):"<<encode( p_fs, pair_to_string_p)<<endl;
-- output
pair (1,second): (1:second)
reseting pair
pair (from string):(1:second)
关联容器
关联容器,例如 map
、set
等,只是序列容器和 pair 的组合(就序列化而言)。因此,使用 sequence_to/from_string
和 pair_to/from_string,您可以轻松地为它们构建序列化器,而无需重新定义新类(编译器将为您生成它们)。
map<float,string> m_fs;
const char* numbers[] = {"one", "two", "three", "four", "five"};
for (i=1;i<5;++i)
m_fs[static_cast<float>(i)]=numbers[i];
// dumping to string
str=encode(
m_fs.begin(),
m_fs.end(),
sequence_to_string_p << pair_to_string_p
);
cerr<<"\tm (to_string): "<<str<<endl;
cerr<<"\tm is cleared..."<<endl;
// reading back the data
m_fs.clear();
m_fs=encode(
str,
sequence_from_string_p % m_fs
<< (
pair_from_string_p
% pair<float,std::string>()
<< from_string<float>()
>> from_string<std::string>()
)
);
cerr<<"\tm (from_string): "<<encode(
m_fs.begin(),
m_fs.end(),
sequence_to_string_p << pair_to_string_p
)
<<endl;
-- output
-- associative container:
combination of sequence_to/from_string and pair
m (to_string): (1:two),(2:three),(3:four),(4:five)
m is cleared...
m (from_string): (1:two),(2:three),(3:four),(4:five)
Zip
zip 策略使用著名的 **zlib C** 库(参见 [2])。它将一个缓冲区压缩到另一个缓冲区。此策略与 base64 结合使用,可用于将字节缓冲区存储在 XML 文件中。
vector<unsigned char> buffer;
string s;
//zipping and converting to base64
s = encode( buffer, to_base64 * to_zip_p );
其他
还有很多其他策略的空间
- 加密、解密,
- URL 编码、解码,
- ...
历史
- 2003-06-30 - 添加了 zip、latin1、case、crc、hex 策略。
- 2003-05-04 - 初始发布。