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

时间:2019-01-08 16:57:35

标签: c++ c++11 c++14 c++17

比方说,我有以下嵌套的struct定义,

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 <<"\t\t"<< a.b.c.r << std::endl;
    std::cout <<"\t\t"<< 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并自动找出其成员变量并以适当的缩进打印它们? (基本上,如果我们更改成员变量的类型或添加或删除成员变量,则所需的函数定义不应更改)

谢谢!

2 个答案:

答案 0 :(得分:1)

目前不,不。这将需要反思,而C ++尚无很好的方法。

如果您接受其他预处理功能(例如,参见https://bytemaster.github.io/boost_reflect/index.html),则可以做得更多,但是您不能直接从现有的结构定义中进行操作而无需重复成员。

答案 1 :(得分:1)

如果有一种方法可以遍历普通结构的数据成员,那么解决问题的方法将很容易。这类功能的流行词是(静态)反射。但是, C ++语言尚未提供反射功能(尚未)。

在某些特殊情况下(从C ++ 14开始),存在可以提供一定程度反射的黑客Boost.PFR (ex magic_get)中给出了概念证明。请注意,它尚未(尚未?)被批准为Boost的正式部分。基础技术也将在this conference talk中进行解释。

或者,您可以通过在结构上使用元信息对结构进行注释来构建自己的反射工具。在Boost.FusionBoost.Hana中可以找到Boost中的示例。其他方法使用外部代码生成工具(例如,参见siplasplas(已停产?)或Qt's Meta-Object System)。

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

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