zeux / pugixml

Light-weight, simple and fast XML parser for C++ with XPath support
http://pugixml.org/
MIT License
4.01k stars 728 forks source link

pugi::xml_node and std::ranges, maybe a documentation request #613

Closed davidhunter22 closed 5 months ago

davidhunter22 commented 6 months ago

I see a number of comments about traversal and iteration of element in pugixml. So this is more an observation which I think should be in the documentation and I'm not sure most people realize. The following

    constexpr bool b = std::ranges::range<pugi::xml_node>;

Is true! I am of course assuming that this is by design, but it may not be obvious to people what the implications are. This means that nodes and less usefully attributes are ranges and can be used as ranges. So

    pugi::xml_node node = ... // Get a node from somewhere

    auto foo_child_elements = 
       node | 
       std::views::filter( []( auto node ) { return node.type( )== pugi::node_element && pugi_node.name( ) == "Foo"; } ) |
       std::ranges::to<std::vector>( );

should work. We use "node as ranges" a lot and it greatly simplifies our code.

For another example consider the following. This creates a range of siblings using next_sibling which is not a range

std::generator<pugi::xml_node> dom_siblings( pugi::xml_node dom_node )
{
    for( auto dom_sibling { dom_node.next_sibling( ) }; dom_sibling != nullptr; dom_sibling = dom_sibling.next_sibling( ) )
    {
        co_yield dom_sibling;
    }
}
davidhunter22 commented 6 months ago

Oh I forgot to add, a traversal of all elements would look like


std::generator<pugi::xml_node> traverse_gen( pugi::xml_node node )
{
    co_yield node;

    for( auto child : node )
    {
        co_yield std::ranges::elements_of( traverse_gen( child ) );
    }
}
zeux commented 5 months ago

This is a side effect of ranged for support (https://pugixml.org/docs/manual.html#access.rangefor); accordingly, you can treat objects returned by .children(), .children(name) and .attributes() as ranges as well.

I'll adjust the documentation linked above to mention this; it might eventually make sense to add a separate documentation section but for now I can't even use std::ranges::to on clang/gcc provided by Ubuntu 24.04 which released last month as their C++23 support is incomplete.