65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (9投票s)

2003年6月4日

7分钟阅读

viewsIcon

80760

downloadIcon

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 文档中。您需要将转义字符(<> 等)替换为 &lt;&gt; 等。

s="escape me:&\"'<>"
s=encode(s, escape_to_xml_p );
cerr<<s<<endl;
-- output
escape me:&&quot;&apos;&lt;&gt;

这里发生的是:字符串已使用 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 是一个将迭代器范围转换为字符串的策略。运算符 + 组合了两个策略(类似于函数组合)并 **创建了一个新的转换策略**。

a * b 应用于值 v 等同于 a( b( v ) )

请注意,您还可以指定一个策略应用于容器的每个值。这与 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()
  1. sequence_from_string_p = A:这是一个预定义的序列转换策略:它读取由逗号分隔的字符串序列。
    • Container = vector<string>
    • Policy = from_string<string>
  2. A % v_int = B:容器已被 v_int 类型替换。
    • Container = vector<int>
    • Policy = from_string<string>
  3. B << from_string() :应用于元素的转换策略从字符串中读取一个 int
    • Container = vector<int>
    • Policy = from_string<int>

因此,通过这个小声明,我们构建了一个新的转换策略,它是一个 AB 的复杂混合。

编译与安装

您需要 Boost(参见 [1])和一个好的编译器(不要指望 VC6 能编译)。IoBind 大量使用 Boost:它使用 type_traitsmplcall_traitsspirit、(可选的 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 *

接受两个策略并组合它们。如果 ab 是两个策略,那么

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 的字符串。它将保留字符 <,>,... 替换为 &lt;, &gt; 等。

  • struct escape_to_xml;
    
    struct escape_to_latin1;
    将字符串转义为 XML(< 到 &lt;)或 Latin1,
  • struct unescape_from_xml;
    
    struct unescape_from_latin1;
    将字符串从 XML(&lt; 到 <)或 Latin1 取消转义,

用法非常直接,类似于 to_base64from_base64

序列容器

此策略处理序列容器,例如 vectorlist 等(正如您稍后将看到的,它也可用于关联容器)。

  • 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_ 是分别应用于 pairfirstsecond 成员的策略,其他参数用于分隔数据。实际上,写入器的核心是
    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_ 是分别应用于 pairfirstsecond 成员的策略,其他参数用于分隔数据。实际上,写入器的核心是
    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)

关联容器

关联容器,例如 mapset 等,只是序列容器和 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 - 初始发布。

参考

© . All rights reserved.