views:

164

answers:

6

Hi, I have a class named Particle which has a std::set as a member. The class looks like this:

class Particle {
private:
    std::set<vtkIdType> cells;
    std::set<vtkIdType>::iterator ipc;

public:

    Particle() {};

    enum state {EXISTS = -1, SUCCESS = 0, ERROR = 1};

    state addCell(const vtkIdType cell);

    int numCells() { return static_cast<int>(cells.size()); }

    vtkIdType getFirstCell() { return (*(ipc = this->cells.begin()));}
    vtkIdType getNextCell() { return *(++ipc); }
    vtkIdType hasNextCell() { ++ipc; if (ipc == this->cells.end()) return false; --ipc; return true; }

    std::string getOutput();
};

I'm very unhappy with the getFirstCell(), getNextCell() and especially hasNextCell(), they exist because I don't want to expose the set itself. I had to use the way through ++ipc and --ipc because if((ipc+1) == this->cells.end()) gives a compiler error, ipc+1 seems to be the problem.

What would be a good way to encapsulate a set and access it? Also, is there a nice way to get rid of the getFirstCell() function?

Thanks in advance.

Edit: The code I posted is just an example of the classes structure. The "real" class contains more sets and other data that is not that important for this question (I assumed).

A: 

what you show doesn't do anything besides the three getters. encapsulate the set by making the operations that would use these getters part of the Particle class, then you won't need the getters at all: voila, encapsulated.

just somebody
The code I put in my question is not complete, there are more sets in it. In the end is only a data container that holds indices of cells, points, scalars ... and there are a lot of those particles stored in a vector. Therefore a can't encapsulate the processing functions in the particle.
DaClown
+4  A: 

The reason that ipc+1 does not work is that std::set only supports bidirectional iterators, which support operator++ and operator--; in order to use operator+, you need to use random access iterators.

One concern I see with your design is that your functions are named like accessors (getSuchAndSuch) but they also modify the internal state of the object (ipc is modified). This might lead to confusion.

One thing that you could try would be to use a few member functions that return iterators (a begin and end, for example), and allow users of your class to use iterators to access the internal set, while still encapsulating the set implementation.

You could return the set's iterator type or if you want more control or encapsulation, you could implement your own iterator class that wraps the set's iterator.

James McNellis
+1 for the "Get" functions setting state comment.
wheaties
One of the "exposing containers" questions (first i found, honest ;): http://stackoverflow.com/questions/1484052/should-i-expose-iterators-and-adaptor-methods-or-a-whole-container-in-c
Georg Fritzsche
I'll go with the exposed begin and end. But I'm not sure how a function that is named getNext... is not semantically allowed to alter the internal state. How else would a next function get to the next element. Btw I borrowed this syntax from Java iterators because I was out of ideas.
DaClown
+4  A: 

I'm not sure why you do not want to expose the set itself, but if it is because you want to ensure that the content of the set cannot be altered outside class Particle just return const iterators which makes the set "read-only", e.g.

typedef std::set<vtkIdType>::const_iterator CellIterator;
CellIterator beginCell() const { return this->cells.begin(); }
CellIterator endCell() const { return this->cells.end(); }
catchmeifyoutry
Thanks, I'll try this. Unfortunately you're the second who suggests this, therefore only an upvote :)
DaClown
No problem, as long as you notice the use of the const iterators instead of normal iterators for your specific problem ;)
catchmeifyoutry
`begin()` and `end()` is more idiomatic if it is clear from the class context what the iterators represent.
Georg Fritzsche
Sure, unless you will add more sets and iterators to the class, as DaClown mentions in another comment in this thread.
catchmeifyoutry
I cut the real code to this example, it is pretty much redundant, more sets, more getters ...Btw is it a problem to call the CellIterator just iterator (typedef std::set<vtkIdType>::const_iterator iterator;) ?
DaClown
Technically, no. But in that case I would actually argue for an idiomatic approach as gf did, since it might later become unclear that `Particle::iterator` is `const`. So I would suggest `typedef std::set<vtkIdType>::const_iterator const_iterator;` to avoid confusion later. And of course, with more iterators to return, it may become even more confusing what the type of `Particle::iterator` is.
catchmeifyoutry
Then I'll typedef an (const_)iterator for each set in Particle. Thanks.
DaClown
A: 

If you'd like to retain the general implementation you already have, but simply eliminate getFirstCell(), you could initialize ipc within the constructor. As stated above, judicious use of const and clear differentiation between accessors and mutators would clarify the interface. Additionally, if you were to implement iterators on your class, then I would recommend that addcell() return an iterator referencing the new cell, and instead throw an exception upon encountering an error.

Adam
Thanks for your reply. addCell doesn't return a reference because the whole point of using a set here is the uniqueness of the elements in it. I simply add a cell and check for an altered size of the set to determine if an element was added into the set. But of course, in combination with another reply and the replacement of the getters by begin and end functions I could return either an iterator and set.end() if it already exists. I'll look into this.
DaClown
+1  A: 

To prevent exposing set::iterator (to not promise to users more than needed) you can create a wrapper:

class Particle::iterator
{
public:
  iterator()
  {}
  iterator &operator++()
  {
    ++InternalIterator;
    return *this;
  }
  vtkIdType &operator*() const
  {
    return *InternalIterator;
  }
  ...//other functionality required by your iterator's contract in the same way
private:
  iterator(const std::set<vtkIdType> &internalIterator)
    :InternalIterator(internalIterator)
  {}
  std::set<vtkIdType>::iterator InternalIterator;
};

Particle::iterator Particle::GetBeginCell()
{
  return iterator(cells.begin());
}
Particle::iterator Particle::GetEndCell()
{
  return iterator(cells.end());
}

Thus you will get rid of internal iterator (because it's quite restrictive to be able to have only one iterator) and will get ability to use algorithms from STL on Particle's iterators.

Also boost::iterator_facade may be useful here...

maxim1000
A: 

The question is really what you're trying to accomplish here. Right now, your class seems (at least to me) to do more harm than good -- it makes working with the contents of the set more difficult instead of easier.

I'd look at Particle, and figure out whether it can provide something meaningful beyond some way of storing/accessing a bunch of cells. If it really is just a simple container, then you'd be a lot better of with something like typedef std::set<cell> Particle;, so the end user can use algorithms and such on this set just like they can any other. I'd only write a class to encapsulate that if you can really encapsulate something meaningful -- i.e. if your Particle class can embody some "knowledge" about particles so other code can work with a particle as a thing that's meaningful in itself.

Right now, your Particle is nothing but a container -- and it doesn't look like a particularly good container either. Unless you can really add something, you might be better off just using what's already there.

Jerry Coffin
Yes, it is a data structure with data in it as mentioned in other comments. I future posts I'll include all my code, cutting it to the essentials seems more to confuse then to increase readability.
DaClown