Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2024-12-07 16:16:34


Klemens Morgenstern wrote:
> Another example that I would be interested in is how one would hash a
> `boost::json::value`.

Good suggestion, thanks.

In theory, this is how one would do it:

namespace boost
{
namespace json
{

template<class Hash, class Flavor>
void tag_invoke( boost::hash2::hash_append_tag const&,
    Hash& h, Flavor const& f, boost::json::value const& v )
{
    boost::json::visit( [&](auto const& w){
        boost::hash2::hash_append( h, f, w ); }, v );
}

} // namespace json
} // namespace boost

Unfortunately though, this doesn't work as-is for subtle reasons, and I
wonder whether I need to change the tag_invoke signature somewhat
so that I can prevent these errors from happening.

The problem here is that boost::json::value is implicitly constructible from
many things, which makes the above tag_invoke overload also match
those many things in addition to json::value. (We want it to only match
json::value and nothing else.)

The correct way to write the tag_invoke overload today is

namespace boost
{
namespace json
{

template<class Hash, class Flavor, class V>
std::enable_if_t< std::is_same<V, boost::json::value>::value >
tag_invoke( boost::hash2::hash_append_tag const&,
    Hash& h, Flavor const& f, V const& v )
{
    boost::json::visit( [&](auto const& w){
        boost::hash2::hash_append( h, f, w ); }, v );
}

} // namespace json
} // namespace boost

The other subtlety here is that boost::json::object should be considered
an unordered range, rather than an ordinary range. It doesn't expose
a `hasher` typedef, which is the heuristic is_unordered_range uses, but
it already specializes is_unordered_range here:

https://github.com/boostorg/json/blob/7f0bceb81280df758c7b4a7cacf8779351ebcc53/include/boost/json/object.hpp#L1682-L1685

so we're covered.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk