Печать значений всех полей в структуре С++
Рассмотрим простую структуру:
struct abc
{
int a;
char b;
}
Я получил некоторое значение в переменной, определенной как ее структура, и теперь я хочу напечатать ниже.
*a = [some value]
b = [some character]*
Каков наилучший способ добиться этого для произвольной структуры без необходимости писать функцию дампа... (...) для каждой из структуры, с которой я сталкиваюсь?
Ответы
Ответ 1
Кажется, вы уже нашли решение, но я немного расширю.
То, к чему вы обращаетесь, называется Reflection
, то есть способностью объекта описывать себя.
Большинство языков могут реализовать отражение благодаря метаданным. Например, в Python функции и атрибуты объекта хранятся в элементе словаря.
C++ не имеет никакой собственной системы отражения в отличие от С# или Java, которая предотвращает (например) этот вид автоматической печати/сериализации или десериализации.
Тем не менее, C++ имеет очень мощную поддержку метапрограммирования, которая позволяет нам (посредством использования шаблонов) эмулировать отражение (во время компиляции). Обычно это делается с помощью Boost.Fusion, библиотеки, предназначенной для перехода от времени компиляции к времени выполнения.
Как показано в примере по вашей ссылке, макрос BOOST_FUSION_ADAPT_STRUCT
позволяет вам взять стандартную struct
и дать ей необходимый интерфейс, который будет обрабатываться как Fusion.Sequence.
Другим примером может быть использование Fusion.Vector
или Fusion.Map
для хранения атрибутов класса, а затем предоставление этой последовательности для автоматических методов печати/сериализации/десериализации.
Однако у этой системы есть ограничение: метапрограммирование плохо сочетается с ОО-программированием.
struct Base { char a; }; // Adapt
struct Derived: Base { char b; }; // Adapt
void print(Base const& b) { boost::fusion::for_each<Base>(b, Print()); }
будет печатать только член Base
(здесь). a
При использовании полиморфизма вам необходимо использовать virtual
методы в той или иной точке :)
Ответ 2
Для этого вам нужно "отражение". Отражение не предоставляется изначально на С++ или только для минимальной информации (введите идентификаторы/имена).
Существуют библиотеки (например, CAMP), которые реализуют функции отражения, поэтому, если вам действительно нужно отражение, вы должны использовать его.
Ответ 3
Существует не один, не в С++. Жесткая удача.
Ответ 4
Компиляторы С++ не создают метаданные. Таким образом, нет никакого способа сделать такую вещь.
Ответ 5
Если вы используете С++ в .NET, вы можете использовать System.Reflection, чтобы посмотреть на внутренности вашей структуры, Неуправляемый С++ редко, если вообще когда-либо, сохраняет такие метаданные об объектах.
Ответ 6
Это невозможно, потому что в С++ нет отражения. Однако, если вы не хотите сами писать код шаблона, вы всегда можете написать какой-нибудь инструмент, чтобы сделать это для вас.
Ответ 7
Это можно сделать с использованием шаблона дизайна посетителя:
struct MyStruct; // Forward declaration
struct Writer
{
virtual void operator(MyStruct& m) = 0;
};
struct MyStruct
{
void write(Writer& w)
{
// Invoke the writer method for this class.
w(*this);
}
int a;
double b;
};
struct File_Writer
{
File_Writer(ofstream& output)
: m_output(output)
{ ; }
void operator(MyStruct& ms)
{
output << ms.a << endl;
output << ms.b << endl;
}
ofstream& m_output;
};
struct Display_Writer
{
void operator(MyStruct& ms)
{
cout << " a: " << ms.a << endl;
cout << " b: " << ms.b << endl;
}
};
int main(void)
{
MyStruct ms;
ms.a = 5;
ms.b = 4.2;
// Write to the display, without changing methods in MyStruct
Display_Writer dw;
ms.write(dw);
// Write to a file without changing methods in MyStruct
ofstream my_file("me.txt");
File_Writer fw(my_file);
ms.write(fw); // Notice that only the writer object changed.
return 0;
}
Ответ 8
С С++ 17 (возможно, даже С++ 14) и некоторыми сумасшедшими русскими взломами - это можно сделать частично. То есть вы можете печатать значения типов по порядку, но не можете получить имена полей.
Соответствующая библиотека - Антоний Полухин "magic_get". В частности, он предлагает механизм итерации "для каждого поля", который использует шаблонную лямбду с auto
типом параметра. Пример:
struct simple {
int a;
char b;
short d;
};
simple x {42, 'a', 3};
std::stringstream ss;
boost::pfr::for_each_field(
x,
[&ss](auto&& val) {
ss << val << ' ';
}
);
Ответ перенесен из похожего/дублирующего вопроса - так как никто здесь не упомянул об этом.