Last active
August 18, 2016 19:15
-
-
Save FreeSlave/397a1e06f4343df914baa9ce11205648 to your computer and use it in GitHub Desktop.
C++ Splitter implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2016 Roman Chistokhodov | |
// Distributed under the Boost Software License, Version 1.0. | |
// http://www.boost.org/LICENSE_1_0.txt | |
#include <cstring> | |
#include <cstdlib> | |
#include <cassert> | |
#include <iostream> | |
#include <string> | |
#include <vector> | |
#include <algorithm> | |
#include <utility> | |
#include <iterator> | |
template<typename SourceIterator> | |
struct Splitter | |
{ | |
typedef typename std::iterator_traits<SourceIterator>::value_type SourceValueType; | |
Splitter(SourceIterator begin, SourceIterator end, const SourceValueType& delim) | |
: _begin(begin), _end(end), _delim(delim) {} | |
typedef std::pair<SourceIterator, SourceIterator> value_type; | |
struct iterator : public std::iterator<std::forward_iterator_tag, value_type> | |
{ | |
friend struct Splitter; | |
private: | |
iterator(SourceIterator begin, SourceIterator end, const Splitter* splitter) | |
: _range(begin, end), _splitter(splitter) { | |
_atEnd = begin == end && end == splitter->_end; | |
} | |
public: | |
const value_type& operator*() const { | |
return _range; | |
} | |
const value_type* operator->() const { | |
return &_range; | |
} | |
iterator& operator++() { | |
forward(); | |
return *this; | |
} | |
iterator operator++(int) { | |
iterator toReturn = *this; | |
forward(); | |
return toReturn; | |
} | |
bool operator==(const iterator& other) const { | |
return equal(other); | |
} | |
bool operator!=(const iterator& other) const { | |
return !equal(other); | |
} | |
private: | |
void forward() { | |
if (_atEnd) { | |
return; | |
} | |
if (_range.second != _splitter->_end) { | |
_range.first = ++_range.second; | |
_range.second = std::find(_range.first, _splitter->_end, _splitter->_delim); | |
} else { | |
_range.first = _range.second; | |
_atEnd = true; | |
} | |
} | |
bool equal(const iterator& other) const { | |
return this->_range == other._range && this->_atEnd == other._atEnd; | |
} | |
std::pair<SourceIterator, SourceIterator> _range; | |
const Splitter* _splitter; | |
bool _atEnd; | |
}; | |
iterator begin() const { | |
return iterator(_begin, std::find(_begin, _end, _delim), this); | |
} | |
iterator end() const { | |
return iterator(_end, _end, this); | |
} | |
private: | |
SourceIterator _begin; | |
SourceIterator _end; | |
SourceValueType _delim; | |
}; | |
template<typename SourceIterator> | |
std::vector<std::string> applySplitter(SourceIterator begin, SourceIterator end) | |
{ | |
typedef Splitter<SourceIterator> SplitterType; | |
SplitterType splitter(begin, end, ':'); | |
typename SplitterType::iterator it = splitter.begin(); | |
std::vector<std::string> toReturn; | |
while(it != splitter.end()) { | |
toReturn.push_back(std::string(it->first, it->second)); | |
++it; | |
} | |
return toReturn; | |
} | |
void splitter_concept_test() | |
{ | |
typedef Splitter<const char*> SplitterType; | |
const char* cstr = "one:two"; | |
SplitterType splitter(cstr, cstr + strlen(cstr), ':'); | |
assert(splitter.begin() == splitter.begin()); | |
assert(splitter.end() == splitter.end()); | |
assert(splitter.begin() != splitter.end()); | |
SplitterType::iterator it = splitter.begin(); | |
assert(std::string(it->first, it->second) == "one"); | |
assert(splitter.begin() == it++); | |
assert(splitter.begin() != it); | |
assert(std::string(it->first, it->second) == "two"); | |
assert(++it == splitter.end()); | |
} | |
void splitter_test() | |
{ | |
std::vector<std::string> vec; | |
vec.push_back("one"); | |
vec.push_back("two"); | |
vec.push_back("three"); | |
const char* cstr = "one:two:three"; | |
assert(applySplitter(cstr, cstr + strlen(cstr)) == vec); | |
std::string str(cstr); | |
assert(applySplitter(str.begin(), str.end()) == vec); | |
std::vector<std::string> singleItemVector; | |
singleItemVector.push_back("one"); | |
cstr = "one"; | |
assert(applySplitter(cstr, cstr + strlen(cstr)) == singleItemVector); | |
str = cstr; | |
assert(applySplitter(str.begin(), str.end()) == singleItemVector); | |
std::vector<std::string> emptyVec; | |
cstr = ""; | |
assert(applySplitter(cstr, cstr + strlen(cstr)) == emptyVec); | |
str = cstr; | |
assert(applySplitter(str.begin(), str.end()) == emptyVec); | |
std::vector<std::string> vecWithSpaces; | |
vecWithSpaces.push_back(""); | |
vecWithSpaces.push_back("one"); | |
vecWithSpaces.push_back(""); | |
vecWithSpaces.push_back("two"); | |
vecWithSpaces.push_back(""); | |
cstr = ":one::two:"; | |
assert(applySplitter(cstr, cstr + strlen(cstr)) == vecWithSpaces); | |
str = cstr; | |
assert(applySplitter(str.begin(), str.end()) == vecWithSpaces); | |
} | |
int main(int argc, char** argv) | |
{ | |
splitter_test(); | |
splitter_concept_test(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment