Reputation: 1509
I've got a class, memberlist, that contains a std::list
of class memberinfo. These represents the peers on a network.
I use the class to add some functionality to the list.
I want to expose some iterators (begin and end) so that outside code can loop through my internal list and read their data. However, I want to have two ways of doing this - one that includes an element for the localhost, and one that doesn't.
What's a good way to do this?
I could put the local node first, then have like begin(showlocal=false)
just give the second element instead of the first. Or someone suggested storing a pair of with the bool saying if it's local or not.
Any suggestions on a good way to do this? I'm not too great on advanced STL
stuff yet.
Upvotes: 1
Views: 139
Reputation: 82041
As I said in comment to your question, I think your first solution is reasonable. However, I'm not sure that giving a parameter to begin
is the best approach for discriminating the two cases. The major problem with this is that you cannot use your full collection (including the localhost member) as a range, meaning that you cannot use Boost.Range algorithms or the C++11 range-based for loop.
A simple solution would be to have two different member functions returning the appropriate range, as a pair of iterators. Boost.Range provides a sub_range
class, which seems rather appropriate (you want to return a sub-range of the list of members). Here is a sample code using this approach:
#include <boost/range.hpp>
#include <iostream>
#include <string>
#include <vector>
struct MemberInfo
{
std::string name;
};
class MemberList
{
public:
typedef std::vector<MemberInfo>::iterator iterator;
typedef std::vector<MemberInfo>::const_iterator const_iterator;
MemberList()
: members_{MemberInfo{"local"}, MemberInfo{"foo"}, MemberInfo{"bar"}}
{}
boost::sub_range<std::vector<MemberInfo>> all() // includes localhost
{
return boost::sub_range<std::vector<MemberInfo>>(
members_.begin(), members_.end());
}
boost::sub_range<std::vector<MemberInfo> const> all() const
{
return boost::sub_range<std::vector<MemberInfo> const>(
members_.begin(), members_.end());
}
boost::sub_range<std::vector<MemberInfo>> some() // excludes localhost
{
return boost::sub_range<std::vector<MemberInfo>>(
++members_.begin(), members_.end());
}
boost::sub_range<std::vector<MemberInfo> const> some() const
{
return boost::sub_range<std::vector<MemberInfo> const>(
++members_.begin(), members_.end());
}
private:
std::vector<MemberInfo> members_;
};
Now, you can use either all()
or some()
depending on whether you want to include local
or not, and both can be used as ranges:
int main()
{
MemberList ml;
for (MemberInfo mi : ml.all()) { std::cout << mi.name << '\n'; }
for (MemberInfo mi : ml.some()) { std::cout << mi.name << '\n'; }
}
And of course, you can still use iterators as usual:
std::find_if(ml.all().begin(), ml.all().end(), ...);
If you don't want to leak the fact that your members are stored in a std::vector
, you can use any_range
, which erases the underlying iterator type.
Upvotes: 0
Reputation: 25799
Personally I would approach this in a different way and have your memberinfo
have a way of telling you if it's local or not.
That way you're not specialising your collection class due to a specialisation of the contained objects. In fact you could just use a standard std::list<memberinfo>
.
E.g.
class memberinfo
{
bool IsLocal( ) const;
}
Then you would choose whether you're interested in local members or not while you're iterating through the contained objects.
E.g.
std::list<memberinfo>::iterator it;
std::list<memberinfo> list;
for ( it = list.begin() ; it != list.end() ; it++ )
{
if ( it->IsLocal() )
{
// blah blah blah
}
else
{
// dum dee dum dee
}
}
Upvotes: 4