使用 boost::json,是否可以在不同的命名空间中声明两个单独的 value_from 转换?

With boost::json, Is it possible to declare two separate value_from conversions in different namespaces?

假设我在命名空间 Data 中有一个 class Customer。使用 boost,我知道我可以通过声明一个带有签名的函数将其转换为有效的 JSON:void tag_invoke( const value_from_tag&, value& jv, Customer const& c ).

但是,在 boost 文档中的所有示例中,此 tag_invoke 函数似乎必须驻留在 Data 命名空间中,即与 Customer 相同的命名空间。

我想知道是否有可能像下面这样:

namespace FileIO
{
void tag_invoke( const value_from_tag&, value& jv, Data::Customer const& c );
{
    jv = {
        { "id", c.id },
        { "name", c.name },
        { "late", c.late }
    };
}
}

namespace Network
{
void tag_invoke( const value_from_tag&, value& jv, Data::Customer const& c )
{
    jv = {
        { "id", c.id },
        { "late", c.late }
    };
}
}

换句话说,有两种不同的值转换实现,其中当前命名空间将决定使用哪一个。我没有成功让它工作,遇到各种“没有匹配调用 value_from_impl”错误。

如果有帮助,我的 CMake 设置是 DataFileIONetwork 都是单独的目标,其中 FileIONetwork 链接 to/depend Data,但彼此不链接,并且 Data 不依赖于其他库。

如有任何建议,我们将不胜感激

标记调用是 old-fashioned ADL 自定义点的更有用的实现。但它仍然大量使用 ADL。这意味着关联的命名空间被考虑用于重载解析。

请参阅 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf 了解基本原理。

您可以在其他命名空间中声明不相关的重载,但是,

  • 名称空间未关联且忽略重载的最佳情况
  • 最坏的情况是,该命名空间 关联,并且调用变得不明确。

In case it helps, my CMake setup is such that Data, FileIO, and Network are all separate targets, where both FileIO and Network are linked to/depend Data, but not to each other, and Data depends on no other libraries.

这听起来像是您有独立的来源(翻译单元)。您可以 在这些 TU 中本地定义不同的自定义点。 (双重)确保它们具有内部链接,这样您就不会冒违反 ODR 的风险。例如

namespace Data
{
     namespace { // anonymous namespace for file static (no external linkage)
        void tag_invoke( const value_from_tag&, value& jv, Customer const& c );
        {
          // ...
        };
     }
}

备选

或者,您可以使用带标签的包装器,有点像操纵器:

Live On Coliru

#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <iostream>
namespace json = boost::json;

template <typename T> struct UPPERCASE {
    T const& ref;
    UPPERCASE(T const& ref) : ref(ref) {}
};

namespace Data {
    struct Customer {
        int         id;
        std::string name;
        bool        late;

        friend void tag_invoke(json::value_from_tag,json::value&jv, Customer const& c) {
            jv = {{"id", c.id}, {"name", c.name}, {"late", c.late}};
        }
        friend void tag_invoke(json::value_from_tag, json::value& jv, UPPERCASE<Customer> const& c) {
            jv = {{"ID", c.ref.id}, {"NAME", c.ref.name}, {"LATE", c.ref.late}};
        }
    };
} // namespace Data

int main() {
    Data::Customer const c{1, "One", true};

    std::cout << json::value_from(c) << "\n";
    std::cout << json::value_from(UPPERCASE(c)) << "\n";
}

版画

{"id":1,"name":"One","late":true}
{"ID":1,"NAME":"One","LATE":true}