John Duffy
John Duffy

Reputation: 103

boost::json tag_invoke() "No suitable tag_invoke overload found for the type" compilation error

(macOS clang++ 15.0.0)

I'm trying to use tag_invoke() to convert between a boost::json::value and a std::chrono::time_point, as follows:

#include <chrono>
#include <iostream>
#include <sstream>
#include <string>

#include <boost/json.hpp>

#include "date.h"  // Howard Hinnant's date library, until <chrono> supports parse().


using Time = std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>;


Time tag_invoke(boost::json::value_to_tag<Time>&, const boost::json::value& jv)
{
    Time t;

    std::istringstream ss{value_to<std::string>(jv)};
    ss >> date::parse("%FT%T%Z", t);
  
    return t;
}


int main()
{
    boost::json::value jv = boost::json::parse(R"({"time": "1970-01-01T00:00:00.000000001Z"})");

    Time t = value_to<Time>(jv);

    std::cout << t.time_since_epoch().count() << '\n';

    return 0;
}

But this is generating a No suitable tag_invoke overload found for the type compile error.

The full error message is:

/usr/local/include/boost/json/detail/value_to.hpp:845:5: error: static assertion failed due to requirement '!std::is_same<std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<long long, std::ratio<1, 1000000000>>>, std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<long long, std::ratio<1, 1000000000>>>>::value': No suitable tag_invoke overload found for the type
    static_assert(
    ^
/usr/local/include/boost/json/value_to.hpp:96:20: note: in instantiation of function template specialization 'boost::json::detail::value_to_impl<std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<long long, std::ratio<1, 1000000000>>>, boost::json::detail::no_context>' requested here
    return detail::value_to_impl( cat(), value_to_tag<bare_T>(), jv, ctx );
                   ^
/usr/local/include/boost/json/value_to.hpp:148:12: note: in instantiation of function template specialization 'boost::json::value_to<std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<long long, std::ratio<1, 1000000000>>>, boost::json::detail::no_context>' requested here
    return value_to<T>( jv, detail::no_context() );
           ^
src/tag_invoke.cpp:29:14: note: in instantiation of function template specialization 'boost::json::value_to<std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<long long, std::ratio<1, 1000000000>>>>' requested here
    Time t = value_to<Time>(jv);

I have studied the documentation but I can't see an error in my tag_invoke() function.

If I wrap my code in a standard function Time parse_time(...){...} it works, but not using tag_invoke().

Any help/suggestions would be much appreciated.

Kind regards

Upvotes: 0

Views: 362

Answers (2)

Tom Krippes
Tom Krippes

Reputation: 31

I know that this may be a little bit too late, but I think I got a solution for your problem. I actually had quite a similar problem. When I was looking at the documentation of boost JSON, I found the following sentence:

We can define a conversion from the user-defined type to a value by defining an overload of tag_invoke in the same namespace.

So because your Time is actually defined in the std::chrono namespace, the tag_invoke function also needs to be in this namespace:

namespace std::chrono {

Time tag_invoke(boost::json::value_to_tag<Time>&, const boost::json::value& jv)
{
    Time t;

    std::istringstream ss{value_to<std::string>(jv)};
    ss >> date::parse("%FT%T%Z", t);
  
    return t;
}

} // namespace std::chrono

This worked for me, even though I had to be careful to not have name conflict due to nested namespaces.

Upvotes: 3

John Duffy
John Duffy

Reputation: 103

Wrapping tag_invoke() in a namespace works, but I'm not sure why.

An expert explanation would be very much appreciated.

namespace boost::json {
Time tag_invoke(value_to_tag<Time>&, const value& jv)
{
    Time t;

    std::istringstream ss{value_to<std::string>(jv.at("time"))};
    ss >> date::parse("%FT%T%Z", t);
  
    return t;
}
} // end namespace

Upvotes: 0

Related Questions