c++ - 如何将 std::chrono::minutes 与 boost::json tag_invoke value_from 配合?

标签 c++ boost c++17 c++-chrono boost-json

我尝试使用 boost::jsons tag_invoke 在 json 之间存储/加载 std::chrono::minutes机制。虽然这对于我的自定义小 struct 效果很好,但我无法为 chrono 类型制定正确的语法。

我尝试了两个简单的版本:

void tag_invoke(boost::json::value_from_tag const&, boost::json::value &value, std::chrono::minutes const &minu);
std::chrono::minutes tag_invoke(boost::json::value_to_tag<std::chrono::minutes> const&, boost::json::value const &value);

namespace std::chrono {
void tag_invoke(boost::json::value_from_tag const&, boost::json::value &value, /*std::chrono::*/minutes const &minu);
/*std::chrono::*/minutes tag_invoke(boost::json::value_to_tag</*std::chrono::*/minutes> const&, boost::json::value const &value);
}

两个版本都会导致相同的编译器错误 (g++ 11.3),其中包括

/.../_deps/boost-src/libs/json/include/boost/json/value_from.hpp:87: error: no matching function for call to ‘value_from_impl(std::chrono::duration<long int, std::ratio<60> >, std::remove_reference<boost::json::storage_ptr&>::type)’

这似乎是未发现/考虑我的重载的典型标记。

我想念这里的怪癖是什么?

最佳答案

您正确地推测需要将声明放入通过 ADL 关联的命名空间中。

boost::jsonstd::chrono 都适合我:

<强> 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";
}

输出:

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

也许您的标准库实现 std::chrono::minutes 是其他内容的 typedef(未在该确切的命名空间中声明)?我不确定标准是否真的允许这样做

概括!

无论您最终做什么,我建议不要特殊的特定比例。相反,一般支持所有持续时间!

<强> 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);
}

打印例如

{"seconds":3E2} true    0ns
{"seconds":3.15E2}  true    0ns
{"seconds":1.07105E5}   true    0ns
{"seconds":2E-2}    true    0ns

或者with -ffast-math :

{"seconds":3E2} true    0ns
{"seconds":3.15E2}  true    2.60209e-08ns
{"seconds":1.07105E5}   true    0ns
{"seconds":2E-2}    true    0ns

没有 float ?

要减少 FP 表示不精确的问题,请选择一个分辨率:http://coliru.stacked-crooked.com/a/c5b8e084b5d47f3f

更严格地说,您可以在重载中禁止浮点表示: http://coliru.stacked-crooked.com/a/caca13072524b0f9

关于c++ - 如何将 std::chrono::minutes 与 boost::json tag_invoke value_from 配合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75503884/

相关文章:

c++ - 复制 boost::shared_ptr

c++ - Boost.Log 同时记录到文件和标准输出?

c++ - 即使没有传入消息,Boost ASIO UDP 客户端 async_receive_from 也会调用处理程序

c++ - 检查两种类型是否具有可比性

c++ - #define 语句中的方括号

c++ - 为什么 `std::istream_iterator` 没有右值构造函数?

c++ - 将唯一 ID 映射到对象的数据结构

c++ - 在 clang++ 中按值怪异传递抽象对象

c++ - 使用&符号后跟下划线命名的变量是什么意思?

c++ - 新的 Constexpr 运算符