在 c++ 中引用任何结构的简单方法



在 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)>{});
}

它的工作原理是尝试使用ubiqs 初始化结构,这是一种具有任何内容的假转换运算符的类型。结合 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 宏生成头文件。

相关内容

  • 没有找到相关文章