Reputation: 1767
I have two classes for example
struct point{
dint data
};
class data{
...
public:
point left;
point right;
..... //more that 50 members of point
point some_other_point;
}example;
Is it possible use something like "for each point
in example
" in this situation?
Because now I need to modify many functions if I add one more point
to data
.
Or maybe, there is any other idea about it.
Upvotes: 0
Views: 79
Reputation: 299760
Yes, and there are two ways to do so:
Then the iteration logic is encapsulated in either of those classes and all the code just uses them.
Using the iterator class.
Pros:
for
and while
loopsCons:
Example:
class DataIterator;
class Data {
public:
friend class DataIterator;
Data(Point a, Point b, Point c): _one(a), _two(b), _three(c) {}
DataIterator begin();
DataIterator end();
private:
Point _one;
Point _two;
Point _three;
}; // class Data
class DataIterator:
public std::iterator<std::forward_iterator_tag, Point>
{
public:
struct BeginTag{};
struct EndTag{};
DataIterator(): _data(0), _member(0) {}
DataIterator(Data& data, BeginTag): _data(&data), _member(0) {}
DataIterator(Data& data, EndTag): _data(&data), _member(N) {}
reference operator*() const {
this->ensure_valid();
MemberPtr const ptr = Pointers[_member];
return _data->*ptr;
}
pointer operator->() const { return std::addressof(*(*this)); }
DataIterator& operator++() { this->ensure_valid(); ++_member; return *this; }
DataIterator operator++(int) { DataIterator tmp(*this); ++*this; return tmp; }
friend bool operator==(DataIterator const& left, DataIterator const& right) {
return left._data == right._data and left._member == right._member;
}
friend bool operator!=(DataIterator const& left, DataIterator const& right) {
return not (left == right);
}
private:
typedef Point Data::*MemberPtr;
static size_t const N = 3;
static MemberPtr const Pointers[N];
void ensure_valid() const { assert(_data and _member < N); }
Data* _data;
size_t _member;
}; // class DataIterator
//
// Implementation
//
DataIterator Data::begin() { return DataIterator(*this, DataIterator::BeginTag{}); }
DataIterator Data::end() { return DataIterator(*this, DataIterator::EndTag{}); }
size_t const DataIterator::N;
DataIterator::MemberPtr const DataIterator::Pointers[DataIterator::N] = {
&Data::_one, &Data::_two, &Data::_three
};
And in case you wonder: yes, it really works.
Using the visitation method, though, is easier.
Pros:
Cons:
Example:
class Data {
public:
Data(Point a, Point b, Point c): _one(a), _two(b), _three(c) {}
template <typename F>
void apply(F&& f) {
f(_one);
f(_two);
f(_three);
}
private:
Point _one;
Point _two;
Point _three;
}; // class Data
And of course, it works too.
Upvotes: 2
Reputation: 275330
You can write a for each function without modifying your example like this:
template<class Func>
void for_each_point(example& e, Func&& f){
f(e.pt1);
f(e.pt2);
f(e.blah);
....
f(e.last_pt);
}
and then call it like:
for_each_point(exam, [&](point & pt){
std::cout<<pt.data<<"\n";
});
or do whatever in the body.
This function could also be a member variable, if you prefer.
Changing tye point storage to an array or std::array
amd exposing begin
and end
or the array also works.
Finally, you could write a custom iterator that walks the points, but that is probably unwise.
Upvotes: 1
Reputation: 49976
You can convert your current fields into:
private:
point left;
// ..
public:
point& left() { return points[LEFT]; }
// ..
where points might be an array of points (as in other answers), and LEFT is a constant index into array. This should allow for a relatively quick and painless transition, you will only have to add ()
, and compiler will output errors where to apply fixes.
Then you can convert your code to iterate your point values.
Upvotes: 1
Reputation: 1639
Do it like this:
class data{
public:
enum POINTS {LEFT=0,RIGHT,SOME_OTHER_POINT};
std::array<point,50> points; // or just point points[50];
}example;
And use it like this:
example.points[data::LEFT]=point{};
Then you can iterate over points array with standard techniques.
Upvotes: 2
Reputation: 385104
No, you cannot enumerate members of a type, because C++ does not have the concept of reflection.
This is a common use case for an array, a vector or a map.
Upvotes: 4