Trying something out of my comfort zone here, and attempting to implement a D-style range around std::string_view
.
Looking for any and all suggestions or improvements.
One of the things I'm not exactly happy about is having to declare difference_type
, pointer
and reference
just to fullfill the forward_iterator concept properly, yet not being able to actually use them.
I feel that this allowing me to initialize a vector straight from the iterator pair is worth the clunkiness, but I welcome any opinion on the subject, or suggestions on how to make it better.
Thanks!
#include <iterator>
#include <string_view>
template <typename CharT>
struct SplitStringIterator {
using difference_type = std::ptrdiff_t;
using value_type = std::basic_string_view<CharT>;
using pointer = std::basic_string_view<CharT>*;
using reference = std::basic_string_view<CharT>&;
using iterator_category = std::forward_iterator_tag;
SplitStringIterator(value_type str, CharT delim)
: remainder_(str), delim_(delim) {
advance_();
}
SplitStringIterator& operator++() {
advance_();
return *this;
}
SplitStringIterator operator++(int) {
SplitStringIterator other = *this;
advance_();
return other;
}
value_type operator*() const { return sub_str_; }
bool operator==(SplitStringIterator const& rhs) const {
return sub_str_.data() == rhs.sub_str_.data() &&
remainder_.data() == rhs.remainder_.data() && delim_ == delim_;
}
bool operator!=(SplitStringIterator const& rhs) const {
return !(*this == rhs);
}
private:
value_type sub_str_;
value_type remainder_;
const CharT delim_;
void advance_() {
auto delim_pos = remainder_.find(delim_);
if (delim_pos == value_type::npos) {
sub_str_ = remainder_;
remainder_ = value_type();
} else {
sub_str_ = value_type(&*remainder_.begin(), delim_pos);
remainder_.remove_prefix(delim_pos + 1);
}
}
};
template <typename CharT>
struct SplitString {
SplitString(std::basic_string_view<CharT> src, CharT d)
: source(src), delim(d) {}
std::basic_string_view<CharT> source;
CharT delim;
SplitStringIterator<CharT> begin() const {
return SplitStringIterator<CharT>(source, delim);
}
SplitStringIterator<CharT> end() const {
return SplitStringIterator<CharT>(std::basic_string_view<CharT>(), delim);
}
};
// Returns a lazy string splitter.
template <typename CharT>
SplitString<CharT> split(std::basic_string_view<CharT> str, CharT delim) {
return SplitString<CharT>(str, delim);
}
Usage example:
#include <vector>
#include <iostream>
void foo() {
std::string data = "aa:bb:cccc";
auto parts = split(std::string_view(data), ':');
for(auto p : parts) {
std::cout << p << std::endl;
}
//if we need random access:
std::vector<std::string_view> parts_indexable(parts.begin(), parts.end());
}