вывести все вложенные переменные-члены структуры с помощью вызова общей функции - PullRequest
0 голосов
/ 08 января 2019

Допустим, у меня есть следующее определение вложенной структуры,

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 голосов
/ 09 января 2019

Если бы был способ перебирать элементы данных простых структур, то решение вашей проблемы было бы простым. Модное слово для такой функциональности: (статическое) отражение . Однако язык 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);
}
0 голосов
/ 08 января 2019

Нет, нет. Это потребует рефлексии, и у C ++ пока нет хорошего способа сделать это.

Вы можете продвинуться немного дальше, если примете дополнительную готовность к предварительной обработке (см., Например, https://bytemaster.github.io/boost_reflect/index.html),, но вы не можете сделать это напрямую из существующего определения структуры без повторения членов.

...