json icon indicating copy to clipboard operation
json copied to clipboard

Adding value_to support without including a Boost.JSON header

Open pdimov opened this issue 4 years ago • 7 comments

Similar to #549, but in the other direction; it should be possible to add value_to support without including a Boost.JSON header. A minimal example is in https://godbolt.org/z/WsG33GrMP. Unlike the value_from case, this compiles, and the only reason it doesn't link is that the signature of value_to does not match the documented one.

I'm not sure why it's necessary for value_to to disappear from overload resolution when T is a reference. value_to is not intended to be an overload set. References can be disallowed by a static_assert in the body.

Not taking value in order to disallow implicit conversions is I suppose legitimate, but it can be achieved by adding a deleted templated overload (https://godbolt.org/z/Tqonvj9bT).

pdimov avatar Apr 27 '21 14:04 pdimov

These declarations should have comments explaining the rationale for their signatures

vinniefalco avatar Apr 27 '21 14:04 vinniefalco

Have you considered not doing this and then they put their value_to into a header that includes their type and the boost.json header. That accomplishes the same thing of firewalling boost into a TU, no?

beached avatar Apr 27 '21 15:04 beached

The easiest fix here is to change the specification of value_to as follows:

template<class T> T value_to( value const& v );
template<class T, class U> T value_to( U const& v ) = delete;

pdimov avatar Apr 27 '21 16:04 pdimov

Unfortunately the changes in 31dd29523292e579497158b79703a4e7e3f56a3c (see the above PR) make value_to available to global ADL. For our codebase that posed a breaking change causing ambiguous calls (we have a value_to in our own namespace). Before the above changes that problem did not occur.

EikeAtOT avatar Jul 27 '22 15:07 EikeAtOT

Can you provide a minimal example of what exactly was broken? We only have value_to overloads in namespace boost::json.

grisumbras avatar Jul 27 '22 15:07 grisumbras

#include <type_traits>
#include <iostream>

namespace A
{
    class Arg {};

    template<typename T, typename U = std::enable_if<std::is_same_v<T, Arg>, void>>
    void foo(const T&)
    {
        std::cout << "A::foo" << std::endl;
    }

    void bar(const Arg&)
    {
        std::cout << "A::bar" << std::endl;
    }
}

namespace B::detail
{
    
inline void foo(const A::Arg&) 
{
    std::cout << "B::detail::foo" << std::endl;
}

inline void bar(const A::Arg&)
{
    std::cout << "B::detail::bar" << std::endl;
}

int client()
{
    A::Arg x;
    foo(x);

    bar(x);
    return 0;
}

}

int main(int argc, char** argv)
{
    B::detail::client();
    return 0;
}

In client() the unqualified call to foo is well defined pointing to B::detail::foo while the unqualified call to bar is regarded ambiguous. The namespace A is basically boost::json. So it looks like when the argument is inferred through a template the overload resolution has lower precedence than the nearest overload in the current enclosing namespace.

EikeAtOT avatar Jul 28 '22 08:07 EikeAtOT

@EikeAtOT can you provide an example with value_to that has become broken after 31dd29523292e579497158b79703a4e7e3f56a3c, so that I can evaluate if this is something we can and should fix?

grisumbras avatar Nov 16 '22 12:11 grisumbras