Reputation: 103
(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
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
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