使用泛型函数调用打印结构的所有嵌套成员变量



比方说,我有以下嵌套结构定义,

struct C {
int r;
char s;
};
struct B {
int p;
char q;
C c;
};
struct A {
int x;
int y;
B b;
char z;
};

我需要一个pretty_print(A &a)函数,它打印所有具有适当缩进的成员变量。我可以用以下方式来做:

#include <iostream>
struct C {
int r;
char s;
};
struct B {
int p;
char q;
C c;
};
struct A {
int x;
int y;
B b;
char z;
};
void pretty_print(A &a) {
std::cout << a.x << std::endl;
std::cout << a.y << std::endl;
std::cout <<"t"<< a.b.p << std::endl;
std::cout <<"t"<< a.b.q << std::endl;
std::cout <<"tt"<< a.b.c.r << std::endl;
std::cout <<"tt"<< a.b.c.s << std::endl;
std::cout << a.z << std::endl;
}
int main() {
A a{1, 2, {3, 'p', {4, 'k'}}, 'w'};
pretty_print(a);
}

有没有一种方法可以让一个成员(或非成员)函数(泛型函数,只写一次)接受结构a并自动计算出其成员变量,并用适当的缩进打印它们?(基本上,如果我们更改成员变量的类型或添加或删除成员变量,则所需的函数定义不应更改)

谢谢!

如果有一种方法可以迭代普通结构的数据成员,那么问题的解决方案就会很容易。这种功能的流行语是(静态)反射。然而,C++语言还没有提供反射功能。

在某些特殊情况下(从C++14开始),存在提供某种程度反射的黑客。Boost.PFR(ex magic_get)中给出了概念证明。请注意,它尚未(尚未?)被批准为Boost的正式组成部分。基本技术也在本次会议演讲中进行了解释。

或者,您可以通过在结构布局上用元信息注释结构来构建自己的反射工具。Boost中的例子可以在Boost.Fusion和Boost.Hana中找到。其他方法使用外部代码生成工具(例如,参见siplasplas(已停产?)或Qt的元对象系统)。

最后,在您的最小示例中,如果您可以将普通结构转换为元组,那么有一种简单的方法可以打印聚合。

static_assert(__cplusplus >= 201703L, "example written for C++17 or later");
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
using CMembers = std::tuple<int, char>;
struct C : CMembers {
using Members = CMembers;
using Members::Members;// inherit constructor
};
using BMembers = std::tuple<int, char, C>;
struct B : BMembers {
using Members = BMembers;
using Members::Members;// inherit constructor
};
using AMembers = std::tuple<int, int, B, char>;
struct A : AMembers {
using Members = AMembers;
using Members::Members;// inherit constructor
};

template<std::size_t... is, class... Ts, class F>
void foreach_tuple_element(
std::index_sequence<is...>,
const std::tuple<Ts...>& tuple,
F f
) {
( f( std::get<is>(tuple) ), ... );
}
template<class... Ts, class F>
void foreach_tuple_element(
const std::tuple<Ts...>& tuple,
F f
) {
foreach_tuple_element(std::index_sequence_for<Ts...>{}, tuple, f);
}

template<class T>
auto pretty_print(const T& x, std::string indent = "")
-> std::void_t<decltype(std::cout << indent << x << "n")>
{
std::cout << indent << x << "n";
}
template<class... Ts>
void pretty_print(const std::tuple<Ts...>& tuple, std::string indent = "") {
foreach_tuple_element(tuple, [indent] (auto&& x) {
pretty_print(x, indent + "t");
});
}
template<class T, class MemberTuple = typename T::Members>
void pretty_print(const T& x, std::string indent = "") {
pretty_print(static_cast<const MemberTuple&>(x), indent);
}

int main() {
A a{1, 2, {3, 'p', {4, 'k'}}, 'w'};
pretty_print(a);
}

目前没有。这需要反射,而C++还没有很好的方法来做到这一点。

如果你接受额外的预处理恐惧,你可以做得更进一步(例如。https://bytemaster.github.io/boost_reflect/index.html),但不能在不重复成员的情况下直接从现有结构定义执行此操作。

最新更新