Итерация через участников Struct и Class
Возможно ли, чтобы на С++ выполнялся итерации через Struct или Class, чтобы найти все его члены? Например, если у меня есть структура a и класс b:
struct a
{
int a;
int b;
int c;
}
class b
{
public:
int a;
int b;
private:
int c;
}
Можно ли их закодировать, чтобы сказать, что вы получите инструкцию печати: "Struct a имеет int с именем a, b, c" или "Class b имеет int с именем a, b, c"
Ответы
Ответ 1
Есть несколько способов сделать это, но вам нужно использовать некоторые макросы, чтобы определить или адаптировать структуру.
Вы можете использовать макрос REFLECTABLE
данный в этом ответе, чтобы определить структуру следующим образом:
struct A
{
REFLECTABLE
(
(int) a,
(int) b,
(int) c
)
};
И затем вы можете перебирать поля и печатать каждое значение следующим образом:
struct print_visitor
{
template<class FieldData>
void operator()(FieldData f)
{
std::cout << f.name() << "=" << f.get() << std::endl;
}
};
template<class T>
void print_fields(T & x)
{
visit_each(x, print_visitor());
}
A x;
print_fields(x);
Другой способ - адаптировать структуру как последовательность слияния (см. Документацию). Вот пример:
struct A
{
int a;
int b;
int c;
};
BOOST_FUSION_ADAPT_STRUCT
(
A,
(int, a)
(int, b)
(int, c)
)
Затем вы можете распечатать поля, используя это:
struct print_visitor
{
template<class Index, class C>
void operator()(Index, C & c)
{
std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call()
<< "="
<< boost:::fusion::at<Index>(c)
<< std::endl;
}
};
template<class C>
void print_fields(C & c)
{
typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), boost::ref(c), _1));
}
Ответ 2
Нет, это невозможно, потому что в С++ нет отражения.
Ответ 3
Если у вас есть члены одного типа (как в первом конкретном примере), которые вы хотите, чтобы оба (a) имели имена, и (b) были итерабельны, вы можете объединить массив с перечислением:
enum names { alice, bob, carl };
struct myStruct;
{
std::array<int, 3> members;
}
Затем вы можете оба
myStruct instance;
// iterate through them...
for (auto elem : instance.members)
{
// work with each element in sequence
}
// and call them by name, taking away the need to remember which element is the first, etc.
instance[bob] = 100;
Очевидно, что это не общее решение, но я нашел это полезным в своей работе.
Ответ 4
При условии, что ваши переменные-члены одного типа, вы можете сделать что-то вроде этого, что я украл из библиотеки GLM:
class Point
{
Point();// you must re-implement the default constructor if you need one
union
{
struct
{
double x;
double y;
double z;
}
std::array<double, 3> components;
}
}
По общему признанию, это не самое элегантное решение с точки зрения ремонтопригодности, вручную ведя подсчет количества переменных, которые у вас возникают, вызывает проблемы. Тем не менее, он будет работать без дополнительных библиотек или макросов и применим в большинстве ситуаций, в которых вы хотите такое поведение.
Объединения не поддерживают автоматически сгенерированные конструкторы по умолчанию, поэтому вам нужно написать тот, который сообщает объекту, как инициализировать объединение.
for (double component : point.components)
{
// do something
}