在 c++ 中,有没有办法定义一个打印每个结构的函数?我的意思是,在我们的项目中,有很多结构;我有时需要打印它们以进行调试。我希望它们易于打印,而无需在每个结构中添加友元 ostream 函数。
struct type{
string name;
int id;
}
type aType;
aType.name= "sahin";
aType.id=10;
该函数可以像这样工作:
printStruct(aType);
output:
{
name:sahin
id:10
}
然后,如果可能的话,我需要一种方法来打印结构的向量。 类似于js中的JSON.stringify。
通常,您需要静态反射来遍历字段并打印它们,而C++没有。
除非你非常努力地破解语言。
首先,您"扫描"结构以获取字段计数
struct ubiq
{
template <typename T>
operator T();
};
template <size_t>
using ubiq_t = ubiq;
template <typename T, typename... Ubiqs>
constexpr auto count_r(size_t& sz, int) -> std::void_t<decltype(T{Ubiqs{}...})>
{
sz = sizeof...(Ubiqs);
}
template <typename T, typename, typename... Ubiqs>
constexpr auto count_r(size_t& sz, float)
{
count_r<T, Ubiqs...>(sz, 0);
}
template<typename T, size_t... Is>
constexpr auto count(std::index_sequence<Is...>)
{
size_t sz;
count_r<T, ubiq_t<Is>...>(sz, 0);
return sz;
}
template<typename T>
constexpr auto count()
{
return count<T>(std::make_index_sequence<sizeof(T)>{});
}
它的工作原理是尝试使用ubiq
s 初始化结构,这是一种具有任何内容的假转换运算符的类型。结合 SFINAE,您可以检查需要多少个,这将是字段计数。
然后,要在不知道字段名称的情况下将字段提取为可用对象,我们可以使用结构化绑定。例如,对于两个字段,我们可以
template <typename T>
inline auto as_tuple(T&& t, std::integral_constant<size_t, 2>)
{
auto& [x0, x1] = t;
return std::forward_as_tuple(x0, x1);
}
不幸的是,这需要为每个 arity 提供单独的函数。但是假设我们拥有我们需要的所有 arity,那么我们可以获取并使用字段作为
struct S { int x; std::string s; };
S s = {42, "42"};
auto tup = as_tuple(s, std::integral_constant<size_t, count<S>()>{});
std::cout << get<0>(tup) << ' ' << get<1>(tup);
现在,您所要做的就是将所有内容粘合在一起,使其自动打印任何聚合类型。
template<typename T>
void println(T&& t)
{
using rT = std::remove_reference_t<T>;
auto tup = as_tuple(t, std::integral_constant<size_t, count<rT>()>{});
std::apply([](auto&... xs) { ((std::cout << xs << ' '), ...); }, tup);
}
碰巧我已经写了一个库 ezprint,它为你(以及更多)做这件事。你得到的最终结果是
ez::println(s); // prints {42 42}
无需为自定义类型定义任何内容。
Boost.PFR被称为"magic get",也无需任何额外的辅助宏即可完成。
该方法看起来类似于 @Passer By 答案中的方法。
X 宏。
来自维基百科的链接"它[X宏的使用]在现代C和C++中仍然有用"和"我们可以生成一个打印[结构]变量的函数"。
#define X(type, name) /*deref or destructure*/name <<
std::cout << X_my_struct std::endl;
#undef X
你可以想办法改进符号。最后一个 X 应该只是"name"而不是"name <<",所以你可以写X_my_struct << std::endl
哪个是首选的。
这是一个简化的工作示例。你需要做更多的工作才能让它按照你喜欢的方式。
如果您有兴趣为一般情况编写 X 宏,这可以完成,但为了简单起见,我没有包含它。如果您有兴趣,请告诉我。例如,您有时需要结构定义X(type, name)
,有时需要X(type, name, value)
。您将使用X(...)
和N_ARG技巧来处理此问题。
一位用户指出,这需要重写你的结构/类。我不认为这是完全准确的。我在 M4 中编写了一个概念验证程序,从 C 头文件生成 X 宏。这可以作为编译阶段的一部分来完成,以便为所有感兴趣的结构的 X 宏生成头文件。