]>
Commit | Line | Data |
---|---|---|
1 | [/============================================================================== | |
2 | Copyright (C) 2001-2011 Joel de Guzman | |
3 | Copyright (C) 2006 Dan Marsden | |
4 | ||
5 | Use, modification and distribution is subject to the Boost Software | |
6 | License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
7 | http://www.boost.org/LICENSE_1_0.txt) | |
8 | ===============================================================================/] | |
9 | [section Quick Start] | |
10 | ||
11 | I assume the reader is already familiar with tuples (__tuple__) and its | |
12 | ancestor `std::pair`. The tuple is a generalization of `std::pair` for | |
13 | multiple heterogeneous elements (triples, quadruples, etc.). The tuple is | |
14 | more or less a synonym for fusion's `__vector__`. | |
15 | ||
16 | For starters, we shall include all of Fusion's __sequence__(s) [footnote There | |
17 | are finer grained header files available if you wish to have more control | |
18 | over which components to include (see section __organization__ for | |
19 | details).]: | |
20 | ||
21 | #include <boost/fusion/sequence.hpp> | |
22 | #include <boost/fusion/include/sequence.hpp> | |
23 | ||
24 | Let's begin with a `__vector__` [footnote Unless otherwise noted, components are | |
25 | in namespace `boost::fusion`. For the sake of simplicity, code in this | |
26 | quick start implies `using` directives for the fusion components we will be | |
27 | using.]: | |
28 | ||
29 | __vector__<int, char, std::string> stuff(1, 'x', "howdy"); | |
30 | int i = __at_c__<0>(stuff); | |
31 | char ch = __at_c__<1>(stuff); | |
32 | std::string s = __at_c__<2>(stuff); | |
33 | ||
34 | Just replace `tuple` for `__vector__` and `get` for `__at_c__` and this is exactly | |
35 | like __tuple__. Actually, either names can be used interchangeably. Yet, | |
36 | the similarity ends there. You can do a lot more with Fusion `__vector__` or | |
37 | `tuple`. Let's see some examples. | |
38 | ||
39 | [heading Print the vector as XML] | |
40 | ||
41 | First, let's include the algorithms: | |
42 | ||
43 | #include <boost/fusion/algorithm.hpp> | |
44 | #include <boost/fusion/include/algorithm.hpp> | |
45 | ||
46 | Now, let's write a function object that prints XML of the form <type>data</type> | |
47 | for each member in the tuple. | |
48 | ||
49 | struct print_xml | |
50 | { | |
51 | template <typename T> | |
52 | void operator()(T const& x) const | |
53 | { | |
54 | std::cout | |
55 | << '<' << typeid(x).name() << '>' | |
56 | << x | |
57 | << "</" << typeid(x).name() << '>' | |
58 | ; | |
59 | } | |
60 | }; | |
61 | ||
62 | Now, finally: | |
63 | ||
64 | __for_each__(stuff, print_xml()); | |
65 | ||
66 | That's it! `__for_each__` is a fusion algorithm. It is a generic algorithm | |
67 | similar to __stl__'s. It iterates over the sequence and calls a user | |
68 | supplied function. In our case, it calls `print_xml`'s `operator()` for | |
69 | each element in `stuff`. | |
70 | ||
71 | [caution The result of `typeid(x).name()` is platform specific. The code | |
72 | here is just for exposition. Of course you already know that :-)] | |
73 | ||
74 | `__for_each__` is generic. With `print_xml`, you can use it to print just about | |
75 | any Fusion __sequence__. | |
76 | ||
77 | [heading Print only pointers] | |
78 | ||
79 | Let's get a little cleverer. Say we wish to write a /generic/ function | |
80 | that takes in an arbitrary sequence and XML prints only those elements | |
81 | which are pointers. Ah, easy. First, let's include the `is_pointer` boost | |
82 | type trait: | |
83 | ||
84 | #include <boost/type_traits/is_pointer.hpp> | |
85 | ||
86 | Then, simply: | |
87 | ||
88 | template <typename Sequence> | |
89 | void xml_print_pointers(Sequence const& seq) | |
90 | { | |
91 | __for_each__(__filter_if__<boost::is_pointer<_> >(seq), print_xml()); | |
92 | } | |
93 | ||
94 | `__filter_if__` is another Fusion algorithm. It returns a __filter_view__, | |
95 | a conforming Fusion sequence. This view reflects only those elements that | |
96 | pass the given predicate. In this case, the predicate is | |
97 | `boost::is_pointer<_>`. This "filtered view" is then passed to the | |
98 | __for_each__ algorithm, which then prints the "filtered view" as XML. | |
99 | ||
100 | Easy, right? | |
101 | ||
102 | [heading Associative tuples] | |
103 | ||
104 | Ok, moving on... | |
105 | ||
106 | Apart from `__vector__`, fusion has a couple of other sequence types to choose | |
107 | from. Each sequence has its own characteristics. We have `__list__`, `__set__`, | |
108 | `__map__`, plus a multitude of `views` that provide various ways to present the | |
109 | sequences. | |
110 | ||
111 | Fusion's `__map__` associate types with elements. It can be used as a cleverer | |
112 | replacement of the `struct`. Example: | |
113 | ||
114 | namespace fields | |
115 | { | |
116 | struct name; | |
117 | struct age; | |
118 | } | |
119 | ||
120 | typedef __map__< | |
121 | __fusion_pair__<fields::name, std::string> | |
122 | , __fusion_pair__<fields::age, int> > | |
123 | person; | |
124 | ||
125 | `__map__` is an associative sequence. Its elements are Fusion pairs which differ | |
126 | somewhat from `std::pair`. Fusion pairs only contain one member, with the type of | |
127 | their second template parameter. The first type parameter of the pair is used as an | |
128 | index to the associated element in the sequence. For example, given a `a_person` | |
129 | of type, `person`, you can do: | |
130 | ||
131 | using namespace fields; | |
132 | std::string person_name = __at_key__<name>(a_person); | |
133 | int person_age = __at_key__<age>(a_person); | |
134 | ||
135 | Why go through all this trouble, you say? Well, for one, unlike the | |
136 | `struct`, we are dealing with a generic data structure. There are a | |
137 | multitude of facilities available at your disposal provided out of the box | |
138 | with fusion or written by others. With these facilities, introspection | |
139 | comes for free, for example. We can write one serialization function (well, | |
140 | two, if you consider loading and saving) that will work for all your fusion | |
141 | `__map__`s. Example: | |
142 | ||
143 | struct saver | |
144 | { | |
145 | template <typename Pair> | |
146 | void operator()(Pair const& data) const | |
147 | { | |
148 | some_archive << data.second; | |
149 | } | |
150 | }; | |
151 | ||
152 | template <typename Stuff> | |
153 | void save(Stuff const& stuff) | |
154 | { | |
155 | __for_each__(stuff, saver()); | |
156 | } | |
157 | ||
158 | The `save` function is generic and will work for all types of `stuff` | |
159 | regardless if it is a `person`, a `dog` or a whole `alternate_universe`. | |
160 | ||
161 | [heading Tip of the Iceberg] | |
162 | ||
163 | And... we've barely scratched the surface! You can compose and expand the | |
164 | data structures, remove elements from the structures, find specific data | |
165 | types, query the elements, filter out types for inspection, transform data | |
166 | structures, etc. What you've seen is just the tip of the iceberg. | |
167 | ||
168 | [endsect] | |
169 |