使用 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 设置是 Data
、FileIO
和 Network
都是单独的目标,其中 FileIO
和 Network
链接 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 );
{
// ...
};
}
}
备选
或者,您可以使用带标签的包装器,有点像操纵器:
#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}
假设我在命名空间 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 设置是 Data
、FileIO
和 Network
都是单独的目标,其中 FileIO
和 Network
链接 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 );
{
// ...
};
}
}
备选
或者,您可以使用带标签的包装器,有点像操纵器:
#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}