{"id":741,"date":"2009-12-14T08:04:28","date_gmt":"2009-12-14T16:04:28","guid":{"rendered":"http:\/\/boost-spirit.com\/home\/?page_id=741"},"modified":"2010-02-24T05:07:16","modified_gmt":"2010-02-24T13:07:16","slug":"the-magical-power-of-attributes-in-spirit-operators","status":"publish","type":"page","link":"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/","title":{"rendered":"The Magical Power of Attributes in Spirit &#8211; Operators"},"content":{"rendered":"<p>This is the second in a series of articles giving an introduction to the attribute handling in <em>Spirit<\/em>. It might be a good idea to have a look at the first part before\u00a0reading on as I\u2019m going to rely on what is described there (<a href=\"http:\/\/boost-spirit.com\/home\/?page_id=713\">The Magical Power of Attributes in Spirit \u2013 Primitives<\/a>).\u00a0 In this article I will talk about <em>Spirit\u2019s<\/em> operators. These are being used to combine other components into more complex expressions so as to build more powerful <em>Qi<\/em> parsers and <em>Karma<\/em> generators. We have four different types of operators in <em>Spirit<\/em>, each of which will be covered in more detail below.<\/p>\n<p>Each operator implements its own set of attribute propagation rules. These rules define the synthesized attributes of <em>Qi<\/em> expressions and the consumed attributes of <em>Karma<\/em> expressions. As we have seen already, it is very important to understand how the synthesized and consumed attributes are formed. Parsers and generators are most elegant and efficient if they are tightly integrated with your data structures, and learning to match parsers to attributes is most crucial to successfully and effectively use <em>Spirit<\/em>.<\/p>\n<p>This article covers\u00a0 the most important of <em>Spirits<\/em> operators only, so for a full list of all available operators and how the they synthesize or consume attributes please see the documentation sections <a href=\"http:\/\/boost-spirit.com\/home\/spirit2\/libs\/spirit\/doc\/html\/spirit\/qi\/quick_reference\/compound_attribute_rules.html\">Parser Compound Attribute Rules<\/a> and <a href=\"http:\/\/boost-spirit.com\/home\/spirit2\/libs\/spirit\/doc\/html\/spirit\/karma\/quick_reference\/compound_attribute_rules.html\">Generator Compound Attribute Rules<\/a>.<\/p>\n<blockquote><p>Throughout Spirit&#8217;s documentation and for the purpose of these articles we use a special notation to describe the attribute propagation rules for compound components. It is of the form:<\/p>\n<p><span style=\"font-family: Courier New;\">a: A, b: B, &#8230; \u2013&gt; (composite-expression): composite-attribute<\/span><\/p>\n<p><span style=\"font-family: Arial;\">where: <\/span><span style=\"font-family: Courier New;\">a, b, etc.<\/span> are the operands; <span style=\"font-family: Courier New;\">A, B, etc.<\/span> are the operand&#8217;s attribute types; <span style=\"font-family: Courier New;\">composite-expression<\/span> is the expression involving the operands; and <span style=\"font-family: Courier New;\">composite-attribute<\/span> is the resulting attribute type of the composite expression. For instance:<\/p>\n<p><span style=\"font-family: Courier New;\">a: A, b: B \u2013&gt; (a &gt;&gt; b): tuple&lt;A, B&gt;<\/span><\/p>\n<p>which reads as: given, <span style=\"font-family: Courier New;\">a<\/span> and <span style=\"font-family: Courier New;\">b<\/span> are components, and <span style=\"font-family: Courier New;\">A<\/span> is the type of the attribute of <span style=\"font-family: Courier New;\">a<\/span>, and <span style=\"font-family: Courier New;\">B<\/span> is the type of the attribute of <span style=\"font-family: Courier New;\">b<\/span>, then the type of the attribute of <span style=\"font-family: Courier New;\">a &gt;&gt; b<\/span> will be <span style=\"font-family: Courier New;\">tuple&lt;A, B&gt;<\/span>.<\/p><\/blockquote>\n<p>All examples shown here are based on the test functions introduced in the <a href=\"http:\/\/boost-spirit.com\/home\/?page_id=713\">first part of this article<\/a>.<\/p>\n<h4>The Optional Operator<\/h4>\n<p>The optional operator is the simplest of all operators used in <em>Spirit<\/em>. As the name implies, it is used to make some part (or the whole) of a <em>Spirit<\/em> expression optional. For <em>Qi<\/em> parsers the corresponding parts of the input are optional and the whole input will match even if those parts are missing. For <em>Karma<\/em> generators the corresponding data will be emitted only if it is present, therefore adapting the generated output to the available data at runtime. Spirit uses the unary minus (<span style=\"font-family: Courier New;\">&#8216;-&#8216;<\/span>) as the optional operator. Here are the attribute propagation rules for the optional operator:<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\" width=\"402\">\n<tbody>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Qi<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A\u00a0\u2013&gt; (-a): optional&lt;A&gt;<br \/>\na: Unused\u00a0\u2013&gt; (-a): Unused<\/span><\/td>\n<\/tr>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Karma<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A\u00a0\u2013&gt; (-a): optional&lt;A&gt;<br \/>\na: Unused\u00a0\u2013&gt; (-a): Unused<br \/>\n<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <span style=\"font-family: Courier New;\">optional&lt;A&gt;<\/span> in the table above is a placeholder for <span style=\"font-family: Courier New;\">boost::optional&lt;A&gt;<\/span>. <code>Unused<\/code> stands for <code>unused_type<\/code>, which is just Spirits way of saying \u2018I don\u2019t care\u2019. In this context it is interpreted as if the component does not expose any attribute at all. The result of executing an optional parser expression will be either a non-initialized <span style=\"font-family: Courier New;\">boost::optional&lt;A&gt;<\/span> if the embedded parser did not match, or it will hold its returned attribute if it did match. An optional generator expression will emit the supplied attribute only if the <span style=\"font-family: Courier New;\">boost::optional&lt;A&gt;<\/span> has been properly initialized, otherwise no output will be generated.<\/p>\n<p>The following examples parse an optional integer showing it succeeds even if no integer is found in the input. The examples also demonstrates the generation of an optional integer, again, always succeeding, even if no integer is passed to the generator.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nassert(test_parser(-qi::int_, &quot;&quot;, boost::optional&lt;int&gt;()));\r\nassert(test_parser(-qi::int_, &quot;1234&quot;, boost::optional&lt;int&gt;(1234)));\r\nassert(test_generator(-karma::int_, boost::optional&lt;int&gt;(), &quot;&quot;)));\r\nassert(test_generator(-karma::int_, boost::optional&lt;int&gt;(1234), &quot;1234&quot;)));\r\nassert(test_generator(-karma::int_, 1234, &quot;1234&quot;)));\r\n<\/pre>\n<p>As the last line shows, even if the consumed attribute of the <em>Karma<\/em> operator is a <span style=\"font-family: Courier New;\">boost::optional<\/span>, it is still compatible with any non-optional attribute as long as it is convertible to the consumed attribute of the embedded generator.<\/p>\n<h4>Repetition Operators<\/h4>\n<p><em>Spirit<\/em> has several repetition operators, such as the Kleene star (unary <span style=\"font-family: Courier New;\">&#8216;*&#8217;<\/span>), the plus (unary <span style=\"font-family: Courier New;\">&#8216;+&#8217;<\/span>) operator, and lists (<span style=\"font-family: Courier New;\">&#8216;%&#8217;<\/span>), etc.. All of them are similar as they handle several elements of the same type. In terms of attribute handling, they are all equivalent. The following table shows the attribute rules based on those for the Kleene star operator, but all others are similar.<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\" width=\"402\">\n<tbody>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Qi<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A\u00a0\u2013&gt; (*a): vector&lt;A&gt;<br \/>\na: Unused\u00a0\u2013&gt; (*a): Unused<\/span><\/td>\n<\/tr>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Karma<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A\u00a0\u2013&gt; (*a): <span style=\"font-family: Courier New;\">vector<\/span>&lt;A&gt;<br \/>\na: Unused \u2013&gt; (*a): Unused<br \/>\n<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <span style=\"font-family: Courier New;\">vector&lt;A&gt;<\/span> is used as a placeholder for any standard container (it is possible to adapt your own data structures). <code>Unused<\/code> stands for <code>unused_type<\/code>. In short, repetitive parsers directly fill any supplied standard container as long as the value type of the container is compatible with the synthesized attribute of the embedded parser. Likewise, repetitive generators directly use the elements of any supplied standard container, one element for each invocation of the embedded generator.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nstd::vector&lt;char&gt; v;\r\nv.push_back('a'); v.push_back('b'); v.push_back('c');\r\nassert(test_parser(*qi::char_, &quot;abc&quot;, v));\r\nassert(test_generator(*karma::char_, v, &quot;abc&quot;)));\r\n<\/pre>\n<h4>Sequence Operators<\/h4>\n<p>Sequences are arguably the most powerful and useful of all operators but, at the same time, their attribute handling is the most difficult to fully understand. Sequence operators are used to build, well, sequences of other components. In order to build a sequence in <em>Qi<\/em> we use the <span style=\"font-family: Courier New;\">&#8216;&gt;&gt;&#8217;<\/span> operator. In <em>Karma<\/em> the <span style=\"font-family: Courier New;\">&#8216;&lt;&lt;&#8216;<\/span> operator is used. This is probably not surprising as it corresponds to the conventions used for C++ streams, where those operators are used for input (<span style=\"font-family: Courier New;\">&#8216;&gt;&gt;&#8217;<\/span>) and output (<span style=\"font-family: Courier New;\">&#8216;&lt;&lt;&#8216;<\/span>) as well. Here are the main attribute propagation rules forsequences:<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\" width=\"402\">\n<tbody>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Qi<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A, b: B\u00a0\u2013&gt; (a &gt;&gt; b): tuple&lt;A, B&gt;<br \/>\na: A, b: Unused\u00a0\u2013&gt; (a &gt;&gt; b): A<\/span><\/td>\n<\/tr>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Karma<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A, b: B\u00a0\u2013&gt; (a &lt;&lt; b): tuple&lt;A, B&gt;<br \/>\na: A, b: Unused\u00a0\u2013&gt; (a &lt;&lt; b): A<br \/>\n<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Again, the <span style=\"font-family: Courier New;\">tuple&lt;A, B, \u2026&gt;<\/span> in the table above is used as a placeholder only. The notation stands for <em>any <a href=\"http:\/\/www.boost.org\/doc\/libs\/1_41_0\/libs\/fusion\/doc\/html\/index.html\">Boost.Fusion<\/a> sequence<\/em> holding elements of types <code>A<\/code>, <code>B<\/code>, &#8230; etc. <code>Unused<\/code> stands for <code>unused_type<\/code>.<\/p>\n<blockquote><p>Sequences in <em>Boost.Fusion<\/em> are a generalization of tuples, which includes <em>Fusion\u2019s<\/em> own data structures (such as <span style=\"font-family: Courier New;\"><a href=\"http:\/\/www.boost.org\/doc\/libs\/1_41_0\/libs\/fusion\/doc\/html\/fusion\/container\/vector.html\">fusion::vector<\/a><\/span>, <span style=\"font-family: Courier New;\"><a href=\"http:\/\/www.boost.org\/doc\/libs\/1_41_0\/libs\/fusion\/doc\/html\/fusion\/container\/list.html\">fusion::list<\/a><\/span>, etc.), Fusion\u2019s <a href=\"http:\/\/www.boost.org\/doc\/libs\/1_41_0\/libs\/fusion\/doc\/html\/fusion\/view.html\">views<\/a>, <span style=\"font-family: Courier New;\">std::pair<\/span>, <span style=\"font-family: Courier New;\"><a href=\"http:\/\/www.boost.org\/doc\/libs\/1_41_0\/doc\/html\/array.html\">boost::array<\/a><\/span>, and any C++ struct (although this requires to use the macro <span style=\"font-family: Courier New;\"><a href=\"http:\/\/www.boost.org\/doc\/libs\/1_41_0\/libs\/fusion\/doc\/html\/fusion\/adapted\/adapt_struct.html\">BOOST_FUSION_ADAPT_STRUCT<\/a><\/span>). Especially the ability to \u2018map\u2019 a plain C++ struct onto a <em>Spirit<\/em> component sequence is a very powerful concept enabling tight integration of parsing and generation with your own (arbitrary) data structures.<\/p><\/blockquote>\n<p>The following example parses two comma separated <span style=\"font-family: Courier New;\">double<\/span> numbers and outputs those in reverse order:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nassert(test_parser(qi::double_ &gt;&gt; ',' &gt;&gt; qi::double_,\r\n    &quot;1.0,2.0&quot;, std::make_pair(1.0, 2.0)));\r\nassert(test_generator(karma::double_ &lt;&lt; ',' &lt;&lt; karma::double_,\r\n    std::make_pair(2.0, 1.0), &quot;2.0,1.0&quot;)));\r\n<\/pre>\n<p>We use a <span style=\"font-family: Courier New;\">std::pair&lt;double, double&gt;<\/span> as the attribute for our sequence (<span style=\"font-family: Courier New;\">std::pair<\/span> perfectly conforms to the notion of a <em>Fusion<\/em> sequence). The literal <span style=\"font-family: Courier New;\">&#8216;,&#8217;<\/span> does not expose any attribute (<span style=\"font-family: Courier New;\">Unused<\/span>), which makes it \u2018disappear\u2019 from the attribute handling (see the related attribute propagation rules in the table above). As a result, the member variable <span style=\"font-family: Courier New;\">first<\/span> of the pair is used as the attribute for the first <span style=\"font-family: Courier New;\">double<\/span> in the sequence,\u00a0 while the pair\u2019s member variable <span style=\"font-family: Courier New;\">second <\/span>is the used as the attribute for the second <span style=\"font-family: Courier New;\">double<\/span> in the corresponding Spirit expression.<\/p>\n<p><em>Qi<\/em> and <em>Karma<\/em> expose a set of API functions usable with sequences. Very much like the functions of the <code>scanf<\/code> and <code>printf<\/code> families these API functions allow to pass the attributes for each of the elements of the sequence separately. Using the corresponding overload of <em>Qi&#8217;s<\/em> <span style=\"font-family: Courier New;\">parse()<\/span> or <em>Karma&#8217;s<\/em> <code>generate()<\/code> the expression above could be rewritten as:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\ndouble d1 = 0.0, d2 = 0.0;\r\nqi::parse(f, l, qi::double_ &gt;&gt; ',' &gt;&gt; qi::double_, d1, d2);\r\nkarma::generate(sink, karma::double_ &lt;&lt; ',' &lt;&lt; karma::double_, d2, d1);\r\n<\/pre>\n<p>where the first argument is used for the first <code>double_<\/code>, and the second argument is used for the second <code>double_<\/code>. This provides a clear and comfortable syntax, more similar to the placeholder based syntax as exposed by <code>printf<\/code> or <code>boost::format<\/code>.<\/p>\n<p>It is possible to pass a (standard) container as an attribute for a <em>Spirit<\/em> sequence if all elements of that sequence expose either the container or its value type as their attributes. So we could rewrite the example above by replacing the <span style=\"font-family: Courier New;\">std::pair&lt;double, double&gt;<\/span> with any standard container type, such as <span style=\"font-family: Courier New;\">std::vector&lt;double&gt;<\/span>. This feature has been added to ensure full semantic compatibility of repetitive constructs like <span style=\"font-family: Courier New;\">double_ % &#8216;,&#8217;<\/span> with the equivalent sequence expression <span style=\"font-family: Courier New;\">double &gt;&gt; *(&#8216;,&#8217; &gt;&gt; double_)<\/span>, but has been proven to be useful in general.<\/p>\n<p>We have seen in the first article about primitives how the special placeholder <span style=\"font-family: Courier New;\">_1<\/span> can be used in a semantic action to refer to the attribute of the expression it is attached to. But wait, there is more! If you attach a semantic action to a sequence, each of the attributes of the elements can be accessed using the corresponding placeholders <span style=\"font-family: Courier New;\">_2<\/span>, <span style=\"font-family: Courier New;\">_3<\/span>, etc.:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n(qi::double_ &gt;&gt; ',' &gt;&gt; qi::double_)\r\n    [ std::cout &lt;&lt; qi::_1 &lt;&lt; ',' &lt;&lt; qi::_2 ]\r\n(karma::double_ &lt;&lt; ',' &lt;&lt; karma::double_)\r\n    [ karma::_1 = 1.0, karma::_2 = 2.0 ]\r\n<\/pre>\n<p>The first expression will print the two matched doubles separated with a comma, while the second expression will emit &#8220;<span style=\"font-family: Courier New;\">1.0,2.0&#8243;<\/span>. This is consistent with the multi-attribute API functions mentioned above.<\/p>\n<h4>The Alternative Operator<\/h4>\n<p>Alternatives in <em>Spirit<\/em> are built using the <span style=\"font-family: Courier New;\">&#8216;|&#8217;<\/span> operator. Parser alternatives are used to specify different possible valid input formats to be matched alternatively. The result of an alternative match are attributes with possibly different types. Each of the alternatives may potentially expose its own attribute type. Generator alternatives are used to specify different output formats to apply depending on the type of the supplied attribute. As a logical consequence the attribute propagation rules for the alternative operator are:<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\" width=\"402\">\n<tbody>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Qi<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A, b: B\u00a0\u2013&gt; (a | b): variant&lt;A, B&gt;<br \/>\na: A, b: A\u00a0\u2013&gt; (a | b): A<br \/>\na: A, b: Unused\u00a0\u2013&gt; (a | b): optional&lt;A&gt;<\/span><\/td>\n<\/tr>\n<tr>\n<td width=\"75\" valign=\"top\"><em><span style=\"font-family: Courier New;\">Karma<\/span><\/em><\/td>\n<td width=\"325\" valign=\"top\"><span style=\"font-family: Courier New;\">a: A, b: B\u00a0\u2013&gt; (a | b): variant&lt;A, B&gt;<br \/>\na: A, b: Unused\u00a0\u2013&gt; (a | b): optional&lt;A&gt;<br \/>\n<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Here the <span style=\"font-family: Courier New;\">variant&lt;A, B&gt;<\/span> stands for a <span style=\"font-family: Courier New;\">boost::variant&lt;A, B&gt;<\/span> and the <span style=\"font-family: Courier New;\">optional&lt;A&gt;<\/span> is a placeholder for <span style=\"font-family: Courier New;\">boost::optional&lt;A&gt;<\/span>. Please note how <span style=\"font-family: Courier New;\">Unused<\/span> is handled in a special way. It disappears from the attribute handling again, but all other alternatives will get optional. If all alternatives expose the same attribute type, the overall attribute will be the same. Let us have a look at an example:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nusing boost::variant;\r\ntest_parser(qi::int_ | qi::bool_, &quot;1234&quot;, variant&lt;bool, int&gt;(1234));\r\ntest_parser(qi::int_ | qi::bool_, &quot;true&quot;, variant&lt;bool, int&gt;(true));\r\ntest_generator(karma::int_ | karma::string, 4321, &quot;4321&quot;));\r\ntest_generator(karma::int_ | karma::string, std::string(&quot;a&quot;), &quot;a&quot;));\r\n<\/pre>\n<p>The alternative parser simply fills the variant with the appropriate attribute type based on the matched input. The generator alternative has the additional feature of doing the selection of the output format based on the supplied attribute type: data driven format selection at work! The generator will choose the first matching alternative for the supplied attribute. You don\u2019t even have to pass a variant to the generator (although, you certainly can), any attribute compatible with any of the types of the alternatives can be used, causing the corresponding formatting expression to be chosen.<\/p>\n<h4>Conclusion<\/h4>\n<p>Building <em>Spirit<\/em> expressions is very similar to building normal C++ expressions: the types of the right hand side and the left hand side must be compatible. The difference is that for <em>Spirit<\/em> expressions you have to think about the attribute types of the parts of the expression, not the C++ types of those. Once you understand the attribute propagation rules of the different operators it will be a lot easier to build <em>Qi<\/em> parsers and <em>Karma<\/em> generators which are compatible with your own data types. Most of the time you should be able to integrate your data without having to resolve to semantics actions. This does not only make your code simpler and more readable, it has the additional benefit of being faster, both at compile time and at runtime.<\/p>\n<div class=\"sharedaddy sd-sharing-enabled\"><div class=\"robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing\"><h3 class=\"sd-title\">Share this:<\/h3><div class=\"sd-content\"><ul><li><a href=\"#\" class=\"sharing-anchor sd-button share-more\"><span>Share<\/span><\/a><\/li><li class=\"share-end\"><\/li><\/ul><div class=\"sharing-hidden\"><div class=\"inner\" style=\"display: none;\"><ul><li class=\"share-facebook\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-facebook-741\" class=\"share-facebook sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=facebook\" target=\"_blank\" title=\"Click to share on Facebook\" ><span>Facebook<\/span><\/a><\/li><li class=\"share-twitter\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-twitter-741\" class=\"share-twitter sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=twitter\" target=\"_blank\" title=\"Click to share on Twitter\" ><span>Twitter<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-pinterest\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-pinterest-741\" class=\"share-pinterest sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=pinterest\" target=\"_blank\" title=\"Click to share on Pinterest\" ><span>Pinterest<\/span><\/a><\/li><li class=\"share-linkedin\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-linkedin-741\" class=\"share-linkedin sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=linkedin\" target=\"_blank\" title=\"Click to share on LinkedIn\" ><span>LinkedIn<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-reddit\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-reddit sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=reddit\" target=\"_blank\" title=\"Click to share on Reddit\" ><span>Reddit<\/span><\/a><\/li><li class=\"share-tumblr\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-tumblr sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=tumblr\" target=\"_blank\" title=\"Click to share on Tumblr\" ><span>Tumblr<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-end\"><\/li><\/ul><\/div><\/div><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>This is the second in a series of articles giving an introduction to the attribute handling in Spirit. It might be a good idea to have a look at the first part before\u00a0reading on as I\u2019m going to rely on what is described there (The Magical Power of Attributes in Spirit \u2013 Primitives).\u00a0 In this [&hellip;]<\/p>\n<div class=\"sharedaddy sd-sharing-enabled\"><div class=\"robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing\"><h3 class=\"sd-title\">Share this:<\/h3><div class=\"sd-content\"><ul><li><a href=\"#\" class=\"sharing-anchor sd-button share-more\"><span>Share<\/span><\/a><\/li><li class=\"share-end\"><\/li><\/ul><div class=\"sharing-hidden\"><div class=\"inner\" style=\"display: none;\"><ul><li class=\"share-facebook\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-facebook-741\" class=\"share-facebook sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=facebook\" target=\"_blank\" title=\"Click to share on Facebook\" ><span>Facebook<\/span><\/a><\/li><li class=\"share-twitter\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-twitter-741\" class=\"share-twitter sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=twitter\" target=\"_blank\" title=\"Click to share on Twitter\" ><span>Twitter<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-pinterest\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-pinterest-741\" class=\"share-pinterest sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=pinterest\" target=\"_blank\" title=\"Click to share on Pinterest\" ><span>Pinterest<\/span><\/a><\/li><li class=\"share-linkedin\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-linkedin-741\" class=\"share-linkedin sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=linkedin\" target=\"_blank\" title=\"Click to share on LinkedIn\" ><span>LinkedIn<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-reddit\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-reddit sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=reddit\" target=\"_blank\" title=\"Click to share on Reddit\" ><span>Reddit<\/span><\/a><\/li><li class=\"share-tumblr\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-tumblr sd-button share-icon\" href=\"http:\/\/boost-spirit.com\/home\/articles\/attribute_handling\/the-magical-power-of-attributes-in-spirit-operators\/?share=tumblr\" target=\"_blank\" title=\"Click to share on Tumblr\" ><span>Tumblr<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-end\"><\/li><\/ul><\/div><\/div><\/div><\/div><\/div>","protected":false},"author":3,"featured_media":0,"parent":422,"menu_order":2,"comment_status":"open","ping_status":"open","template":"article-page.php","meta":{"_s2mail":"","spay_email":""},"jetpack_shortlink":"https:\/\/wp.me\/PIHdZ-bX","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/pages\/741"}],"collection":[{"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/comments?post=741"}],"version-history":[{"count":28,"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/pages\/741\/revisions"}],"predecessor-version":[{"id":759,"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/pages\/741\/revisions\/759"}],"up":[{"embeddable":true,"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/pages\/422"}],"wp:attachment":[{"href":"http:\/\/boost-spirit.com\/home\/wp-json\/wp\/v2\/media?parent=741"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}