You correctly surmised you need to put the declaration in a namespace associated via ADL.
Both the boost::json
and std::chrono
work for me:
Live On Coliru
#include <boost/json/src.hpp> //for header-only
#include <chrono>
#include <iostream>
namespace json = boost::json;
using namespace std::chrono_literals;
using M = std::chrono::minutes;
namespace NS {
void tag_invoke(json::value_from_tag, json::value& v, M const& d) {
v = {{"minutes", d.count()}};
}
M tag_invoke(json::value_to_tag<M>, json::value const& v) {
return M(v.at("minutes").as_int64());
}
} // namespace NS
int main() {
auto jv = json::value_from(5min);
std::cout << jv << " " << std::boolalpha
<< (value_to<M>(jv) == 300s) << "\n";
}
Output:
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -o bj -DNS=boost::json
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -o sc -DNS=std::chrono
./bj
{"minutes":5} true
./sc
{"minutes":5} true
Perhaps with your standard library implementation std::chrono::minutes
is a typedef for something other (not declared in that exact namespace)? I'm not sure whether the standard allows for that actually
Generalize!
Regardless of what you end up doing, I recommend not special casing a particular ratio. Instead generically support all durations!
Live On Coliru
#include <boost/json/src.hpp> // for header-only
#include <chrono>
#include <iostream>
namespace json = boost::json;
using namespace std::chrono_literals;
namespace boost::json {
template <typename Rep, typename Ratio>
void tag_invoke(value_from_tag, value& v, std::chrono::duration<Rep, Ratio> const& d) {
v = {{"seconds", static_cast<double>(d / 1.0s)}};
}
template <typename Duration, typename = typename Duration::rep>
Duration tag_invoke(value_to_tag<Duration>, value const& v) {
return std::chrono::duration_cast<Duration>(v.at("seconds").as_double() * 1s);
}
} // namespace boost::json
template <typename D> void foo(D d) {
auto jv = json::value_from(d);
auto roundtrip = value_to<D>(jv);
auto delta = (roundtrip - d) / 1.0ns;
auto ok = std::abs(delta) < 0.000'1; // < 100 femtosecond
std::cout << jv << "\t" << ok << '\t' << delta << "ns\n";
}
int main() {
std::cout << std::boolalpha;
foo(5min);
foo(5.25min);
foo(30h - 15min + 5s);
foo(20ms);
}
Prints e.g.
{"seconds":3E2} true 0ns
{"seconds":3.15E2} true 0ns
{"seconds":1.07105E5} true 0ns
{"seconds":2E-2} true 0ns
Or with -ffast-math
:
{"seconds":3E2} true 0ns
{"seconds":3.15E2} true 2.60209e-08ns
{"seconds":1.07105E5} true 0ns
{"seconds":2E-2} true 0ns
No Floating Point?
To reduce the inexact FP representation issue, pick a resolution: http://coliru.stacked-crooked.com/a/c5b8e084b5d47f3f
More strictly, you can outlaw floating point representation in the overload: http://coliru.stacked-crooked.com/a/caca13072524b0f9