This code currently works and works:
string word="test,"; string::iterator it = word.begin(); for (; it != word.end(); it++) { if (!isalpha(*it)) { break; } else { *it = toupper(*it); } } word.erase(it, word.end());
I would like to make it more compact and readable:
- Compilation of existing standard C ++ algorithms (*)
- Run the loop only once
(*) I assume that combining existing algorithms makes my code more readable ...
Alternative solution
In addition to defining a custom transform_until algorithm, as suggested by jrok, it would be possible to define a custom iterator adapter that will iterate using the base iterator, but override the * () operator by changing the base link before returning it, Something like this :
template <typename Iterator, typename UnaryFunction = typename Iterator::value_type (*)(typename Iterator::value_type)> class sidefx_iterator: public std::iterator< typename std::forward_iterator_tag, typename std::iterator_traits<Iterator>::value_type, typename std::iterator_traits<Iterator>::difference_type, typename std::iterator_traits<Iterator>::pointer, typename std::iterator_traits<Iterator>::reference > { public: explicit sidefx_iterator(Iterator x, UnaryFunction fx) : current_(x), fx_(fx) {} typename Iterator::reference operator*() const { *current_ = fx_(*current_); return *current_; } typename Iterator::pointer operator->() const { return current_.operator->(); } Iterator& operator++() { return ++current_; } Iterator& operator++(int) { return current_++; } bool operator==(const sidefx_iterator<Iterator>& other) const { return current_ == other.current_; } bool operator==(const Iterator& other) const { return current_ == other; } bool operator!=(const sidefx_iterator<Iterator>& other) const { return current_ != other.current_; } bool operator!=(const Iterator& other) const { return current_ != other; } operator Iterator() const { return current_; } private: Iterator current_; UnaryFunction fx_; };
Of course, this is still very rude, but this should give an idea. Using the above adapter, I could write the following:
word.erase(std::find_if(it, it_end, std::not1(std::ref(::isalpha))), word.end());
with the following predefined (which can be simplified with the help of some template magic):
using TransformIterator = sidefx_iterator<typename std::string::iterator>; TransformIterator it(word.begin(), reinterpret_cast<typename std::string::value_type(*)(typename std::string::value_type)>(static_cast<int(*)(int)>(std::toupper))); TransformIterator it_end(word.end(), nullptr);
If the standard included such an adapter, I would use it because it would mean that it was perfect, but since it is not, I will probably keep my cycle as it is.
Such an adapter will allow you to reuse existing algorithms and mix them in different ways, which is impossible today, but may have drawbacks, which I probably don’t notice right now ...