{"id":633,"date":"2017-03-29T10:55:33","date_gmt":"2017-03-29T09:55:33","guid":{"rendered":"http:\/\/www.martyndavis.com\/?p=633"},"modified":"2021-08-24T06:17:37","modified_gmt":"2021-08-24T05:17:37","slug":"getting-started-with-boost-multiindex","status":"publish","type":"post","link":"https:\/\/www.martyndavis.com\/?p=633","title":{"rendered":"Getting Started with Boost MultiIndex"},"content":{"rendered":"\n<p><em>Note: this is an introduction to \/ overview of Boost MultiIndex. It is not intended to be in-depth.<\/em><\/p>\n\n\n\n<p>So. Containers.&nbsp; 99% of the time, std::vector, out of all the choices you have in the STL, is the right choice. The next most obvious is <code>std::map<\/code> if you need to &#8220;index&#8221; access to data: for example if you store 1,000,000 phone numbers and you want to quickly find one given a name, a <code>std:map<\/code> will give you &#8220;indexed&#8221; access &#8211; you look up your data in O(log n) time (or potentially quicker if you&#8217;re using a <code>std::unordered_map<\/code>) as opposed to O(n) time for an unsorted std::vector. (Obviously these are theoretical complexities &#8211; see <a href=\"http:\/\/www.martyndavis.com\/?p=542\">a previous post<\/a> which advocates actual benchmarking, as processor cache and branch prediction can have a huge effect).<br><\/p>\n\n\n\n<!--more-->\n\n\n\n<p>But what if you want to treat your collection a bit more like a database table, with perhaps one &#8220;primary key&#8221; and several other indexed &#8220;fields&#8221;?<\/p>\n\n\n\n<p>Imagine you&#8217;ve got a struct like this:<\/p>\n\n\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstruct Person\n{\n    std::string  Name,\n    unsigned int Age,\n    std::string  PostCode \/\/ \"Zip\" code\n};\n<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>If you created a <code>std::map<\/code> of Person, &#8220;indexed&#8221; by, say, an arbitrary ID value, you&#8217;d be able to find them quickly by that ID. But what if you wanted then to find everyone over the age of 50? You might consider creating a secondary &#8220;index&#8221; structure, a std::multimap that keys on age and its data is the ID, i.e. the key into your main structure. This will work, of course, but you now have two data structures to worry about, and to keep synchronised. And control access to in the presence of multiple threads. If you then find you want other &#8220;indices&#8221; it just gets even more complicated.<\/p>\n\n\n\n<p>This is when you need Boost MultiIndex. <strong>It&#8217;s helpful to think of your collection (when implemented with Boost MultiIndex) as a database table.<\/strong> You can specify multiple indices (even using &#8220;composite keys&#8221; of more than one member) into one set of data, and query it in multiple ways. Hopefully the following code is self-explanatory, sufficiently to get you started.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;boost\/multi_index_container.hpp&gt;\n#include &lt;boost\/multi_index\/ordered_index.hpp&gt;\n#include &lt;boost\/multi_index\/member.hpp&gt;\n#include &lt;boost\/multi_index\/composite_key.hpp&gt;\n\n#include &lt;string&gt;\n#include &lt;iostream&gt;\n#include &lt;functional&gt;\n\nstruct Vehicle\n{\n    size_t       Id;\n    std::string  Make;\n    std::string  Model;\n    unsigned int YearMade;\n    bool         SingleCylinder;\n};\n\n\/\/ Index &quot;tags&quot; used by Boost MultiIndex.\n\/\/ Only purpose is to allow us to refer to indices\n\/\/ by name rather than a &quot;magic&quot; positional number\nstruct IdxPrimaryKey{};\nstruct IdxYearMade{};\nstruct IdxMakeModel{};\nstruct IdxSingleCylinder{};\n\ntemplate&lt;typename Tidx, typename Trecord&gt;\nvoid IterateOver(\n    const Tidx&amp; idx,\n    std::function&lt;void(const Trecord&amp; rec)&gt; func\n    )\n{\n    auto it = idx.begin();\n    while ( it != idx.end() )\n    {\n        func(*it++);\n    }\n}\n\nint main()\n{\n    namespace bmi = boost::multi_index;\n\n    \/\/ Definition of our &quot;database&quot; format:\n    bmi::multi_index_container&lt;\n        Vehicle,\n        bmi::indexed_by&lt;\n            \/\/ The &quot;primary key&quot;\n            bmi::ordered_unique&lt;\n                bmi::tag&lt;IdxPrimaryKey&gt;,\n                bmi::member&lt;\n                    Vehicle, size_t, &amp;Vehicle::Id\n                &gt;\n            &gt;,\n            \/\/ A simple key\n            bmi::ordered_non_unique&lt;\n                bmi::tag&lt;IdxYearMade&gt;,\n                bmi::member&lt;\n                    Vehicle, unsigned int, &amp;Vehicle::YearMade\n                &gt;\n            &gt;,\n            \/\/ Another simple key\n            bmi::ordered_non_unique&lt;\n                bmi::tag&lt;IdxSingleCylinder&gt;,\n                bmi::member&lt;\n                    Vehicle, bool, &amp;Vehicle::SingleCylinder\n                &gt;\n            &gt;,\n            \/\/ A composite key:\n            bmi::ordered_non_unique&lt;\n                bmi::tag&lt;IdxMakeModel&gt;,\n                bmi::composite_key&lt;\n                    Vehicle,\n                    bmi::member&lt;\n                        Vehicle, std::string, &amp;Vehicle::Make\n                    &gt;,\n                    bmi::member&lt;\n                        Vehicle, std::string, &amp;Vehicle::Model\n                    &gt;\n                &gt;\n            &gt;\n        &gt;\n    &gt; vehicles;\n\n    vehicles.insert( { 1, &quot;Vincent&quot;, &quot;Rapide&quot;,    1950, false } );\n    vehicles.insert( { 2, &quot;BSA&quot;,     &quot;Gold Star&quot;, 1958, true  } );\n    vehicles.insert( { 3, &quot;Triumph&quot;, &quot;Thruxton&quot;,  2010, false } );\n    vehicles.insert( { 4, &quot;Ariel&quot;,   &quot;Square 4&quot;,  1959, false } );\n    vehicles.insert( { 5, &quot;Brough&quot;,  &quot;Superior&quot;,  1925, false } );\n    vehicles.insert( { 6, &quot;Vincent&quot;, &quot;Comet&quot;,     1951, true  } );\n    vehicles.insert( { 7, &quot;Norton&quot;,  &quot;Commando&quot;,  1973, false } );\n\n    \/\/ Iterate using &quot;primary key&quot; (ID) as index\n    auto&amp; idxId = vehicles.get&lt;IdxPrimaryKey&gt;();\n    std::cout &lt;&lt; &quot;---------------------\\n&quot;\n              &lt;&lt; &quot;Ordered by ID:\\n&quot; &lt;&lt; std::endl;\n    IterateOver&lt;decltype(idxId), Vehicle&gt;(\n            idxId,\n            &#x5B;](const Vehicle&amp; vec\n        )\n        {\n            std::cout   &lt;&lt; vec.Id &lt;&lt; &quot;: &quot; &lt;&lt; vec.Make\n                        &lt;&lt; &quot; &quot; &lt;&lt; vec.Model &lt;&lt; std::endl;\n        }\n    );\n\n    std::cout &lt;&lt; &quot;---------------------\\n&quot;\n              &lt;&lt; &quot;Ordered by year of manufacture:\\n&quot; &lt;&lt; std::endl;\n    \/\/ Iterate using year of manufacture as our index:\n    auto&amp; idxSimple = vehicles.get&lt;IdxYearMade&gt;();\n    IterateOver&lt;decltype(idxSimple), Vehicle&gt;(\n            idxSimple,\n            &#x5B;](const Vehicle&amp; vec\n        )\n        {\n            std::cout   &lt;&lt; vec.YearMade &lt;&lt; &quot;: &quot; &lt;&lt; vec.Make\n                        &lt;&lt; &quot; &quot; &lt;&lt; vec.Model &lt;&lt; std::endl;\n        }\n    );\n\n    std::cout &lt;&lt; &quot;---------------------\\n&quot;\n              &lt;&lt; &quot;Ordered by marque then model:\\n&quot; &lt;&lt; std::endl;\n    \/\/ Iterate using our composite key (make\/model):\n    auto&amp; idxComposite = vehicles.get&lt;IdxMakeModel&gt;();\n    IterateOver&lt;decltype(idxComposite), Vehicle&gt;(\n        idxComposite, &#x5B;](const Vehicle&amp; vec)\n        {\n            std::cout &lt;&lt; vec.Make &lt;&lt; &quot; &quot;\n                      &lt;&lt; vec.Model &lt;&lt; std::endl;\n        }\n    );\n    std::cout &lt;&lt; &quot;---------------------&quot; &lt;&lt; std::endl;\n\n    {\n        \/\/ Find a specific item:\n        auto it = idxId.find(5); \/\/ should check result IRL!\n        std::cout &lt;&lt; &quot;Item 5 is &quot; &lt;&lt; it-&gt;Make &lt;&lt; &quot; &quot; &lt;&lt; it-&gt;Model\n                  &lt;&lt; std::endl;\n    }\n    std::cout &lt;&lt; &quot;---------------------\\n&quot;\n              &lt;&lt; &quot;Bikes made in the fifties:\\n&quot; &lt;&lt; std::endl;\n\n    {\n        \/\/ Find a range of items -- bikes made in the 50s:\n        auto it    = idxSimple.lower_bound( 1950 );\n        auto itEnd = idxSimple.upper_bound( 1959 );\n        while ( it != itEnd )\n        {\n            std::cout &lt;&lt; it-&gt;Make &lt;&lt; &quot; &quot; &lt;&lt; it-&gt;Model &lt;&lt; &quot; (&quot;\n                      &lt;&lt; it-&gt;YearMade &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;\n            ++it;\n        }\n    }\n    std::cout &lt;&lt; &quot;---------------------\\n&quot;\n              &lt;&lt; &quot;Single-cylinder bikes:\\n&quot; &lt;&lt; std::endl;\n    {\n        \/\/ Find a range of items -- bikes with just one pot\n        auto&amp; idxSinglePot = vehicles.get&lt;IdxSingleCylinder&gt;();\n        auto pr = idxSinglePot.equal_range( true );\n        auto it = pr.first;\n        auto itEnd = pr.second;\n        while ( it != itEnd )\n        {\n            std::cout &lt;&lt; it-&gt;Make &lt;&lt; &quot; &quot; &lt;&lt; it-&gt;Model &lt;&lt; &quot; (&quot;\n                      &lt;&lt; it-&gt;YearMade &lt;&lt; &quot;)&quot; &lt;&lt; std::endl;\n            ++it;\n        }\n    }\n    std::cout &lt;&lt; &quot;---------------------&quot; &lt;&lt; std::endl;\n\n    return 0;\n}    \n<\/pre><\/div>\n\n\n<p>You can compile it (assuming you have Boost installed) with a simple <code>g++ -std=c++14 -Wall -Wextra -pedantic main.cpp<\/code> (and assuming you saved the code as main.cpp)<\/p>\n\n\n\n<p>Output should be:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n---------------------\nOrdered by ID:\n\n1: Vincent Rapide\n2: BSA Gold Star\n3: Triumph Thruxton\n4: Ariel Square 4\n5: Brough Superior\n6: Vincent Comet\n7: Norton Commando\n---------------------\nOrdered by year of manufacture:\n\n1925: Brough Superior\n1950: Vincent Rapide\n1951: Vincent Comet\n1958: BSA Gold Star\n1959: Ariel Square 4\n1973: Norton Commando\n2010: Triumph Thruxton\n---------------------\nOrdered by marque then model:\n\nAriel Square 4\nBSA Gold Star\nBrough Superior\nNorton Commando\nTriumph Thruxton\nVincent Comet\nVincent Rapide\n---------------------\nItem 5 is Brough Superior\n---------------------\nBikes made in the fifties:\n\nVincent Rapide (1950)\nVincent Comet (1951)\nBSA Gold Star (1958)\nAriel Square 4 (1959)\n---------------------\nSingle-cylinder bikes:\n\nBSA Gold Star (1958)\nVincent Comet (1951)\n---------------------\n<\/pre><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Note: this is an introduction to \/ overview of Boost MultiIndex. It is not intended to be in-depth. So. Containers.&nbsp; 99% of the time, std::vector, out of all the choices you have in the STL, is the right choice. The next most obvious is std::map if you need to &#8220;index&#8221; access to data: for example &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.martyndavis.com\/?p=633\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Getting Started with Boost MultiIndex&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,1],"tags":[],"_links":{"self":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts\/633"}],"collection":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=633"}],"version-history":[{"count":24,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts\/633\/revisions"}],"predecessor-version":[{"id":826,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts\/633\/revisions\/826"}],"wp:attachment":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=633"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=633"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}