在 C++ 中,有没有办法定义一个打印每个结构的函数?我的意思是,在我们的项目中,有很多结构体;我有时需要打印它们以进行调试。我希望它们可以轻松打印,而无需向每个结构添加友元 ostream 函数。
struct type{
string name;
int id;
}
type aType;
aType.name= sahin;
aType.id=10;
Run Code Online (Sandbox Code Playgroud)
该函数可以这样工作:
printStruct(aType);
{
name:sahin
id:10
}
Run Code Online (Sandbox Code Playgroud)
然后,如果可能的话,我需要一种打印结构向量的方法。类似于 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)>{});
}
Run Code Online (Sandbox Code Playgroud)
它的工作原理是尝试用ubiqs初始化结构体,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);
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这需要为每个元设置一个单独的函数。但是假设我们拥有我们需要的所有 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);
Run Code Online (Sandbox Code Playgroud)
现在,您所要做的就是将所有内容粘合在一起,使其自动打印任何聚合类型。
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);
}
Run Code Online (Sandbox Code Playgroud)
碰巧的是,我已经编写了一个库ezprint,它可以为您(以及更多)执行此操作。你得到的最终结果是
ez::println(s); // prints {42 42}
Run Code Online (Sandbox Code Playgroud)
无需为自定义类型定义任何内容。
oko*_*vko -5
从维基百科链接“它[X宏的使用]在现代C和C++中仍然有用”和“我们可以生成一个打印[结构体]变量的函数”。
#define X(type, name) /*deref or destructure*/name <<
std::cout << X_my_struct std::endl;
#undef X
Run Code Online (Sandbox Code Playgroud)
您可以想出一些方法来改进符号。最后一个 X 应该只是“名称”而不是“名称 <<”,这样您就可以写出X_my_struct << std::endl首选的名称。
这是一个简化的工作示例。您需要做更多的工作才能达到您喜欢的效果。
如果您有兴趣为一般情况编写 X 宏,可以这样做,但为了简单起见,我没有包含它。如果您有兴趣,请告诉我。例如,您有时需要结构定义X(type, name),有时X(type, name, value)需要结构定义。您可以使用N_ARGX(...)技巧来处理这个问题。
用户指出这需要重写您的结构/类。我不认为这完全准确。我在 M4 中编写了一个概念验证程序,用于从 C 头文件生成 X 宏。这可以作为编译阶段的一部分来完成,以便为所有感兴趣的结构的 X 宏生成头文件。