Efficiently parse trivial files with boost spirit X3
<p>I am a novice with C++ and Boost Spirit X3. For my project I parse a geo-social graph from two files with the following structure with boost spirit X3 into a boost graph.</p>

<p>I have a working implementation. As I don't have any prior experience with the libraries I wonder what you think about the approach and if you'd recommend to take a different approach.</p>

<p>In the graph file there is one line for each edge.
While parsing the edges I have to create the nodes of the graph, in case the node has not been seen before. I use a semantic action that checks every time it encounters a node-id, if that node is already in the graph. Having read a full line I use a semantic action that then adds the edge.</p>

<p>In the location file there is one line per known location of a node at a given time. I store the first location that is known for a node in the graph (using a custom boost graph property).</p>

<p>I have to concrete questions but would be happy to receive any thoughts and suggestions:</p>

<ul>
<li>Is it ok to use nested semantic actions as I do for the graph file? Does this hurt performance?</li>
<li>Is it recommended to parse the whole file at once with Spirit X3 or should I parse every line individually with Spirit X3?</li>
</ul>

<p>Graph (denoting the edges in the graph)</p>

<pre><code>[user1]     [user2]
0           3
</code></pre>

<p>Locations</p>

<pre><code>[user]  [check-in time]         [latitude]      [longitude]     [location id]
0       2010-10-19T23:55:27Z    30.2359091167   -97.7951395833      22847
</code></pre>

<p>Spirit X3 parsing code</p>

<pre><code>// Parse the gowalla edge file
boost::spirit::istream_iterator file_iterator(edge_file), eof;

x3::phrase_parse(file_iterator, eof,
        // Begin grammar
        (
         *((x3::int_[add_vertex] &gt;&gt; x3::int_[add_vertex])[add_edge])
        ),
        // End grammar
        x3::space
        );

// Fail if we couldn't parse the whole edges file
if (file_iterator != eof) {
    std::cerr &lt;&lt; "Couldn't parse whole edges file" &lt;&lt; std::endl;
}

// Parse the gowalla location file
file_iterator = boost::spirit::istream_iterator(location_file);

x3::phrase_parse(file_iterator, eof,
        // Begin grammar
        (
         // vertex_id   time of checkin       latitude  longitude             location id
         *((x3::int_ &gt;&gt; x3::lexeme[*x3::graph] &gt;&gt; x3::double_ &gt;&gt; x3::double_)[add_location] &gt;&gt; x3::int_ &gt;&gt; x3::eol)
        ),
        // End grammar
        x3::blank
        );

// Fail if we couldn't parse the whole location file
if (file_iterator != eof) {
    std::cerr &lt;&lt; "Couldn't parse whole location file" &lt;&lt; std::endl;
}
</code></pre>

<p>Semantic actions called by X3</p>

<pre><code>// Lambda function that adds vertex to graph if not already added
auto add_vertex = [&amp;](auto&amp; ctx){
    // Return if the vertex is already known
    if (vertices.find(x3::_attr(ctx)) != vertices.end())    {
        return false;
    }

    // Otherwise add vertex to graph
    auto v = boost::add_vertex(g);

    // And add vertex descriptor to map
    vertices[x3::_attr(ctx)] = v;
};

// Lambda function that adds edge to graph
auto add_edge = [&amp;](auto&amp; ctx){
    // _attr(ctx) returns a boost fusion tuple
    auto attr = x3::_attr(ctx);

    // Add edge from the vertices returned from context
    boost::add_edge(vertices[fusion::at_c&lt;0&gt;(attr)],
            vertices[fusion::at_c&lt;1&gt;(attr)], g);
};

// Lambda function that adds locations to vertices in the graph
auto add_location = [&amp;](auto&amp; ctx){
    // _attr(ctx) returns a boost fusion tuple
    auto attr = x3::_attr(ctx);
    auto vertex_id = fusion::at_c&lt;0&gt;(attr);

    if (location_already_added.find(vertex_id) != location_already_added.end()) {
        // Exit, as we already stored the location for this vertex
        return true;
    }
    location_already_added.insert(vertex_id);

    // Test if vertex is in our graph
    // We are parsing locations from a different file than the graph,
    // so there might be inconsistencies
    if (vertices.find(vertex_id) == vertices.end()) {
        std::cerr &lt;&lt; "Tried to add location to vertex " &lt;&lt; vertex_id &lt;&lt; ", but this vertex is not in our graph" &lt;&lt; std::endl;
        return false;
    }

    auto vertex = vertices[vertex_id];

    // Add location to the vertex
    g[vertex].latitude = fusion::at_c&lt;2&gt;(attr);
    g[vertex].longitude = fusion::at_c&lt;3&gt;(attr);

    return true;
};
</code></pre>

<p>Boost graph</p>

<pre><code>struct vertex_property {
    double longitude;
    double latitude;
};

// Define our graph
// We use setS to enforce our graph not to become a multigraph
typedef boost::adjacency_list&lt;boost::setS, boost::vecS, boost::undirectedS, vertex_property, edge_property &gt; graph;
</code></pre>