在 C++ 中计算任何结构的简单方法

Sah*_*hin 4 c++ struct

在 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。

Pas*_* By 5

通常,您需要使用静态反射来遍历字段并打印它们,而 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)

无需为自定义类型定义任何内容。

  • @okovko C++17 现在或多或少已经被假设了,它已经存在了三年,而且是一个未标记的问题。我认为您不明白应该如何使用它:它是一个库,最后一个代码片段是用户编写的所有代码。它实际上是一个函数调用,没有任何修改。 (2认同)

oko*_*vko -5

X 宏。

从维基百科链接“它[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 宏生成头文件。

  • 请注意,该链接示例中的内容比此答案中提供的内容更加完整。您应该考虑迁移它并使这个答案变得有用。 (2认同)