简体   繁体   中英

Error indicator for std::iterator

I require a good substitute of std::string::npos for std::iterator which is has no relation to "next position after last valid address" ( end ).

Here is some background:

I have a method, where I need to find a certain value between two iterators and return iterator to a position NEXT to it (which may or may not be end ). It may happen that the method call is just a dummy and no action is required - in this case we simply return begin . I also need some indication that we were unable to find the value in question and here lies the crux of the matter: I don't know how to indicate it.

Here is a simple example:

class FinderClass
{
    public:
    FinderClass(uint8_t* byte): byte_(byte) {}
    std::iterator findEndOfFrame(std::iterator begin, std::iterator end) 
    {
        if (byte_ == NULL)
        {
            return byte_;
        }

        std::iterator result = std::find(begin, end, *byte_);

        if (result != end)
        {
            return ++result;
        }

        return ???;
    }

    private:
    uint8_t* byte_;
}

And somewhere down the line we may use it this way:

std::vector<uint8_t> myVector;
myVector.push_back(1);
myVector.push_back(2);
myVector.push_back(3);

uint8_t val = 2;
FinderClass finder(&val);
std::iterator result = finder.findEndOfFrame(myVector.begin(), myVector.end());

if (/* no error */)
{
    size_t len = std::distance(myVector.begin(), result);
}

In this case expected value of len is 2. Had val 's value was set to 3 expected result would be 3 and in case of 4 - error. 0 is only appropriate result if NULL was provided in constructor.

Please keep in mind, that this is a very simplified version of more complex architecture. In reality, I cannot simply check what was provided in the constructor, since it is separated by multiple abstraction layers and the real FinderClass implements an interface (and it's rather bizzare implementation, because in other implementation there is no option for NULL value for byte_ ). At the point where we call std::distance only a pointer to superclass is available.

I also do not use std::iterator itself, but a derivative class, but since std::iterator does not have some sort of npos value defined, I thought that there is probably a good reason for it, so I didn't do it myself either. I only used std::iterator in this example for simplicity.

So summing up : if end cannot be used to indicate an error, what should be used instead?

Several considerations:

  1. Exceptions are forbidden by team's coding standards
  2. Changes in interface (for example to change returned value to a pointer to std::iterator and simply return NULL on error) are considered "nuclear option, since it would require updating many other implementations and their unit tests.
  3. C++11 is not avaialable

Given the constraints, I would either return a boost::optional<Iterator> or a pointer to the next element (in which case you can return NULL if there is no next element).

optional is more expressive and safer of course, and requires less gerrymandering to convert the result back into an iterator.

Code using iterators is usually (and arguably should) pretty much agnostic of the actual type of iterator and instead just expects something with, roughly, incrementing and dereferencing operators. You don't specify whether the iterator has to be of a specific type or not, if not it's relatively easy to return a wrapper around an actual vector iterator which itself implements iterator behavior and also the extra functionality of having an invalid state. As said in the other answers though, going for optional<> is probably a better choice if possible as it clearly expresses what you actually mean. Anyway, pseudocode:

//template argument is the actual iterator type
template< class Iter >
class iterator : std::iterator< Iter::iterator_category,
                                Iter::value_type, .... >
{
public:
  enum tag { invalid_tag };

  //construct the invalid 'error' iterator
  iterator( invalid_tag ) :
    error( true )
  {}

  //construct actual iterator
  iterator( Iter iter ) :
    error( false ),
    iter( iter )
  {}

  //forward all functions
  reference operator * ()
  {
    assert( valid() );
    return *iter;
  }

  ....

  //check validity
  bool valid() const
  {
    return !error;
  }

private:
  bool error;
  Iter iter; 
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM