<h2>Introduction</h2>
<p>IoBind is a highly flexible library for serializing objects. It uses
meta-programming and policies to create reader and writer for complex objects.
In oder words, IoBind is a code factory that produces customized readers and
writers: you give the action to take and the objects to process and it will
generate the corresponding code.
</p>
<h2>Quick examples</h2>
Let's start with some quick examples to see what IoBind is about.
<h3>Converting a string to a ... string</h3>
Let see a first snippet:<br />
<pre lang="c++">
string s("<xml/>");
// we convert s to a string
s = <b>encode</b>( s, <b>to_string()</b> );
cerr<<s<<endl;
</pre>
<pre>
-- output
<xml/>
</pre>
<p>This seems to be useless since I'm converting a string to string. Anyway, let's
see what's in the second line:
</p>
<ul>
<li>
<code>encode</code> is a template function that applies a conversion policy
(see below) to <code>s</code>,</li>
<li>
<code>to_string</code> is a <b>conversion policy</b> that takes a value and
converts it to a string ( using <code>boost::format</code> library ).</li>
</ul>
<h3>Escaping string to XML</h3>
<p>Now, suppose that you need to store this string into an xml document. You need
replace escape characters (<code><</code>, <code>></code>, etc..) by <code>&lt;</code>,
<code>&gt;</code>, etc:</p>
<pre lang="c++">
s=encode(s, <b>escape_to_xml()</b> );
cerr<<s<<endl;
</pre>
<pre>
-- output
<xml/>
</pre>
<p>What has happened here: the string has been converted to an escaped string using
the <code>escape_to_xml</code> conversion policy.
</p>
<p>Hence using predefined policies you can easily transform a string:
</p>
<ul>
<li>
base64,</li>
<li>
encryption,</li>
<li>
zipping,</li>
<li>
url,</li>
<li>
etc...</li>
</ul>
<h3>Combining policies</h3>
Did I talk about about code factory ? This is where meta-programming takes
place.<br />
Suppose that you have a vector of string, you want to convert it to string and
to escape this string to xml:
<pre lang="c++">
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() <b>+ sequence_to_string_p</b>
);
cerr<<s<<endl;
</pre>
<pre>
-- output
a string,<string/>
</pre>
<p>In this call, sequence_to_string_p is a policy that converts an iterator range
to a string. The operator + combines the two policies (similar to function
composition) and <b>creates a new conversion policy</b>:
</p>
<center><B>a+b</B> applied to a value <b>v</b> is equivalent to <b>a( b( v ) )</b></center>
<p>Note that you can also specify a policy to be applied to each value of the
container. This, combined with a pair policy, can be used to serializes
associatives containers.</p>
<h3>Reading back data</h3>
<p>All "to" policies have "from" counter part to read data. For example, we can
easily read a <code>vector<int></code>:
<pre lang="c++">
vector<int> v_int;
string str("0,1,2,3,4");
// reading vector of ints
v_int = encode(str,
<b>sequence_from_string_p
% v_int
<< from_string<int>() </b>
);
</pre>
where
<ul>
<li>
<code>sequence_from_string_p</code> is a generic conversion policy that read a
sequence of strings. This conversion policy is a template class that depends on
two template paramters:
<ul>
<li>
<code>Container</code>, the type of container to fill,</li>
<li>
<code>Policy</code>, the conversion policy to apply to each element,</li>
</ul>
,
</li>
<li>
the operator <code>%</code> build a new <code>sequence_from_string</code> conversion
policy that adds values to a container of same type as <code>v_int</code>.
Concretely, this operator replaces the <code>Container</code> template paramter
with the type of <code>v_int</code>,</li>
<li>
the operator <code><<</code> is similar to <code>%</code> but it works on
the policy.</li>
</ul>
<p>Ok the above is rather confusion little confusion, let's deeper see how policies
are build step by steps:
<pre lang="c++">
sequence_from_string_p
% v_int
<< from_string<int>()
</pre>
<ol>
<li>
<code>sequence_from_string_p</code> = <b>A</b>: this is a pre-build sequence
conversion policy: it reads a sequence of strings separated by commas,
<ul>
<li>
Container = vector<string></li>
<li>
Policy = from_string<string>()</li>
</ul>
</li>
<li>
<code><b>A</b> % v_int</code> = <b>B</b>: the container has been replaced by <code>v_int</code>
type:
<ul>
<li>
Container = <code>vector<int></code></li>
<li>
Policy = <code>from_string<string>()</code></li>
</ul>
</li>
<li>
<code><b>B</b> << from_string<int>() </int></code>: the conversion poliy
applied to the elements reads a <code>int</code> from a string:
<ul>
<li>
Container = <code>vector<int></code></li>
<li>
Policy = <code>from_string<int>()</code></li>
</ul>
</li>
</ol>
<p>So with this little statement, we have build a new conversion policy which is a
complex mixture of A and B.</p>
<h2>Compiling and installation</h2>
You need Boost (see [1]) and a good compiler (don't think VC6 will manage to
compile). IoBind heavily uses boost: it uses <em>type_traits, mpl, call_traits,
spirit, (regex optional) and format</em>. The headers can be included by
<pre lang="c++">
#include <iobind/iobind.hpp>
</pre>
Note also that all the IoBind classes are in the <b>iobind</b> namespace.
<h2>The <code>encode</code> method</h2>
<code>encode</code> is a template method that applies a conversion policy to an
object. It's return type depends on the policy return type. It comes with two
overloads:
<pre lang="c++">
template<
typename Value,
typename Policy
>
typename Policy::return_type encode(Value value_, Policy const& policy_);
// range version
template<
typename Iterator,
typename Policy
>
typename Policy::return_type encode(Iterator begin_, Iterator end_, Policy const& policy_);
</pre>
This is the front end of IoBind to the user. Example of use of this method are
given in the secion above.
<h2>Conversion policies</h2>
<left><em>The individual steering behaviors [...] are components of a larger structure,
like notes of a melody or words of a story.</em> Craig Reynolds, Steering Behaviors for Autonomous Characters, GDC99.</left>
Conversion policies are the constitutive piece of IoBind. You can combine them
to create complex serializers.
<h3>Simple string</h3>
These are basic string conversion policies not very useful used alone, but they
become quite handy when combining with others.
<ul>
<li>
<pre lang="c++">struct <b>to_string()</b>;</pre>
converts the value using <em>boost::format</em>. If the vale does not support
this, the compiler will fail.</li>
<li>
<pre lang="c++">
template<typename Value>
struct <b>from_string</b>;</pre>
transform a string to Value using <code>ostringstream</code>,
</li>
</ul>
Example:
<pre lang="c++">
int i;
string str=encode(i, to_string() );
i=encode(str, from_string<int>() );
</pre>
<h3>Base64</h3>
<ul>
<li>
<pre lang="c++">struct <b>to_base64</b>;</pre>
converts a stream to base64 notation,
</li>
<li>
<pre lang="c++">struct <b>from_base64</b>;</pre>
converts back a stream from base64 notation,
</li>
</ul>
Example:
<pre lang="c++">
string str="test";
str=encode( str, to_base64());
str=encode( str, from_base64());
</pre>
These policies are base on the base64 iostream converter from Konstant
Pilipchuk:
<pre>
// base64.hpp
// Autor Konstantin Pilipchuk
// mailto:lostd@ukr.net
</pre>
<h3>XML escaping</h3>
This policy takes care of transforming a string to XML conformant string. It
replaces reserved characters <,>,... by &lt;, &gt;, etc...
<ul>
<li>
<pre lang="c++">struct <b>escape_to_xml</b>;</pre>
escapes an string to XML (< to &lt;),
</li>
<li>
<pre lang="c++">struct <b>unescape_from_xml</b>;</pre>
unescapes an string from XML (&lt; to <),
</li>
</ul>
Their usage is straight forward and similar to <code>to_base64</code>, <code>from_base64</code>.
<h3>Sequence container</h3>
This policy hangles sequence containers such as <code>vector</code>, <code>list</code>, etc
(as you will see later, it can also be used for associative containers).
<ul>
<li>
<pre lang="c++">template<
typename Policy
>
struct sequence_to_string;</pre> converts a sequence to a string. This policy has the following constructor:
<pre lang="c++">
sequence_to_string(
policy_const_reference policy_,
string_param_type begin_ = "",
string_param_type delimiter_ = ",",
string_param_type end_ = ""
)
</pre>
where <code>policy_</code> is the policy applied sequence elements,
and the other parameters are used to separated the data. In fact, the core of
the reader is:
<pre lang="c++">output
<<m_begin
..
<<m_policy.encode(value)<<m_delimiter, // this is done multiple times
..
<<m_end;
</pre>
</li>
<li>
<pre lang="c++">template<
typename Container,
typename Policy
>
struct sequence_from_string;</pre> reads a sequence from a string and fills a container.
This policy has the following constructor:
<pre lang="c++">
sequence_from_string(
policy_const_reference policy_,
string_param_type begin_ = "",
string_param_type delimiter_ = ",",
string_param_type end_ = ""
)
</pre>
where the parameters have similar behavior as above. The policy is used to transform the string before adding it
to the container.
</li>
</ul>
These policies support new operators that take care of policy, container change:
<ul>
<li><code><<</code>, changes the policy,</li>
<li><code>%</code>, changes the container type (only for <code>pair_from_string</code>).</li>
</ul>
Example converting elements of a vector<float> to base64 and back:
<pre lang="c++">
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;
</pre>
<pre>
-- output
v (to_string, base64): MQA=,MC41AA==,MC4zMzMzMzMA,MC4yNQA=
v is cleared...
v (from_string from base64): 1,0.5,0.333333,0.25
</pre>
<h3>Pair</h3>
These policies handle the famous <code>std::pair</code> structure.
<ul>
<li>
<pre>
template<
typename FirstPolicy,
typename SecondPolicy
>
class <b>pair_to_string</b>;
</pre>
converts a <code>pair</code> to a string. This class has the following
constructor:
<pre lang="c++">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_ = ")"
)
</pre>
where <code>first/second_policy_</code> are the policies applied respectively
to the <code>first</code> and <code>second</code> members of <code>pair</code>,
and the other parameters are used to separated the data. In fact, the core of
the writer is:
<pre lang="c++">output
<<m_begin
<<m_first_policy.encode(value.first)
<<m_delimiter,
<<m_second_policy.encode(value.second),
<<m_end;
</pre>
</li>
<li>
<pre>
template<
typename Pair,
typename FirstPolicy,
typename SecondPolicy
>
class <b>pair_from_string</b>;
</pre>
reads a <code>pair</code> from a string. This class has the following
constructor:
<pre lang="c++">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_ = ")"
)
</pre>
where <code>first/second_policy_</code> are the policies applied respectively
to the <code>first</code> and <code>second</code> members of <code>pair</code>,
and the other parameters are used to separated the data. In fact, the core of
the writer is:
<pre lang="c++">pair.first=m_frist_policy.encode(first_string);
pair.second=m_second_policy.encode(second_string); </pre>
</li>
</ul>
These policies support new operators that take care of policy, pair type change:
<ul>
<li><code><<</code>, changes the first policy,</li>
<li><code>>></code>, changes the second policy,</li>
<li><code>%</code>, changes the pair type (only for <code>pair_from_string</code>).</li>
</ul>
<p>Example:</p>
<pre lang"c++">
pair<float,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<float>()
>> from_string<std::string>()
);
cerr<<"\tpair (from string):"<<encode( p_fs, pair_to_string_p)<<endl;
</pre>
<pre>
-- output
pair (1,second): (1:second)
reseting pair
pair (from string):(1:second)
</pre>
<h3>Associative containers</h3>
Associative containers such as <code>map</code>,<code>set</code>, etc... are just a combination of a sequence container and a pair
(speaking about serialization). Hence, using <code>sequence_to/from_string</code> and pair_to/from_string, you can easily build
serializers for them, witout redifining new classes (the compiler will build them for you):
<pre lang="c++">
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;
</pre>
<pre>
-- 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)
</pre>
<h3>Others...</h3>
There is room for a lot of other policies:
<ul>
<li>
encryption, decryption,</li>
<li>
zipping, unzipping,</li>
<li>
url encoding, decoding,</li>
<li>
...</li>
</ul>
<h2>History</h2>
<tr>
<td>05-04-2003</td>
<td>Initial release.</td>
</tr>
<h2>Reference</h2>
<table border="0">
<tr>
<td>[1]</td>
<td><a href="http://www.boost.org">Boost</a></td>
</tr>
</table>